Xft support under OpenMotif 2.3.3 - I've been using this for quite a while on
[nedit.git] / Microline / XmL / Progress.c
blob6e76d2ba2670505625f9d6aa8855720fb6229d1a
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is the Microline Widget Library, originally made available under the NPL by Neuron Data <http://www.neurondata.com>.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * In addition, as a special exception to the GNU GPL, the copyright holders
38 * give permission to link the code of this program with the Motif and Open
39 * Motif libraries (or with modified versions of these that use the same
40 * license), and distribute linked combinations including the two. You
41 * must obey the GNU General Public License in all respects for all of
42 * the code used other than linking with Motif/Open Motif. If you modify
43 * this file, you may extend this exception to your version of the file,
44 * but you are not obligated to do so. If you do not wish to do so,
45 * delete this exception statement from your version.
47 * ***** END LICENSE BLOCK ***** */
49 #include "ProgressP.h"
50 #include <stdio.h>
51 #include <sys/time.h>
52 #include <time.h>
54 static void ClassInitialize(void);
55 static void Initialize(Widget , Widget, ArgList, Cardinal *);
56 static void Resize(Widget);
57 static void Destroy(Widget);
58 static void Realize(Widget w, XtValueMask *valueMask,
59 XSetWindowAttributes *attr);
60 static void Redisplay(Widget, XEvent *, Region);
61 static Boolean SetValues(Widget, Widget, Widget, ArgList, Cardinal *);
62 static void CopyFontList(XmLProgressWidget p);
63 static void TimeStr(char *, int);
64 static void DrawBarMeter(XmLProgressWidget p, XRectangle *rect);
65 static void DrawBoxesMeter(XmLProgressWidget p, XRectangle *rect);
66 static void DrawString(XmLProgressWidget, XmString, int, int,
67 int, XRectangle *, XRectangle *);
68 static Boolean CvtStringToMeterStyle(Display *dpy, XrmValuePtr args,
69 Cardinal *numArgs, XrmValuePtr fromVal, XrmValuePtr toVal,
70 XtPointer *data);
72 static XtResource resources[] =
75 XmNcompleteValue, XmCCompleteValue,
76 XtRInt, sizeof(int),
77 XtOffset(XmLProgressWidget, progress.completeValue),
78 XtRImmediate, (caddr_t)100
81 XmNnumBoxes, XmCNumBoxes,
82 XtRInt, sizeof(int),
83 XtOffset(XmLProgressWidget, progress.numBoxes),
84 XtRImmediate, (caddr_t)10
87 XmNvalue, XmCValue,
88 XtRInt, sizeof(int),
89 XtOffset(XmLProgressWidget, progress.value),
90 XtRImmediate, (caddr_t)0
93 XmNfontList, XmCFontList,
94 XmRFontList, sizeof(XmFontList),
95 XtOffset(XmLProgressWidget, progress.fontList),
96 XmRImmediate, (XtPointer)0,
99 XmNmeterStyle, XmCMeterStyle,
100 XmRMeterStyle, sizeof(unsigned char),
101 XtOffset(XmLProgressWidget, progress.meterStyle),
102 XmRImmediate, (XtPointer)XmMETER_BAR,
105 XmNshowTime, XmCShowTime,
106 XmRBoolean, sizeof(Boolean),
107 XtOffset(XmLProgressWidget, progress.showTime),
108 XmRImmediate, (XtPointer)False
111 XmNshowPercentage, XmCShowPercentage,
112 XmRBoolean, sizeof(Boolean),
113 XtOffset(XmLProgressWidget, progress.showPercentage),
114 XmRImmediate, (XtPointer)True
118 XmLProgressClassRec xmlProgressClassRec =
120 { /* Core */
121 (WidgetClass)&xmPrimitiveClassRec, /* superclass */
122 "XmLProgress", /* class_name */
123 sizeof(XmLProgressRec), /* widget_size */
124 ClassInitialize, /* class_initialize */
125 NULL, /* class_part_initialize */
126 FALSE, /* class_inited */
127 (XtInitProc)Initialize, /* initialize */
128 NULL, /* initialize_hook */
129 (XtRealizeProc)Realize, /* realize */
130 NULL, /* actions */
131 0, /* num_actions */
132 resources, /* resources */
133 XtNumber(resources), /* num_resources */
134 NULLQUARK, /* xrm_class */
135 TRUE, /* compress_motion */
136 FALSE, /* compress_exposure */
137 TRUE, /* compress_enterleave */
138 TRUE, /* visible_interest */
139 (XtWidgetProc)Destroy, /* destroy */
140 (XtWidgetProc)Resize, /* resize */
141 (XtExposeProc)Redisplay, /* expose */
142 (XtSetValuesFunc)SetValues, /* set_values */
143 NULL, /* set_values_hook */
144 XtInheritSetValuesAlmost, /* set_values_almost */
145 NULL, /* get_values_hook */
146 NULL, /* accept_focus */
147 XtVersion, /* version */
148 NULL, /* callback_private */
149 XtInheritTranslations, /* tm_table */
150 NULL, /* query_geometry */
151 NULL, /* display_accelerator */
152 NULL, /* extension */
154 { /* Primitive */
155 (XtWidgetProc)_XtInherit, /* border_highlight */
156 (XtWidgetProc)_XtInherit, /* border_unhighlight */
157 XtInheritTranslations, /* translations */
158 NULL, /* arm_and_activate */
159 NULL, /* syn_resources */
160 0, /* num_syn_resources */
161 NULL, /* extension */
163 { /* Progress */
164 0, /* unused */
168 WidgetClass xmlProgressWidgetClass = (WidgetClass)&xmlProgressClassRec;
170 static void
171 ClassInitialize(void)
173 XmLInitialize();
175 XtSetTypeConverter(XmRString, XmRMeterStyle, CvtStringToMeterStyle,
176 0, 0, XtCacheNone, 0);
179 static void
180 Initialize(Widget reqW,
181 Widget newW,
182 ArgList args,
183 Cardinal *narg)
185 XmLProgressWidget p;
187 p = (XmLProgressWidget)newW;
189 if (!p->core.width)
190 p->core.width = 200;
191 if (!p->core.height)
192 p->core.height = 30;
194 p->progress.gc = 0;
195 p->progress.startTime = time(0);
196 CopyFontList(p);
197 if (p->progress.completeValue < 1)
199 XmLWarning(newW, "Initialize() - complete value can't be < 1");
200 p->progress.completeValue = 1;
202 if (p->progress.numBoxes < 1)
204 XmLWarning(newW, "Initialize() - number of boxes can't be < 1");
205 p->progress.numBoxes = 1;
207 if (p->progress.value < 0)
209 XmLWarning(newW, "Initialize() - value can't be < 0");
210 p->progress.value = 0;
212 if (p->progress.value > p->progress.completeValue)
214 XmLWarning(newW, "Initialize() - value can't be > completeValue");
215 p->progress.value = p->progress.completeValue;
217 XtVaSetValues(newW, XmNtraversalOn, False, NULL);
220 static void
221 Resize(Widget w)
223 Display *dpy;
224 Window win;
226 if (!XtIsRealized(w))
227 return;
228 dpy = XtDisplay(w);
229 win = XtWindow(w);
230 XClearArea(dpy, win, 0, 0, 0, 0, True);
233 static void
234 Destroy(Widget w)
236 Display *dpy;
237 XmLProgressWidget p;
239 p = (XmLProgressWidget)w;
240 dpy = XtDisplay(w);
241 if (p->progress.gc)
243 XFreeGC(dpy, p->progress.gc);
244 XFreeFont(dpy, p->progress.fallbackFont);
246 XmFontListFree(p->progress.fontList);
249 static void
250 Realize(Widget w,
251 XtValueMask *valueMask,
252 XSetWindowAttributes *attr)
254 XmLProgressWidget p;
255 Display *dpy;
256 WidgetClass superClass;
257 XtRealizeProc realize;
258 XGCValues values;
259 /* XtGCMask mask;*/
261 p = (XmLProgressWidget)w;
262 dpy = XtDisplay(p);
263 superClass = xmlProgressWidgetClass->core_class.superclass;
264 realize = superClass->core_class.realize;
265 (*realize)(w, valueMask, attr);
267 if (!p->progress.gc)
269 p->progress.fallbackFont = XLoadQueryFont(dpy, "fixed");
270 values.font = p->progress.fallbackFont->fid;
271 p->progress.gc = XCreateGC(dpy, XtWindow(p), GCFont, &values);
275 static void
276 Redisplay(Widget w,
277 XEvent *event,
278 Region region)
280 XmLProgressWidget p;
281 Display *dpy;
282 Window win;
283 XRectangle rect;
284 int st;
286 if (!XtIsRealized(w) || !w->core.visible)
287 return;
289 p = (XmLProgressWidget)w;
290 dpy = XtDisplay(w);
291 win = XtWindow(w);
292 st = p->primitive.shadow_thickness;
293 rect.x = st;
294 rect.y = st;
295 rect.width = p->core.width - st * 2;
296 rect.height = p->core.height - st * 2;
298 if (p->progress.meterStyle == XmMETER_BAR)
299 DrawBarMeter(p, &rect);
300 else if (p->progress.meterStyle == XmMETER_BOXES)
301 DrawBoxesMeter(p, &rect);
303 #ifdef MOTIF11
304 _XmDrawShadow(dpy, win,
305 p->primitive.bottom_shadow_GC,
306 p->primitive.top_shadow_GC,
307 p->primitive.shadow_thickness,
308 0, 0, p->core.width, p->core.height);
309 #else
310 _XmDrawShadows(dpy, win,
311 p->primitive.top_shadow_GC,
312 p->primitive.bottom_shadow_GC,
313 0, 0, p->core.width, p->core.height,
314 p->primitive.shadow_thickness,
315 XmSHADOW_IN);
316 #endif
319 static void
320 DrawBoxesMeter(XmLProgressWidget p,
321 XRectangle *rect)
323 Display *dpy;
324 Window win;
325 int i, j, st, nb, x1, x2;
327 dpy = XtDisplay(p);
328 win = XtWindow(p);
329 st = p->primitive.shadow_thickness;
330 nb = p->progress.numBoxes;
331 if (nb * st * 2 > (int)rect->width)
332 return;
334 if (p->progress.completeValue)
335 j = (int)((long)nb * (long)p->progress.value /
336 (long)p->progress.completeValue);
337 else
338 j = 0;
339 x2 = 0;
340 for (i = 0; i < nb; i++)
342 if (i < j)
343 XSetForeground(dpy, p->progress.gc, p->primitive.foreground);
344 else
345 XSetForeground(dpy, p->progress.gc, p->core.background_pixel);
346 x1 = x2;
347 if (i == nb - 1)
348 x2 = rect->width;
349 else
350 x2 = ((int)rect->width * (i + 1)) / nb;
351 XFillRectangle(dpy, win, p->progress.gc,
352 rect->x + x1 + st, rect->y + st,
353 x2 - x1 - st * 2, rect->height - st * 2);
354 #ifdef MOTIF11
355 _XmDrawShadow(dpy, win,
356 p->primitive.bottom_shadow_GC,
357 p->primitive.top_shadow_GC,
358 p->primitive.shadow_thickness,
359 rect->x + x1, rect->y,
360 x2 - x1, rect->height);
361 #else
362 _XmDrawShadows(dpy, win,
363 p->primitive.top_shadow_GC,
364 p->primitive.bottom_shadow_GC,
365 rect->x + x1, rect->y,
366 x2 - x1, rect->height,
367 p->primitive.shadow_thickness,
368 XmSHADOW_IN);
369 #endif
373 static void
374 DrawBarMeter(XmLProgressWidget p,
375 XRectangle *rect)
377 Display *dpy;
378 Window win;
379 int timeLeft, timeSoFar;
380 time_t currentTime;
381 XmString str;
382 Dimension strWidth, strHeight;
383 XRectangle lRect, rRect;
384 int percent;
385 char c[10];
386 long l;
388 dpy = XtDisplay(p);
389 win = XtWindow(p);
391 /* Left Rect */
392 if (p->progress.completeValue)
393 l = (long)rect->width * (long)p->progress.value /
394 (long)p->progress.completeValue;
395 else
396 l = 0;
397 lRect.x = rect->x;
398 lRect.y = rect->y;
399 lRect.width = (Dimension)l;
400 lRect.height = rect->height;
401 XSetForeground(dpy, p->progress.gc, p->primitive.foreground);
402 XFillRectangle(dpy, win, p->progress.gc, lRect.x, lRect.y,
403 lRect.width, lRect.height);
405 /* Right Rect */
406 rRect.x = rect->x + (int)l;
407 rRect.y = rect->y;
408 rRect.width = rect->width - (Dimension)l;
409 rRect.height = rect->height;
410 XSetForeground(dpy, p->progress.gc, p->core.background_pixel);
411 XFillRectangle(dpy, win, p->progress.gc, rRect.x, rRect.y,
412 rRect.width, rRect.height);
414 if (p->progress.completeValue)
415 percent = (int)(((long)p->progress.value * 100) /
416 (long)p->progress.completeValue);
417 else
418 percent = 0;
420 /* percent complete */
421 sprintf(c, "%d%c", percent, '%');
422 str = XmStringCreateSimple(c);
423 XmStringExtent(p->progress.fontList, str, &strWidth, &strHeight);
424 if (p->progress.showPercentage)
425 DrawString(p, str, rect->x + rect->width / 2 - (int)strWidth / 2,
426 rect->y + rect->height / 2 - (int)strHeight / 2, strWidth,
427 &lRect, &rRect);
428 XmStringFree(str);
430 /* Left Time */
431 currentTime = time(0);
432 timeSoFar = (int)(currentTime - p->progress.startTime);
433 if (p->progress.showTime && p->progress.value &&
434 p->progress.value != p->progress.completeValue && timeSoFar)
436 TimeStr(c, timeSoFar);
437 str = XmStringCreateSimple(c);
438 XmStringExtent(p->progress.fontList, str,
439 &strWidth, &strHeight);
440 DrawString(p, str, rect->x + 5, rect->y + rect->height / 2 -
441 (int)strHeight / 2, strWidth, &lRect, &rRect);
442 XmStringFree(str);
445 /* Right Time */
446 timeLeft = 0;
447 if (percent)
448 timeLeft = (timeSoFar * 100 / percent) - timeSoFar;
449 if (p->progress.showTime && percent && percent != 100 && timeLeft)
451 TimeStr(c, timeLeft);
452 str = XmStringCreateSimple(c);
453 XmStringExtent(p->progress.fontList, str,
454 &strWidth, &strHeight);
455 DrawString(p, str, rect->x + rect->width - strWidth - 5,
456 rect->y + rect->height / 2 - (int)strHeight / 2,
457 strWidth, &lRect, &rRect);
458 XmStringFree(str);
462 static void
463 DrawString(XmLProgressWidget p,
464 XmString str,
465 int x,
466 int y,
467 int strWidth,
468 XRectangle *lRect,
469 XRectangle *rRect)
471 Display *dpy;
472 Window win;
474 dpy = XtDisplay(p);
475 win = XtWindow(p);
476 if (lRect->width && lRect->height)
478 XSetForeground(dpy, p->progress.gc, p->core.background_pixel);
479 XSetClipRectangles(dpy, p->progress.gc, 0, 0, lRect, 1, Unsorted);
480 XmStringDraw(dpy, win, p->progress.fontList, str,
481 p->progress.gc, x, y, strWidth, XmALIGNMENT_BEGINNING,
482 XmSTRING_DIRECTION_L_TO_R, 0);
483 XSetClipMask(dpy, p->progress.gc, None);
485 if (rRect->width && rRect->height)
487 XSetForeground(dpy, p->progress.gc, p->primitive.foreground);
488 XSetClipRectangles(dpy, p->progress.gc, 0, 0, rRect, 1, Unsorted);
489 XmStringDraw(dpy, win, p->progress.fontList, str,
490 p->progress.gc, x, y, strWidth, XmALIGNMENT_BEGINNING,
491 XmSTRING_DIRECTION_L_TO_R, 0);
492 XSetClipMask(dpy, p->progress.gc, None);
496 static void
497 TimeStr(char *c,
498 int seconds)
500 int h, m, s;
502 s = seconds;
503 m = s / 60;
504 s -= m * 60;
505 h = m / 60;
506 m -= h * 60;
507 if (h > 99)
508 h = 99;
509 if (h > 0 && m < 10)
510 sprintf(c, "%d:0%d hr", h, m);
511 else if (h > 0)
512 sprintf(c, "%d:%d hr", h, m);
513 else if (m > 0 && s < 10)
514 sprintf(c, "%d:0%d min", m, s);
515 else if (m > 0)
516 sprintf(c, "%d:%d min", m, s);
517 else
518 sprintf(c, "%d sec", s);
521 static Boolean
522 SetValues(Widget curW,
523 Widget reqW,
524 Widget newW,
525 ArgList args,
526 Cardinal *narg)
528 XmLProgressWidget cur, p;
529 XtAppContext app;
531 cur = (XmLProgressWidget)curW;
532 p = (XmLProgressWidget)newW;
533 app = XtWidgetToApplicationContext(curW);
534 if (p->progress.value == 0)
535 p->progress.startTime = time(0);
536 if (p->progress.completeValue < 1)
538 XmLWarning(newW, "SetValues() - complete value can't be < 1");
539 p->progress.completeValue = 1;
541 if (p->progress.numBoxes < 1)
543 XmLWarning(newW, "SetValues() - number of boxes can't be < 1");
544 p->progress.numBoxes = 1;
546 if (p->progress.value < 0)
548 XmLWarning(newW, "SetValues() - value can't be < 0");
549 p->progress.value = 0;
551 if (p->progress.value > p->progress.completeValue)
553 XmLWarning(newW, "SetValues() - value can't be > completeValue");
554 p->progress.value = p->progress.completeValue;
556 if (p->progress.fontList != cur->progress.fontList)
558 XmFontListFree(cur->progress.fontList);
559 CopyFontList(p);
561 /* display changes immediately since we may be not get back
562 to XNextEvent if the calling application is computing */
563 if (p->core.background_pixel != cur->core.background_pixel ||
564 p->primitive.foreground != cur->primitive.foreground ||
565 p->progress.value != cur->progress.value ||
566 p->progress.completeValue != cur->progress.completeValue ||
567 p->progress.fontList != cur->progress.fontList ||
568 p->progress.showTime != cur->progress.showTime ||
569 p->progress.showPercentage != cur->progress.showPercentage ||
570 p->progress.meterStyle != cur->progress.meterStyle ||
571 p->progress.numBoxes != cur->progress.numBoxes ||
572 p->primitive.shadow_thickness != cur->primitive.shadow_thickness)
574 Redisplay(newW, 0, 0);
575 XFlush(XtDisplay(newW));
576 XmUpdateDisplay(newW);
578 return FALSE;
581 static void
582 CopyFontList(XmLProgressWidget p)
584 if (!p->progress.fontList)
585 p->progress.fontList = XmLFontListCopyDefault((Widget)p);
586 else
587 p->progress.fontList = XmFontListCopy(p->progress.fontList);
588 if (!p->progress.fontList)
589 XmLWarning((Widget)p, "- fatal error - font list NULL");
592 static Boolean
593 CvtStringToMeterStyle(Display *dpy,
594 XrmValuePtr args,
595 Cardinal *narg,
596 XrmValuePtr fromVal,
597 XrmValuePtr toVal,
598 XtPointer *data)
600 static XmLStringToUCharMap map[] =
602 { "METER_BAR", XmMETER_BAR },
603 { "METER_BOXES", XmMETER_BOXES },
604 { 0, 0 },
607 return XmLCvtStringToUChar(dpy, "XmRMeterStyle", map, fromVal, toVal);
611 Public Functions
614 Widget
615 XmLCreateProgress(Widget parent,
616 char *name,
617 ArgList arglist,
618 Cardinal argcount)
620 return XtCreateWidget(name, xmlProgressWidgetClass, parent,
621 arglist, argcount);