1# XfwfCommon -- the common superclass for all widgets -*-wbuild-*- 2# Author: Bert Bos <bert@let.rug.nl> 3# Copyright: see README file 4# Version: 1.6 5 6@class XfwfCommon (Composite) @file=Common 7 8@ The Common class is not meant to be instantiated. It only serves as 9the common superclass for a family of widgets, to ensure that these 10widgets have some common methods and resources. The Common class 11defines common types, symbolic constants, and type converters and it 12also provides the basis for keyboard traversal. The code for keyboard 13traversal is roughly based on that in the Xw widget set (created by 14Hewlett Packard), but it uses the |accept_focus| method. 15 16When the resource |traversalOn| is set to |True| (either at creation 17time, or via a |XtSetValues| later), a set of translations is added to 18the widget. If the widget's parent is also a subclass of Common, these 19translations will then implement keyboard traversal, using the cursor 20keys (up, down, prev, etc.) Of course, when the widget already uses 21these keys for other purposes, the keyboard traversal will not work. 22 23The Common widget makes use of John Cwickla's X Color Context (XCC) 24library, a system that makes it easier to select colors, since it 25automatically uses standard colormaps where available and also rounds 26requested colors to the nearest available one. For best results, an 27application should create an XCC and pass it as a resource to Common 28and its descendants, but Common will also create an XCC itself if none 29is provided. 30 31@EXPORTS 32 33@ The type |Alignment| is actually an integer, but it is given a 34different name to allow a type converter to be installed for it. 35 36 @type Alignment = int 37 38@ The symbolic constants can be added together to form an alignment. 39Various widgets use this to position labels, other widgets, etcetera. 40 41 @def XfwfCenter = 0 42 @def XfwfLeft = 1 43 @def XfwfRight = 2 44 @def XfwfTop = 4 45 @def XfwfBottom = 8 46 47 48@ For convenience, the eight possible combinations also have symbolic 49names. 50 51 @def XfwfTopLeft = (XfwfTop + XfwfLeft) 52 @def XfwfTopRight = (XfwfTop + XfwfRight) 53 @def XfwfBottomLeft = (XfwfBottom + XfwfLeft) 54 @def XfwfBottomRight = (XfwfBottom + XfwfRight) 55 56 57@ The directions of traversal are used as arguments to the |traverse| 58method. They are probably only useful to subclasses. 59 60 @type TraversalDirection = enum { 61 TraverseLeft, TraverseRight, TraverseUp, TraverseDown, 62 TraverseNext, TraversePrev, TraverseHome, TraverseNextTop } 63 64@ The |Color| type is an alias for |Pixel|, so that a new resource 65converter can be installed. 66 67 @type Color = Pixel 68 69 70@ To know the inside area of a Common widget might be useful to other 71widgets than subclasses alone. Calling |XfwfCallComputeInside| will 72call the |compute_inside| method, if available. 73 74@proc XfwfCallComputeInside($,Position*x, Position*y, Dimension*w, Dimension*h) 75{ 76 if (XtIsSubclass($, xfwfCommonWidgetClass) && $compute_inside) 77 $compute_inside($, x, y, w, h); 78 else 79 XtWarning 80 ("XfwfCallComputeInside only applies to subclasses of Common"); 81} 82 83@ Another convenience function is |XfwfCallFrameWidth|, which uses the 84method |total_frame_width| to compute the thickness of the frame that 85the widget will draw. 86 87@proc Dimension XfwfCallFrameWidth($) 88{ 89 if (XtIsSubclass($, xfwfCommonWidgetClass) && $total_frame_width) 90 return $total_frame_width($); 91 else 92 XtWarning 93 ("XfwfCallFrameWidth only applies to subclasses of Common"); 94 return 0; 95} 96 97@ All subclasses need to know the |XCC| type. 98 99@incl "XCC.h" 100 101@PUBLIC 102 103@ By default, Common and all its subclasses use XCC's to control their 104color allocations. But the use of XCC can be turned off. Some goodies, 105like approximate colors instead of failures and use of standard 106colormaps where available, will not be used, but the application may 107start faster. 108 109 @var Boolean useXCC = TRUE 110 111 112@ If |usePrivateColormap| is |TRUE| when the widget is created, it 113will try to install a private colormap, from which all colors are 114allocated. Changing this resource after the widget has been created 115has no effect. (No effect if |useXCC| is false.) 116 117 @var Boolean usePrivateColormap = FALSE 118 119 120@ |useStandardColormaps| directs the widget to try to find an already 121installed standard colormap. When the widget is created, it will look 122for a list of standard colormap, both color and greyscale. It won't 123create a new standard colormap itself. (No effect if |useXCC| is 124false.) 125 126 @var Boolean useStandardColormaps = TRUE 127 128 129@ Normally, when |useStandardColormaps| is |TRUE|, the widget will try 130to find the best standard colormap from those that are installed on 131the X server. You can force it to use a specific standard colormap by 132setting |standardColormap| to the Atom of that map. (No effect if 133|useXCC| is false.) 134 135 @var Atom standardColormap = 0 136 137 138@ |xcc| holds the XCC descriptor, which is used when allocating colors 139through |XCCGetPixel|. Normally, the widget creates its own. (No 140effect if |useXCC| is false.) 141 142 @var XCC xcc = <CallProc> create_xcc 143 144 145@ The resource |traversalOn| determines whether keyboard traversal is 146used. If it is |True| initially, or if it is set to |True| later, a 147set of translations will be added to the widget. 148 149 @var Boolean traversalOn = True 150 151 152@ Keyboard focus is indicated by border highlighting. When keyboard 153traversal is on and the widget receives the focus, the highlight border 154is filled with the highlight color or tile. If the widget does not 155have the focus, the area is left in the default background. 156 157 @var Dimension highlightThickness = 2 158 159 160@ The highlight border can have a color or it can be tiled with a 161pixmap. Whichever of the resources |highlightColor| or 162|highlightPixmap| is set latest, is used. When both are set, the 163pixmap is used. 164 165 @var Color highlightColor = <String> XtDefaultForeground 166 167 168@ In order to use the String to Color resource converter for the 169background color as well, a new background resource is created, 170conveniently called |background|. The Core field |background_pixel| 171should not be used anymore. 172 173 @var Color background = <String> XtDefaultBackground 174 175 176@ The |highlightPixmap| can be set to a pixmap with which the 177highlight border will be tiled. Only one of |highlightPixmap| and 178|highlightColor| can be set, see above. 179 180 @var Pixmap highlightPixmap = None 181 182 183@ When an application has several top level windows, it should have a 184way of setting the focus between windows. The Enter key in any widget 185with keyboard traversal on normally invokes the |traverseNextTop| 186action, that will call the callbacks of the topmost Common (or 187subclass) widget in the hierarchy. The callback may set the focus to 188another top level widget, with |XtCallAcceptFocus|. 189 190 @var <Callback> XtCallbackList nextTop = NULL 191 192@ The resource |userData| is provided for applications that want to 193attach their own data to a widget. It is not used by the widget itself 194in any way. 195 196 @var <Pointer> XtPointer userData = NULL 197 198 199 200@PRIVATE 201 202@ |abs|, |min| and |max| are used often enough in various subclasses 203to define them here. They will end up in the private(!) header file. 204 205 @def max(a, b) = ((a) > (b) ? (a) : (b)) 206 @def min(a, b) = ((a) < (b) ? (a) : (b)) 207 @def abs(a) = ((a) < 0 ? -(a) : (a)) 208 209 210@ A private variable is used to track the keyboard focus, but only 211while traversal is on. If |traversal_focus| is |True|, it means that 212the widget has keyboard focus and that that focus is a result of 213keyboard traversal. It also means that the widget's border is 214highlighted, although that is only visible if the |highlightThickness| 215is positive. 216 217 @var Bool traversal_focus 218 219@ The highlight border is filled with a color or a tile. 220 221 @var GC bordergc 222 223@ When the |xcc| is created by the widget itself, it must also be 224destroyed again. 225 226 @var Bool own_xcc 227 228 229 230@CLASSVARS 231 232@ |traversal_trans| holds the compiled version of the 233|extraTranslations|. 234 235 @var XtTranslations traversal_trans = NULL 236 237@ Set a few class variables. 238 239 @var compress_motion = True 240 @var compress_exposure = XtExposeCompressMultiple 241 @var compress_enterleave = True 242 243@ The |visible_interest| field is set to |TRUE|, because we need 244the |visible| field to check if it is OK to set the focus. 245 246 @var visible_interest = TRUE 247 248 249@METHODS 250 251@ The type converter |cvtStringToAlignment| is installed in the 252|class_initialize| method, after the quarks for the recognized strings 253are created. 254 255The converter from String to Icon needs one extra argument, viz., the 256widget for which the icon is loaded. An offset of 0 should give a 257pointer to the widget itself. 258 259@proc class_initialize 260{ 261#ifndef NO_XPM 262 static XtConvertArgRec args[] = { 263 { XtWidgetBaseOffset, 264 0, 265 sizeof(Widget) }, 266 { XtWidgetBaseOffset, 267 (XtPointer) XtOffsetOf(XfwfCommonRec, core.colormap), 268 sizeof(Colormap) }, 269 { XtWidgetBaseOffset, 270 (XtPointer) XtOffsetOf(XfwfCommonRec, xfwfCommon.xcc), 271 sizeof(XCC) } }; 272#endif 273 static XtConvertArgRec colorargs[] = { 274 { XtWidgetBaseOffset, 275 (XtPointer) XtOffsetOf(XfwfCommonRec, core.colormap), 276 sizeof(Colormap) }, 277 { XtWidgetBaseOffset, 278 (XtPointer) XtOffsetOf(XfwfCommonRec, xfwfCommon.xcc), 279 sizeof(XCC) } }; 280 281 XtSetTypeConverter(XtRString, "Alignment", cvtStringToAlignment, 282 NULL, 0, XtCacheNone, NULL); 283 XtSetTypeConverter("Alignment", XtRString, cvtAlignmentToString, 284 NULL, 0, XtCacheNone, NULL); 285 /* init_icon_quarks(); */ 286#ifndef NO_XPM 287 XtSetTypeConverter(XtRString, "Icon", cvtStringToIcon, 288 args, XtNumber(args), XtCacheByDisplay, 289 cvtIconDestructor); 290#endif 291 XtSetTypeConverter(XtRString, "StringArray", cvtStringToStringArray, 292 NULL, 0, XtCacheNone, NULL); 293 XtSetTypeConverter(XtRString, XtRColor, cvtStringToColor, 294 colorargs, XtNumber(colorargs), XtCacheNone, NULL); 295} 296 297 298@ The |extraTranslations| are compiled into Xt's internal form and 299stored in a class variable |traversal_trans|, but only if that hasn't 300been done before. (It should have been done in the |class_initialize| 301method, but wbuild's `|$|' syntax doesn't work there (yet)). 302 303If the widget has the |traversalOn| resource set, the translations are 304merged with the widgets existing translations. 305 306@proc initialize 307{ 308 Dimension frame; 309 310 if ($traversal_trans == NULL) 311 $traversal_trans = XtParseTranslationTable(extraTranslations); 312 if ($traversalOn) { 313 XtAugmentTranslations($, $traversal_trans); 314 $visible_interest = True; 315 } 316 $traversal_focus = FALSE; 317 $bordergc = NULL; 318 frame = $total_frame_width($); 319 if ($width < 2 * frame + 2) $width = 2 * frame + 2; 320 if ($height < 2 * frame + 2) $height = 2 * frame + 2; 321 if ($width == 0) $width = 2; 322 if ($height == 0) $height = 2; 323 create_bordergc($); 324 $background_pixel = $background; 325 $visible = FALSE; /* Why is this needed? */ 326 /* fprintf(stderr, "initialize(%s): visible=%s\n", 327 XtName($), $visible?"TRUE":"FALSE"); */ 328} 329 330 331@ The |set_values| method checks if the keyboard traversal has been 332turned on and adds the traversal translations. (It can only be turned 333on, not turned off.) 334 335If something changes that causes the widget to loose keyboard focus, 336the parent is asked to put the focus somewhere else. Otherwise the 337whole application might suddenly loose keyboard focus. 338 339@proc set_values 340{ 341 Boolean need_redraw = False; 342 Widget parent = XtParent($); 343 Time time = CurrentTime; 344 345 if ($traversalOn != $old$traversalOn && $traversalOn) { 346 XtAugmentTranslations($, $traversal_trans); 347 $visible_interest = True; 348 } 349 if (($sensitive != $old$sensitive 350 || $ancestor_sensitive != $old$ancestor_sensitive 351 || $traversalOn != $old$traversalOn) 352 && $traversal_focus) { 353 if (XtIsSubclass(parent, xfwfCommonWidgetClass)) { 354 if (!$sensitive) { 355 /* 356 * Necessary when $ is highlighted and $sensitive is 357 * changed to FALSE. When this is left out keyboard 358 * traversal doesn't work when $sensitive is changed back 359 * to TRUE. 360 */ 361 $unhighlight_border($); 362 $traversal_focus = FALSE; 363 } 364 $parent$traverse(parent, TraverseHome, $, &time); 365 } 366 } 367 if ($highlightThickness != $old$highlightThickness) 368 need_redraw = True; 369 if ($highlightPixmap != $old$highlightPixmap) { 370 create_bordergc($); 371 need_redraw = True; 372 } else if ($highlightColor != $old$highlightColor) { 373 $highlightPixmap = None; 374 create_bordergc($); 375 need_redraw = True; 376 } 377 if ($background != $old$background) { 378 $background_pixel = $background; 379 need_redraw = True; 380 } 381 return need_redraw; 382} 383 384 385@ A new method |compute_inside| is defined, that returns the area 386inside the highlight border. Subclasses should use this to compute 387their drawable area, in preference to computing it from |$width| and 388|$height|. Subclasses, such as the Frame widget, redefine the method 389if they add more border material. 390 391@proc compute_inside($, Position *x, Position *y, Dimension *w, Dimension *h) 392{ 393 *x = $highlightThickness; 394 *y = $highlightThickness; 395 *w = $width - 2 * $highlightThickness; 396 *h = $height - 2 * $highlightThickness; 397} 398 399@ Another new method, |total_frame_width|, returns the thickness of 400the frame that will be drawn onside the widget. Subclasses will need 401to override this method if they draw other frames. 402 403@proc Dimension total_frame_width($) 404{ 405 return $highlightThickness; 406} 407 408@ The |expose| method draws the highlight border, if there is one. 409 410@proc expose 411{ 412 if (! XtIsRealized($)) return; 413 if (region != NULL) XSetRegion(XtDisplay($), $bordergc, region); 414 if ($traversal_focus) $highlight_border($); 415 if (region != NULL) XSetClipMask(XtDisplay($), $bordergc, None); 416} 417 418 419@ When the widget is destroyed and the widget still has the keyboard 420focus, the parent is asked to give it to another widget. 421 422@proc destroy 423{ 424 Widget parent = XtParent($); 425 Time time = CurrentTime; 426 427 if ($traversal_focus) { 428 $sensitive = False; 429 if (XtIsSubclass(parent, xfwfCommonWidgetClass)) 430 $parent$traverse(parent, TraverseHome, $, &time); 431 } 432 if ($own_xcc) XCCFree($xcc); 433} 434 435@proc realize 436{ 437 Widget p; 438 439 #realize($, mask, attributes); 440 p = $; 441 do p = XtParent(p); while (!XtIsShell(p)); 442 if ($useXCC && XCCGetColormap($xcc) != $p$colormap) 443 XtVaSetValues(p, XtNcolormap, XCCGetColormap($xcc), NULL); 444} 445 446@ The border highlight is drawn and removed with two methods, although 447few subclasses will want to redefine them. The methods are called by 448the |focusIn| and |focusOut| actions and |highlight_border| is also 449called by |expose|. 450 451@proc highlight_border($) 452{ 453 XRectangle rect[4]; 454 455 if ($highlightThickness == 0) return; 456 457 rect[0].x = 0; 458 rect[0].y = 0; 459 rect[0].width = $width; 460 rect[0].height = $highlightThickness; 461 462 rect[1].x = 0; 463 rect[1].y = 0; 464 rect[1].width = $highlightThickness; 465 rect[1].height = $height; 466 467 rect[2].x = $width - $highlightThickness; 468 rect[2].y = 0; 469 rect[2].width = $highlightThickness; 470 rect[2].height = $height; 471 472 rect[3].x = 0; 473 rect[3].y = $height - $highlightThickness; 474 rect[3].width = $width; 475 rect[3].height = $highlightThickness; 476 477 XFillRectangles(XtDisplay($), XtWindow($), $bordergc, &rect[0], 4); 478} 479 480@proc unhighlight_border($) 481{ 482 if ($highlightThickness == 0) return; 483 484 XClearArea(XtDisplay($), XtWindow($), 485 0, 0, $width, $highlightThickness, False); 486 XClearArea(XtDisplay($), XtWindow($), 487 0, 0, $highlightThickness, $height, False); 488 XClearArea(XtDisplay($), XtWindow($), 489 $width - $highlightThickness, 0, 490 $highlightThickness, $height, False); 491 XClearArea(XtDisplay($), XtWindow($), 492 0, $height - $highlightThickness, 493 $width, $highlightThickness, False); 494} 495 496 497@ When the |accept_focus| method is called, the widget should try to set 498the focus to itself or one of its children. If it succeeds, it returns 499|True| else |False|. If there are children, each is asked in turn, 500until one is found that accepts the focus. If none is found, the 501widget checks it's own |sensitive| resource, to see if it can receive 502keyboard events. If so, it sets the focus to itself and returns 503|True|, otherwise |False|. 504 505@proc accept_focus 506{ 507 int i; 508 509 /* fprintf(stderr, "accept_focus(%s): visible=%s\n", 510 XtName($), $visible?"TRUE":"FALSE"); */ 511 if (! XtIsRealized($) || ! XtIsSensitive($) || ! $traversalOn 512 || ! $visible || ! $ancestor_sensitive || $being_destroyed) 513 return FALSE; 514 for (i = 0; i < $num_children; i++) 515 if (XtCallAcceptFocus($children[i], time)) return TRUE; 516 if (! $traversal_focus) { 517 XSetInputFocus(XtDisplay($), XtWindow($), RevertToParent, *time); 518 $traversal_focus = True; 519 $highlight_border($); 520 } 521 return TRUE; 522} 523 524 525@ A Common widget (and most subclasses) return |True| for 526|would_accept_focus|, if the |sensitive|, |visible| and |traversalOn| 527resources are set and none of the children wants the focus. 528 529@proc Boolean would_accept_focus($) 530{ 531 int i; 532 Widget child; 533 534 if (! XtIsRealized($) || ! XtIsSensitive($) || ! $traversalOn 535 || ! $visible || ! $ancestor_sensitive || $being_destroyed) 536 return FALSE; 537 else { 538 for (i = 0; i < $num_children; i++) { 539 child = $children[i]; 540 if (XtIsSubclass(child, xfwfCommonWidgetClass) 541 && $child$would_accept_focus(child)) 542 return FALSE; 543 } 544 return TRUE; 545 } 546} 547 548 549@ The algorithm behind keyboard traversal 550 551@ @ * Handling focus events 552 553If a widget receives a (non-virtual) FocusIn event, this is usually 554caused by the |accept_focus| method of that widget, except in the case 555that a top level widget receives the focus from the window manager. In 556the first case, the window can just draw the highlight border, in the 557second case, the widget should try to set the focus to one of its 558children. 559 560To be able to distinguish the two cases, the |accept_focus| method 561sets the private instance variable |traversal_focus| to |True| before 562it calls |XSetInputFocus|. The |focusIn| action then checks this 563variable and if it is not set, calls the |accept_focus| method. 564 565The |focusOut| action resets |traversal_focus| to |False|. 566 567The |traversal_focus| variable can be interpreted to mean, that the 568widget has the keyboard focus and that it is because of keyboard 569traversal. At least in the Common widget, it can never be |True| when 570|traversalOn| is not set. It can also only be |True| when the widget 571actually has the focus, except in the short time between the 572|XSetInputFocus| call and the delivery of the |FocusIn| event. 573(However, this scheme depends on the |focusOut| action for resetting 574|traversal_focus| to |False|, so, if the translation for the 575|FocusOut| event is overridden, it will break down.) 576 577@ @ * User events 578 579The |traverseXXX| actions can be bound to keyboard events. They call 580the |traverse| method, which will try to change the focus in the 581indicated direction. The directions are: Home, Up, Left, Down, Right, 582Next, Prev. Each direction can be considered a constraint or 583criterium for choosing the focus widget, e.g., `Up' selects the 584nearest widget that is above the current widget. `Next' and `Prev' are 585simpler, in that they do not check the distance, but only the order in 586the list of children. 587 588The |traverseCurrent| action is different. It is usually bound to a 589mouse click and its task is to set the focus to the widget itself. It 590does this by calling |accept_focus| on itself. 591 592The |traverse| method looks for a widget in the indicated direction, 593within the same application. If the direction is not `Next' or `Prev', 594the method first recurses upwards, to the toplevel widget. From there 595it recurses down again, to all children, grandchildren, etc., looking 596for the widget that best matches the criterium. If a widget is found, 597the focus will be set to it with a call to |XSetInputFocus|. The 598private variable |traversal_focus| will be set to |True| to indicate 599that the widget received the focus as a result of keyboard traversal, 600and not from the window manager or any other source. 601 602If the |direction| argument is `Next' or `Prev', |traverse| will try 603to set the focus to a sister widget, using the |accept_focus| method. 604If there is no suitable sister, the parent will be asked to find an 605aunt widget, and so on. 606 607Note that the |traverse| and |accept_focus| methods of the Common 608widget only set the focus to a child, if the widget itself has 609|traversalOn|. Thus, setting |traversalOn| to |False| for a certain 610widget not only excludes the widget itself from keyboard traversal, 611but also all its children. 612 613The |traverse| function is a method and not a utility function, 614because it is expected that a few subclasses may want to redefine it. 615E.g., the (not yet existing) Group widget may want to limit traversal 616to widgets within itself. (And presumably define new actions to jump 617outside the group.) 618 619To check if a widget suits the criterium, two things must be 620determined: is the widget eligible for the focus and what is the 621distance between the widget and the target position. To be able to 622determine if the widget can accept the focus without actually setting 623it, a method |would_accept_focus| is defined, that returns |True| if 624the widget is willing to set the focus to itself. 625 626@ @ If the |dir| argument to |traverse| is |TraverseNext| or 627|TraversePrev|, the |traverse_to_next| or |traverse_to_prev| utility 628functions are called. Otherwise, the |traverse| method checks the 629class of the parent. If the parent is a subclass of |XfwfCommon|, it 630also has a |traverse| method and the task of finding a widget to 631traverse to is delegated to the parent. Otherwise, the desired widget 632is looked for with the help of a utility function. 633 634The |dir| argument is one of Home, Up, Down, Left, Right, Next or 635Prev. The |current| argument holds the widget that currently has the 636focus and relative to which the focus will have to move. 637 638@def LARGE_NUMBER = 2000000000 639 640@proc traverse($, TraversalDirection dir, Widget current, Time *time) 641{ 642 Widget w, parent = XtParent($); 643 Position x, y; 644 int distance = LARGE_NUMBER; 645 646 if (dir == TraverseNextTop) 647 traverse_to_next_top($, current, time); 648 else if (dir == TraverseNext) 649 traverse_to_next($, current, time); 650 else if (dir == TraversePrev) 651 traverse_to_prev($, current, time); 652 else if (XtIsSubclass(parent, xfwfCommonWidgetClass)) 653 $parent$traverse(parent, dir, current, time); 654 else { 655 if ($being_destroyed) return; /* LW, 951114 */ 656 switch (dir) { 657 case TraverseHome: x = 0; y = 0; break; 658 case TraverseLeft: x = 0; y = $current$height/2; break; 659 case TraverseDown: x = $current$width/2; y = $current$height; break; 660 case TraverseRight: x = $current$width; y = $current$height/2; break; 661 case TraverseUp: x = $current$width/2; y = 0; break; 662 } 663 if (dir != TraverseHome) XtTranslateCoords(current, x, y, &x, &y); 664 if (traverse_to_direction($, dir, x, y, &w, &distance)) 665 XtCallAcceptFocus(w, time); 666 } 667} 668 669 670 671@UTILITIES 672 673@ The converter |cvtStringToAlignment| converts strings like `right', 674`top left' and `bottom center' to values of type |Alignment|. 675 676@def done(type, value) = 677 do { 678 if (to->addr != NULL) { 679 if (to->size < sizeof(type)) { 680 to->size = sizeof(type); 681 return False; 682 } 683 *(type*)(to->addr) = (value); 684 } else { 685 static type static_val; 686 static_val = (value); 687 to->addr = (XtPointer)&static_val; 688 } 689 to->size = sizeof(type); 690 return True; 691 } while (0) 692 693@proc Boolean cvtStringToAlignment(Display *display, XrmValuePtr args, Cardinal *num_args, XrmValuePtr from, XrmValuePtr to, XtPointer *converter_data) 694{ 695 Alignment a = 0; 696 char c, *t, *s = (char*) from->addr; 697 698 if (*num_args != 0) 699 XtAppErrorMsg(XtDisplayToApplicationContext(display), 700 "cvtStringToAlignment", "wrongParameters", 701 "XtToolkitError", 702 "String to Alignment conversion needs no arguments", 703 (String*) NULL, (Cardinal*) NULL); 704 705 while (*s) { 706 for (; isspace(*s); s++) ; 707 for (t = s; *t && ! isspace(*t); t++) ; 708 c = *t; 709 *t = '\0'; 710 if (XmuCompareISOLatin1(s, "top") == 0) a |= XfwfTop; 711 else if (XmuCompareISOLatin1(s, "bottom") == 0) a |= XfwfBottom; 712 else if (XmuCompareISOLatin1(s, "center") == 0) ; /* skip */ 713 else if (XmuCompareISOLatin1(s, "left") == 0) a |= XfwfLeft; 714 else if (XmuCompareISOLatin1(s, "right") == 0) a |= XfwfRight; 715 else { 716 XtDisplayStringConversionWarning(display, (char*) from->addr, 717 "Alignment"); 718 break; 719 } 720 *t = c; 721 s = t; 722 } 723 done(Alignment, a); 724 /* NOTREACHED */ 725} 726 727@ The converter |cvtAlignmentToString| does the reverse: it convertes values of type |Alignment| (|int|'s) to strings. 728 729@proc Boolean cvtAlignmentToString(Display *display, XrmValuePtr args, Cardinal *num_args, XrmValuePtr from, XrmValuePtr to, XtPointer *converter_data) 730{ 731 Alignment *a = (Alignment*) from->addr; 732 733 if (*num_args != 0) 734 XtAppErrorMsg(XtDisplayToApplicationContext(display), 735 "cvtAlignmentToString", "wrongParameters", 736 "XtToolkitError", 737 "Alignment to String conversion needs no arguments", 738 (String*) NULL, (Cardinal*) NULL); 739 switch (*a) { 740 case XfwfCenter: done(String, "center"); 741 case XfwfBottom: done(String, "bottom"); 742 case XfwfTop: done(String, "top"); 743 case XfwfLeft: done(String, "left"); 744 case XfwfRight: done(String, "right"); 745 case XfwfBottom + XfwfLeft: done(String, "bottom left"); 746 case XfwfBottom + XfwfRight: done(String, "bottom right"); 747 case XfwfTop + XfwfLeft: done(String, "top left"); 748 case XfwfTop + XfwfRight: done(String, "top right"); 749 default: done(String, "unknown"); 750 } 751 /* NOTREACHED */ 752} 753 754@ The following string is the set of translations that will be added 755to any widget that has |traversalOn| set to |True|. The string is 756compiled into Xt's internal representation by the |class_initialize| 757method. 758 759@var char extraTranslations[] = "\ 760 <FocusIn>: focusIn()\n\ 761 <FocusOut>: focusOut()\n\ 762 <Key>Up: traverseUp()\n\ 763 <Key>Down: traverseDown()\n\ 764 <Key>Left: traverseLeft()\n\ 765 <Key>Right: traverseRight()\n\ 766 <Key>Next: traverseNext()\n\ 767 ~Shift<Key>Tab: traverseNext()\n\ 768 <Key>Prior: traversePrev()\n\ 769 Shift<Key>Tab: traversePrev()\n\ 770 <Key>KP_Enter: traverseNextTop()\n\ 771 <Key>Home: traverseHome()" 772 773 774@ The |create_bordergc| function creates a new GC for filling the 775highlight border with. 776 777@proc create_bordergc($) 778{ 779 XtGCMask mask; 780 XGCValues values; 781 782 if ($bordergc) XtReleaseGC($, $bordergc); 783 if ($highlightPixmap != None) { 784 mask = GCFillStyle | GCTile; 785 values.fill_style = FillTiled; 786 values.tile = $highlightPixmap; 787 } else { 788 mask = GCFillStyle | GCForeground; 789 values.fill_style = FillSolid; 790 values.foreground = $highlightColor; 791 } 792 $bordergc = XtGetGC($, mask, &values); 793} 794 795 796@ The |traverse_to_direction| function returns the nearest child, 797grandchild, etc. in the indicated direction that is willing to accept 798the focus. It returns |False| if no widget is found. The position is the 799absolute coordinates, i.e., relative to the root window. The |distance| 800argument holds the distance from |x,y| of the best widget so far. If the 801function finds a better one, it will return the new distance through 802this parameter. 803 804@proc Boolean traverse_to_direction($, TraversalDirection dir, int x, int y, Widget *found, int *distance) 805{ 806 int i; 807 Position rx, ry; 808 int dist; 809 Boolean found_child = False; 810 811 if (! $traversalOn) return False; 812 /* 813 * First recurse to all descendants 814 */ 815 for (i = 0; i < $num_children; i++) 816 if (XtIsSubclass($children[i], xfwfCommonWidgetClass) 817 && traverse_to_direction($children[i], dir, x, y, found, distance)) 818 found_child = True; 819 if (found_child) return True; 820 /* 821 * No child found, now check own position and distance 822 */ 823 switch (dir) { 824 case TraverseHome: rx = 0; ry = 0; break; 825 case TraverseLeft: rx = $width; ry = $height/2; break; 826 case TraverseDown: rx = $width/2; ry = 0; break; 827 case TraverseRight: rx = 0; ry = $height/2; break; 828 case TraverseUp: rx = $width/2; ry = $height; break; 829 } 830 XtTranslateCoords($, rx, ry, &rx, &ry); 831 if ((dir == TraverseUp && ry > y) 832 || (dir == TraverseLeft && rx > x) 833 || (dir == TraverseDown && ry < y) 834 || (dir == TraverseRight && rx < x)) return False; 835 dist = (rx - x)*(rx - x) + (ry - y)*(ry - y); 836 if (dist >= *distance) return False; 837 /* 838 * We are the best so far, but do we want the focus? 839 */ 840 if (! $would_accept_focus($)) return False; 841 *distance = dist; 842 *found = $; 843 return True; 844} 845 846 847@ The |traverse_to_next| routine looks for the |current| widget among 848its children. If it is found, all children following it will be tried 849until one accepts the focus. If no child does, the routine will try to 850ask the parent to find a sister widget instead. 851 852@proc traverse_to_next($, Widget current, Time *time) 853{ 854 int i = 0; 855 Widget parent = XtParent($); 856 857 while (i < $num_children && $children[i] != current) i++; 858 for (i++; i < $num_children; i++) 859 if (XtCallAcceptFocus($children[i], time)) return; 860 if (XtIsSubclass(parent, xfwfCommonWidgetClass)) 861 $parent$traverse(parent, TraverseNext, $, time); 862} 863 864@ |traverse_to_prev| looks for the |current| widget among the children, 865if it is found, all children before it will be asked in turn to accept 866the focus. If none does, the parent is asked to set the focus to a 867sister instead. 868 869@proc traverse_to_prev($, Widget current, Time *time) 870{ 871 int i = 0; 872 Widget parent = XtParent($); 873 874 while (i < $num_children && $children[i] != current) i++; 875 for (i--; i >= 0; i--) 876 if (XtCallAcceptFocus($children[i], time)) return; 877 if (XtIsSubclass(parent, xfwfCommonWidgetClass)) 878 $parent$traverse(parent, TraversePrev, $, time); 879} 880 881 882@proc traverse_to_next_top($, Widget current, Time *time) 883{ 884 Widget parent = XtParent($); 885 886 if (XtIsSubclass(parent, xfwfCommonWidgetClass)) 887 $parent$traverse(parent, TraverseNextTop, current, time); 888 else 889 XtCallCallbackList($, $nextTop, NULL); 890} 891 892@ |create_xcc| is a resource default proc. It calls |XCCCreate| to 893create an XCC, from which colors will be allocated. It looks for the 894closest ancestor of type shell to get the visual. 895 896However, if the resource |useXCC| is false, it simply returns |NULL|. 897 898@proc create_xcc($, int offset, XrmValue *value) 899{ 900 Visual *visual; 901 Widget w; 902 static XCC xcc; 903 904 if (! $useXCC) { 905 xcc = NULL; 906 $own_xcc = FALSE; 907 } else { 908 for (w = XtParent($); w; w = XtParent(w)) { 909 if (XtIsSubclass(w, xfwfCommonWidgetClass)) { 910 xcc = $w$xcc; 911 $own_xcc = FALSE; 912 break; 913 } else if (XtIsShell(w)) { 914 if (((ShellWidget)w)->shell.visual) 915 visual = ((ShellWidget)w)->shell.visual; 916 else 917 visual = DefaultVisualOfScreen(XtScreen($)); 918 xcc = XCCCreate 919 (XtDisplay($), visual, $usePrivateColormap, 920 $useStandardColormaps, $standardColormap, 921 &$colormap); 922 $own_xcc = TRUE; 923 break; 924 } 925 } 926 } 927 value->addr = (XtPointer) &xcc; 928} 929 930 931@METHODS 932 933@ The method |lighter_color| uses |choose_color| to compute a color 934that is 1.35 times as bright as the color passed in as argument. The 935function result is |True| if a color was allocated, else |False|. 936 937@proc Boolean lighter_color($, Pixel base, Pixel *result) 938{ 939 if (! $useXCC) { 940 return choose_color($, 1.5 /* was 1.35 */, base, result); 941 } else { 942 assert($xcc != NULL); 943 choose_xcc_color($, $xcc, 1.5 /* was 1.35 */, base, result); 944 return TRUE; 945 } 946} 947 948 949@ The method |darker_color| uses |choose_color| to compute a color 950that is 0.6 times as bright as the color passed in as argument. The 951function result is |True| if a color was allocated, else |False|. 952 953@proc Boolean darker_color($, Pixel base, Pixel *result) 954{ 955 if (! $useXCC) { 956 return choose_color($, 0.5 /* was 0.6 */, base, result); 957 } else { 958 assert($xcc != NULL); 959 choose_xcc_color($, $xcc, 0.5 /* was 0.6 */, base, result); 960 return TRUE; 961 } 962} 963 964 965 966@ACTIONS 967 968@ When the widget receives or looses the focus, the border highlight 969is drawn or removed. This action function draws the highlight border 970and in case the widget has set |traversalOn|, it also sets the 971keyboard focus to the widget itself, or one of its children. 972 973However, FocusIn events may also be so-called virtual events, meaning 974that not the receiving widget, but one of its descendants gets the 975real focus. When |focusIn| receives one of those, it removes the 976highlight border. 977 978@def focus_detail(detail) = 979 (detail == NotifyAncestor ? "NotifyAncestor" : 980 detail == NotifyVirtual ? "NotifyVirtual" : 981 detail == NotifyInferior ? "NotifyInferior" : 982 detail == NotifyNonlinear ? "NotifyNonlinear" : 983 detail == NotifyNonlinearVirtual ? "NotifyNonlinearVirtual" : 984 detail == NotifyPointer ? "NotifyPointer" : 985 detail == NotifyPointerRoot ? "NotifyPointerRoot" : 986 detail == NotifyDetailNone ? "NotifyDetailNone" : 987 "???") 988 989@proc focusIn 990{ 991 Time time = CurrentTime; 992 993 if (event->type != FocusIn) 994 XtError("focusIn action may only be bound to FocusIn events"); 995 if (! $traversalOn) 996 return; 997 if (event->xfocus.detail == NotifyAncestor 998 || event->xfocus.detail == NotifyInferior 999 || event->xfocus.detail == NotifyNonlinear) { 1000 if (! $traversal_focus) (void) $accept_focus($, &time); 1001 } else if ($traversal_focus) { 1002 $unhighlight_border($); 1003 $traversal_focus = False; 1004 } 1005} 1006 1007 1008@ This action removes the highlight border. 1009 1010@proc focusOut 1011{ 1012 if (event->type != FocusOut) 1013 XtError("focusOut action may only be bound to FocusOut events"); 1014 if ($traversal_focus) { 1015 $unhighlight_border($); 1016 $traversal_focus = False; 1017 } 1018} 1019 1020 1021@ This and the following actions all call the |traverse| method of the 1022widget's parent, with the appropiate direction arguments. 1023|traverseDown| tries to set the focus to a widget that is located 1024roughly below the current one. 1025 1026@proc traverseDown 1027{ 1028 $traverse($, TraverseDown, $, &event->xkey.time); 1029} 1030 1031 1032@ The action tries to set the focus to a widget that is above the this 1033one. 1034 1035@proc traverseUp 1036{ 1037 $traverse($, TraverseUp, $, &event->xkey.time); 1038} 1039 1040 1041@ |traverseLeft| looks for a widget to the left of the current one and 1042sets the keyboard focus to that. 1043 1044@proc traverseLeft 1045{ 1046 $traverse($, TraverseLeft, $, &event->xkey.time); 1047} 1048 1049 1050@ The action looks for a widget that will aceept the focus to the 1051right of the current one. 1052 1053@proc traverseRight 1054{ 1055 $traverse($, TraverseRight, $, &event->xkey.time); 1056} 1057 1058 1059@ The next sibling gets the focus. The precise order is determined by 1060the parent, but usually is will be the order in which the widgets were 1061created. If there is no suitable sibling, the request is passed to the 1062grandparent, so that an `aunt widget' or other relation can get the 1063focus. 1064 1065@proc traverseNext 1066{ 1067 $traverse($, TraverseNext, $, &event->xkey.time); 1068} 1069 1070 1071@ The previous widget gets the focus. See also the description of 1072|traverseNext| above. 1073 1074@proc traversePrev 1075{ 1076 $traverse($, TraversePrev, $, &event->xkey.time); 1077} 1078 1079 1080@ |traverseNextTop| finds the topmost ancestor that is a subclass of 1081Common and lets it call the |nextTop| callbacks that have been 1082registered there. These callbacks can be used by an application that 1083has multiple top level windows to set the focus to another window. 1084 1085@proc traverseNextTop 1086{ 1087 $traverse($, TraverseNextTop, $, &event->xkey.time); 1088} 1089 1090 1091@ The action sets the focus to the sibling widget that is closest to 1092the upper left corner of the parent. 1093 1094@proc traverseHome 1095{ 1096 $traverse($, TraverseHome, $, &event->xkey.time); 1097} 1098 1099 1100@ The |traverseCurrent| action can be used by widgets to set the focus 1101to themselves. It is not used in the set of translations that is added 1102when |traversalOn| is set to |True|. 1103 1104@proc traverseCurrent 1105{ 1106 Time time = CurrentTime; 1107 1108 if ($traversalOn) (void) $accept_focus($, &time); 1109} 1110 1111 1112 1113 1114@IMPORTS 1115 1116@incl <stdio.h> 1117@incl <assert.h> 1118@incl <X11/Xmu/Converters.h> 1119@incl "Converters.h" 1120@incl <X11/ShellP.h> 1121