(3)

NAME
XfwfSlider4

DESCRIPTION
The Slider4 class is equal to the Slider2 class, except for the addition of a sash in the lower right corner of the thumb, with which the thumb can be resized. Since this adds two more dgrees of freedom to the widget (viz., width and height in addition to horizontal and vertical position), the name becomes ``Slider4.'' A callback function is called when the thumb is being resized and another one when the resizing has finished. The control area in the lower right corner of the thumb looks like a triangular button. The actions that already existed in the Slider2 class now also check if the mouse is on that button and resize instead of drag the button in response.

  Public variables
XfwfSlider4
Name | Class | Type | Default
XtNsashColor | XtCSashColor | Pixel | XtDefaultBackground
XtNsashPixmap | XtCSashPixmap | Pixmap | NULL
XtNsashFrameType | XtCSashFrameType | FrameType | XfwfRaised
XtNsashFrameWidth | XtCSashFrameWidth | Dimension | 2

XtNsashColor
    The existence of another area in the widget means that that there are additional resources needed for determining how it will look. The first of these resources is triangleColor, which gives the color of the triangle, unless trianglePixmap is also defined. In that case the pixmap will be used to tile the triangle with.

    Pixel  sashColor = <String>XtDefaultBackground

XtNsashPixmap
    When the sashPixmap resource is set, it overrides the sashColor resource and causes the triangle to be tiled with a pixmap.

    Pixmap  sashPixmap = NULL

XtNsashFrameType
    The triangle also has a frame in one of the four types XfwfRaised, XfwfSunken, XfwfLedged or XfwfChiseled. This frame is also influenced by the setting of the shadowScheme, lightColor, etc., resources (see the Frame class). The type of frame is set with sashFrameType.

    FrameType  sashFrameType = XfwfRaised

XtNsashFrameWidth
    The width of the frame around the sash:

    Dimension  sashFrameWidth = 2
    XfwfSlider2
    Name | Class | Type | Default
    XtNthumbColor | XtCThumbColor | Pixel | XtDefaultBackground
    XtNthumbPixmap | XtCThumbPixmap | Pixmap | NULL
    XtNminsize | XtCMinsize | Dimension | 20
    XtNthumbFrameWidth | XtCThumbFrameWidth | Dimension | 2
    XtNthumbFrameType | XtCThumbFrameType | FrameType | XfwfRaised
    XtNscrollCallback | XtCScrollCallback | Callback | NULL
    XtNscrollResponse | XtCScrollResponse | XTCallbackProc | scroll_response
    XfwfLabel
    Name | Class | Type | Default
    XtNlabel | XtCLabel | String | NULL
    XtNfont | XtCFont | FontStruct | XtDefaultFont
    XtNforeground | XtCForeground | Pixel | XtDefaultForeground
    XtNalignment | XtCAlignment | Alignment | 0
    XtNmargin | XtCMargin | Int | 2
    XtNshrinkToFit | XtCShrinkToFit | Boolean | False
    XtNrvStart | XtCRvStart | Int | 0
    XtNrvLength | XtCRvLength | Int | 0
    XfwfBoard
    Name | Class | Type | Default
    XtNabs_x | XtCAbs_x | Int | 0
    XtNrel_x | XtCRel_x | Float | "0.0"
    XtNabs_y | XtCAbs_y | Int | 0
    XtNrel_y | XtCRel_y | Float | "0.0"
    XtNabs_width | XtCAbs_width | Int | 0
    XtNrel_width | XtCRel_width | Float | "1.0"
    XtNabs_height | XtCAbs_height | Int | 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
    XtNframeOffset | XtCFrameOffset | Dimension | 0
    XtNshadowScheme | XtCShadowScheme | ShadowScheme | XfwfAuto
    XtNlightColor | XtCLightColor | Pixel | XtDefaultBackground
    XtNdarkColor | XtCDarkColor | Pixel | XtDefaultForeground
    XtNlightStipple | XtCLightStipple | Bitmap | NULL
    XtNdarkStipple | XtCDarkStipple | 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
    XtNtranslations | XtCTranslations | XTTranslations | 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
