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