(3)

NAME
XfwfArrow

DESCRIPTION
The Arrow widget is usually part of a composite scrollbar widget. It draws a triangle pointing up, down, left or right, depending on the direction resource. It has a single callback, that is repeatedly called as long as a mouse button -- button 1 by default -- is pressed. The triangle has a 3D shadow, the size of which can be controlled with a resource. The shadow is either stippled or colored, depending on the shadowScheme and associated resources (see the XfwfFrame widget).

  Public variables
XfwfArrow
Name | Class | Type | Default
XtNdirection | XtCDirection | Alignment | XfwfTop
XtNforeground | XtCForeground | Pixel | XtDefaultBackground
XtNarrowShadow | XtCArrowShadow | Dimension | 2
XtNinitialDelay | XtCInitialDelay | Cardinal | 500
XtNrepeatDelay | XtCRepeatDelay | Cardinal | 200
XtNcallback | XtCCallback | Callback | NULL

XtNdirection
    The direction of the arrow (triangle) is given by the direction resource, which is of type Alignment. Only "top" (XfwfTop), "bottom" (XfwfBottom), "left" (XfwfLeft) and "right" (XfwfRight) are valid directions. Other directions result in a warning.
    Alignment  direction = XfwfTop

XtNforeground
    The color of the arrow also determines the color of the 3D shadow, at least if shadowScheme is set to XfwfAuto, as it is by default.
    Pixel  foreground = <String>XtDefaultBackground

XtNarrowShadow
    The width of the arrow's shadow is by default 2 pixels.
    Dimension  arrowShadow = 2

XtNinitialDelay
    When the user presses and then holds the mouse button, the action function waits some milliseconds before it starts repeating the callbacks.
    Cardinal  initialDelay = 500

XtNrepeatDelay
    Between repeated calls to the callback routines, the arrow widget will wait a few milliseconds.
    Cardinal  repeatDelay = 200

XtNcallback
    The callback function is called by the activate action. It is called repeatedly until the mouse button that triggered the action is released again.
    <Callback> XtCallbackList  callback = NULL
    XfwfBoard
    Name | Class | Type | Default
    XtNabs_x | XtCAbs_x | Position | 0
    XtNrel_x | XtCRel_x | Float | "0.0"
    XtNabs_y | XtCAbs_y | Position | 0
    XtNrel_y | XtCRel_y | Float | "0.0"
    XtNabs_width | XtCAbs_width | Position | 0
    XtNrel_width | XtCRel_width | Float | "1.0"
    XtNabs_height | XtCAbs_height | Position | 0
    XtNrel_height | XtCRel_height | Float | "1.0"
    XtNhunit | XtCHunit | Float | "1.0"
    XtNvunit | XtCVunit | Float | "1.0"
    XtNlocation | XtCLocation | String | NULL
    XfwfFrame
    Name | Class | Type | Default
    XtNcursor | XtCCursor | Cursor | None
    XtNframeType | XtCFrameType | FrameType | XfwfRaised
    XtNframeWidth | XtCFrameWidth | Dimension | 0
    XtNouterOffset | XtCOuterOffset | Dimension | 0
    XtNinnerOffset | XtCInnerOffset | Dimension | 0
    XtNshadowScheme | XtCShadowScheme | ShadowScheme | XfwfAuto
    XtNtopShadowColor | XtCTopShadowColor | Pixel | compute_topcolor
    XtNbottomShadowColor | XtCBottomShadowColor | Pixel | compute_bottomcolor
    XtNtopShadowStipple | XtCTopShadowStipple | Bitmap | NULL
    XtNbottomShadowStipple | XtCBottomShadowStipple | Bitmap | NULL
    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
    Composite
    Name | Class | Type | Default
    XtNchildren | XtCChildren | WidgetList | NULL
    insertPosition | XtCInsertPosition | XTOrderProc | NULL
    numChildren | XtCNumChildren | Cardinal | 0
    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

  Translations
<Btn1Down>: activate_and_start_timer()
<Btn1Up>: stop_timer()

  Actions

activate_and_start_timer
    The activate action calls the callback routine once and installs a timeout routine.
    void activate_and_start_timer($, XEvent* event, String* params, Cardinal* num_params)
    {
        if (event->type != ButtonPress) {
            XtWarning("The Arrow activate action isn't bound to a BtnDown event");
    	return;
        }
        XtCallCallbackList($, $callback, NULL);
        $timer = XtAppAddTimeOut(XtWidgetToApplicationContext($),
    			     $initialDelay, timer_callback, $);
    }

stop_timer
    void stop_timer($, XEvent* event, String* params, Cardinal* num_params)
    {
        XtRemoveTimeOut($timer);
    }

