UNIXworkcode

1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * 3 * ***** BEGIN LICENSE BLOCK ***** 4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 5 * 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/ 10 * 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. 15 * 16 * The Original Code is the Microline Widget Library, originally made available under the NPL by Neuron Data <http://www.neurondata.com>. 17 * 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. 22 * 23 * Contributor(s): 24 * 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. 36 * 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. 46 * 47 * ***** END LICENSE BLOCK ***** */ 48 49 #include "ProgressP.h" 50 #include <stdio.h> 51 #include <sys/time.h> 52 #include <time.h> 53 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 CopyRenderTable(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); 71 static void CheckSetRenderTable(Widget wid, 72 int offset, 73 XrmValue *value); 74 75 76 static XtResource resources[] = 77 { 78 { 79 XmNcompleteValue, XmCCompleteValue, 80 XtRInt, sizeof(int), 81 XtOffset(XmLProgressWidget, progress.completeValue), 82 XtRImmediate, (caddr_t)100 83 }, 84 { 85 XmNnumBoxes, XmCNumBoxes, 86 XtRInt, sizeof(int), 87 XtOffset(XmLProgressWidget, progress.numBoxes), 88 XtRImmediate, (caddr_t)10 89 }, 90 { 91 XmNvalue, XmCValue, 92 XtRInt, sizeof(int), 93 XtOffset(XmLProgressWidget, progress.value), 94 XtRImmediate, (caddr_t)0 95 }, 96 { 97 "pri.vate","Pri.vate",XmRBoolean, 98 sizeof(Boolean), XtOffset(XmLProgressWidget, progress.check_set_render_table), 99 XmRImmediate, (XtPointer) False 100 }, 101 { 102 XmNfontList, XmCFontList, 103 XmRFontList, sizeof(XmFontList), 104 XtOffset(XmLProgressWidget, progress.renderTable), 105 XmRCallProc, (XtPointer)CheckSetRenderTable, 106 }, 107 { 108 XmNrenderTable, XmCRenderTable, 109 XmRRenderTable, sizeof(XmRenderTable), 110 XtOffset(XmLProgressWidget, progress.renderTable), 111 XmRCallProc, (XtPointer)CheckSetRenderTable, 112 }, 113 { 114 XmNmeterStyle, XmCMeterStyle, 115 XmRMeterStyle, sizeof(unsigned char), 116 XtOffset(XmLProgressWidget, progress.meterStyle), 117 XmRImmediate, (XtPointer)XmMETER_BAR, 118 }, 119 { 120 XmNshowTime, XmCShowTime, 121 XmRBoolean, sizeof(Boolean), 122 XtOffset(XmLProgressWidget, progress.showTime), 123 XmRImmediate, (XtPointer)False 124 }, 125 { 126 XmNshowPercentage, XmCShowPercentage, 127 XmRBoolean, sizeof(Boolean), 128 XtOffset(XmLProgressWidget, progress.showPercentage), 129 XmRImmediate, (XtPointer)True 130 } 131 }; 132 133 XmLProgressClassRec xmlProgressClassRec = 134 { 135 { /* Core */ 136 (WidgetClass)&xmPrimitiveClassRec, /* superclass */ 137 "XmLProgress", /* class_name */ 138 sizeof(XmLProgressRec), /* widget_size */ 139 ClassInitialize, /* class_initialize */ 140 NULL, /* class_part_initialize */ 141 FALSE, /* class_inited */ 142 (XtInitProc)Initialize, /* initialize */ 143 NULL, /* initialize_hook */ 144 (XtRealizeProc)Realize, /* realize */ 145 NULL, /* actions */ 146 0, /* num_actions */ 147 resources, /* resources */ 148 XtNumber(resources), /* num_resources */ 149 NULLQUARK, /* xrm_class */ 150 TRUE, /* compress_motion */ 151 FALSE, /* compress_exposure */ 152 TRUE, /* compress_enterleave */ 153 TRUE, /* visible_interest */ 154 (XtWidgetProc)Destroy, /* destroy */ 155 (XtWidgetProc)Resize, /* resize */ 156 (XtExposeProc)Redisplay, /* expose */ 157 (XtSetValuesFunc)SetValues, /* set_values */ 158 NULL, /* set_values_hook */ 159 XtInheritSetValuesAlmost, /* set_values_almost */ 160 NULL, /* get_values_hook */ 161 NULL, /* accept_focus */ 162 XtVersion, /* version */ 163 NULL, /* callback_private */ 164 XtInheritTranslations, /* tm_table */ 165 NULL, /* query_geometry */ 166 NULL, /* display_accelerator */ 167 NULL, /* extension */ 168 }, 169 { /* Primitive */ 170 (XtWidgetProc)_XtInherit, /* border_highlight */ 171 (XtWidgetProc)_XtInherit, /* border_unhighlight */ 172 XtInheritTranslations, /* translations */ 173 NULL, /* arm_and_activate */ 174 NULL, /* syn_resources */ 175 0, /* num_syn_resources */ 176 NULL, /* extension */ 177 }, 178 { /* Progress */ 179 0, /* unused */ 180 } 181 }; 182 183 WidgetClass xmlProgressWidgetClass = (WidgetClass)&xmlProgressClassRec; 184 185 static void 186 CheckSetRenderTable(Widget wid, 187 int offset, 188 XrmValue *value) 189 { 190 XmLProgressWidget lw = (XmLProgressWidget)wid; 191 192 /* Check if been here before */ 193 if (lw->progress.check_set_render_table) 194 value->addr = NULL; 195 else { 196 lw->progress.check_set_render_table = True; 197 value->addr = (char*)&(lw->progress.renderTable); 198 } 199 200 } 201 202 static void 203 ClassInitialize(void) 204 { 205 XmLInitialize(); 206 207 XtSetTypeConverter(XmRString, XmRMeterStyle, CvtStringToMeterStyle, 208 0, 0, XtCacheNone, 0); 209 } 210 211 static void 212 Initialize(Widget reqW, 213 Widget newW, 214 ArgList args, 215 Cardinal *narg) 216 { 217 XmLProgressWidget p; 218 219 p = (XmLProgressWidget)newW; 220 221 if (!p->core.width) 222 p->core.width = 200; 223 if (!p->core.height) 224 p->core.height = 30; 225 226 p->progress.gc = 0; 227 p->progress.startTime = time(0); 228 CopyRenderTable(p); 229 if (p->progress.completeValue < 1) 230 { 231 XmLWarning(newW, "Initialize() - complete value can''t be < 1"); 232 p->progress.completeValue = 1; 233 } 234 if (p->progress.numBoxes < 1) 235 { 236 XmLWarning(newW, "Initialize() - number of boxes can''t be < 1"); 237 p->progress.numBoxes = 1; 238 } 239 if (p->progress.value < 0) 240 { 241 XmLWarning(newW, "Initialize() - value can''t be < 0"); 242 p->progress.value = 0; 243 } 244 if (p->progress.value > p->progress.completeValue) 245 { 246 XmLWarning(newW, "Initialize() - value can''t be > completeValue"); 247 p->progress.value = p->progress.completeValue; 248 } 249 XtVaSetValues(newW, XmNtraversalOn, False, NULL); 250 } 251 252 static void 253 Resize(Widget w) 254 { 255 Display *dpy; 256 Window win; 257 258 if (!XtIsRealized(w)) 259 return; 260 dpy = XtDisplay(w); 261 win = XtWindow(w); 262 XClearArea(dpy, win, 0, 0, 0, 0, True); 263 } 264 265 static void 266 Destroy(Widget w) 267 { 268 Display *dpy; 269 XmLProgressWidget p; 270 271 p = (XmLProgressWidget)w; 272 dpy = XtDisplay(w); 273 if (p->progress.gc) 274 { 275 XFreeGC(dpy, p->progress.gc); 276 XFreeFont(dpy, p->progress.fallbackFont); 277 } 278 XmRenderTableFree(p->progress.renderTable); 279 } 280 281 static void 282 Realize(Widget w, 283 XtValueMask *valueMask, 284 XSetWindowAttributes *attr) 285 { 286 XmLProgressWidget p; 287 Display *dpy; 288 WidgetClass superClass; 289 XtRealizeProc realize; 290 XGCValues values; 291 /* XtGCMask mask;*/ 292 293 p = (XmLProgressWidget)w; 294 dpy = XtDisplay(p); 295 superClass = xmlProgressWidgetClass->core_class.superclass; 296 realize = superClass->core_class.realize; 297 (*realize)(w, valueMask, attr); 298 299 if (!p->progress.gc) 300 { 301 p->progress.fallbackFont = XLoadQueryFont(dpy, "fixed"); 302 values.font = p->progress.fallbackFont->fid; 303 p->progress.gc = XCreateGC(dpy, XtWindow(p), GCFont, &values); 304 } 305 } 306 307 static void 308 Redisplay(Widget w, 309 XEvent *event, 310 Region region) 311 { 312 XmLProgressWidget p; 313 Display *dpy; 314 Window win; 315 XRectangle rect; 316 int st; 317 318 if (!XtIsRealized(w) || !w->core.visible) 319 return; 320 321 p = (XmLProgressWidget)w; 322 dpy = XtDisplay(w); 323 win = XtWindow(w); 324 st = p->primitive.shadow_thickness; 325 rect.x = st; 326 rect.y = st; 327 rect.width = p->core.width - st * 2; 328 rect.height = p->core.height - st * 2; 329 330 if (p->progress.meterStyle == XmMETER_BAR) 331 DrawBarMeter(p, &rect); 332 else if (p->progress.meterStyle == XmMETER_BOXES) 333 DrawBoxesMeter(p, &rect); 334 335 #ifdef MOTIF11 336 _XmDrawShadow(dpy, win, 337 p->primitive.bottom_shadow_GC, 338 p->primitive.top_shadow_GC, 339 p->primitive.shadow_thickness, 340 0, 0, p->core.width, p->core.height); 341 #else 342 _XmDrawShadows(dpy, win, 343 p->primitive.top_shadow_GC, 344 p->primitive.bottom_shadow_GC, 345 0, 0, p->core.width, p->core.height, 346 p->primitive.shadow_thickness, 347 XmSHADOW_IN); 348 #endif 349 } 350 351 static void 352 DrawBoxesMeter(XmLProgressWidget p, 353 XRectangle *rect) 354 { 355 Display *dpy; 356 Window win; 357 int i, j, st, nb, x1, x2; 358 359 dpy = XtDisplay(p); 360 win = XtWindow(p); 361 st = p->primitive.shadow_thickness; 362 nb = p->progress.numBoxes; 363 if (nb * st * 2 > (int)rect->width) 364 return; 365 366 if (p->progress.completeValue) 367 j = (int)((long)nb * (long)p->progress.value / 368 (long)p->progress.completeValue); 369 else 370 j = 0; 371 x2 = 0; 372 for (i = 0; i < nb; i++) 373 { 374 if (i < j) 375 XSetForeground(dpy, p->progress.gc, p->primitive.foreground); 376 else 377 XSetForeground(dpy, p->progress.gc, p->core.background_pixel); 378 x1 = x2; 379 if (i == nb - 1) 380 x2 = rect->width; 381 else 382 x2 = ((int)rect->width * (i + 1)) / nb; 383 XFillRectangle(dpy, win, p->progress.gc, 384 rect->x + x1 + st, rect->y + st, 385 x2 - x1 - st * 2, rect->height - st * 2); 386 #ifdef MOTIF11 387 _XmDrawShadow(dpy, win, 388 p->primitive.bottom_shadow_GC, 389 p->primitive.top_shadow_GC, 390 p->primitive.shadow_thickness, 391 rect->x + x1, rect->y, 392 x2 - x1, rect->height); 393 #else 394 _XmDrawShadows(dpy, win, 395 p->primitive.top_shadow_GC, 396 p->primitive.bottom_shadow_GC, 397 rect->x + x1, rect->y, 398 x2 - x1, rect->height, 399 p->primitive.shadow_thickness, 400 XmSHADOW_IN); 401 #endif 402 } 403 } 404 405 static void 406 DrawBarMeter(XmLProgressWidget p, 407 XRectangle *rect) 408 { 409 Display *dpy; 410 Window win; 411 int timeLeft, timeSoFar; 412 time_t currentTime; 413 XmString str; 414 Dimension strWidth, strHeight; 415 XRectangle lRect, rRect; 416 int percent; 417 char c[10]; 418 long l; 419 420 dpy = XtDisplay(p); 421 win = XtWindow(p); 422 423 /* Left Rect */ 424 if (p->progress.completeValue) 425 l = (long)rect->width * (long)p->progress.value / 426 (long)p->progress.completeValue; 427 else 428 l = 0; 429 lRect.x = rect->x; 430 lRect.y = rect->y; 431 lRect.width = (Dimension)l; 432 lRect.height = rect->height; 433 XSetForeground(dpy, p->progress.gc, p->primitive.foreground); 434 XFillRectangle(dpy, win, p->progress.gc, lRect.x, lRect.y, 435 lRect.width, lRect.height); 436 437 /* Right Rect */ 438 rRect.x = rect->x + (int)l; 439 rRect.y = rect->y; 440 rRect.width = rect->width - (Dimension)l; 441 rRect.height = rect->height; 442 XSetForeground(dpy, p->progress.gc, p->core.background_pixel); 443 XFillRectangle(dpy, win, p->progress.gc, rRect.x, rRect.y, 444 rRect.width, rRect.height); 445 446 if (p->progress.completeValue) 447 percent = (int)(((long)p->progress.value * 100) / 448 (long)p->progress.completeValue); 449 else 450 percent = 0; 451 452 /* percent complete */ 453 sprintf(c, "%d%c", percent, '%'); 454 str = XmStringCreateSimple(c); 455 XmStringExtent(p->progress.renderTable, str, &strWidth, &strHeight); 456 if (p->progress.showPercentage) 457 DrawString(p, str, rect->x + rect->width / 2 - (int)strWidth / 2, 458 rect->y + rect->height / 2 - (int)strHeight / 2, strWidth, 459 &lRect, &rRect); 460 XmStringFree(str); 461 462 /* Left Time */ 463 currentTime = time(0); 464 timeSoFar = (int)(currentTime - p->progress.startTime); 465 if (p->progress.showTime && p->progress.value && 466 p->progress.value != p->progress.completeValue && timeSoFar) 467 { 468 TimeStr(c, timeSoFar); 469 str = XmStringCreateSimple(c); 470 XmStringExtent(p->progress.renderTable, str, 471 &strWidth, &strHeight); 472 DrawString(p, str, rect->x + 5, rect->y + rect->height / 2 - 473 (int)strHeight / 2, strWidth, &lRect, &rRect); 474 XmStringFree(str); 475 } 476 477 /* Right Time */ 478 timeLeft = 0; 479 if (percent) 480 timeLeft = (timeSoFar * 100 / percent) - timeSoFar; 481 if (p->progress.showTime && percent && percent != 100 && timeLeft) 482 { 483 TimeStr(c, timeLeft); 484 str = XmStringCreateSimple(c); 485 XmStringExtent(p->progress.renderTable, str, 486 &strWidth, &strHeight); 487 DrawString(p, str, rect->x + rect->width - strWidth - 5, 488 rect->y + rect->height / 2 - (int)strHeight / 2, 489 strWidth, &lRect, &rRect); 490 XmStringFree(str); 491 } 492 } 493 494 static void 495 DrawString(XmLProgressWidget p, 496 XmString str, 497 int x, 498 int y, 499 int strWidth, 500 XRectangle *lRect, 501 XRectangle *rRect) 502 { 503 Display *dpy; 504 Window win; 505 506 dpy = XtDisplay(p); 507 win = XtWindow(p); 508 if (lRect->width && lRect->height) 509 { 510 XSetForeground(dpy, p->progress.gc, p->core.background_pixel); 511 XSetClipRectangles(dpy, p->progress.gc, 0, 0, lRect, 1, Unsorted); 512 XmStringDraw(dpy, win, p->progress.renderTable, str, 513 p->progress.gc, x, y, strWidth, XmALIGNMENT_BEGINNING, 514 XmSTRING_DIRECTION_L_TO_R, 0); 515 XSetClipMask(dpy, p->progress.gc, None); 516 } 517 if (rRect->width && rRect->height) 518 { 519 XSetForeground(dpy, p->progress.gc, p->primitive.foreground); 520 XSetClipRectangles(dpy, p->progress.gc, 0, 0, rRect, 1, Unsorted); 521 XmStringDraw(dpy, win, p->progress.renderTable, str, 522 p->progress.gc, x, y, strWidth, XmALIGNMENT_BEGINNING, 523 XmSTRING_DIRECTION_L_TO_R, 0); 524 XSetClipMask(dpy, p->progress.gc, None); 525 } 526 } 527 528 static void 529 TimeStr(char *c, 530 int seconds) 531 { 532 int h, m, s; 533 534 s = seconds; 535 m = s / 60; 536 s -= m * 60; 537 h = m / 60; 538 m -= h * 60; 539 if (h > 99) 540 h = 99; 541 if (h > 0 && m < 10) 542 sprintf(c, "%d:0%d hr", h, m); 543 else if (h > 0) 544 sprintf(c, "%d:%d hr", h, m); 545 else if (m > 0 && s < 10) 546 sprintf(c, "%d:0%d min", m, s); 547 else if (m > 0) 548 sprintf(c, "%d:%d min", m, s); 549 else 550 sprintf(c, "%d sec", s); 551 } 552 553 static Boolean 554 SetValues(Widget curW, 555 Widget reqW, 556 Widget newW, 557 ArgList args, 558 Cardinal *narg) 559 { 560 XmLProgressWidget cur, p; 561 562 cur = (XmLProgressWidget)curW; 563 p = (XmLProgressWidget)newW; 564 if (p->progress.value == 0) 565 p->progress.startTime = time(0); 566 if (p->progress.completeValue < 1) 567 { 568 XmLWarning(newW, "SetValues() - complete value can''t be < 1"); 569 p->progress.completeValue = 1; 570 } 571 if (p->progress.numBoxes < 1) 572 { 573 XmLWarning(newW, "SetValues() - number of boxes can''t be < 1"); 574 p->progress.numBoxes = 1; 575 } 576 if (p->progress.value < 0) 577 { 578 XmLWarning(newW, "SetValues() - value can''t be < 0"); 579 p->progress.value = 0; 580 } 581 if (p->progress.value > p->progress.completeValue) 582 { 583 XmLWarning(newW, "SetValues() - value can''t be > completeValue"); 584 p->progress.value = p->progress.completeValue; 585 } 586 if (p->progress.renderTable != cur->progress.renderTable) 587 { 588 XmRenderTableFree(cur->progress.renderTable); 589 CopyRenderTable(p); 590 } 591 /* display changes immediately since we may be not get back 592 to XNextEvent if the calling application is computing */ 593 if (p->core.background_pixel != cur->core.background_pixel || 594 p->primitive.foreground != cur->primitive.foreground || 595 p->progress.value != cur->progress.value || 596 p->progress.completeValue != cur->progress.completeValue || 597 p->progress.renderTable != cur->progress.renderTable || 598 p->progress.showTime != cur->progress.showTime || 599 p->progress.showPercentage != cur->progress.showPercentage || 600 p->progress.meterStyle != cur->progress.meterStyle || 601 p->progress.numBoxes != cur->progress.numBoxes || 602 p->primitive.shadow_thickness != cur->primitive.shadow_thickness) 603 { 604 Redisplay(newW, 0, 0); 605 XFlush(XtDisplay(newW)); 606 XmUpdateDisplay(newW); 607 } 608 return FALSE; 609 } 610 611 static void 612 CopyRenderTable(XmLProgressWidget p) 613 { 614 if (!p->progress.renderTable) 615 p->progress.renderTable = XmLRenderTableCopyDefault((Widget)p); 616 else 617 p->progress.renderTable = XmRenderTableCopy(p->progress.renderTable, NULL, 0); 618 if (!p->progress.renderTable) 619 XmLWarning((Widget)p, "- fatal error - font list NULL"); 620 } 621 622 static Boolean 623 CvtStringToMeterStyle(Display *dpy, 624 XrmValuePtr args, 625 Cardinal *narg, 626 XrmValuePtr fromVal, 627 XrmValuePtr toVal, 628 XtPointer *data) 629 { 630 static XmLStringToUCharMap map[] = 631 { 632 { "METER_BAR", XmMETER_BAR }, 633 { "METER_BOXES", XmMETER_BOXES }, 634 { 0, 0 }, 635 }; 636 637 return XmLCvtStringToUChar(dpy, "XmRMeterStyle", map, fromVal, toVal); 638 } 639 640 /* 641 Public Functions 642 */ 643 644 Widget 645 XmLCreateProgress(Widget parent, 646 char *name, 647 ArgList arglist, 648 Cardinal argcount) 649 { 650 return XtCreateWidget(name, xmlProgressWidgetClass, parent, 651 arglist, argcount); 652 } 653 654 655