The default translations are the same as in the Slider2 widget, but the actions have added functionality.

<Btn1Down>: start()
<Btn1Motion>: drag()
<Btn1Up>: finish()

  Actions

start
    The start action checks where the mouse is and does one of three things depending on the mouse position. If the mouse is outside the thumb, the scrollCallback list is called with as call_data argument the direction in which the mouse click occurred. If the mouse is on the thumb, but not in the triangle, a drag operation is started and start sets some private variables for use by the next action. If the mouse is on the triangle, a resize operation is started and the private variables are set accordingly.
    void start($, XEvent* event, String* params, Cardinal* num_params)
    {
        Dimension w, h;
        Position x, y;
        float b;
        Boolean outside = False;
        XfwfScrollInfo info;
        if (event->type != ButtonPress  event->type != ButtonRelease
             event->type != MotionNotify)
            XtError("The start action must be bound to a mouse event");
        $compute_thumb($, x, y, w, h);
        if (event->xbutton.x < x) {			/* Left of thumb */
    	info.reason = XfwfSPageLeft;
    	info.flags = XFWF_HPOS;			/* Suggest a value: */
    	info.hpos = max(0.0, $thumb_x - $thumb_wd);
    	outside = True;
    	XtCallCallbackList($, $scrollCallback, info);
        }
        if (event->xbutton.x >= x + w) {		/* Right of thumb */
    	info.reason = XfwfSPageRight;
    	info.flags = XFWF_HPOS;			/* Suggest a value: */
    	info.hpos = min(1.0, $thumb_x + $thumb_wd);
    	outside = True;
    	XtCallCallbackList($, $scrollCallback, info);
        }
        if (event->xbutton.y < y) {			/* Above thumb */
    	info.reason = XfwfSPageUp;
    	info.flags = XFWF_VPOS;			/* Suggest a value: */
    	info.vpos = max(0.0, $thumb_y - $thumb_ht);
    	outside = True;
    	XtCallCallbackList($, $scrollCallback, info);
        }
        if (event->xbutton.y >= y + h) {		/* Below thumb */
    	info.reason = XfwfSPageDown;
    	info.flags = XFWF_VPOS;			/* Suggest a value: */
    	info.vpos = min(1.0, $thumb_y + $thumb_ht);
    	outside = True;
    	XtCallCallbackList($, $scrollCallback, info);
        }
        if (! outside) {				/* Mouse inside the thumb */
            b = y + x + h + w - 2*$thumbFrameWidth
    	    - (min(h, w) - 2 * $thumbFrameWidth)/2;
            if (event->xbutton.y >= -event->xbutton.x + b) {
                $resize_in_progress = True;		/* Inside the triangle */
                $m_delta_x = w - event->xbutton.x;
                $m_delta_y = h - event->xbutton.y;
            } else {
                $drag_in_progress = True;		/* Outside the triangle */
                $m_delta_x = x - event->xbutton.x;
                $m_delta_y = y - event->xbutton.y;
            }
        }
    }

finish
    The finish action checks what type of action just finished and either does nothing (if the action was a click outside the thumb), invokes the drop callbacks (if the mouse was dragging the thumb) or invokes the resize callbacks (if the mouse was pulling the sash). The callbacks get a pointer to a XfwfScrollInfo structure as call_data argument.
    void finish($, XEvent* event, String* params, Cardinal* num_params)
    {
        XfwfScrollInfo info;
        if ($drag_in_progress) {
            $drag_in_progress = False;
    	info.reason = XfwfSMove;
    	info.flags = XFWF_VPOS | XFWF_HPOS;
    	info.hpos = $thumb_x;
    	info.vpos = $thumb_y;
    	XtCallCallbackList($, $scrollCallback, info);
        } else if ($resize_in_progress) {
            $resize_in_progress = False;
    	info.reason = XfwfSZoom;
    	info.flags = XFWF_VSIZE | XFWF_HSIZE;
    	info.vsize = $thumb_ht;
    	info.hsize = $thumb_wd;
    	XtCallCallbackList($, $scrollCallback, info);
        }
    }

