Line data Source code
1 : /* Copyright (C) 2000-2012 by George Williams */
2 : /*
3 : * Redistribution and use in source and binary forms, with or without
4 : * modification, are permitted provided that the following conditions are met:
5 :
6 : * Redistributions of source code must retain the above copyright notice, this
7 : * list of conditions and the following disclaimer.
8 :
9 : * Redistributions in binary form must reproduce the above copyright notice,
10 : * this list of conditions and the following disclaimer in the documentation
11 : * and/or other materials provided with the distribution.
12 :
13 : * The name of the author may not be used to endorse or promote products
14 : * derived from this software without specific prior written permission.
15 :
16 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 : * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 : * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 : * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 : * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 : * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 : * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 : * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 : */
27 :
28 : #include <gprogress.h>
29 : #include <ggadget.h>
30 : #include <gwidget.h>
31 : #include <gresource.h>
32 : #include <ustring.h>
33 : #include <memory.h>
34 : #include <sys/time.h>
35 : #include "ggadgetP.h" /* For the font family names */
36 :
37 : typedef struct gprogress {
38 : struct timeval start_time; /* Don't pop up unless we're after this */
39 : struct timeval pause_time;
40 : unichar_t *line1;
41 : unichar_t *line2;
42 : int sofar;
43 : int tot;
44 : int16 stage, stages;
45 : int16 width;
46 : int16 l1width, l2width;
47 : int16 l1y, l2y, boxy;
48 : int16 last_amount;
49 : unsigned int aborted: 1;
50 : unsigned int visible: 1;
51 : unsigned int dying: 1;
52 : unsigned int paused: 1;
53 : unsigned int sawmap: 1;
54 : GWindow gw;
55 : GFont *font;
56 : struct gprogress *prev;
57 : } GProgress;
58 :
59 : static Color progress_background, progress_foreground;
60 : static Color progress_fillcol = 0xc0c0ff;
61 : static GFont *progress_font = NULL;
62 : static int progress_init = false;
63 :
64 : static GProgress *current;
65 :
66 0 : static void GProgressDisplay(void) {
67 0 : GDrawSetVisible(current->gw,true);
68 0 : current->visible = true;
69 0 : if ( current->prev!=NULL && current->prev->visible ) {
70 0 : GDrawSetVisible(current->prev->gw,false);
71 0 : current->prev->visible = false;
72 : }
73 0 : }
74 :
75 0 : static void GProgressTimeCheck() {
76 : struct timeval tv;
77 :
78 0 : if ( current==NULL || current->visible || current->dying || current->paused )
79 0 : return;
80 0 : gettimeofday(&tv,NULL);
81 0 : if ( tv.tv_sec>current->start_time.tv_sec ||
82 0 : (tv.tv_sec==current->start_time.tv_sec && tv.tv_usec>current->start_time.tv_usec )) {
83 0 : if ( current->tot>0 &&
84 0 : current->sofar+current->stage*current->tot>(9*current->stages*current->tot)/10 )
85 0 : return; /* If it's almost done, no point in making it visible */
86 0 : GProgressDisplay();
87 : }
88 : }
89 :
90 0 : static void GProgressDraw(GProgress *p,GWindow pixmap,GRect *rect) {
91 : GRect r, old;
92 : int width, amount;
93 :
94 0 : GDrawPushClip(pixmap,rect,&old);
95 0 : GDrawSetFont(pixmap,p->font);
96 0 : if ( p->line1!=NULL )
97 0 : GDrawDrawText(pixmap, (p->width-p->l1width)/2, p->l1y, p->line1, -1,
98 : progress_foreground );
99 0 : if ( p->line2!=NULL )
100 0 : GDrawDrawText(pixmap, (p->width-p->l2width)/2, p->l2y, p->line2, -1,
101 : progress_foreground );
102 :
103 0 : r.x = GDrawPointsToPixels(pixmap,10);
104 0 : r.y = p->boxy;
105 0 : r.height = r.x;
106 0 : width = p->width-2*r.x;
107 :
108 0 : if ( p->tot==0 )
109 0 : amount = 0;
110 : else
111 0 : amount = width * (p->stage*p->tot + p->sofar)/(p->stages*p->tot);
112 0 : if ( amount>0 ) {
113 0 : r.width = amount;
114 0 : GDrawFillRect(pixmap,&r,progress_fillcol);
115 0 : } else if ( p->tot==0 ) {
116 0 : r.width = width;
117 0 : GDrawSetStippled(pixmap,1,0,0);
118 0 : GDrawFillRect(pixmap,&r,progress_fillcol);
119 0 : GDrawSetStippled(pixmap,0,0,0);
120 : }
121 0 : r.width = width;
122 0 : GDrawDrawRect(pixmap,&r,progress_foreground);
123 0 : GDrawPopClip(pixmap,&old);
124 0 : }
125 :
126 0 : static int GProgressProcess(GProgress *p) {
127 : int width, amount;
128 : int tenpt;
129 :
130 0 : if ( !p->visible )
131 0 : GProgressTimeCheck();
132 :
133 0 : tenpt = GDrawPointsToPixels(p->gw,10);
134 0 : width = p->width-2*tenpt;
135 0 : if ( p->tot==0 )
136 0 : amount = 0;
137 : else
138 0 : amount = width * (p->stage*p->tot + p->sofar)/(p->stages*p->tot);
139 0 : if ( amount!=p->last_amount ) {
140 0 : if ( amount<p->last_amount || p->last_amount==0 )
141 0 : GDrawRequestExpose(p->gw,NULL,false);
142 : else {
143 : GRect r;
144 0 : r.height = tenpt-1;
145 0 : r.width = amount - p->last_amount;
146 0 : r.x = tenpt + p->last_amount;
147 0 : r.y = p->boxy+1;
148 0 : GDrawFillRect(p->gw,&r,progress_fillcol);
149 : }
150 0 : p->last_amount = amount;
151 : }
152 0 : GDrawProcessPendingEvents(NULL);
153 0 : return( !p->aborted );
154 : }
155 :
156 0 : static int progress_eh(GWindow gw, GEvent *event) {
157 0 : GProgress *p = GDrawGetUserData(gw);
158 :
159 0 : switch ( event->type ) {
160 : case et_destroy:
161 0 : free(p->line1);
162 0 : free(p->line2);
163 0 : free(p);
164 0 : break;
165 : case et_close:
166 0 : p->aborted = true;
167 0 : GDrawSetVisible(gw,false);
168 0 : break;
169 : case et_expose:
170 0 : GProgressDraw(p,gw,&event->u.expose.rect);
171 0 : break;
172 : case et_controlevent:
173 0 : if ( event->u.control.subtype == et_buttonactivate )
174 0 : p->aborted = true;
175 0 : break;
176 : case et_char:
177 0 : if ( (event->u.chr.state&ksm_control) && event->u.chr.chars[0]=='.' )
178 0 : p->aborted = true;
179 0 : break;
180 : case et_map:
181 0 : p->sawmap = true;
182 0 : break;
183 : }
184 0 : return( true );
185 : }
186 :
187 : /* ************************************************************************** */
188 : static struct resed progress_re[] = {
189 : {N_("Color|Foreground"), "Foreground", rt_color, &progress_foreground, N_("Text color for progress windows"), NULL, { 0 }, 0, 0 },
190 : {N_("Color|FillColor"), "FillColor", rt_color, &progress_fillcol, N_("Color used to draw the progress bar"), NULL, { 0 }, 0, 0 },
191 : {N_("Color|Background"), "Background", rt_color, &progress_background, N_("Background color for progress windows"), NULL, { 0 }, 0, 0 },
192 : RESED_EMPTY
193 : };
194 : static GResInfo progress_ri = {
195 : NULL, NULL, NULL,NULL,
196 : NULL, /* No box */
197 : &progress_font,
198 : NULL,
199 : progress_re,
200 : N_("Progress"),
201 : N_("Progress Bars"),
202 : "GProgress",
203 : "Gdraw",
204 : false,
205 : 0,
206 : NULL,
207 : GBOX_EMPTY,
208 : NULL,
209 : NULL,
210 : NULL
211 : };
212 :
213 0 : static void GProgressResInit(void) {
214 0 : if ( !progress_init ) {
215 0 : progress_foreground = GResourceFindColor("GProgress.Foreground",
216 : GDrawGetDefaultForeground(NULL));
217 0 : progress_background = GResourceFindColor("GProgress.Background",
218 : GDrawGetDefaultBackground(NULL));
219 0 : progress_fillcol = GResourceFindColor("GProgress.FillColor",
220 : progress_fillcol);
221 0 : progress_font = GResourceFindFont("GProgress.Font",NULL);
222 0 : progress_init = true;
223 : }
224 0 : }
225 :
226 119 : void GProgressStartIndicator(
227 : int delay, /* in tenths of seconds */
228 : const unichar_t *win_title, /* for the window decoration */
229 : const unichar_t *line1, /* First line of description */
230 : const unichar_t *line2, /* Second line */
231 : int tot, /* Number of sub-entities in the operation */
232 : int stages /* Number of stages, each processing tot sub-entities */
233 : ) {
234 : GProgress *new;
235 : FontRequest rq;
236 : GWindowAttrs wattrs;
237 : GWindow root;
238 : GGadgetData gd;
239 : int ld, as, ds;
240 : GRect pos;
241 : struct timeval tv;
242 : GTextInfo label;
243 :
244 119 : if ( screen_display==NULL )
245 238 : return;
246 :
247 0 : if ( !progress_init )
248 0 : GProgressResInit();
249 0 : new = calloc(1,sizeof(GProgress));
250 0 : new->line1 = u_copy(line1);
251 0 : new->line2 = u_copy(line2);
252 0 : new->tot = tot;
253 0 : new->stages = stages;
254 0 : new->prev = current;
255 :
256 0 : root = GDrawGetRoot(NULL);
257 0 : if ( progress_font == NULL ) {
258 0 : memset(&rq,'\0',sizeof(rq));
259 0 : rq.utf8_family_name = MONO_UI_FAMILIES;
260 0 : rq.point_size = 12;
261 0 : rq.weight = 400;
262 0 : progress_font = GDrawAttachFont(root,&rq);
263 : } else {
264 0 : GDrawSetFont(root, progress_font);
265 : }
266 0 : GDrawWindowFontMetrics(root,new->font = progress_font,&as,&ds,&ld);
267 :
268 0 : if ( new->line1!=NULL )
269 0 : new->l1width = GDrawGetTextWidth(root,new->line1,-1);
270 0 : if ( new->line2!=NULL )
271 0 : new->l2width = GDrawGetTextWidth(root,new->line2,-1);
272 0 : new->l1y = GDrawPointsToPixels(root,5) + as;
273 0 : new->l2y = new->l1y + as+ds;
274 0 : new->boxy = new->l2y + as+ds;
275 0 : pos.width = (new->l1width>new->l2width)?new->l1width:new->l2width;
276 0 : if ( pos.width<GDrawPointsToPixels(root,100) )
277 0 : pos.width = GDrawPointsToPixels(root,100);
278 0 : pos.width += 2 * GDrawPointsToPixels(root,10);
279 0 : pos.height = new->boxy + GDrawPointsToPixels(root,44);
280 0 : new->width = pos.width;
281 :
282 0 : memset(&wattrs,0,sizeof(wattrs));
283 0 : wattrs.mask = wam_events|wam_cursor|(win_title!=NULL?wam_wtitle:0)|
284 : wam_centered|wam_restrict|wam_redirect|wam_isdlg|wam_backcol;
285 0 : wattrs.event_masks = ~(1<<et_charup);
286 0 : wattrs.cursor = ct_watch;
287 0 : wattrs.window_title = u_copy(win_title);
288 0 : wattrs.centered = true;
289 0 : wattrs.restrict_input_to_me = true;
290 0 : wattrs.redirect_chars_to_me = true;
291 0 : wattrs.is_dlg = true;
292 0 : wattrs.redirect_from = NULL;
293 0 : wattrs.background_color = progress_background;
294 0 : pos.x = pos.y = 0;
295 0 : new->gw = GDrawCreateTopWindow(NULL,&pos,progress_eh,new,&wattrs);
296 0 : free((void *) wattrs.window_title);
297 :
298 0 : memset(&gd,'\0',sizeof(gd)); memset(&label,'\0',sizeof(label));
299 0 : gd.pos.width = GDrawPointsToPixels(new->gw,50);
300 0 : gd.pos.x = pos.width-gd.pos.width-10;
301 0 : gd.pos.y = pos.height-GDrawPointsToPixels(new->gw,29);
302 0 : gd.flags = gg_visible | gg_enabled | gg_pos_in_pixels | gg_pos_use0;
303 0 : gd.mnemonic = 'S';
304 0 : label.text = (unichar_t *) _("_Stop");
305 0 : label.text_is_1byte = true;
306 0 : label.text_in_resource = true;
307 0 : gd.label = &label;
308 0 : GButtonCreate( new->gw, &gd, NULL);
309 :
310 : /* If there's another progress indicator up, it will not move and ours */
311 : /* won't be visible if we have a delay, so force delay to 0 here */
312 0 : if ( current!=NULL ) delay = 0;
313 0 : gettimeofday(&tv,NULL);
314 0 : new->start_time = tv;
315 0 : new->start_time.tv_usec += (delay%10)*100000;
316 0 : new->start_time.tv_sec += delay/10;
317 0 : if ( new->start_time.tv_usec >= 1000000 ) {
318 0 : ++new->start_time.tv_sec;
319 0 : new->start_time.tv_usec -= 1000000;
320 : }
321 :
322 0 : current = new;
323 0 : GProgressTimeCheck();
324 : }
325 :
326 0 : void GProgressStartIndicatorR( int delay, int win_titler, int line1r, int line2r,
327 : int tot, int stages ) {
328 0 : GProgressStartIndicator(delay,
329 : GStringGetResource(win_titler,NULL),
330 : GStringGetResource(line1r,NULL),
331 : line2r==0?NULL:GStringGetResource(line2r,NULL),
332 : tot,stages);
333 0 : }
334 :
335 119 : void GProgressEndIndicator(void) {
336 119 : GProgress *old=current;
337 :
338 119 : if ( old==NULL )
339 238 : return;
340 0 : current = old->prev;
341 :
342 0 : old->dying = true;
343 : /* the X server sometimes crashes if we: */
344 : /* Create a window */
345 : /* map it */
346 : /* but destroy it before the server can send us the map notify event */
347 : /* next three lines seem to deal with it */
348 : /* unmapping the window also causes a crash */
349 0 : if ( old->visible ) while ( !old->sawmap ) {
350 0 : GDrawSync(NULL);
351 0 : GDrawProcessPendingEvents(NULL);
352 : }
353 0 : GDrawDestroyWindow(old->gw);
354 0 : GDrawSync(NULL);
355 0 : GDrawProcessPendingEvents(NULL);
356 : }
357 :
358 69 : void GProgressChangeLine1(const unichar_t *line1) {
359 69 : if ( current==NULL )
360 138 : return;
361 0 : free( current->line1 );
362 0 : current->line1 = u_copy(line1);
363 0 : if ( current->line1!=NULL ) {
364 0 : GDrawSetFont(current->gw,current->font);
365 0 : current->l1width = GDrawGetTextWidth(current->gw,current->line1,-1);
366 : }
367 0 : if ( current->visible )
368 0 : GDrawRequestExpose(current->gw,NULL,false);
369 : }
370 :
371 0 : void GProgressChangeLine1R(int line1r) {
372 0 : GProgressChangeLine1(GStringGetResource(line1r,NULL));
373 0 : }
374 :
375 120 : void GProgressChangeLine2(const unichar_t *line2) {
376 120 : if ( current==NULL )
377 240 : return;
378 0 : free( current->line2 );
379 0 : current->line2 = u_copy(line2);
380 0 : if ( current->line2!=NULL ) {
381 0 : GDrawSetFont(current->gw,current->font);
382 0 : current->l2width = GDrawGetTextWidth(current->gw,current->line2,-1);
383 : }
384 0 : if ( current->visible )
385 0 : GDrawRequestExpose(current->gw,NULL,false);
386 : }
387 :
388 0 : void GProgressChangeLine2R(int line2r) {
389 0 : GProgressChangeLine2(GStringGetResource(line2r,NULL));
390 0 : }
391 :
392 72 : void GProgressChangeTotal(int tot) {
393 72 : if ( current==NULL )
394 144 : return;
395 0 : current->tot = tot;
396 : }
397 :
398 113 : void GProgressChangeStages(int stages) {
399 113 : if ( current==NULL )
400 226 : return;
401 0 : if ( stages<=0 )
402 0 : stages = 1;
403 0 : current->stages = stages;
404 0 : if ( current->stage>=stages )
405 0 : current->stage = stages-1;
406 : }
407 :
408 67 : void GProgressEnableStop(int enabled) {
409 67 : if ( current==NULL )
410 134 : return;
411 0 : GGadgetSetEnabled(GWidgetGetControl(current->gw,0),enabled);
412 : }
413 :
414 216 : int GProgressNextStage(void) {
415 :
416 216 : if ( current==NULL )
417 216 : return(true);
418 0 : ++current->stage;
419 0 : current->sofar = 0;
420 0 : if ( current->stage>=current->stages )
421 0 : current->stage = current->stages-1;
422 0 : return( GProgressProcess(current));
423 : }
424 :
425 161602 : int GProgressNext(void) {
426 :
427 161602 : if ( current==NULL )
428 161602 : return(true);
429 0 : ++current->sofar;
430 0 : if ( current->sofar>=current->tot )
431 0 : current->sofar = current->tot-1;
432 0 : return( GProgressProcess(current));
433 : }
434 :
435 10 : int GProgressIncrementBy(int cnt) {
436 :
437 10 : if ( current==NULL )
438 10 : return(true);
439 0 : current->sofar += cnt;
440 0 : if ( current->sofar>=current->tot )
441 0 : current->sofar = current->tot-1;
442 0 : return( GProgressProcess(current));
443 : }
444 :
445 0 : int GProgressReset(void) {
446 :
447 0 : if ( current==NULL )
448 0 : return(true);
449 0 : current->sofar = 0;
450 0 : return( GProgressProcess(current));
451 : }
452 :
453 0 : void GProgressPauseTimer(void) {
454 0 : if ( current==NULL || current->visible || current->dying || current->paused )
455 0 : return;
456 0 : gettimeofday(¤t->pause_time,NULL);
457 0 : current->paused = true;
458 : }
459 :
460 0 : void GProgressResumeTimer(void) {
461 : struct timeval tv, res;
462 :
463 0 : if ( current==NULL || current->visible || current->dying || !current->paused )
464 0 : return;
465 0 : current->paused = false;
466 0 : gettimeofday(&tv,NULL);
467 0 : res.tv_sec = tv.tv_sec - current->pause_time.tv_sec;
468 0 : if ( (res.tv_usec = tv.tv_usec - current->pause_time.tv_usec)<0 ) {
469 0 : --res.tv_sec;
470 0 : res.tv_usec += 1000000;
471 : }
472 0 : current->start_time.tv_sec += res.tv_sec;
473 0 : if ( (current->start_time.tv_usec += res.tv_usec)>= 1000000 ) {
474 0 : ++current->start_time.tv_sec;
475 0 : current->start_time.tv_usec -= 1000000;
476 : }
477 : }
478 :
479 0 : void GProgressShow(void) {
480 :
481 0 : if ( current==NULL || current->visible || current->dying )
482 0 : return;
483 :
484 0 : GProgressDisplay();
485 0 : GDrawSync(NULL);
486 0 : GDrawProcessPendingEvents(NULL);
487 0 : GDrawSync(NULL);
488 0 : GDrawProcessPendingEvents(NULL);
489 : }
490 :
491 119 : void GProgressStartIndicator8(int delay, const char *title, const char *line1,
492 : const char *line2, int tot, int stages) {
493 119 : unichar_t *tit = utf82u_copy(title),
494 119 : *l1 = utf82u_copy(line1),
495 119 : *l2 = utf82u_copy(line2);
496 119 : GProgressStartIndicator(delay,
497 : tit,l1,l2,
498 : tot,stages);
499 119 : free(l1); free(l2); free(tit);
500 119 : }
501 :
502 69 : void GProgressChangeLine1_8(const char *line1) {
503 69 : unichar_t *l1 = utf82u_copy(line1);
504 69 : GProgressChangeLine1(l1);
505 69 : free(l1);
506 69 : }
507 :
508 120 : void GProgressChangeLine2_8(const char *line2) {
509 120 : unichar_t *l2 = utf82u_copy(line2);
510 120 : GProgressChangeLine2(l2);
511 120 : free(l2);
512 120 : }
513 :
514 0 : GResInfo *_GProgressRIHead(void) {
515 :
516 0 : if ( !progress_init )
517 0 : GProgressResInit();
518 0 : return( &progress_ri );
519 : }
|