/* $XConsortium: TabB.c /main/5 1995/07/15 20:42:02 drk $ */ /* * @OPENGROUP_COPYRIGHT@ * COPYRIGHT NOTICE * Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc. * Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group * ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for * the full copyright text. * * This software is subject to an open license. It may only be * used on, with or for operating systems which are themselves open * source systems. You must contact The Open Group for a license * allowing distribution and sublicensing of this software on, with, * or for operating systems which are not Open Source programs. * * See http://www.opengroup.org/openmotif/license for full * details of the license agreement. Any use, reproduction, or * distribution of the program constitutes recipient's acceptance of * this agreement. * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS * PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY * WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY * OR FITNESS FOR A PARTICULAR PURPOSE * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT * NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE * EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. */ /* * HISTORY */ /*********************************** WARNING ********************************** * * ExmTabButton is a demonstration widget. OSF provides this widget * solely to teach programmers how to write their own Motif widgets. * OSF does not support this widget in any way *********************************** WARNING *********************************/ /****************************************************************************** * * TabB.c - ExmTabButton widget. This widget can serve as a Notebook tab. * This widget can affix itself to any parent widget that knows how * to call the appropriate XmQTjoinSide trait methods. * The ExmTabButton widget demonstrates how to * * install the XmQTjoinSide trait. * * use XmeDrawHighlight * * use XmeClearBorder * * use XmeDrawShadows * * use XmeResolvePartOffsets * See the "OSF/Motif Widget Writer's Guide" for details. * ******************************************************************************/ #include /* private header file for XmPrimitive */ #include /* private header file for ExmTabButton widget */ #include /* for XmeDraw functions */ #include /* for XmeTrait functions */ #include /* for representation type facility */ #include /* defines RENDER_TABLE types. */ #include /* for XmQTjoinSide trait */ #include #define WARNING_SHAPE_CHANGED "Widget not allowed to change shape" #define WARNING_NO_SHAPE_EXTENSION "No Shape extension, style = rectangular" /* Define the macros that we want to go thru offset. */ /* No need to offset primitive or core fields; the Intrinsics will not break binary compatibility. */ #define OpenSide(w) \ XmField(w, offsets, ExmTabButton, open_side, XtEnum) #define JoinShadowThickness(w) \ XmField(w, offsets, ExmTabButton, join_shadow_thickness, Dimension) #define VisualArmed(w) \ XmField(w, offsets, ExmCommandButton, visual_armed, Boolean) #define SimpleShape(w) \ XmField(w, offsets, ExmSimple, simple_shape, unsigned char) #define NeedToReconfigure(w) \ XmField(w, offsets, ExmSimple, need_to_reconfigure, Boolean) #define DEFAULT_CORNER_ROUND_PERCENT 40 /* corber size based on percent of the smaller dimension of the button */ #define CORNER_SIZE(w) \ ((((w->core.width < w->core.height) ? w->core.width \ : w->core.height) * DEFAULT_CORNER_ROUND_PERCENT) / 100) /* Declare all static functions. */ static void ClassInitialize(void) ; static void ClassPartInitialize( WidgetClass wc) ; static void Initialize( Widget rw, Widget nw, ArgList args, Cardinal *num_args) ; static void FixVisualPosition( Widget w ); static void Resize( Widget w ); static void FillOpenRoundedRectangle( Display *dpy, Drawable draw, GC top_gc, GC bottom_gc, GC bg_gc, int x, int y, int w, int h, int shad_thick, int corner_size, unsigned char open_side); static void ShapeButton( Widget w); static void Realize( Widget w, XtValueMask *p_valueMask, XSetWindowAttributes *attributes ); static Boolean SetValues( Widget cw, Widget rw, Widget nw, ArgList args, Cardinal *num_args) ; static void GetHighlightRectangle ( ExmTabButtonWidget tb, XRectangle * hrect); static void BorderHighlight( Widget wid) ; static void BorderUnhighlight( Widget wid) ; static void DrawShadow( Widget wid); static void JoinSideSetValue(Widget tab, unsigned char join_side, Dimension join_thickness); static unsigned char JoinSideGetValue(Widget tab, Dimension *join_thickness); /* ExmTabButton provides no new translations. All of its translations are inherited from ExmCommandButton. */ /* ExmTabButton provides only one new resource. Use the resolve part offset type of resource */ static XmPartResource resources[] = { { ExmNopenSide, ExmCOpenSide, ExmROpenSide, sizeof (XtEnum), XmPartOffset (ExmTabButton, open_side), XmRImmediate, (XtPointer) XmLEFT }, }; /* No synthetic resources. */ /* Define the widget class record. See Chapter 3 of the "OSF/Motif Widget Writer's Guide for details. */ externaldef(exmtabbuttonclassrec) ExmTabButtonClassRec exmTabButtonClassRec = { { /* Here is the Core class record. */ /* superclass */ (WidgetClass) &exmCommandButtonClassRec, /* class_name */ "ExmTabButton", /* widget_size */ sizeof(ExmTabButtonPart), /* class_initialize */ ClassInitialize, /* class_part_init */ ClassPartInitialize, /* class_inited */ FALSE, /* initialize */ Initialize, /* initialize_hook */ (XtArgsProc)NULL, /* realize */ Realize, /* actions */ NULL, /* num_actions */ 0, /* resources */ (XtResourceList) resources, /* num_resources */ XtNumber(resources), /* xrm_class */ NULLQUARK, /* compress_motion */ TRUE, /* compress_exposure */ XtExposeCompressMaximal, /* compress_enterlv */ TRUE, /* visible_interest */ FALSE, /* destroy */ NULL, /* resize */ Resize, /* expose */ XtInheritExpose, /* set_values */ SetValues, /* set_values_hook */ (XtArgsFunc)NULL, /* set_values_almost */ XtInheritSetValuesAlmost, /* get_values_hook */ (XtArgsProc)NULL, /* accept_focus */ (XtAcceptFocusProc)NULL, /* version */ XtVersionDontCheck, /* callback_private */ NULL, /* tm_table */ XtInheritTranslations, /* query_geometry */ XtInheritQueryGeometry, /* display_accelerator */ (XtStringProc)NULL, /* extension record */ NULL, }, { /* Here is the XmPrimitive class record. */ /* border_highlight */ BorderHighlight, /* border_unhighlight */ BorderUnhighlight, /* translations */ XtInheritTranslations, /* arm_and_activate */ XmInheritArmAndActivate, /* get resources */ NULL, /* num get_resources */ 0, /* extension */ NULL, }, { /* Here is the ExmSimple class record. */ /* draw_visual */ ExmInheritDrawVisual, /* draw_shadow */ DrawShadow, /* create_gc */ ExmInheritCreateGC, /* destroy_gc */ ExmInheritDestroyGC, /* select_gc */ ExmInheritSelectGC, /* calc_visual_size */ ExmInheritCalcVisualSize, /* calc_widget_size */ ExmInheritCalcWidgetSize, /* reconfigure */ ExmInheritReconfigure, /* extension */ NULL, }, { /* Here is the ExmString class record. */ /* default_render_table_type */ XmBUTTON_RENDER_TABLE, /* extension */ NULL, }, { /* Here is the ExmCommandButton class record. */ /* extension */ NULL, }, { /* Here is the ExmTabButton class record. */ /* extension */ NULL, }, }; /* Establish the widget class name as an externally accessible symbol. Use the "externaldef" macro rather than the "extern" keyword. */ externaldef(exmtabbuttonwidgetclass) WidgetClass exmTabButtonWidgetClass = (WidgetClass)&exmTabButtonClassRec; /* Define static representation type variables here. ExmTabButton introduces one new representation type. */ static String OpenSideNames[] = { "none", "left", "right", "top", "bottom" }; static XmRepTypeId openSideId; /* Define trait structure variables here. */ /* This widget will install the XmQTjoinSide trait. */ static XmConst XmJoinSideTraitRec tabButtonOST = { 0, /* version */ JoinSideSetValue, JoinSideGetValue, }; /* Part Offset table for XmResolvePartOffsets */ static XmOffsetPtr offsets; /************************************************************************* * * ClassInitialize * Called by the Intrinsics the first time a widget of this class is * instantiated. * ************************************************************************/ static void ClassInitialize() { /* Register new representation types. These new representation types will hold the names of the valid values of the ExmNopenSide resource. */ openSideId = XmRepTypeRegister (ExmROpenSide, OpenSideNames, NULL, XtNumber(OpenSideNames)); /* resolve the offsets so that our XmField macro works fine */ XmeResolvePartOffsets(exmTabButtonWidgetClass, &offsets, NULL); } /************************************************************************ * * ClassPartInitialize * Called by the Intrinsics when this widget or a subclass of this * widget is intantiated. * ************************************************************************/ static void ClassPartInitialize( WidgetClass wc ) { /* Install the XmQTjoinSide trait on the ExmTabButton class and on all its subclasses. */ XmeTraitSet((XtPointer) wc, XmQTjoinSide, (XtPointer) &tabButtonOST); } /***************************************************************************** * * Initialize * Called when the widget is instantiated. * ***************************************************************************/ static void Initialize( Widget rw, Widget nw, ArgList args, Cardinal *num_args ) { ExmTabButtonWidgetClass wc = (ExmTabButtonWidgetClass)XtClass(nw); int shape_event_base, shape_error_base; /* Validate the value of ExmNopenSide. If ExmNopenSide does not have a valid value, set its value to XmLEFT. */ if (!XmRepTypeValidValue(openSideId, OpenSide(nw), nw)) OpenSide(nw) = XmLEFT ; /* check if the shape extention is on the server */ if (SimpleShape(nw) != ExmSHAPE_RECTANGLE && !XShapeQueryExtension(XtDisplay(nw), &shape_event_base, &shape_error_base)) { XmeWarning(nw, WARNING_NO_SHAPE_EXTENSION); SimpleShape(nw) = ExmSHAPE_RECTANGLE; } /* default the shadow join to the shadow */ JoinShadowThickness(nw) = Prim_ShadowThickness(nw); if (wc->simple_class.reconfigure) (*(wc->simple_class.reconfigure))(exmTabButtonWidgetClass, nw, NULL); } /***************************************************************************** * * FillOpenRoundedRectangle * Xlib style API for round edge graphics. * This is used for both the bitmap used as a shape and the rendering * of the shaped shadows. * *****************************************************************************/ static void FillOpenRoundedRectangle( Display *dpy, Drawable draw, GC top_gc, GC bottom_gc, GC bg_gc, int x, int y, int w, int h, int shad_thick, int corner_size, unsigned char open_side) { XArc arcs[4]; XRectangle rects[2]; XGCValues top_gcvalues, bottom_gcvalues; Dimension demi_shad = (shad_thick > 1)? (shad_thick/2):shad_thick; if (corner_size > w/2) corner_size = w/2; if (corner_size > h/2) corner_size = h/2; /* first save the current line attributes (have to do all of them because of the weak Xlib API). The default in the GC is 1, so do not change the line width there */ if (shad_thick != 1) { XGetGCValues(dpy, top_gc, GCLineWidth|GCLineStyle|GCCapStyle|GCJoinStyle, &top_gcvalues); /* change the line width and draw the lines */ XSetLineAttributes(dpy, top_gc, shad_thick, LineSolid, CapButt, JoinMiter); if (bottom_gc != top_gc) { XGetGCValues(dpy, bottom_gc, GCLineWidth|GCLineStyle|GCCapStyle|GCJoinStyle, &bottom_gcvalues); XSetLineAttributes(dpy, bottom_gc, shad_thick, LineSolid, CapButt, JoinMiter); } } /* this code is rather long and boring, but there is nothing complex here: we use arcs even for the vertical and horizontal lines, so that the requests are minimized. There are some known visual glitch with shadow = 1. */ if (open_side == XmLEFT) { arcs[0].x = x ; arcs[0].y = y + demi_shad; arcs[0].width = w - corner_size; arcs[0].height = 0; arcs[0].angle1 = 180*64; arcs[0].angle2 = -180*64; arcs[1].x = x + w - 2*corner_size + demi_shad; arcs[1].y = y + demi_shad; arcs[1].width = 2*corner_size - 2*demi_shad; arcs[1].height = 2*corner_size - 2*demi_shad; arcs[1].angle1 = 90*64; arcs[1].angle2 = -50*64; if (shad_thick) XDrawArcs (dpy, draw, top_gc, arcs, 2); if (bg_gc) XFillArcs (dpy, draw, bg_gc, arcs+1, 1); arcs[0].x = x + w - 2*corner_size + demi_shad; arcs[0].y = y + demi_shad; arcs[0].width = 2*corner_size - 2*demi_shad; arcs[0].height = 2*corner_size - 2*demi_shad; arcs[0].angle1 = 40*64; arcs[0].angle2 = -40*64; arcs[1].x = x + w - demi_shad; arcs[1].y = y + corner_size; arcs[1].width = 0; arcs[1].height = h - 2*corner_size; arcs[1].angle1 = 90*64; arcs[1].angle2 = -180*64; arcs[2].x = x + w - 2*corner_size + demi_shad; arcs[2].y = y + h - 2*corner_size + demi_shad ; arcs[2].width = 2*corner_size - 2*demi_shad; arcs[2].height = 2*corner_size - 2*demi_shad; arcs[2].angle1 = 0*64; arcs[2].angle2 = -90*64; arcs[3].x = x ; arcs[3].y = y + h - demi_shad; arcs[3].width = w - corner_size; arcs[3].height = 0 ; arcs[3].angle1 = 180*64; arcs[3].angle2 = -180*64; if (shad_thick) XDrawArcs (dpy, draw, bottom_gc, arcs, 4); if (bg_gc) { XFillArcs (dpy, draw, bg_gc, arcs, 3); /* fill middle with rectangle */ rects[0].x = x; rects[0].y = y; rects[0].width = w - corner_size; rects[0].height = h; rects[1].x = x + w - corner_size; rects[1].y = y + corner_size; rects[1].width = corner_size; rects[1].height = h - corner_size*2; XFillRectangles (dpy, draw, bg_gc, rects, 2); } } else if (open_side == XmBOTTOM) { arcs[0].x = x + demi_shad; arcs[0].y = y + corner_size; arcs[0].width = 0; arcs[0].height = h - corner_size; arcs[0].angle1 = 90*64; arcs[0].angle2 = -180*64; arcs[1].x = x + demi_shad; arcs[1].y = y + demi_shad; arcs[1].width = 2*corner_size - 2*demi_shad; arcs[1].height = 2*corner_size - 2*demi_shad; arcs[1].angle1 = 180*64; arcs[1].angle2 = -90*64; arcs[2].x = x + corner_size ; arcs[2].y = y + demi_shad ; arcs[2].width = w - 2*corner_size ; arcs[2].height = 0; arcs[2].angle1 = 180*64; arcs[2].angle2 = -180*64; arcs[3].x = x + w - 2*corner_size + demi_shad; arcs[3].y = y + demi_shad; arcs[3].width = 2*corner_size - 2*demi_shad; arcs[3].height = 2*corner_size - 2*demi_shad ; arcs[3].angle1 = 90*64; arcs[3].angle2 = -50*64; if (shad_thick) XDrawArcs (dpy, draw, top_gc, arcs, 4); if (bg_gc) XFillArcs (dpy, draw, bg_gc, arcs+1, 3); arcs[0].x = x + w - 2*corner_size + demi_shad; arcs[0].y = y + demi_shad; arcs[0].width = 2*corner_size - 2*demi_shad; arcs[0].height = 2*corner_size - 2*demi_shad; arcs[0].angle1 = 40*64; arcs[0].angle2 = -40*64; arcs[1].x = x + w - demi_shad; arcs[1].y = y + corner_size; arcs[1].width = 0; arcs[1].height = h - corner_size; arcs[1].angle1 = 90*64; arcs[1].angle2 = -180*64; if (shad_thick) XDrawArcs (dpy, draw, bottom_gc, arcs, 2); if (bg_gc) { XFillArcs (dpy, draw, bg_gc, arcs, 1); /* fill middle with rectangle */ rects[0].x = x + corner_size; rects[0].y = y; rects[0].width = w - 2*corner_size; rects[0].height = corner_size; rects[1].x = x ; rects[1].y = y + corner_size; rects[1].width = w; rects[1].height = h - corner_size; XFillRectangles (dpy, draw, bg_gc, rects, 2); } } else if (open_side == XmTOP) { arcs[0].x = x + demi_shad; arcs[0].y = y ; arcs[0].width = 0; arcs[0].height = h - corner_size; arcs[0].angle1 = 90*64; arcs[0].angle2 = -180*64; arcs[1].x = x + demi_shad; arcs[1].y = y + h - 2*corner_size + demi_shad; arcs[1].width = 2*corner_size - 2*demi_shad; arcs[1].height = 2*corner_size - 2*demi_shad; arcs[1].angle1 = 180*64; arcs[1].angle2 = 50*64; if (shad_thick) XDrawArcs (dpy, draw, top_gc, arcs, 2); if (bg_gc) XFillArcs (dpy, draw, bg_gc, arcs+1, 1); arcs[0].x = x + demi_shad; arcs[0].y = y + h - 2*corner_size + demi_shad; arcs[0].width = 2*corner_size - 2*demi_shad; arcs[0].height = 2*corner_size - 2*demi_shad; arcs[0].angle1 = 230*64; arcs[0].angle2 = 40*64; arcs[1].x = x + corner_size; arcs[1].y = y + h - demi_shad; arcs[1].width = w - 2*corner_size; arcs[1].height = 0; arcs[1].angle1 = 180*64; arcs[1].angle2 = -180*64; arcs[2].x = x + w - 2*corner_size + demi_shad; arcs[2].y = y + h - 2*corner_size + demi_shad ; arcs[2].width = 2*corner_size - 2*demi_shad; arcs[2].height = 2*corner_size - 2*demi_shad; arcs[2].angle1 = 270*64; arcs[2].angle2 = 90*64; arcs[3].x = x + w - demi_shad; arcs[3].y = y ; arcs[3].width = 0; arcs[3].height = h - corner_size ; arcs[3].angle1 = 90*64; arcs[3].angle2 = -180*64; if (shad_thick) XDrawArcs (dpy, draw, bottom_gc, arcs, 4); if (bg_gc) { XFillArcs (dpy, draw, bg_gc, arcs, 3); /* fill middle with rectangle */ rects[0].x = x; rects[0].y = y; rects[0].width = w ; rects[0].height = h - corner_size; rects[1].x = x + corner_size; rects[1].y = y + h - corner_size; rects[1].width = w - 2*corner_size; rects[1].height = corner_size; XFillRectangles (dpy, draw, bg_gc, rects, 2); } } else if (open_side == XmRIGHT) { arcs[0].x = x + corner_size; arcs[0].y = y + demi_shad; arcs[0].width = w - corner_size; arcs[0].height = 0; arcs[0].angle1 = 180*64; arcs[0].angle2 = -180*64; arcs[1].x = x + demi_shad; arcs[1].y = y + demi_shad; arcs[1].width = 2*corner_size - 2*demi_shad; arcs[1].height = 2*corner_size - 2*demi_shad; arcs[1].angle1 = 180*64; arcs[1].angle2 = -90*64; arcs[2].x = x + demi_shad ; arcs[2].y = y + corner_size ; arcs[2].width = 0; arcs[2].height = h - 2*corner_size; arcs[2].angle1 = 90*64; arcs[2].angle2 = -180*64; arcs[3].x = x + demi_shad; arcs[3].y = y + h - 2*corner_size + demi_shad; arcs[3].width = 2*corner_size - 2*demi_shad; arcs[3].height = 2*corner_size - 2*demi_shad ; arcs[3].angle1 = 180*64; arcs[3].angle2 = 50*64; if (shad_thick) XDrawArcs (dpy, draw, top_gc, arcs, 4); if (bg_gc) XFillArcs (dpy, draw, bg_gc, arcs+1, 3); arcs[0].x = x + demi_shad; arcs[0].y = y + h - 2*corner_size + demi_shad; arcs[0].width = 2*corner_size - 2*demi_shad; arcs[0].height = 2*corner_size - 2*demi_shad; arcs[0].angle1 = 230*64; arcs[0].angle2 = 40*64; arcs[1].x = x + corner_size; arcs[1].y = y + h - demi_shad; arcs[1].width = w - corner_size; arcs[1].height = 0; arcs[1].angle1 = 180*64; arcs[1].angle2 = -180*64; if (shad_thick) XDrawArcs (dpy, draw, bottom_gc, arcs, 2); if (bg_gc) { XFillArcs (dpy, draw, bg_gc, arcs, 1); /* fill middle with rectangle */ rects[0].x = x ; rects[0].y = y + corner_size; rects[0].width = corner_size; rects[0].height = h - 2*corner_size; rects[1].x = x + corner_size; rects[1].y = y ; rects[1].width = w - corner_size; rects[1].height = h ; XFillRectangles (dpy, draw, bg_gc, rects, 2); } } /* put the line attributes back */ if (shad_thick != 1) { XSetLineAttributes(dpy, top_gc, top_gcvalues.line_width, top_gcvalues.line_style, top_gcvalues.cap_style, top_gcvalues.join_style); if (bottom_gc != top_gc) XSetLineAttributes(dpy, bottom_gc, bottom_gcvalues.line_width, bottom_gcvalues.line_style, bottom_gcvalues.cap_style, bottom_gcvalues.join_style); } } /***************************************************************************** * * ShapeButton * Call on a realized window from Realize and Resize. * Style is rounded edge at this point. * *****************************************************************************/ static void ShapeButton( Widget w) { Pixmap p = XCreatePixmap(XtDisplay(w), XtWindow(w), XtWidth(w), XtHeight(w), 1 ); XGCValues values; GC gc; /* create a gc width 0 to blank the pixmap and then with 1 for the mask */ values.foreground = 0; gc = XCreateGC (XtDisplay(w), p, GCForeground, &values ); XFillRectangle( XtDisplay(w), p, gc, 0, 0, XtWidth(w), XtHeight(w)); XSetForeground (XtDisplay(w), gc, 1); FillOpenRoundedRectangle(XtDisplay(w), p, gc, gc, gc, 0, 0, XtWidth(w), XtHeight(w), Prim_ShadowThickness(w), CORNER_SIZE(w), OpenSide(w)); XShapeCombineMask(XtDisplay(w), XtWindow(w), ShapeBounding, 0, 0, p, ShapeSet ); XShapeCombineMask(XtDisplay(w), XtWindow(w),ShapeClip, 0, 0, p, ShapeSet ); XFreePixmap(XtDisplay(w), p ); XFreeGC (XtDisplay(w), gc); } /***************************************************************************** * * Realize * Called by the Intrinsics to create the window for the widget. This * class's realize method creates a shaped window for this * exact class, but uses the default window otherwise * *****************************************************************************/ static void Realize(Widget w, XtValueMask *p_valueMask, XSetWindowAttributes *attributes ) { /* First call Primitive's method */ xmPrimitiveClassRec.core_class.realize(w, p_valueMask, attributes); /* then reshape the button if needed */ if (SimpleShape(w) != ExmSHAPE_RECTANGLE) ShapeButton(w); } /***************************************************************************** * * FixVisualPosition: tabs are asymmetric, so we need to recenter * the string so it looks right. * ***************************************************************************/ static void FixVisualPosition( Widget w) { /* apply some little correction to the string position as the shadow is only on one side for the TabButton */ if (OpenSide(w) == XmLEFT) ((ExmSimpleWidget)w)->simple.visual.x -= Prim_ShadowThickness(w) ; else if (OpenSide(w) == XmTOP) ((ExmSimpleWidget)w)->simple.visual.y -= Prim_ShadowThickness(w) ; else if (OpenSide(w) == XmRIGHT) ((ExmSimpleWidget)w)->simple.visual.x += Prim_ShadowThickness(w) ; else if (OpenSide(w) == XmBOTTOM) ((ExmSimpleWidget)w)->simple.visual.y += Prim_ShadowThickness(w) ; } /***************************************************************************** * * Resize * Must reshape the window. * ***************************************************************************/ static void Resize( Widget w) { if (XtIsRealized(w) && SimpleShape(w) != ExmSHAPE_RECTANGLE) ShapeButton(w); /* call superclass Resize */ (*exmCommandButtonClassRec.core_class.superclass->core_class.resize)(w); FixVisualPosition(w); } /***************************************************************************** * * SetValues * Called by the Intrinsics when the application tries to set a value * of one of the resources. * ***************************************************************************/ static Boolean SetValues( Widget cw, Widget rw, Widget nw, ArgList args, Cardinal *num_args ) { ExmSimpleWidgetClass wc = (ExmSimpleWidgetClass)XtClass(nw); Boolean expose_flag = False ; /* Make sure that the new value of ExmNopenSide is a valid one. */ if (OpenSide(nw) != OpenSide(cw)) { if (!XmRepTypeValidValue(openSideId, OpenSide(nw), nw)) OpenSide(nw) = OpenSide(cw); else { /* call Resize so the shape and the visual position be reset */ if (wc->core_class.resize) (*(wc->core_class.resize))(nw); expose_flag = True ; } } /* Do no allow change in shape */ if (SimpleShape(cw) != SimpleShape(nw)) { SimpleShape(nw) = SimpleShape(cw); XmeWarning(nw, WARNING_SHAPE_CHANGED); } /* must call our Resize for the shape update */ if (SimpleShape(nw) == ExmSHAPE_OVAL && NeedToReconfigure(nw)) { if (wc->simple_class.reconfigure) (*(wc->simple_class.reconfigure))(exmTabButtonWidgetClass, nw, cw); } return expose_flag ; } /***************************************************************************** * * BorderHighlight * ***************************************************************************/ static void BorderHighlight( Widget wid ) { XRectangle hrect ; ExmTabButtonWidget tb = (ExmTabButtonWidget) wid; tb->primitive.highlighted = True ; /* If there is no need to draw a border highlight, then don't bother. */ if (XtWidth(wid) == 0 || XtHeight( wid) == 0 || tb->primitive.highlight_thickness == 0) return; if (SimpleShape(wid) == ExmSHAPE_OVAL) { /* Render the label using the highlight color for a change. The app must be sure the highlight color and the background and different */ if (tb->string.compound_string && (tb->simple.visual.width != 0) && (tb->simple.visual.height != 0)) { XmStringDraw (XtDisplay(tb), XtWindow(tb), tb->string.render_table, tb->string.compound_string, tb->primitive.highlight_GC, tb->simple.visual.x, tb->simple.visual.y, tb->simple.visual.width, tb->string.alignment, tb->primitive.layout_direction, NULL); } } else { /* Determine where the highlight should be drawn and then draw it. */ GetHighlightRectangle(tb, &hrect); XmeDrawHighlight(XtDisplay( wid), XtWindow( wid), tb->primitive.highlight_GC, hrect.x, hrect.y, hrect.width, hrect.height, tb->primitive.highlight_thickness); } } /***************************************************************************** * * BorderUnhighlight * ***************************************************************************/ static void BorderUnhighlight( Widget wid ) { XRectangle hrect ; ExmTabButtonWidget tb = (ExmTabButtonWidget) wid; /* This flag tells XmPrimitive what the state of the highlights is. By turning it to False, we are alerting XmPrimitive that the highlight is off. */ tb->primitive.highlighted = False ; /* If there is no need to undraw a border highlight, then don't bother. */ if (XtWidth(wid) == 0 || XtHeight(wid) == 0 || tb->primitive.highlight_thickness == 0) return; if (SimpleShape(wid) == ExmSHAPE_OVAL) { /* Undo the render label using the highlight color */ if (tb->string.compound_string && (tb->simple.visual.width != 0) && (tb->simple.visual.height != 0)) { XmStringDraw (XtDisplay(tb), XtWindow(tb), tb->string.render_table, tb->string.compound_string, tb->simple.normal_gc, tb->simple.visual.x, tb->simple.visual.y, tb->simple.visual.width, tb->string.alignment, tb->primitive.layout_direction, NULL); } } else { /* Determine where the highlight should be drawn and then draw it. */ GetHighlightRectangle(tb, &hrect); /* Erase the border highlights. The border highlights of ExmTabButton are located inside the widget; therefore, we can XmeClearBorder rather than XmeDrawHighlight (which is used with the parent color in Motif). */ XmeClearBorder(XtDisplay (wid), XtWindow (wid), hrect.x, hrect.y, hrect.width, hrect.height, tb->primitive.highlight_thickness) ; } } /************************************************************************ * * DrawShadow * ************************************************************************/ static void DrawShadow ( Widget wid) { XPoint pts[3] ; XRectangle hrect ; GC tune_gc; ExmTabButtonWidget tb = (ExmTabButtonWidget) wid; /* ExmTabButton cannot inherit the draw_shadow routine of ExmSimple. This is because ExmSimple's draw_shadow method assumes rectangular shadows; however, ExmTabButton will require both rectangular and curved shadows. That is, the shadows in an ExmTabButton will be partly rectangular and partly curved. The rectangular portions will be drawn with XmeDrawShadows and the curved portions with XFillPolygon. The resulting shadow will be drawn just outside the border highlight. The shadow will have one open side and a 3D appearance. */ if (SimpleShape(wid) == ExmSHAPE_RECTANGLE) { /* Find the dimensions of the border highlight. */ GetHighlightRectangle (tb, &hrect) ; /* Apply a correction to the returned border highlight dimensions since we're drawing the shadow this time around. */ hrect.x -= tb->primitive.shadow_thickness ; hrect.y -= tb->primitive.shadow_thickness ; hrect.width += 2*tb->primitive.shadow_thickness ; hrect.height += 2*tb->primitive.shadow_thickness ; /* Draw the rectangular portion of the shadows. We're drawing a square shadow, but because the position are negative on the open side, it'll look like an open shadow */ XmeDrawShadows (XtDisplay (tb), XtWindow (tb), tb->primitive.top_shadow_GC, tb->primitive.bottom_shadow_GC, hrect.x, hrect.y, hrect.width, hrect.height, tb->primitive.shadow_thickness, VisualArmed(tb) ? XmSHADOW_IN: XmSHADOW_OUT); } else { FillOpenRoundedRectangle(XtDisplay(wid), XtWindow (wid), VisualArmed(tb) ? tb->primitive.bottom_shadow_GC: tb->primitive.top_shadow_GC, VisualArmed(tb) ? tb->primitive.top_shadow_GC: tb->primitive.bottom_shadow_GC, NULL, 0, 0, XtWidth(wid), XtHeight(wid), tb->primitive.shadow_thickness, CORNER_SIZE(wid), OpenSide(wid)); } /* Now draw the shadow join triangle */ /* Determine the dimensions of the curved portions of the shadow. */ if (OpenSide(wid) == XmLEFT) { pts[0].x = 0 ; pts[1].x = JoinShadowThickness(tb) ; pts[2].x = 0 ; if (!VisualArmed(tb)) { /* top part starts with half bottom gc to join clean */ pts[0].y = 0 ; pts[1].y = 0 ; pts[2].y = tb->primitive.shadow_thickness ; } else { /* bottom part starts with half bottom gc to join clean */ pts[0].y = tb->core.height - tb->primitive.shadow_thickness ; pts[1].y = tb->core.height ; pts[2].y = tb->core.height ; } tune_gc = tb->primitive.bottom_shadow_GC ; } else if (OpenSide(wid) == XmRIGHT) { pts[0].x = tb->core.width - JoinShadowThickness(tb) ; pts[1].x = tb->core.width ; pts[2].x = tb->core.width ; if (!VisualArmed(tb)) { /* bottom part ends with half top gc to join clean */ pts[0].y = tb->core.height ; pts[1].y = tb->core.height - tb->primitive.shadow_thickness ; pts[2].y = tb->core.height ; } else { /* top part ends with half top gc to join clean */ pts[0].y = 0 ; pts[1].y = tb->primitive.shadow_thickness ; pts[2].y = 0 ; } tune_gc = tb->primitive.top_shadow_GC ; } else if (OpenSide(wid) == XmTOP) { pts[0].y = 0 ; pts[1].y = 0 ; pts[2].y = JoinShadowThickness(tb) ; if (!VisualArmed(tb)) { /* right part starts with half bottom gc to join clean */ pts[0].x = 0 ; pts[1].x = tb->primitive.shadow_thickness ; pts[2].x = 0 ; } else { /* left part ends with half bottom gc to join clean */ pts[0].x = tb->core.width - tb->primitive.shadow_thickness ; pts[1].x = tb->core.width ; pts[2].x = tb->core.width ; } tune_gc = tb->primitive.bottom_shadow_GC ; } else if (OpenSide(wid) == XmBOTTOM) { pts[0].y = tb->core.height - JoinShadowThickness(tb) ; pts[1].y = tb->core.height ; pts[2].y = tb->core.height ; if (!VisualArmed(tb)) { /* left part ends with half top gc to join clean */ pts[0].x = tb->core.width ; pts[1].x = tb->core.width - tb->primitive.shadow_thickness ; pts[2].x = tb->core.width ; } else { /* right part ends with half top gc to join clean */ pts[0].x = 0 ; pts[1].x = tb->primitive.shadow_thickness ; pts[2].x = 0 ; } tune_gc = tb->primitive.top_shadow_GC ; } /* else if (OpenSide(wid) == XmNONE) do nothing */ /* Draw the curved portion of the shadows, unless join thickness is zero */ if ((OpenSide(wid) != XmNONE) && (JoinShadowThickness(wid) > (Dimension)0)) XFillPolygon (XtDisplay (tb), XtWindow (tb), tune_gc, pts, 3, Convex, CoordModeOrigin); } /***************************************************************************** * * GetHighlightRectangle * Called by BorderHighlight, BorderUnhighlight, and DrawShadow. , * in the case a rectangular tab only. * ***************************************************************************/ static void GetHighlightRectangle ( ExmTabButtonWidget tb, XRectangle *hrect) { /* This function writes the dimensions of the highlight rectangle into *hrect. These dimensions depend on the value of ExmNopenSide. */ if (OpenSide(tb) == XmLEFT) { hrect->x = 0 ; hrect->y = tb->primitive.shadow_thickness ; hrect->width = XtWidth( tb) - tb->primitive.shadow_thickness ; hrect->height = XtHeight(tb) - 2* tb->primitive.shadow_thickness ; } else if (OpenSide(tb) == XmRIGHT) { hrect->x = tb->primitive.shadow_thickness ; hrect->y = tb->primitive.shadow_thickness ; hrect->width = XtWidth(tb) - tb->primitive.shadow_thickness ; hrect->height = XtHeight(tb) - 2* tb->primitive.shadow_thickness ; } else if (OpenSide(tb) == XmTOP) { hrect->x = tb->primitive.shadow_thickness ; hrect->y = 0 ; hrect->width = XtWidth(tb) - 2* tb->primitive.shadow_thickness ; hrect->height = XtHeight(tb) - tb->primitive.shadow_thickness ; } else if (OpenSide(tb) == XmBOTTOM) { hrect->x = tb->primitive.shadow_thickness ; hrect->y = tb->primitive.shadow_thickness ; hrect->width = XtWidth(tb) - 2* tb->primitive.shadow_thickness ; hrect->height = XtHeight(tb) - tb->primitive.shadow_thickness ; } else if (OpenSide(tb) == XmNONE) { hrect->x = tb->primitive.shadow_thickness ; hrect->y = tb->primitive.shadow_thickness ; hrect->width = XtWidth(tb) - 2* tb->primitive.shadow_thickness ; hrect->height = XtHeight(tb) - 2*tb->primitive.shadow_thickness ; } } /*************************************************************** * Trait methods for XmQTjoinSide trait ***************************************************************/ /*************************************************************** * * JoinSideSetValue: * The setValue trait method of XmQTjoinSide. * ***************************************************************/ static void JoinSideSetValue(Widget tab, unsigned char join_side, Dimension join_thickness) { ExmTabButtonWidget tb = (ExmTabButtonWidget) tab; Arg args[6]; int n = 0; if (OpenSide(tb) != join_side) { XtSetArg (args[n], ExmNopenSide, join_side), n++; XtSetValues (tab, args, n); } if (JoinShadowThickness(tb) != join_thickness) { JoinShadowThickness(tb) = join_thickness; /* redraw, in the case XtSetValues will not */ if (XtIsRealized(tab)) DrawShadow (tab); } } /*************************************************************** * * JoinSideGetValue: * The getValue trait method of XmQTjoinSide. * ***************************************************************/ static unsigned char JoinSideGetValue(Widget tab, Dimension * join_thickness) { ExmTabButtonWidget tb = (ExmTabButtonWidget) tab; *join_thickness = JoinShadowThickness(tb); return OpenSide(tb) ; } /************************************************************************ * Application Accessible External Functions ************************************************************************/ /************************************************************************ * * ExmCreateTabButton * Create an instance of a TabButton and return its widget id. * ************************************************************************/ Widget ExmCreateTabButton( Widget parent, char *name, ArgList arglist, Cardinal argcount ) { return (XtCreateWidget (name, exmTabButtonWidgetClass, parent, arglist, argcount)); }