drag
    The drag action moves the thumb if a drag is in progress or resizes the thumb if a resize operation is in progress; otherwise, it does nothing. It also calls the scrollCallbacks. Moving the thumb is done by calling the move_thumb method, resizing is done by calling expose.
    void drag($, XEvent* event, String* params, Cardinal* num_params)
    {
        Dimension oldw, oldh, neww, newh, fw, fh, wd, ht;
        Position newx, newy, x, y, oldx, oldy, fx, fy;
        float dum1, dum2;
        XfwfScrollInfo info;
        XEvent pseudoevent;
        XRectangle rect;
        Region reg;
        if (event->type != ButtonPress  event->type != ButtonRelease
             event->type != MotionNotify)
            XtError("The drag action must be bound to a mouse event");
        if ($drag_in_progress) {
            $compute_thumb($, oldx, oldy, wd, ht);
            newx = event->xbutton.x + $m_delta_x;
            newy = event->xbutton.y + $m_delta_y;
            $compute_info($, newx, newy,wd,ht,$thumb_x,$thumb_y,dum1,dum2);
            $move_thumb($, oldx, oldy, wd, ht, newx, newy);
    	info.reason = XfwfSDrag;
    	info.flags = XFWF_VPOS | XFWF_HPOS;
            info.hpos = $thumb_x;
            info.vpos = $thumb_y;
            XtCallCallbackList($, $scrollCallback, info);
        } else if ($resize_in_progress) {
            $compute_thumb($, x, y, oldw, oldh);
            neww = event->xbutton.x + $m_delta_x;
            newh = event->xbutton.y + $m_delta_y;
            $compute_info($, x, y,neww,newh,dum1,dum2,$thumb_wd,$thumb_ht);
            rect.x = pseudoevent.xexpose.x = x;
            rect.y = pseudoevent.xexpose.y = y;
    	rect.height = pseudoevent.xexpose.height = max(oldh, newh);
    	rect.width = pseudoevent.xexpose.width = max(oldw, neww);
    	XClearArea(XtDisplay($), XtWindow($), x, y, rect.width,
    		   rect.height, False);
            reg = XCreateRegion();
            XUnionRectWithRegion(rect, reg, reg);
            $expose($, pseudoevent, reg);
            XDestroyRegion(reg);
    	info.reason= XfwfSStretch;
    	info.flags = XFWF_VSIZE | XFWF_HSIZE;
            info.hsize = $thumb_wd;
            info.vsize = $thumb_ht;
            XtCallCallbackList($, $scrollCallback, info);
        }
    }

  Private variables
Three more GC's are needed to draw the sash and the frame around it.

GC  sashgc
GC  sashlightgc
GC  sashdarkgc
The boolean resize_in_progress is set to True when a resizing action is initiated (by pressing a mouse button on the sash).

Boolean  resize_in_progress

  Methods
