.\"remove .ig hn for full docs .de hi .ig eh .. .de eh .. .TH "" 3 "" "Version 3.0" "Free Widget Foundation" .SH NAME XfwfCommon .SH DESCRIPTION The Common class is not meant to be instantiated. It only serves as the common superclass for a family of widgets, to ensure that these widgets have some common methods and resources. The Common class defines common types, symbolic constants, and type converters and it also provides the basis for keyboard traversal. The code for keyboard traversal is roughly based on that in the Xw widget set (created by Hewlett Packard), but it uses the \fIaccept_focus\fP method. When the resource \fItraversalOn\fP is set to \fITrue\fP (either at creation time, or via a \fIXtSetValues\fP later), a set of translations is added to the widget. If the widget's parent is also a subclass of Common, these translations will then implement keyboard traversal, using the cursor keys (up, down, prev, etc.) Of course, when the widget already uses these keys for other purposes, the keyboard traversal will not work. .SS "Public variables" .ps-2 .TS center box; cBsss lB|lB|lB|lB l|l|l|l. XfwfCommon Name Class Type Default XtNtraversalOn XtCTraversalOn Boolean True XtNhighlightThickness XtCHighlightThickness Dimension 2 XtNhighlightColor XtCHighlightColor Pixel XtDefaultForeground XtNhighlightPixmap XtCHighlightPixmap Pixmap None XtNnextTop XtCNextTop Callback NULL XtNuserData XtCUserData Pointer NULL .TE .ps .TP .I "XtNtraversalOn" The resource \fItraversalOn\fP determines whether keyboard traversal is used. If it is \fITrue\fP initially, or if it is set to \fITrue\fP later, a set of translations will be added to the widget. .hi .nf Boolean traversalOn = True .fi .eh .TP .I "XtNhighlightThickness" Keyboard focus is indicated by border highlighting. When keyboard traversal is on and the widget receives the focus, the highlight border is filled with the highlight color or tile. If the widget does not have the focus, the area is left in the default background. .hi .nf Dimension highlightThickness = 2 .fi .eh .TP .I "XtNhighlightColor" The highlight border can have a color or it can be tiled with a pixmap. Whichever of the resources \fIhighlightColor\fP or \fIhighlightPixmap\fP is set latest, is used. When both are set, the pixmap is used. .hi .nf Pixel highlightColor = XtDefaultForeground .fi .eh .TP .I "XtNhighlightPixmap" The \fIhighlightPixmap\fP can be set to a pixmap with which the highlight border will be tiled. Only one of \fIhighlightPixmap\fP and \fIhighlightColor\fP can be set, see above. .hi .nf Pixmap highlightPixmap = None .fi .eh .TP .I "XtNnextTop" When an application has several top level windows, it should have a way of setting the focus between windows. The Enter key in any widget with keyboard traversal on normally invokes the \fItraverseNextTop\fP action, that will call the callbacks of the topmost Common (or subclass) widget in the hierarchy. The callback may set the focus to another top level widget, with \fIXtCallAcceptFocus\fP. .hi .nf XtCallbackList nextTop = NULL .fi .eh .TP .I "XtNuserData" The resource \fIuserData\fP is provided for applications that want to attach their own data to a widget. It is not used by the widget itself in any way. .hi .nf XtPointer userData = NULL .fi .eh .ps-2 .TS center box; cBsss lB|lB|lB|lB l|l|l|l. Composite Name Class Type Default XtNchildren XtCChildren WidgetList NULL insertPosition XtCInsertPosition XTOrderProc NULL numChildren XtCNumChildren Cardinal 0 .TE .ps .ps-2 .TS center box; cBsss lB|lB|lB|lB l|l|l|l. Core Name Class Type Default XtNx XtCX Position 0 XtNy XtCY Position 0 XtNwidth XtCWidth Dimension 0 XtNheight XtCHeight Dimension 0 borderWidth XtCBorderWidth Dimension 0 XtNcolormap XtCColormap Colormap NULL XtNdepth XtCDepth Int 0 destroyCallback XtCDestroyCallback XTCallbackList NULL XtNsensitive XtCSensitive Boolean True XtNtm XtCTm XTTMRec NULL ancestorSensitive XtCAncestorSensitive Boolean False accelerators XtCAccelerators XTTranslations NULL borderColor XtCBorderColor Pixel 0 borderPixmap XtCBorderPixmap Pixmap NULL background XtCBackground Pixel 0 backgroundPixmap XtCBackgroundPixmap Pixmap NULL mappedWhenManaged XtCMappedWhenManaged Boolean True XtNscreen XtCScreen Screen * NULL .TE .ps .SS "Exports" The type \fIAlignment\fP is actually an integer, but it is given a different name to allow a type converter to be installed for it. .nf .B type Alignment = int .fi The symbolic constants can be added together to form an alignment. Various widgets use this to position labels, other widgets, etcetera. \fBdef\fP XfwfCenter = 0 \fBdef\fP XfwfLeft = 1 \fBdef\fP XfwfRight = 2 \fBdef\fP XfwfTop = 4 \fBdef\fP XfwfBottom = 8 For convenience, the eight possible combinations also have symbolic names. \fBdef\fP XfwfTopLeft = (XfwfTop +XfwfLeft ) \fBdef\fP XfwfTopRight = (XfwfTop +XfwfRight ) \fBdef\fP XfwfBottomLeft = (XfwfBottom +XfwfLeft ) \fBdef\fP XfwfBottomRight = (XfwfBottom +XfwfRight ) The directions of traversal are used as arguments to the \fItraverse\fP method. They are probably only useful to subclasses. .nf .B type TraversalDirection = enum { TraverseLeft, TraverseRight, TraverseUp, TraverseDown, TraverseNext, TraversePrev, TraverseHome, TraverseNextTop } .fi To know the inside area of a Common widget might be useful to other widgets than subclasses alone. Calling \fIXfwfCallComputeInside\fP will call the \fIcompute_inside\fP method, if available. .nf XfwfCallComputeInside( $, Position * x, Position * y, Dimension * w, Dimension * h) .fi .hi { if (XtIsSubclass($, xfwfCommonWidgetClass) $compute_inside) $compute_inside($, x, y, w, h); } .eh .hi .SS "Actions" When the widget receives or looses the focus, the border highlight is drawn or removed. This action function draws the highlight border and in case the widget has set \fItraversalOn\fP, it also sets the keyboard focus to the widget itself, or one of its children. However, FocusIn events may also be so-called virtual events, meaning that not the receiving widget, but one of its descendants gets the real focus. When \fIfocusIn\fP receives one of those, it removes the highlight border. .hi \fBdef\fP focus_detail(detail) = (detail ==NotifyAncestor ?"NotifyAncestor":detail ==NotifyVirtual ?"NotifyVirtual":detail ==NotifyInferior ?"NotifyInferior":detail ==NotifyNonlinear ?"NotifyNonlinear":detail ==NotifyNonlinearVirtual ?"NotifyNonlinearVirtual":detail ==NotifyPointer ?"NotifyPointer":detail ==NotifyPointerRoot ?"NotifyPointerRoot":detail ==NotifyDetailNone ?"NotifyDetailNone":"???") .eh .TP .I "focusIn .hi .nf void focusIn($, XEvent* event, String* params, Cardinal* num_params) { Time time = CurrentTime; if (event->type != FocusIn) XtError("focusIn action may only be bound to FocusIn events"); if (! $traversalOn) return; if (event->xfocus.detail == NotifyAncestor || event->xfocus.detail == NotifyInferior || event->xfocus.detail == NotifyNonlinear) { if (! $traversal_focus) (void) $accept_focus($, time); } else if ($traversal_focus) { $unhighlight_border($); $traversal_focus = False; } } .fi .eh .TP .I "focusOut This action removes the highlight border. .hi .nf void focusOut($, XEvent* event, String* params, Cardinal* num_params) { if (event->type != FocusOut) XtError("focusOut action may only be bound to FocusOut events"); if ($traversal_focus) { $unhighlight_border($); $traversal_focus = False; } } .fi .eh .TP .I "traverseDown This and the following actions all call the \fItraverse\fP method of the widget's parent, with the appropiate direction arguments. \fItraverseDown\fP tries to set the focus to a widget that is located roughly below the current one. .hi .nf void traverseDown($, XEvent* event, String* params, Cardinal* num_params) { $traverse($, TraverseDown, $, event->xkey.time); } .fi .eh .TP .I "traverseUp The action tries to set the focus to a widget that is above the this one. .hi .nf void traverseUp($, XEvent* event, String* params, Cardinal* num_params) { $traverse($, TraverseUp, $, event->xkey.time); } .fi .eh .TP .I "traverseLeft \fItraverseLeft\fP looks for a widget to the left of the current one and sets the keyboard focus to that. .hi .nf void traverseLeft($, XEvent* event, String* params, Cardinal* num_params) { $traverse($, TraverseLeft, $, event->xkey.time); } .fi .eh .TP .I "traverseRight The action looks for a widget that will aceept the focus to the right of the current one. .hi .nf void traverseRight($, XEvent* event, String* params, Cardinal* num_params) { $traverse($, TraverseRight, $, event->xkey.time); } .fi .eh .TP .I "traverseNext The next sibling gets the focus. The precise order is determined by the parent, but usually is will be the order in which the widgets were created. If there is no suitable sibling, the request is passed to the grandparent, so that an `aunt widget' or other relation can get the focus. .hi .nf void traverseNext($, XEvent* event, String* params, Cardinal* num_params) { $traverse($, TraverseNext, $, event->xkey.time); } .fi .eh .TP .I "traversePrev The previous widget gets the focus. See also the description of \fItraverseNext\fP above. .hi .nf void traversePrev($, XEvent* event, String* params, Cardinal* num_params) { $traverse($, TraversePrev, $, event->xkey.time); } .fi .eh .TP .I "traverseNextTop \fItraverseNextTop\fP finds the topmost ancestor that is a subclass of Common and lets it call the \fInextTop\fP callbacks that have been registered there. These callbacks can be used by an application that has multiple top level windows to set the focus to another window. .hi .nf void traverseNextTop($, XEvent* event, String* params, Cardinal* num_params) { $traverse($, TraverseNextTop, $, event->xkey.time); } .fi .eh .TP .I "traverseHome The action sets the focus to the sibling widget that is closest to the upper left corner of the parent. .hi .nf void traverseHome($, XEvent* event, String* params, Cardinal* num_params) { $traverse($, TraverseHome, $, event->xkey.time); } .fi .eh .TP .I "traverseCurrent The \fItraverseCurrent\fP action can be used by widgets to set the focus to themselves. It is not used in the set of translations that is added when \fItraversalOn\fP is set to \fITrue\fP. .hi .nf void traverseCurrent($, XEvent* event, String* params, Cardinal* num_params) { Time time = CurrentTime; if ($traversalOn) (void) $accept_focus($, time); } .fi .eh .hi .hi .SH "Importss" Most of the included files have to do with the \fIIcon\fP type. Six icons are preloaded. .nf .B incl .fi .nf .B incl .fi .nf .B incl .fi .hi .hi .SS "Private variables" \fIabs\fP, \fImin\fP and \fImax\fP are used often enough in various subclasses to define them here. They will end up in the private(!) header file. .nf max .fi .nf min .fi .nf abs .fi A private variable is used to track the keyboard focus, but only while traversal is on. If \fItraversal_focus\fP is \fITrue\fP, it means that the widget has keyboard focus and that that focus is a result of keyboard traversal. It also means that the widget's border is highlighted, although that is only visible if the \fIhighlightThickness\fP is positive. .nf Boolean traversal_focus .fi The highlight border is filled with a color or a tile. .nf GC bordergc .fi .hi .hi .SH "Class variables" \fItraversal_trans\fP holds the compiled version of the \fIextraTranslations\fP. .nf XtTranslations traversal_trans = NULL .fi .hi .hi .SS "Methods" The type converter \fIcvtStringToAlignment\fP is installed in the \fIclass_initialize\fP method, after the quarks for the recognized strings are created. The converter from String to Icon needs one extra argument, viz., the widget for which the icon is loaded. An offset of 0 should give a pointer to the widget itself. .nf class_initialize() { static XtConvertArgRec args[] = { { XtWidgetBaseOffset, 0, sizeof(Widget) } }; XtSetTypeConverter(XtRString, "Alignment", cvtStringToAlignment, NULL, 0, XtCacheNone, NULL); XtSetTypeConverter("Alignment", XtRString, cvtAlignmentToString, NULL, 0, XtCacheNone, NULL); /* init_icon_quarks(); */ XtSetTypeConverter(XtRString, "Icon", cvtStringToIcon, args, XtNumber(args), XtCacheNone, NULL); } .fi The \fIextraTranslations\fP are compiled into Xt's internal form and stored in a class variable \fItraversal_trans\fP, but only if that hasn't been done before. (It should have been done in the \fIclass_initialize\fP method, but wbuild's `\fI$\fP' syntax doesn't work there (yet)). If the widget has the \fItraversalOn\fP resource set, the translations are merged with the widgets existing translations. .nf initialize(Widget request, $, ArgList args, Cardinal * num_args) { if ($traversal_trans == NULL) $traversal_trans = XtParseTranslationTable(extraTranslations); if ($traversalOn) { XtAugmentTranslations($, $traversal_trans); $visible_interest = True; } $traversal_focus = False; $bordergc = NULL; create_bordergc($); } .fi The \fIset_values\fP method checks if the keyboard traversal has been turned on and adds the traversal translations. (It can only be turned on, not turned off.) If something changes that causes the widget to loose keyboard focus, the parent is asked to put the focus somewhere else. Otherwise the whole application might suddenly loose keyboard focus. .nf Boolean set_values(Widget old, Widget request, $, ArgList args, Cardinal * num_args) { Boolean need_redraw = False; Widget parent = XtParent($); Time time = CurrentTime; if ($traversalOn != $old$traversalOn $traversalOn) { XtAugmentTranslations($, $traversal_trans); $visible_interest = True; } if (($sensitive != $old$sensitive || $ancestor_sensitive != $old$ancestor_sensitive || $traversalOn != $old$traversalOn) $traversal_focus) { if (XtIsSubclass(parent, xfwfCommonWidgetClass)) $parent$traverse(parent, TraverseHome, $, time); } if ($highlightThickness != $old$highlightThickness) need_redraw = True; if ($highlightPixmap != $old$highlightPixmap) { create_bordergc($); need_redraw = True; } else if ($highlightColor != $old$highlightColor) { $highlightPixmap = None; create_bordergc($); need_redraw = True; } return need_redraw; } .fi A new method \fIcompute_inside\fP is defined, that returns the area inside the highlight border. Subclasses should use this to compute their drawable area, in preference to computing it from \fI$width\fP and \fI$height\fP. Subclasses, such as the Frame widget, redefine the method if they add more border material. .nf compute_inside($, Position * x, Position * y, Dimension * w, Dimension * h) { *x = $highlightThickness; *y = $highlightThickness; *w = $width - 2 * $highlightThickness; *h = $height - 2 * $highlightThickness; } .fi The \fIexpose\fP method draws the highlight border, if there is one. .nf expose($, XEvent * event, Region region) { if (! XtIsRealized($)) return; if (region != NULL) XSetRegion(XtDisplay($), $bordergc, region); if ($traversal_focus) $highlight_border($); if (region != NULL) XSetClipMask(XtDisplay($), $bordergc, None); } .fi When the widget is destroyed and the widget still has the keyboard focus, the parent is asked to give it to another widget. .nf destroy($) { Widget parent = XtParent($); Time time = CurrentTime; if ($traversal_focus) { $sensitive = False; if (XtIsSubclass(parent, xfwfCommonWidgetClass)) $parent$traverse(parent, TraverseHome, $, time); } } .fi The border highlight is drawn and removed with two methods, although few subclasses will want to redefine them. The methods are called by the \fIfocusIn\fP and \fIfocusOut\fP actions and \fIhighlight_border\fP is also called by \fIexpose\fP. .nf highlight_border($) { XRectangle rect[4]; if ($highlightThickness == 0) return; rect[0].x = 0; rect[0].y = 0; rect[0].width = $width; rect[0].height = $highlightThickness; rect[1].x = 0; rect[1].y = 0; rect[1].width = $highlightThickness; rect[1].height = $height; rect[2].x = $width - $highlightThickness; rect[2].y = 0; rect[2].width = $highlightThickness; rect[2].height = $height; rect[3].x = 0; rect[3].y = $height - $highlightThickness; rect[3].width = $width; rect[3].height = $highlightThickness; XFillRectangles(XtDisplay($), XtWindow($), $bordergc, rect[0], 4); } .fi .nf unhighlight_border($) { if ($highlightThickness == 0) return; XClearArea(XtDisplay($), XtWindow($), 0, 0, $width, $highlightThickness, False); XClearArea(XtDisplay($), XtWindow($), 0, 0, $highlightThickness, $height, False); XClearArea(XtDisplay($), XtWindow($), $width - $highlightThickness, 0, $highlightThickness, $height, False); XClearArea(XtDisplay($), XtWindow($), 0, $height - $highlightThickness, $width, $highlightThickness, False); } .fi When the \fIaccept_focus\fP method is called, the widget should try to set the focus to itself or one of its children. If it succeeds, it returns \fITrue\fP else \fIFalse\fP. If there are children, each is asked in turn, until one is found that accepts the focus. If none is found, the widget checks it's own \fIsensitive\fP resource, to see if it can receive keyboard events. If so, it sets the focus to itself and returns \fITrue\fP, otherwise \fIFalse\fP. .nf Boolean accept_focus($, Time * time) { int i; if (! XtIsRealized($) || ! $sensitive || ! $traversalOn || ! $visible || ! $ancestor_sensitive || ! $managed || ! $mapped_when_managed || $being_destroyed) return False; for (i = 0; i < $num_children; i++) if (XtCallAcceptFocus($children[i], time)) return True; if (! $traversal_focus) { XSetInputFocus(XtDisplay($), XtWindow($), RevertToParent, *time); $traversal_focus = True; $highlight_border($); } return True; } .fi A Common widget (and most subclasses) return \fITrue\fP for \fIwould_accept_focus\fP, if the \fIsensitive\fP, \fIvisible\fP and \fItraversalOn\fP resources are set and none of the children wants the focus. .nf Boolean would_accept_focus($) { int i; Widget child; if (! XtIsRealized($) || ! $sensitive || ! $visible || ! $traversalOn) return False; else { for (i = 0; i < $num_children; i++) { child = $children[i]; if (XtIsSubclass(child, xfwfCommonWidgetClass) $child$would_accept_focus(child)) return False; } return True; } } .fi The algorithm behind keyboard traversal * Handling focus events If a widget receives a (non-virtual) FocusIn event, this is usually caused by the \fIaccept_focus\fP method of that widget, except in the case that a top level widget receives the focus from the window manager. In the first case, the window can just draw the highlight border, in the second case, the widget should try to set the focus to one of its children. To be able to distinguish the two cases, the \fIaccept_focus\fP method sets the private instance variable \fItraversal_focus\fP to \fITrue\fP before it calls \fIXSetInputFocus\fP. The \fIfocusIn\fP action then checks this variable and if it is not set, calls the \fIaccept_focus\fP method. The \fIfocusOut\fP action resets \fItraversal_focus\fP to \fIFalse\fP. The \fItraversal_focus\fP variable can be interpreted to mean, that the widget has the keyboard focus and that it is because of keyboard traversal. At least in the Common widget, it can never be \fITrue\fP when \fItraversalOn\fP is not set. It can also only be \fITrue\fP when the widget actually has the focus, except in the short time between the \fIXSetInputFocus\fP call and the delivery of the \fIFocusIn\fP event. (However, this scheme depends on the \fIfocusOut\fP action for resetting \fItraversal_focus\fP to \fIFalse\fP, so, if the translation for the \fIFocusOut\fP event is overridden, it will break down.) * User events The \fItraverseXXX\fP actions can be bound to keyboard events. They call the \fItraverse\fP method, which will try to change the focus in the indicated direction. The directions are: Home, Up, Left, Down, Right, Next, Prev. Each direction can be considered a constraint or criterium for choosing the focus widget, e.g., `Up' selects the nearest widget that is above the current widget. `Next' and `Prev' are simpler, in that they do not check the distance, but only the order in the list of children. The \fItraverseCurrent\fP action is different. It is usually bound to a mouse click and its task is to set the focus to the widget itself. It does this by calling \fIaccept_focus\fP on itself. The \fItraverse\fP method looks for a widget in the indicated direction, within the same application. If the direction is not `Next' or `Prev', the method first recurses upwards, to the toplevel widget. From there it recurses down again, to all children, grandchildren, etc., looking for the widget that best matches the criterium. If a widget is found, the focus will be set to it with a call to \fIXSetInputFocus\fP. The private variable \fItraversal_focus\fP will be set to \fITrue\fP to indicate that the widget received the focus as a result of keyboard traversal, and not from the window manager or any other source. If the \fIdirection\fP argument is `Next' or `Prev', \fItraverse\fP will try to set the focus to a sister widget, using the \fIaccept_focus\fP method. If there is no suitable sister, the parent will be asked to find an aunt widget, and so on. Note that the \fItraverse\fP and \fIaccept_focus\fP methods of the Common widget only set the focus to a child, if the widget itself has \fItraversalOn\fP. Thus, setting \fItraversalOn\fP to \fIFalse\fP for a certain widget not only excludes the widget itself from keyboard traversal, but also all its children. The \fItraverse\fP function is a method and not a utility function, because it is expected that a few subclasses may want to redefine it. E.g., the (not yet existing) Group widget may want to limit traversal to widgets within itself. (And presumably define new actions to jump outside the group.) To check if a widget suits the criterium, two things must be determined: is the widget eligible for the focus and what is the distance between the widget and the target position. To be able to determine if the widget can accept the focus without actually setting it, a method \fIwould_accept_focus\fP is defined, that returns \fITrue\fP if the widget is willing to set the focus to itself. If the \fIdir\fP argument to \fItraverse\fP is \fITraverseNext\fP or \fITraversePrev\fP, the \fItraverse_to_next\fP or \fItraverse_to_prev\fP utility functions are called. Otherwise, the \fItraverse\fP method checks the class of the parent. If the parent is a subclass of \fIXfwfCommon\fP, it also has a \fItraverse\fP method and the task of finding a widget to traverse to is delegated to the parent. Otherwise, the desired widget is looked for with the help of a utility function. The \fIdir\fP argument is one of Home, Up, Down, Left, Right, Next or Prev. The \fIcurrent\fP argument holds the widget that currently has the focus and relative to which the focus will have to move. \fBdef\fP LARGE_NUMBER = 2000000000 .nf traverse($, TraversalDirection dir, Widget current, Time * time) { Widget w, parent = XtParent($); Position x, y; int distance = LARGE_NUMBER; if (dir == TraverseNextTop) traverse_to_next_top($, current, time); else if (dir == TraverseNext) traverse_to_next($, current, time); else if (dir == TraversePrev) traverse_to_prev($, current, time); else if (XtIsSubclass(parent, xfwfCommonWidgetClass)) $parent$traverse(parent, dir, current, time); else { switch (dir) { case TraverseHome: x = 0; y = 0; break; case TraverseLeft: x = 0; y = $current$height/2; break; case TraverseDown: x = $current$width/2; y = $current$height; break; case TraverseRight: x = $current$width; y = $current$height/2; break; case TraverseUp: x = $current$width/2; y = 0; break; } if (dir != TraverseHome) XtTranslateCoords(current, x, y, x, y); if (traverse_to_direction($, dir, x, y, w, distance)) XtCallAcceptFocus(w, time); } } .fi To choose a color that is somewhat darker or lighter than another color, the function \fIchoose_color\fP queries the RGB values of a pixel and multiplies them with a factor. If all goes well, the function returns \fITrue\fP. If the chosen color ends up being the same as the original, the color gray75 is returned instead. It is defined as a method, to make it accessible to subclasses. There seems to be no need to make it available globally. .nf Boolean choose_color($, double factor, Pixel base, Pixel * result) { XColor color1, color2, dummy; color1.pixel = base; XQueryColor(XtDisplay($), DefaultColormapOfScreen(XtScreen($)), color1); color2.red = min(65535, factor * color1.red); color2.green = min(65535, factor * color1.green); color2.blue = min(65535, factor * color1.blue); if (! XAllocColor(XtDisplay($), DefaultColormapOfScreen(XtScreen($)), color2)) return False; if (base == color2.pixel) { if (! XAllocNamedColor(XtDisplay($), DefaultColormapOfScreen(XtScreen($)), "gray75", color2, dummy)) return False; } *result = color2.pixel; return True; } .fi The method \fIlighter_color\fP uses \fIchoose_color\fP to compute a color that is 1.5 times as bright as the color passed in as argument. The function result is \fITrue\fP if a color was allocated, else \fIFalse\fP. .nf Boolean lighter_color($, Pixel base, Pixel * result) { return choose_color($, 1.5, base, result); } .fi The method \fIdarker_color\fP uses \fIchoose_color\fP to compute a color that is 2/3 times as bright as the color passed in as argument. The function result is \fITrue\fP if a color was allocated, else \fIFalse\fP. .nf Boolean darker_color($, Pixel base, Pixel * result) { return choose_color($, 0.667, base, result); } .fi .hi .hi .SH "Utilities" The converter \fIcvtStringToAlignment\fP converts strings like `right', `top left' and `bottom center' to values of type \fIAlignment\fP. \fBdef\fP done(type, value) = do { if (to->addr != NULL) { if (to->size < sizeof(type)) { to->size = sizeof(type); return False; } *(type*)(to->addr) = (value); } else { static type static_val; static_val = (value); to->addr = (XtPointer)static_val; } to->size = sizeof(type); return True; }while (0 ) .nf Boolean cvtStringToAlignment(Display * display, XrmValuePtr args, Cardinal * num_args, XrmValuePtr from, XrmValuePtr to, XtPointer * converter_data) { Alignment a = 0; char c, *t, *s = (char*) from->addr; if (*num_args != 0) XtAppErrorMsg(XtDisplayToApplicationContext(display), "cvtStringToAlignment", "wrongParameters", "XtToolkitError", "String to Alignment conversion needs no arguments", (String*) NULL, (Cardinal*) NULL); while (*s) { for (; isspace(*s); s++) ; for (t = s; *t ! isspace(*t); t++) ; c = *t; *t = '\\0'; if (XmuCompareISOLatin1(s, "top") == 0) a |= XfwfTop; else if (XmuCompareISOLatin1(s, "bottom") == 0) a |= XfwfBottom; else if (XmuCompareISOLatin1(s, "center") == 0) ; /* skip */ else if (XmuCompareISOLatin1(s, "left") == 0) a |= XfwfLeft; else if (XmuCompareISOLatin1(s, "right") == 0) a |= XfwfRight; else { XtDisplayStringConversionWarning(display, (char*) from->addr, "Alignment"); break; } *t = c; s = t; } done(Alignment, a); } .fi The converter \fIcvtAlignmentToString\fP does the reverse: it convertes values of type \fIAlignment\fP (\fIint\fP's) to strings. .nf Boolean cvtAlignmentToString(Display * display, XrmValuePtr args, Cardinal * num_args, XrmValuePtr from, XrmValuePtr to, XtPointer * converter_data) { Alignment *a = (Alignment*) from->addr; if (*num_args != 0) XtAppErrorMsg(XtDisplayToApplicationContext(display), "cvtAlignmentToString", "wrongParameters", "XtToolkitError", "Alignment to String conversion needs no arguments", (String*) NULL, (Cardinal*) NULL); switch (*a) { case XfwfCenter: done(String, "center"); case XfwfBottom: done(String, "bottom"); case XfwfTop: done(String, "top"); case XfwfLeft: done(String, "left"); case XfwfRight: done(String, "right"); case XfwfBottom + XfwfLeft: done(String, "bottom left"); case XfwfBottom + XfwfRight: done(String, "bottom right"); case XfwfTop + XfwfLeft: done(String, "top left"); case XfwfTop + XfwfRight: done(String, "top right"); default: done(String, "unknown"); } } .fi The following string is the set of translations that will be added to any widget that has \fItraversalOn\fP set to \fITrue\fP. The string is compiled into Xt's internal representation by the \fIclass_initialize\fP method. .nf char extraTranslations[] = "\\ : focusIn()\\n\\ : focusOut()\\n\\ Up: traverseUp()\\n\\ Down: traverseDown()\\n\\ Left: traverseLeft()\\n\\ Right: traverseRight()\\n\\ Next: traverseNext()\\n\\ ~ShiftTab: traverseNext()\\n\\ Prior: traversePrev()\\n\\ ShiftTab: traversePrev()\\n\\ KP_Enter: traverseNextTop()\\n\\ Home: traverseHome()" .fi The \fIcreate_bordergc\fP function creates a new GC for filling the highlight border with. .nf create_bordergc($) { XtGCMask mask; XGCValues values; if ($bordergc) XtReleaseGC($, $bordergc); if ($highlightPixmap != None) { mask = GCFillStyle | GCTile; values.fill_style = FillTiled; values.tile = $highlightPixmap; } else { mask = GCFillStyle | GCForeground; values.fill_style = FillSolid; values.foreground = $highlightColor; } $bordergc = XtGetGC($, mask, values); } .fi The \fItraverse_to_direction\fP function returns the nearest child, grandchild, etc. in the indicated direction that is willing to accept the focus. It returns \fIFalse\fP if no widget is found. The position is the absolute coordinates, i.e., relative to the root window. The \fIdistance\fP argument holds the distance from \fIx,y\fP of the best widget so far. If the function finds a better one, it will return the new distance through this parameter. .nf Boolean traverse_to_direction($, TraversalDirection dir, int x, int y, Widget * found, int * distance) { int i; Widget child, w; Position rx, ry; int dist; Boolean found_child = False; if (! $traversalOn) return False; /* * First recurse to all descendants */ for (i = 0; i < $num_children; i++) if (XtIsSubclass($children[i], xfwfCommonWidgetClass) traverse_to_direction($children[i], dir, x, y, found, distance)) found_child = True; if (found_child) return True; /* * No child found, now check own position and distance */ switch (dir) { case TraverseHome: rx = 0; ry = 0; break; case TraverseLeft: rx = $width; ry = $height/2; break; case TraverseDown: rx = $width/2; ry = 0; break; case TraverseRight: rx = 0; ry = $height/2; break; case TraverseUp: rx = $width/2; ry = $height; break; } XtTranslateCoords($, rx, ry, rx, ry); if ((dir == TraverseUp ry > y) || (dir == TraverseLeft rx > x) || (dir == TraverseDown ry < y) || (dir == TraverseRight rx < x)) return False; dist = (rx - x)*(rx - x) + (ry - y)*(ry - y); if (dist >= *distance) return False; /* * We are the best so far, but do we want the focus? */ if (! $would_accept_focus($)) return False; *distance = dist; *found = $; return True; } .fi The \fItraverse_to_next\fP routine looks for the \fIcurrent\fP widget among its children. If it is found, all children following it will be tried until one accepts the focus. If no child does, the routine will try to ask the parent to find a sister widget instead. .nf traverse_to_next($, Widget current, Time * time) { int i = 0; Widget parent = XtParent($); while (i < $num_children $children[i] != current) i++; for (i++; i < $num_children; i++) if (XtCallAcceptFocus($children[i], time)) return; if (XtIsSubclass(parent, xfwfCommonWidgetClass)) $parent$traverse(parent, TraverseNext, $, time); } .fi \fItraverse_to_prev\fP looks for the \fIcurrent\fP widget among the children, if it is found, all children before it will be asked in turn to accept the focus. If none does, the parent is asked to set the focus to a sister instead. .nf traverse_to_prev($, Widget current, Time * time) { int i = 0; Widget parent = XtParent($); while (i < $num_children $children[i] != current) i++; for (i--; i >= 0; i--) if (XtCallAcceptFocus($children[i], time)) return; if (XtIsSubclass(parent, xfwfCommonWidgetClass)) $parent$traverse(parent, TraversePrev, $, time); } .fi .nf traverse_to_next_top($, Widget current, Time * time) { Widget parent = XtParent($); if (XtIsSubclass(parent, xfwfCommonWidgetClass)) $parent$traverse(parent, TraverseNextTop, current, time); else XtCallCallbackList($, $nextTop, NULL); } .fi .hi