Importss
The stipple for the shadows are loaded from a bitmap file.
incl
 "stip4.bm"
incl
 <stdio.h>
incl
 <assert.h>

  Private variables
The three GC's are used for drawing the arrow and its shadows.
GC  arrowgc
GC  arrowlightgc
GC  arrowdarkgc
The repeating callback is implemented with a time out routine. The timer is a private variable of the widget.
XtIntervalId  timer

  Methods
The initialize method sets initial values for the three GC's and checks the direction resource.
initialize(Widget  request, $, ArgList  args, Cardinal * num_args)
{
    if ($direction != XfwfTop  $direction != XfwfLeft
	 $direction != XfwfRight  $direction != XfwfBottom) {
	XtWarning("direction of Arrow widget incorrect; set to `top'");
	$direction = XfwfTop;
    }
    $arrowgc = NULL; create_arrowgc($);
    $arrowlightgc = NULL; create_arrowlightgc($);
    $arrowdarkgc = NULL; create_arrowdarkgc($);
}
When the foreground, arrowShadow or direction resource changes, the widget has to be redrawn. Like in the initialize method, the direction resource needs to be checked for valid values. If the inherited resource shadowScheme or one of its family changes, new GC's need to be created.
Boolean  set_values(Widget  old, Widget  request, $, ArgList  args, Cardinal * num_args)
{
    Boolean need_redisplay = False;
    if ($direction != XfwfTop  $direction != XfwfLeft
	 $direction != XfwfRight  $direction != XfwfBottom) {
	XtWarning("direction of Arrow widget incorrect; set to `top'");
	$direction = XfwfTop;
    }
    if ($old$direction != $direction)
	need_redisplay = True;
    if ($old$foreground != $foreground) {
	create_arrowgc($);
	need_redisplay = True;
    }
    if ($old$arrowShadow != $arrowShadow)
	need_redisplay = True;
    if ($shadowScheme != $old$shadowScheme) {
	create_arrowdarkgc($);
	create_arrowlightgc($);
	need_redisplay = True;
    } else if ($shadowScheme == XfwfColor) {
	if ($topShadowColor != $old$topShadowColor) {
	    create_arrowlightgc($);
	    need_redisplay = True;
	}
	if ($bottomShadowColor != $old$bottomShadowColor) {
	    create_arrowdarkgc($);
	    need_redisplay = True;
	}
    } else if ($shadowScheme = XfwfStipple) {
	if ($topShadowStipple != $old$topShadowStipple) {
	    create_arrowlightgc($);
	    need_redisplay = True;
	}
	if ($bottomShadowStipple != $old$bottomShadowStipple) {
	    create_arrowdarkgc($);
	    need_redisplay = True;
	}
    }
    return need_redisplay;
}
The arrow is drawn as large as possible. The arrow is actually a triangle with 3D shadows. p1 is the triangle itself, p2, p3 and p4 are the shadows. def point(p, i, xx, yy) = (p[i ].x =xx ),(p[i ].y =yy )
expose($, XEvent * event, Region  region)
{
    XPoint p1[3], p2[4], p3[4], p4[4];
    Position x, y;
    Dimension width, height, a, a2, a3;
    assert($direction == XfwfTop || $direction == XfwfLeft
	   || $direction == XfwfRight || $direction == XfwfBottom);
    if (! XtIsRealized($)) return;
    if (region != NULL) {
	XSetRegion(XtDisplay($), $arrowgc, region);
	XSetRegion(XtDisplay($), $arrowlightgc, region);
	XSetRegion(XtDisplay($), $arrowdarkgc, region);
    }
    $compute_inside($, x, y, width, height);
    a = $arrowShadow;
    switch ($direction) {
    case XfwfTop:
	a2 = (1.0 + 0.71*width/height) * a;
	a3 = (1.0 + 0.83*height/width) * a;
	point(p1, 0, x + width/2,	y + a3);
	point(p1, 1, x + a2,		y + height - a);
	point(p1, 2, x + width - a2,	y + height - a);
	XFillPolygon(XtDisplay($), $window, $arrowgc, p1, 3, Convex,
		     CoordModeOrigin);
	if (a == 0) break;
	point(p2, 0, x + width/2,	y);
	point(p2, 1, x + width/2,	y + a3);
	point(p2, 2, x + width - a2,	y + height - a);
	point(p2, 3, x + width,		y + height);
	point(p3, 0, x + a2,		y + height - a);
	point(p3, 1, x,			y + height);
	point(p3, 2, x + width,		y + height);
	point(p3, 3, x + width - a2,	y + height - a);
	point(p4, 0, x + width/2,	y);
	point(p4, 1, x,			y + height);
	point(p4, 2, x + a2,		y + height - a);
	point(p4, 3, x + width/2,	y + a3);
	XFillPolygon(XtDisplay($), $window, $arrowdarkgc, p2, 4, Convex,
		     CoordModeOrigin);
	XFillPolygon(XtDisplay($), $window, $arrowdarkgc, p3, 4, Convex,
		     CoordModeOrigin);
	XFillPolygon(XtDisplay($), $window, $arrowlightgc, p4, 4, Convex,
		     CoordModeOrigin);
	break;
    case XfwfLeft:
	a2 = (1.0 + 0.83*width/height) * a;
	a3 = (1.0 + 0.71*height/width) * a;
	point(p1, 0, x + a2,		y + height/2);
	point(p1, 1, x + width - a,	y + a3);
	point(p1, 2, x + width - a,	y + height - a3);
	XFillPolygon(XtDisplay($), $window, $arrowgc, p1, 3, Convex,
		     CoordModeOrigin);
	if ($arrowShadow == 0) break;
	point(p2, 0, x + width,		y);
	point(p2, 1, x,			y + height/2);
	point(p2, 2, x + a2,		y + height/2);
	point(p2, 3, x + width - a,	y + a3);
	point(p3, 0, x,			y + height/2);
	point(p3, 1, x + width,		y + height);
	point(p3, 2, x + width - a,	y + height - a3);
	point(p3, 3, x + a2,		y + height/2);
	point(p4, 0, x + width,		y);
	point(p4, 1, x + width - a,	y + a3);
	point(p4, 2, x + width - a,	y + height - a3);
	point(p4, 3, x + width,		y + height);
	XFillPolygon(XtDisplay($), $window, $arrowlightgc, p2, 4, Convex,
		     CoordModeOrigin);
	XFillPolygon(XtDisplay($), $window, $arrowdarkgc, p3, 4, Convex,
		     CoordModeOrigin);
	XFillPolygon(XtDisplay($), $window, $arrowdarkgc, p4, 4, Convex,
		     CoordModeOrigin);
	break;
    case XfwfBottom:
	a2 = (1.0 + 0.71*width/height) * a;
	a3 = (1.0 + 0.83*height/width) * a;
	point(p1, 0, x + width/2,	y + height - a3);
	point(p1, 1, x + a2,		y + a);
	point(p1, 2, x + width - a2,	y + a);
	XFillPolygon(XtDisplay($), $window, $arrowgc, p1, 3, Convex,
		     CoordModeOrigin);
	if ($arrowShadow == 0) break;
	point(p2, 0, x,			y);
	point(p2, 1, x + width/2,	y + height);
	point(p2, 2, x + width/2,	y + height - a3);
	point(p2, 3, x + a2,		y + a);
	point(p3, 0, x + width,		y);
	point(p3, 1, x + width - a2,	y + a);
	point(p3, 2, x + width/2,	y + height - a3);
	point(p3, 3, x + width/2,	y + height);
	point(p4, 0, x,			y);
	point(p4, 1, x + a2,		y + a);
	point(p4, 2, x + width - a2,	y + a);
	point(p4, 3, x + width,		y);
	XFillPolygon(XtDisplay($), $window, $arrowlightgc, p2, 4, Convex,
		     CoordModeOrigin);
	XFillPolygon(XtDisplay($), $window, $arrowdarkgc, p3, 4, Convex,
		     CoordModeOrigin);
	XFillPolygon(XtDisplay($), $window, $arrowlightgc, p4, 4, Convex,
		     CoordModeOrigin);
	break;
    case XfwfRight:
	a2 = (1.0 + 0.83*width/height) * a;
	a3 = (1.0 + 0.71*height/width) * a;
	point(p1, 0, x + width - $arrowShadow,	y + height/2);
	point(p1, 1, x + $arrowShadow,		y + $arrowShadow);
	point(p1, 2, x + $arrowShadow,		y + height - $arrowShadow);
	XFillPolygon(XtDisplay($), $window, $arrowgc, p1, 3, Convex,
		     CoordModeOrigin);
	if ($arrowShadow == 0) break;
	point(p2, 0, x,			y + height);
	point(p2, 1, x + width,		y + height/2);
	point(p2, 2, x + width - a2,	y + height/2);
	point(p2, 3, x + a,		y + height - a3);
	point(p3, 0, x,			y);
	point(p3, 1, x + a,		y + a3);
	point(p3, 2, x + width - a2,	y + height/2);
	point(p3, 3, x + width,		y + height/2);
	point(p4, 0, x,			y);
	point(p4, 1, x,			y + height);
	point(p4, 2, x + a,		y + height - a3);
	point(p4, 3, x + a,		y + a3);
	XFillPolygon(XtDisplay($), $window, $arrowdarkgc, p2, 4, Convex,
		     CoordModeOrigin);
	XFillPolygon(XtDisplay($), $window, $arrowlightgc, p3, 4, Convex,
		     CoordModeOrigin);
	XFillPolygon(XtDisplay($), $window, $arrowlightgc, p4, 4, Convex,
		     CoordModeOrigin);
	break;
    }
    if (region != NULL) {
	XSetClipMask(XtDisplay($), $arrowgc, None);
	XSetClipMask(XtDisplay($), $arrowlightgc, None);
	XSetClipMask(XtDisplay($), $arrowdarkgc, None);
    }
}