The initialize method sets the private variables and calls three routines to set the GC's for drawing the triangle.
initialize(Widget  request, $, ArgList  args, Cardinal * num_args)
{
    $sashgc = NULL; create_sashgc($);
    $sashlightgc = NULL; create_sashlightgc($);
    $sashdarkgc = NULL; create_sashdarkgc($);
    $resize_in_progress = False;
}
The expose method uses the expose method of the superclass to draw the widget and then adds the triangle in the lower right corner of the thumb. Drawing the triangle is so simple (for the X server, not for the programmer :-) that it isn't useful to set the clipping rectangle.
expose($, XEvent * event, Region  region)
{
    Position x, y;
    Dimension w, h, b;
    XPoint p[9];
    if (! XtIsRealized($)) return;
    #expose($, event, region);
    $compute_inside($, x, y, w, h);
    b = min(h, w)/2;
    p[0].x = x + w - b;
    p[0].y = y + h;
    p[1].x = x + w;
    p[1].y = p[0].y;
    p[2].x = p[1].x;
    p[2].y = y + h - b;
    XFillPolygon(XtDisplay($), XtWindow($), $sashgc, p, 3, Convex,
		 CoordModeOrigin);
    if ($sashFrameType == XfwfRaised || $sashFrameType == XfwfSunken) {
	p[0].x = x + w - b + 2 * $sashFrameWidth;
	p[0].y = y + h - $sashFrameWidth;
	p[1].x = x + w - $sashFrameWidth;
	p[1].y = p[0].y;
	p[2].x = p[1].x;
	p[2].y = y + h - b + 2 * $sashFrameWidth;
        p[3].x = x + w;
        p[3].y = y + h - b;
        p[4].x = p[3].x;
        p[4].y = y + h;
        p[5].x = x + w - b;
        p[5].y = p[4].y;
        XFillPolygon(XtDisplay($), XtWindow($),
		     $sashFrameType == XfwfRaised ? $sashdarkgc : $sashlightgc,
		     p, 6, Nonconvex, CoordModeOrigin);
	p[1].x = p[2].x;
	p[1].y = p[2].y;
	p[2].x = p[3].x;
	p[2].y = p[3].y;
	p[3].x = p[5].x;
	p[3].y = p[5].y;
        XFillPolygon(XtDisplay($), XtWindow($),
		     $sashFrameType == XfwfSunken ? $sashdarkgc : $sashlightgc,
		     p, 4, Nonconvex, CoordModeOrigin);
    } else {
	p[0].x = x + w - b + 2 * $sashFrameWidth;
	p[0].y = y + h - $sashFrameWidth;
	p[1].x = x + w - $sashFrameWidth;
	p[1].y = p[0].y;
	p[2].x = p[1].x;
	p[2].y = y + h - b + 2 * $sashFrameWidth;
        p[3].x = x + w - $sashFrameWidth/2;
        p[3].y = y + h - b + $sashFrameWidth;
        p[4].x = p[3].x;
        p[4].y = y + h - $sashFrameWidth/2;
        p[5].x = x + w - b + $sashFrameWidth;
        p[5].y = p[4].y;
        XFillPolygon(XtDisplay($), XtWindow($),
		     $sashFrameType == XfwfChiseled ? $sashdarkgc : $sashlightgc,
		     p, 6, Nonconvex, CoordModeOrigin);
        p[2].x = x + w;
        p[2].y = y + h - b;
        p[1].x = p[2].x;
        p[1].y = y + h;
        p[0].x = x + w - b;
        p[0].y = p[1].y;
        XFillPolygon(XtDisplay($), XtWindow($),
		     $sashFrameType == XfwfChiseled ? $sashlightgc : $sashdarkgc,
		     p, 6, Nonconvex, CoordModeOrigin);
        p[0].x = x + w - b + 2 * $sashFrameWidth;
        p[0].y = y + h - $sashFrameWidth;
        p[1].x = x + w - $sashFrameWidth;
        p[1].y = p[0].y;
        p[2].x = x + w - $sashFrameWidth/2;
        p[2].y = y + h - b + $sashFrameWidth;
        p[3].x = x + w - b + $sashFrameWidth;
        p[3].y = y + h - $sashFrameWidth/2;
        XFillPolygon(XtDisplay($), XtWindow($),
		     $sashFrameType == XfwfChiseled ? $sashlightgc : $sashdarkgc,
		     p, 4, Convex, CoordModeOrigin);
        p[0].x = x + w - b;
        p[0].y = y + h;
        p[1].x = x + w;
        p[1].y = y + h - b;
        XFillPolygon(XtDisplay($), XtWindow($),
		     $sashFrameType == XfwfChiseled ? $sashdarkgc : $sashlightgc,
		     p, 4, Convex, CoordModeOrigin);
    }
}
The set_values method is responsible for creating new GC's when resources change. It also makes sure the sashFrameWidth is even when the frame type is chiseled or ledged.
Boolean  set_values(Widget  old, Widget  request, $, ArgList  args, Cardinal * num_args)
{
    Boolean need_redisplay = False;
    if ($sashPixmap != $old$sashPixmap) {
        XFreePixmap(XtDisplay($), $old$sashPixmap);
        create_sashgc($);
        need_redisplay = True;
    } else if ($sashColor != $old$sashColor) {
        XFreePixmap(XtDisplay($), $sashPixmap);
        $sashPixmap = NULL;
        create_sashgc($);
        need_redisplay = True;
    }
    if ($sashFrameType != $old$sashFrameType)
        need_redisplay = True;
    if ($sashFrameType == XfwfChiseled || $sashFrameType == XfwfLedged)
        $sashFrameWidth = 2 * ($sashFrameWidth/2);
    if ($sashFrameWidth != $old$sashFrameWidth)
        need_redisplay = True;
    if ($shadowScheme != $old$shadowScheme
        || ($shadowScheme == XfwfAuto  $sashColor != $old$sashColor)
        || ($shadowScheme == XfwfStipple  $lightStipple != $old$lightStipple)
        || ($shadowScheme == XfwfColor  $lightColor != $old$lightColor)) {
        create_sashlightgc($);
        need_redisplay = True;
    }
    if ($shadowScheme != $old$shadowScheme
        || ($shadowScheme == XfwfAuto  $sashColor != $old$sashColor)
        || ($shadowScheme == XfwfStipple  $darkStipple != $old$darkStipple)
        || ($shadowScheme == XfwfColor  $darkColor != $old$darkColor)) {
        create_sashdarkgc($);
        need_redisplay = True;
    }
    return need_redisplay = True;
}