Utilities
The time-out calls the timer_callback routine. The routine re-installs the time-out and calls the callback function (but in the reverse order, because we do not want time-outs to overtake each other). The delay is now repeatDelay instead of initialDelay.
timer_callback(XtPointer  client_data, XtIntervalId * timer)
{
    Widget $ = (Widget) client_data;
    XtCallCallbackList($, $callback, NULL);
    $timer = XtAppAddTimeOut(XtWidgetToApplicationContext($),
			     $repeatDelay, timer_callback, $);
}
The GC for the triangle is created by a utility function. It destroys the old GC and then creates a new one, based on the foreground resource.
create_arrowgc($)
{
    XtGCMask mask;
    XGCValues values;
    if ($arrowgc != NULL) XtReleaseGC($, $arrowgc);
    mask = GCForeground;
    values.foreground = $foreground;
    $arrowgc = XtGetGC($, mask, values);
}
The GC for the light shadow is dependent on the inherited shadowScheme resource. It is the same routine as for the shadows in the XfwfFrame widget.
create_arrowlightgc($)
{
    XtGCMask mask;
    XGCValues values;
    if ($arrowlightgc != NULL) XtReleaseGC($, $arrowlightgc);
    switch ($shadowScheme) {
    case XfwfColor:
	mask = GCForeground;
	values.foreground = $topShadowColor;
	break;
    case XfwfStipple:
	mask = GCFillStyle | GCStipple | GCForeground | GCBackground;
	values.fill_style = FillOpaqueStippled;
	values.background = $background_pixel;
	values.stipple = $topShadowStipple;
	values.foreground = WhitePixelOfScreen(XtScreen($));
	break;
    case XfwfAuto:
	if (DefaultDepthOfScreen(XtScreen($)) > 4
	     $lighter_color($, $foreground, values.foreground)) {
	    mask = GCForeground;
	} else {
	    mask = GCFillStyle | GCBackground | GCForeground | GCStipple;
	    values.fill_style = FillOpaqueStippled;
	    values.background = $foreground;
	    values.foreground = WhitePixelOfScreen(XtScreen($));
	    values.stipple =
		XCreateBitmapFromData(XtDisplay($),
				      RootWindowOfScreen(XtScreen($)),
				      stip4_bits, stip4_width, stip4_height);
	}
	break;
    }
    $arrowlightgc = XtGetGC($, mask, values);
}
The routine for the dark part of the shadow is analogous.
create_arrowdarkgc($)
{
    XtGCMask mask;
    XGCValues values;
    if ($arrowdarkgc != NULL) XtReleaseGC($, $arrowdarkgc);
    switch ($shadowScheme) {
    case XfwfColor:
	mask = GCForeground;
	values.foreground = $bottomShadowColor;
	break;
    case XfwfStipple:
	mask = GCFillStyle | GCStipple | GCForeground | GCBackground;
	values.fill_style = FillOpaqueStippled;
	values.stipple = $bottomShadowStipple;
	values.foreground = BlackPixelOfScreen(XtScreen($));
	values.background = $background_pixel;
	break;
    case XfwfAuto:
	if (DefaultDepthOfScreen(XtScreen($)) > 4
	     $darker_color($, $foreground, values.foreground)) {
	    mask = GCForeground;
	} else {
	    mask = GCFillStyle | GCBackground | GCForeground | GCStipple;
	    values.fill_style = FillOpaqueStippled;
	    values.background = $foreground;
	    values.foreground = WhitePixelOfScreen(XtScreen($));
	    values.stipple =
		XCreateBitmapFromData(XtDisplay($),
				      RootWindowOfScreen(XtScreen($)),
				      stip4_bits, stip4_width, stip4_height);
	}
	break;
    }
    $arrowdarkgc = XtGetGC($, mask, values);
}