Utilities
The create_sashgc function creates a GC for drawing the sash.
create_sashgc($)
{
    XtGCMask mask;
    XGCValues values;
    if ($sashPixmap != NULL) {
        mask = GCTile | GCFillStyle;
        values.tile = $sashPixmap;
        values.fill_style = FillTiled;
    } else {
        mask = GCForeground;
        values.foreground = $sashColor;
    }
    $sashgc = XtGetGC($, mask, values);
}
The GC for the light parts of the frame around the triangle depends on the setting of the shadowScheme resource.
create_sashlightgc($)
{
    XtGCMask mask;
    XGCValues values;
    switch ($shadowScheme) {
    case XfwfColor:
        mask = GCForeground;
        values.foreground = $lightColor;
        break;
    case XfwfStipple:
        mask = GCForeground | GCFillStyle | GCStipple;
        values.foreground = WhitePixelOfScreen(XtScreen($));
        values.fill_style = FillStippled;
        values.stipple = $lightStipple;
        break;
    case XfwfAuto:
        mask = GCForeground;
        if (DefaultDepthOfScreen(XtScreen($)) < 5
            || ! $lighter_color($, $sashColor, values.foreground)) {
            mask = GCForeground | GCFillStyle | GCStipple;
            values.foreground = WhitePixelOfScreen(XtScreen($));
            values.fill_style = FillStippled;
            values.stipple = $stip4;
        }
    }
    $sashlightgc = XtGetGC($, mask, values);
}
The GC for the dark parts of the frame around the triangle.
create_sashdarkgc($)
{
    XtGCMask mask;
    XGCValues values;
    switch ($shadowScheme) {
    case XfwfColor:
        mask = GCForeground;
        values.foreground = $darkColor;
        break;
    case XfwfStipple:
        mask = GCForeground | GCFillStyle | GCStipple;
        values.foreground = BlackPixelOfScreen(XtScreen($));
        values.fill_style = FillStippled;
        values.stipple = $darkStipple;
        break;
    case XfwfAuto:
        mask = GCForeground;
        if (DefaultDepthOfScreen(XtScreen($)) < 5
            || ! $darker_color($, $sashColor, values.foreground)) {
            mask = GCForeground | GCFillStyle | GCStipple;
            values.foreground = BlackPixelOfScreen(XtScreen($));
            values.fill_style = FillStippled;
            values.stipple = $stip4;
        }
    }
    $sashdarkgc = XtGetGC($, mask, values);
}