1 /* $XConsortium: TabB.c /main/5 1995/07/15 20:42:02 drk $ */
2 /*
3  * Motif
4  *
5  * Copyright (c) 1987-2012, The Open Group. All rights reserved.
6  *
7  * These libraries and programs are free software; you can
8  * redistribute them and/or modify them under the terms of the GNU
9  * Lesser General Public License as published by the Free Software
10  * Foundation; either version 2 of the License, or (at your option)
11  * any later version.
12  *
13  * These libraries and programs are distributed in the hope that
14  * they will be useful, but WITHOUT ANY WARRANTY; without even the
15  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16  * PURPOSE. See the GNU Lesser General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with these librararies and programs; if not, write
21  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
22  * Floor, Boston, MA 02110-1301 USA
23  */
24 /*
25  * HISTORY
26  */
27 
28 /*********************************** WARNING **********************************
29  *
30  * ExmTabButton is a demonstration widget.  OSF provides this widget
31  * solely to teach programmers how to write their own Motif widgets.
32  * OSF does not support this widget in any way
33  *********************************** WARNING *********************************/
34 
35 
36 /******************************************************************************
37  *
38  * TabB.c - ExmTabButton widget.  This widget can serve as a Notebook tab.
39  *          This widget can affix itself to any parent widget that knows how
40  *          to call the appropriate XmQTjoinSide trait methods.
41  *          The ExmTabButton widget demonstrates how to
42  *               * install the XmQTjoinSide trait.
43  *               * use XmeDrawHighlight
44  *               * use XmeClearBorder
45  *               * use XmeDrawShadows
46  *               * use XmeResolvePartOffsets
47  *          See the "OSF/Motif Widget Writer's Guide" for details.
48  *
49 ******************************************************************************/
50 
51 #include <Xm/XmP.h>         /* private header file for XmPrimitive */
52 #include <Exm/TabBP.h>      /* private header file for ExmTabButton widget */
53 #include <Xm/DrawP.h>       /* for XmeDraw functions */
54 #include <Xm/TraitP.h>      /* for XmeTrait functions */
55 #include <Xm/RepType.h>     /* for representation type facility */
56 #include <Xm/SpecRenderT.h> /* defines RENDER_TABLE types. */
57 #include <Xm/JoinSideT.h>   /* for XmQTjoinSide trait */
58 #include <X11/extensions/shape.h>
59 
60 #define WARNING_SHAPE_CHANGED "Widget not allowed to change shape"
61 #define WARNING_NO_SHAPE_EXTENSION "No Shape extension, style = rectangular"
62 
63 /* Define the macros that we want to go thru offset. */
64 /* No need to offset primitive or core fields; the Intrinsics will not break
65    binary compatibility. */
66 #define OpenSide(w) \
67     XmField(w, offsets, ExmTabButton, open_side, XtEnum)
68 #define JoinShadowThickness(w) \
69     XmField(w, offsets, ExmTabButton, join_shadow_thickness, Dimension)
70 #define VisualArmed(w) \
71     XmField(w, offsets, ExmCommandButton, visual_armed, Boolean)
72 #define SimpleShape(w) \
73     XmField(w, offsets, ExmSimple, simple_shape, unsigned char)
74 #define NeedToReconfigure(w) \
75     XmField(w, offsets, ExmSimple, need_to_reconfigure, Boolean)
76 
77 #define DEFAULT_CORNER_ROUND_PERCENT 40
78 /* corber size based on percent of the smaller dimension of the button */
79 #define CORNER_SIZE(w) \
80    ((((w->core.width < w->core.height) ? w->core.width \
81      : w->core.height) * DEFAULT_CORNER_ROUND_PERCENT) / 100)
82 
83 /* Declare all static functions. */
84 static void ClassInitialize(void) ;
85 static void ClassPartInitialize(
86                         WidgetClass wc) ;
87 static void Initialize(
88                         Widget rw,
89                         Widget nw,
90                         ArgList args,
91                         Cardinal *num_args) ;
92 static void FixVisualPosition(
93 		    Widget w );
94 static void Resize(
95 		    Widget w );
96 static void FillOpenRoundedRectangle(
97 				     Display		*dpy,
98 				     Drawable		draw,
99 				     GC			top_gc,
100 				     GC                 bottom_gc,
101 				     GC                 bg_gc,
102 				     int		x,
103 				     int                y,
104 				     int                w,
105 				     int                h,
106 				     int                shad_thick,
107 				     int                corner_size,
108 				     unsigned char       open_side);
109 static void ShapeButton(
110 		    Widget w);
111 static void Realize(
112 		    Widget w,
113 		    XtValueMask *p_valueMask,
114 		    XSetWindowAttributes *attributes );
115 static Boolean SetValues(
116                         Widget cw,
117                         Widget rw,
118                         Widget nw,
119                         ArgList args,
120                         Cardinal *num_args) ;
121 static void GetHighlightRectangle (
122 			ExmTabButtonWidget tb,
123 			XRectangle * hrect);
124 static void BorderHighlight(
125                         Widget wid) ;
126 static void BorderUnhighlight(
127                         Widget wid) ;
128 static void DrawShadow(
129 		        Widget wid);
130 static void JoinSideSetValue(Widget tab,
131                         unsigned char  join_side,
132                         Dimension join_thickness);
133 static unsigned char JoinSideGetValue(Widget tab,
134                         Dimension *join_thickness);
135 
136 
137 /* ExmTabButton provides no new translations.  All of its translations
138    are inherited from ExmCommandButton. */
139 
140 
141 /* ExmTabButton provides only one new resource.
142    Use the resolve part offset type of resource */
143 static XmPartResource resources[] = {
144     {
145      ExmNopenSide,
146      ExmCOpenSide,
147      ExmROpenSide, sizeof (XtEnum),
148      XmPartOffset (ExmTabButton, open_side),
149      XmRImmediate,
150      (XtPointer) XmLEFT
151    },
152 };
153 
154 /* No synthetic resources. */
155 
156 /* Define the widget class record.  See Chapter 3 of the
157    "OSF/Motif Widget Writer's Guide for details. */
158 
159 externaldef(exmtabbuttonclassrec)
160 ExmTabButtonClassRec exmTabButtonClassRec = {
161   { /* Here is the Core class record. */
162     /* superclass */	             (WidgetClass) &exmCommandButtonClassRec,
163     /* class_name */	             "ExmTabButton",
164     /* widget_size */	             sizeof(ExmTabButtonPart),
165     /* class_initialize */           ClassInitialize,
166     /* class_part_init */            ClassPartInitialize,
167     /* class_inited */	             FALSE,
168     /* initialize */	             Initialize,
169     /* initialize_hook */            (XtArgsProc)NULL,
170     /* realize */	             Realize,
171     /* actions */	             NULL,
172     /* num_actions */	             0,
173     /* resources */	             (XtResourceList) resources,
174     /* num_resources */	             XtNumber(resources),
175     /* xrm_class */	             NULLQUARK,
176     /* compress_motion */	     TRUE,
177     /* compress_exposure */	     XtExposeCompressMaximal,
178     /* compress_enterlv */           TRUE,
179     /* visible_interest */	     FALSE,
180     /* destroy */	             NULL,
181     /* resize */	             Resize,
182     /* expose */	             XtInheritExpose,
183     /* set_values */	             SetValues,
184     /* set_values_hook */            (XtArgsFunc)NULL,
185     /* set_values_almost */          XtInheritSetValuesAlmost,
186     /* get_values_hook */	     (XtArgsProc)NULL,
187     /* accept_focus */	             (XtAcceptFocusProc)NULL,
188     /* version */	             XtVersionDontCheck,
189     /* callback_private */           NULL,
190     /* tm_table */                   XtInheritTranslations,
191     /* query_geometry */	     XtInheritQueryGeometry,
192     /* display_accelerator */        (XtStringProc)NULL,
193     /* extension record */           NULL,
194   },
195   { /* Here is the XmPrimitive class record. */
196     /* border_highlight */	     BorderHighlight,
197     /* border_unhighlight */	     BorderUnhighlight,
198     /* translations */               XtInheritTranslations,
199     /* arm_and_activate */           XmInheritArmAndActivate,
200     /* get resources */              NULL,
201     /* num get_resources */          0,
202     /* extension */                  NULL,
203   },
204   { /* Here is the ExmSimple class record. */
205     /* draw_visual */                ExmInheritDrawVisual,
206     /* draw_shadow */                DrawShadow,
207     /* create_gc */                  ExmInheritCreateGC,
208     /* destroy_gc */                 ExmInheritDestroyGC,
209     /* select_gc */                  ExmInheritSelectGC,
210     /* calc_visual_size */           ExmInheritCalcVisualSize,
211     /* calc_widget_size */           ExmInheritCalcWidgetSize,
212     /* reconfigure */                ExmInheritReconfigure,
213     /* extension */                  NULL,
214   },
215   { /* Here is the ExmString class record. */
216     /* default_render_table_type */  XmBUTTON_RENDER_TABLE,
217     /* extension */                  NULL,
218   },
219   { /* Here is the ExmCommandButton class record. */
220     /* extension */                  NULL,
221   },
222   { /* Here is the ExmTabButton class record. */
223     /* extension */                  NULL,
224   },
225 };
226 
227 
228 /* Establish the widget class name as an externally accessible symbol.
229    Use the "externaldef" macro rather than the "extern" keyword. */
230 externaldef(exmtabbuttonwidgetclass)
231    WidgetClass exmTabButtonWidgetClass = (WidgetClass)&exmTabButtonClassRec;
232 
233 
234 /* Define static representation type variables here.  ExmTabButton
235    introduces one new representation type. */
236 static String OpenSideNames[] = {
237     "none", "left", "right", "top", "bottom"
238 };
239 
240 static XmRepTypeId openSideId;
241 
242 
243 /* Define trait structure variables here. */
244 /* This widget will install the XmQTjoinSide trait. */
245 static XmConst XmJoinSideTraitRec tabButtonOST = {
246   0,		/* version */
247   JoinSideSetValue,
248   JoinSideGetValue,
249 };
250 
251 
252 /* Part Offset table for XmResolvePartOffsets */
253 static XmOffsetPtr offsets;
254 
255 
256 /*************************************************************************
257  *
258  *  ClassInitialize
259  *      Called by the Intrinsics the first time a widget of this class is
260  *      instantiated.
261  *
262  ************************************************************************/
ClassInitialize()263 static void ClassInitialize()
264 {
265  /* Register new representation types.  These new representation types
266     will hold the names of the valid values of the ExmNopenSide resource. */
267 
268    openSideId = XmRepTypeRegister (ExmROpenSide, OpenSideNames,
269 				   NULL, XtNumber(OpenSideNames));
270 
271  /* resolve the offsets so that our XmField macro works fine */
272    XmeResolvePartOffsets(exmTabButtonWidgetClass, &offsets, NULL);
273 }
274 
275 
276 
277 /************************************************************************
278  *
279  *  ClassPartInitialize
280  *      Called by the Intrinsics when this widget or a subclass of this
281  *      widget is intantiated.
282  *
283  ************************************************************************/
284 static void
ClassPartInitialize(WidgetClass wc)285 ClassPartInitialize(
286         WidgetClass wc )
287 {
288  /* Install the XmQTjoinSide trait on the ExmTabButton class and on all
289     its subclasses. */
290    XmeTraitSet((XtPointer) wc, XmQTjoinSide, (XtPointer) &tabButtonOST);
291 }
292 
293 
294 /*****************************************************************************
295  *
296  *  Initialize
297  *      Called when the widget is instantiated.
298  *
299  ***************************************************************************/
300 static void
Initialize(Widget rw,Widget nw,ArgList args,Cardinal * num_args)301 Initialize(
302         Widget rw,
303         Widget nw,
304         ArgList args,
305         Cardinal *num_args )
306 {
307  ExmTabButtonWidgetClass wc = (ExmTabButtonWidgetClass)XtClass(nw);
308  int shape_event_base, shape_error_base;
309 
310   /* Validate the value of ExmNopenSide.  If ExmNopenSide does not
311      have a valid value, set its value to XmLEFT. */
312    if (!XmRepTypeValidValue(openSideId, OpenSide(nw), nw))
313      OpenSide(nw) = XmLEFT ;
314 
315  /* check if the shape extention is on the server */
316   if (SimpleShape(nw) != ExmSHAPE_RECTANGLE
317       && !XShapeQueryExtension(XtDisplay(nw), &shape_event_base,
318 			       &shape_error_base)) {
319       XmeWarning(nw, WARNING_NO_SHAPE_EXTENSION);
320       SimpleShape(nw) = ExmSHAPE_RECTANGLE;
321   }
322 
323    /* default the shadow join to the shadow */
324     JoinShadowThickness(nw) = Prim_ShadowThickness(nw);
325 
326    if (wc->simple_class.reconfigure)
327      (*(wc->simple_class.reconfigure))(exmTabButtonWidgetClass, nw, NULL);
328 }
329 
330 
331 /*****************************************************************************
332  *
333  * FillOpenRoundedRectangle
334  *      Xlib style API for round edge graphics.
335  *      This is used for both the bitmap used as a shape and the rendering
336  *      of the shaped shadows.
337  *
338  *****************************************************************************/
339 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)340 FillOpenRoundedRectangle(
341 			 Display		*dpy,
342 			 Drawable		draw,
343 			 GC			top_gc,
344 			 GC                 bottom_gc,
345 			 GC                 bg_gc,
346 			 int		x,
347 			 int                y,
348 			 int                w,
349 			 int                h,
350 			 int                shad_thick,
351 			 int                corner_size,
352 			 unsigned char       open_side)
353 {
354     XArc	arcs[4];
355     XRectangle  rects[2];
356     XGCValues top_gcvalues, bottom_gcvalues;
357     Dimension demi_shad = (shad_thick > 1)? (shad_thick/2):shad_thick;
358 
359     if (corner_size > w/2) corner_size = w/2;
360     if (corner_size > h/2) corner_size = h/2;
361 
362    /* first save the current line attributes (have to do all of them
363       because of the weak Xlib API).
364       The default in the GC is 1, so do not change the line width there */
365     if (shad_thick != 1) {
366 	XGetGCValues(dpy, top_gc,
367 		     GCLineWidth|GCLineStyle|GCCapStyle|GCJoinStyle,
368 		     &top_gcvalues);
369 	/* change the line width and draw the lines */
370 	XSetLineAttributes(dpy, top_gc,  shad_thick,
371 			   LineSolid, CapButt, JoinMiter);
372 
373 	if (bottom_gc != top_gc) {
374 	    XGetGCValues(dpy, bottom_gc,
375 			 GCLineWidth|GCLineStyle|GCCapStyle|GCJoinStyle,
376 			 &bottom_gcvalues);
377 	    XSetLineAttributes(dpy, bottom_gc,  shad_thick,
378 			       LineSolid, CapButt, JoinMiter);
379 	}
380     }
381 
382 
383     /* this code is rather long and boring, but there is nothing
384        complex here: we use arcs even for the vertical and horizontal
385        lines, so that the requests are minimized. There are some
386        known visual glitch with shadow = 1. */
387     if (open_side == XmLEFT) {
388 	arcs[0].x = x ;
389 	arcs[0].y = y + demi_shad;
390 	arcs[0].width = w - corner_size;
391 	arcs[0].height = 0;
392 	arcs[0].angle1 = 180*64;
393 	arcs[0].angle2 = -180*64;
394 	arcs[1].x = x + w - 2*corner_size + demi_shad;
395 	arcs[1].y = y + demi_shad;
396 	arcs[1].width = 2*corner_size - 2*demi_shad;
397 	arcs[1].height = 2*corner_size - 2*demi_shad;
398 	arcs[1].angle1 = 90*64;
399 	arcs[1].angle2 = -50*64;
400 	if (shad_thick) XDrawArcs (dpy, draw, top_gc, arcs, 2);
401 	if (bg_gc) XFillArcs (dpy, draw, bg_gc, arcs+1, 1);
402 
403 	arcs[0].x = x + w - 2*corner_size + demi_shad;
404 	arcs[0].y = y + demi_shad;
405 	arcs[0].width = 2*corner_size - 2*demi_shad;
406 	arcs[0].height = 2*corner_size - 2*demi_shad;
407 	arcs[0].angle1 = 40*64;
408 	arcs[0].angle2 = -40*64;
409 	arcs[1].x = x + w - demi_shad;
410 	arcs[1].y = y + corner_size;
411 	arcs[1].width = 0;
412 	arcs[1].height = h - 2*corner_size;
413 	arcs[1].angle1 = 90*64;
414 	arcs[1].angle2 = -180*64;
415 	arcs[2].x = x + w - 2*corner_size + demi_shad;
416 	arcs[2].y = y + h - 2*corner_size + demi_shad ;
417 	arcs[2].width = 2*corner_size - 2*demi_shad;
418 	arcs[2].height = 2*corner_size - 2*demi_shad;
419 	arcs[2].angle1 = 0*64;
420 	arcs[2].angle2 = -90*64;
421 	arcs[3].x = x ;
422 	arcs[3].y = y + h - demi_shad;
423 	arcs[3].width = w - corner_size;
424 	arcs[3].height = 0 ;
425 	arcs[3].angle1 = 180*64;
426 	arcs[3].angle2 = -180*64;
427 	if (shad_thick) XDrawArcs (dpy, draw, bottom_gc, arcs, 4);
428 
429 	if (bg_gc) {
430 	    XFillArcs (dpy, draw, bg_gc, arcs, 3);
431 	    /* fill middle with rectangle */
432 	    rects[0].x = x;  rects[0].y = y;
433 	    rects[0].width = w - corner_size;
434 	    rects[0].height = h;
435 	    rects[1].x = x + w - corner_size;
436 	    rects[1].y = y + corner_size;
437 	    rects[1].width = corner_size;
438 	    rects[1].height = h - corner_size*2;
439 	    XFillRectangles (dpy, draw, bg_gc, rects, 2);
440 	}
441     } else
442     if (open_side == XmBOTTOM) {
443 	arcs[0].x = x + demi_shad;
444 	arcs[0].y = y + corner_size;
445 	arcs[0].width = 0;
446 	arcs[0].height = h - corner_size;
447 	arcs[0].angle1 = 90*64;
448 	arcs[0].angle2 = -180*64;
449 	arcs[1].x = x + demi_shad;
450 	arcs[1].y = y + demi_shad;
451 	arcs[1].width = 2*corner_size - 2*demi_shad;
452 	arcs[1].height = 2*corner_size - 2*demi_shad;
453 	arcs[1].angle1 = 180*64;
454 	arcs[1].angle2 = -90*64;
455 	arcs[2].x = x + corner_size ;
456 	arcs[2].y = y + demi_shad ;
457 	arcs[2].width = w - 2*corner_size ;
458 	arcs[2].height = 0;
459 	arcs[2].angle1 = 180*64;
460 	arcs[2].angle2 = -180*64;
461 	arcs[3].x = x + w - 2*corner_size + demi_shad;
462 	arcs[3].y = y + demi_shad;
463 	arcs[3].width = 2*corner_size - 2*demi_shad;
464 	arcs[3].height = 2*corner_size - 2*demi_shad ;
465 	arcs[3].angle1 = 90*64;
466 	arcs[3].angle2 = -50*64;
467 	if (shad_thick) XDrawArcs (dpy, draw, top_gc, arcs, 4);
468 	if (bg_gc) XFillArcs (dpy, draw, bg_gc, arcs+1, 3);
469 
470 	arcs[0].x = x + w - 2*corner_size + demi_shad;
471 	arcs[0].y = y + demi_shad;
472 	arcs[0].width = 2*corner_size - 2*demi_shad;
473 	arcs[0].height = 2*corner_size - 2*demi_shad;
474 	arcs[0].angle1 = 40*64;
475 	arcs[0].angle2 = -40*64;
476 	arcs[1].x = x + w - demi_shad;
477 	arcs[1].y = y + corner_size;
478 	arcs[1].width = 0;
479 	arcs[1].height = h - corner_size;
480 	arcs[1].angle1 = 90*64;
481 	arcs[1].angle2 = -180*64;
482  	if (shad_thick) XDrawArcs (dpy, draw, bottom_gc, arcs, 2);
483 
484 	if (bg_gc) {
485 	    XFillArcs (dpy, draw, bg_gc, arcs, 1);
486 	    /* fill middle with rectangle */
487 	    rects[0].x = x + corner_size;  rects[0].y = y;
488 	    rects[0].width = w - 2*corner_size;
489 	    rects[0].height = corner_size;
490 	    rects[1].x = x ;
491 	    rects[1].y = y + corner_size;
492 	    rects[1].width = w;
493 	    rects[1].height = h - corner_size;
494 	    XFillRectangles (dpy, draw, bg_gc, rects, 2);
495 	}
496     } else
497     if (open_side == XmTOP) {
498 	arcs[0].x = x + demi_shad;
499 	arcs[0].y = y ;
500 	arcs[0].width = 0;
501 	arcs[0].height = h - corner_size;
502 	arcs[0].angle1 = 90*64;
503 	arcs[0].angle2 = -180*64;
504 	arcs[1].x = x + demi_shad;
505 	arcs[1].y = y + h - 2*corner_size + demi_shad;
506 	arcs[1].width = 2*corner_size - 2*demi_shad;
507 	arcs[1].height = 2*corner_size - 2*demi_shad;
508 	arcs[1].angle1 = 180*64;
509 	arcs[1].angle2 = 50*64;
510 	if (shad_thick) XDrawArcs (dpy, draw, top_gc, arcs, 2);
511 	if (bg_gc) XFillArcs (dpy, draw, bg_gc, arcs+1, 1);
512 
513 	arcs[0].x = x + demi_shad;
514 	arcs[0].y = y + h - 2*corner_size + demi_shad;
515 	arcs[0].width = 2*corner_size - 2*demi_shad;
516 	arcs[0].height = 2*corner_size - 2*demi_shad;
517 	arcs[0].angle1 = 230*64;
518 	arcs[0].angle2 = 40*64;
519 	arcs[1].x = x + corner_size;
520 	arcs[1].y = y + h - demi_shad;
521 	arcs[1].width = w - 2*corner_size;
522 	arcs[1].height = 0;
523 	arcs[1].angle1 = 180*64;
524 	arcs[1].angle2 = -180*64;
525 	arcs[2].x = x + w - 2*corner_size + demi_shad;
526 	arcs[2].y = y + h - 2*corner_size + demi_shad ;
527 	arcs[2].width = 2*corner_size - 2*demi_shad;
528 	arcs[2].height = 2*corner_size - 2*demi_shad;
529 	arcs[2].angle1 = 270*64;
530 	arcs[2].angle2 = 90*64;
531 	arcs[3].x = x + w - demi_shad;
532 	arcs[3].y = y ;
533 	arcs[3].width = 0;
534 	arcs[3].height = h - corner_size ;
535 	arcs[3].angle1 = 90*64;
536 	arcs[3].angle2 = -180*64;
537 	if (shad_thick) XDrawArcs (dpy, draw, bottom_gc, arcs, 4);
538 
539 	if (bg_gc) {
540 	    XFillArcs (dpy, draw, bg_gc, arcs, 3);
541 	    /* fill middle with rectangle */
542 	    rects[0].x = x;  rects[0].y = y;
543 	    rects[0].width = w ;
544 	    rects[0].height = h - corner_size;
545 	    rects[1].x = x + corner_size;
546 	    rects[1].y = y + h - corner_size;
547 	    rects[1].width = w - 2*corner_size;
548 	    rects[1].height = corner_size;
549 	    XFillRectangles (dpy, draw, bg_gc, rects, 2);
550 	}
551     }  else
552     if (open_side == XmRIGHT) {
553 	arcs[0].x = x + corner_size;
554 	arcs[0].y = y + demi_shad;
555 	arcs[0].width = w - corner_size;
556 	arcs[0].height = 0;
557 	arcs[0].angle1 = 180*64;
558 	arcs[0].angle2 = -180*64;
559 	arcs[1].x = x + demi_shad;
560 	arcs[1].y = y + demi_shad;
561 	arcs[1].width = 2*corner_size - 2*demi_shad;
562 	arcs[1].height = 2*corner_size - 2*demi_shad;
563 	arcs[1].angle1 = 180*64;
564 	arcs[1].angle2 = -90*64;
565 	arcs[2].x = x + demi_shad ;
566 	arcs[2].y = y + corner_size ;
567 	arcs[2].width = 0;
568 	arcs[2].height = h - 2*corner_size;
569 	arcs[2].angle1 = 90*64;
570 	arcs[2].angle2 = -180*64;
571 	arcs[3].x = x + demi_shad;
572 	arcs[3].y = y + h - 2*corner_size + demi_shad;
573 	arcs[3].width = 2*corner_size - 2*demi_shad;
574 	arcs[3].height = 2*corner_size - 2*demi_shad ;
575 	arcs[3].angle1 = 180*64;
576 	arcs[3].angle2 = 50*64;
577 	if (shad_thick) XDrawArcs (dpy, draw, top_gc, arcs, 4);
578 	if (bg_gc) XFillArcs (dpy, draw, bg_gc, arcs+1, 3);
579 
580 	arcs[0].x = x + demi_shad;
581 	arcs[0].y = y + h - 2*corner_size + demi_shad;
582 	arcs[0].width = 2*corner_size - 2*demi_shad;
583 	arcs[0].height = 2*corner_size - 2*demi_shad;
584 	arcs[0].angle1 = 230*64;
585 	arcs[0].angle2 = 40*64;
586 	arcs[1].x = x + corner_size;
587 	arcs[1].y = y + h - demi_shad;
588 	arcs[1].width = w - corner_size;
589 	arcs[1].height = 0;
590 	arcs[1].angle1 = 180*64;
591 	arcs[1].angle2 = -180*64;
592  	if (shad_thick) XDrawArcs (dpy, draw, bottom_gc, arcs, 2);
593 
594 	if (bg_gc) {
595 	    XFillArcs (dpy, draw, bg_gc, arcs, 1);
596 	    /* fill middle with rectangle */
597 	    rects[0].x = x ;  rects[0].y = y + corner_size;
598 	    rects[0].width = corner_size;
599 	    rects[0].height = h - 2*corner_size;
600 	    rects[1].x = x + corner_size;
601 	    rects[1].y = y ;
602 	    rects[1].width = w - corner_size;
603 	    rects[1].height = h ;
604 	    XFillRectangles (dpy, draw, bg_gc, rects, 2);
605 	}
606     }
607 
608    /* put the line attributes back */
609     if (shad_thick != 1) {
610 	XSetLineAttributes(dpy, top_gc,
611 			   top_gcvalues.line_width, top_gcvalues.line_style,
612 			   top_gcvalues.cap_style, top_gcvalues.join_style);
613 	if (bottom_gc != top_gc)
614 	    XSetLineAttributes(dpy, bottom_gc,
615 			       bottom_gcvalues.line_width,
616 			       bottom_gcvalues.line_style,
617 			       bottom_gcvalues.cap_style,
618 			       bottom_gcvalues.join_style);
619     }
620 }
621 
622 
623 
624 /*****************************************************************************
625  *
626  * ShapeButton
627  *      Call on a realized window from Realize and Resize.
628  *      Style is rounded edge at this point.
629  *
630  *****************************************************************************/
631 static void
ShapeButton(Widget w)632 ShapeButton(
633 Widget w)
634 {
635     Pixmap p = XCreatePixmap(XtDisplay(w), XtWindow(w),
636 			     XtWidth(w), XtHeight(w), 1 );
637     XGCValues values;
638     GC gc;
639 
640     /* create a gc width 0 to blank the pixmap and then with 1
641        for the mask */
642     values.foreground = 0;
643     gc = XCreateGC (XtDisplay(w), p, GCForeground, &values );
644     XFillRectangle( XtDisplay(w), p, gc, 0, 0, XtWidth(w), XtHeight(w));
645     XSetForeground (XtDisplay(w), gc, 1);
646 
647     FillOpenRoundedRectangle(XtDisplay(w), p, gc, gc, gc, 0, 0,
648 			     XtWidth(w), XtHeight(w),
649 			     Prim_ShadowThickness(w),
650 			     CORNER_SIZE(w), OpenSide(w));
651 
652     XShapeCombineMask(XtDisplay(w), XtWindow(w), ShapeBounding,
653 		      0, 0, p, ShapeSet );
654     XShapeCombineMask(XtDisplay(w), XtWindow(w),ShapeClip,
655 		      0, 0, p, ShapeSet );
656 
657     XFreePixmap(XtDisplay(w), p );
658     XFreeGC (XtDisplay(w), gc);
659 }
660 
661 
662 /*****************************************************************************
663  *
664  * Realize
665  *      Called by the Intrinsics to create the window for the widget.  This
666  *      class's realize method creates a shaped window for this
667  *      exact class,  but uses the default window otherwise
668  *
669  *****************************************************************************/
670 static void
Realize(Widget w,XtValueMask * p_valueMask,XSetWindowAttributes * attributes)671 Realize(Widget w,
672         XtValueMask *p_valueMask,
673         XSetWindowAttributes *attributes )
674 {
675     /* First call Primitive's method */
676     xmPrimitiveClassRec.core_class.realize(w, p_valueMask, attributes);
677 
678     /* then reshape the button if needed */
679     if (SimpleShape(w) != ExmSHAPE_RECTANGLE) ShapeButton(w);
680 }
681 
682 
683 /*****************************************************************************
684  *
685  *  FixVisualPosition: tabs are asymmetric, so we need to recenter
686  *                     the string so it looks right.
687  *
688  ***************************************************************************/
689 static void
FixVisualPosition(Widget w)690 FixVisualPosition(
691     Widget w)
692 {
693     /* apply some little correction to the string position as the
694        shadow is only on one side for the TabButton */
695     if (OpenSide(w) == XmLEFT)
696 	((ExmSimpleWidget)w)->simple.visual.x -= Prim_ShadowThickness(w) ;
697     else
698     if (OpenSide(w) == XmTOP)
699 	((ExmSimpleWidget)w)->simple.visual.y -= Prim_ShadowThickness(w) ;
700     else
701     if (OpenSide(w) == XmRIGHT)
702 	((ExmSimpleWidget)w)->simple.visual.x += Prim_ShadowThickness(w) ;
703     else
704     if (OpenSide(w) == XmBOTTOM)
705 	((ExmSimpleWidget)w)->simple.visual.y += Prim_ShadowThickness(w) ;
706 }
707 
708 /*****************************************************************************
709  *
710  *  Resize
711  *      Must reshape the window.
712  *
713  ***************************************************************************/
714 static void
Resize(Widget w)715 Resize(
716     Widget w)
717 {
718     if (XtIsRealized(w) && SimpleShape(w) != ExmSHAPE_RECTANGLE)
719 	ShapeButton(w);
720 
721     /* call superclass Resize */
722     (*exmCommandButtonClassRec.core_class.superclass->core_class.resize)(w);
723 
724     FixVisualPosition(w);
725 }
726 
727 /*****************************************************************************
728  *
729  *  SetValues
730  *      Called by the Intrinsics when the application tries to set a value
731  *      of one of the resources.
732  *
733  ***************************************************************************/
734 static Boolean
SetValues(Widget cw,Widget rw,Widget nw,ArgList args,Cardinal * num_args)735 SetValues(
736         Widget cw,
737         Widget rw,
738         Widget nw,
739         ArgList args,
740         Cardinal *num_args )
741 {
742     ExmSimpleWidgetClass wc = (ExmSimpleWidgetClass)XtClass(nw);
743     Boolean expose_flag = False ;
744 
745     /* Make sure that the new value of ExmNopenSide is a valid one. */
746     if (OpenSide(nw) != OpenSide(cw)) {
747 	if (!XmRepTypeValidValue(openSideId, OpenSide(nw), nw))
748 	    OpenSide(nw) = OpenSide(cw);
749 	else {
750 	    /* call Resize so the shape and the visual position be reset */
751 	    if (wc->core_class.resize)
752 		(*(wc->core_class.resize))(nw);
753 	    expose_flag = True ;
754 	}
755     }
756 
757     /* Do no allow change in shape */
758     if (SimpleShape(cw) != SimpleShape(nw)) {
759 	SimpleShape(nw) = SimpleShape(cw);
760 	XmeWarning(nw, WARNING_SHAPE_CHANGED);
761     }
762 
763     /* must call our Resize for the shape update */
764     if (SimpleShape(nw) == ExmSHAPE_OVAL && NeedToReconfigure(nw)) {
765 	if (wc->simple_class.reconfigure)
766 	    (*(wc->simple_class.reconfigure))(exmTabButtonWidgetClass, nw, cw);
767     }
768 
769     return expose_flag ;
770 }
771 
772 
773 /*****************************************************************************
774  *
775  *  BorderHighlight
776  *
777  ***************************************************************************/
778 static void
BorderHighlight(Widget wid)779 BorderHighlight(
780         Widget wid )
781 {
782     XRectangle hrect ;
783     ExmTabButtonWidget tb = (ExmTabButtonWidget) wid;
784 
785     tb->primitive.highlighted = True ;
786 
787   /* If there is no need to draw a border highlight, then don't bother. */
788     if (XtWidth(wid) == 0 ||
789         XtHeight( wid) == 0 ||
790         tb->primitive.highlight_thickness == 0)
791       return;
792 
793     if (SimpleShape(wid) == ExmSHAPE_OVAL) {
794 	/* Render the label using the highlight color for a change.
795 	   The app must be sure the highlight color and the background and
796 	   different */
797 	if (tb->string.compound_string &&
798 	    (tb->simple.visual.width != 0) &&
799 	    (tb->simple.visual.height != 0)) {
800 	    XmStringDraw (XtDisplay(tb), XtWindow(tb),
801 			  tb->string.render_table,
802 			  tb->string.compound_string,
803 			  tb->primitive.highlight_GC,
804 			  tb->simple.visual.x, tb->simple.visual.y,
805 			  tb->simple.visual.width, tb->string.alignment,
806 			  tb->primitive.layout_direction, NULL);
807 	}
808     } else {
809 	/* Determine where the highlight should be drawn and then draw it. */
810 	GetHighlightRectangle(tb, &hrect);
811 
812 	XmeDrawHighlight(XtDisplay( wid), XtWindow( wid),
813 			 tb->primitive.highlight_GC,
814 			 hrect.x, hrect.y, hrect.width, hrect.height,
815 			 tb->primitive.highlight_thickness);
816     }
817 }
818 
819 
820 /*****************************************************************************
821  *
822  *  BorderUnhighlight
823  *
824  ***************************************************************************/
825 static void
BorderUnhighlight(Widget wid)826 BorderUnhighlight(
827         Widget wid )
828 {
829     XRectangle hrect ;
830     ExmTabButtonWidget tb = (ExmTabButtonWidget) wid;
831 
832   /* This flag tells XmPrimitive what the state of the highlights is.
833      By turning it to False, we are alerting XmPrimitive that the
834      highlight is off. */
835     tb->primitive.highlighted = False ;
836 
837   /* If there is no need to undraw a border highlight, then don't bother. */
838     if (XtWidth(wid) == 0 ||
839         XtHeight(wid) == 0 ||
840         tb->primitive.highlight_thickness == 0)
841       return;
842 
843     if (SimpleShape(wid) == ExmSHAPE_OVAL) {
844 	/* Undo the render label using the highlight color */
845 	if (tb->string.compound_string &&
846 	    (tb->simple.visual.width != 0) &&
847 	    (tb->simple.visual.height != 0)) {
848 	    XmStringDraw (XtDisplay(tb), XtWindow(tb),
849 			  tb->string.render_table,
850 			  tb->string.compound_string,
851 			  tb->simple.normal_gc,
852 			  tb->simple.visual.x, tb->simple.visual.y,
853 			  tb->simple.visual.width, tb->string.alignment,
854 			  tb->primitive.layout_direction, NULL);
855 	}
856     } else {
857 	/* Determine where the highlight should be drawn and then draw it. */
858 	GetHighlightRectangle(tb, &hrect);
859 
860 	/* Erase the border highlights.  The border highlights of ExmTabButton
861 	   are located inside the widget; therefore, we can XmeClearBorder
862 	   rather than XmeDrawHighlight (which is used with the parent color
863 	   in Motif). */
864 	XmeClearBorder(XtDisplay (wid), XtWindow (wid),
865 		       hrect.x, hrect.y, hrect.width, hrect.height,
866 		       tb->primitive.highlight_thickness) ;
867     }
868 }
869 
870 
871 /************************************************************************
872  *
873  *  DrawShadow
874  *
875  ************************************************************************/
876 static void
DrawShadow(Widget wid)877 DrawShadow (
878         Widget wid)
879 {
880     XPoint pts[3] ;
881     XRectangle hrect ;
882     GC tune_gc = 0;
883     ExmTabButtonWidget tb = (ExmTabButtonWidget) wid;
884 
885   /* ExmTabButton cannot inherit the draw_shadow routine of ExmSimple.
886      This is because ExmSimple's draw_shadow method assumes rectangular
887      shadows; however, ExmTabButton will require both rectangular
888      and curved shadows.  That is, the shadows in an ExmTabButton will
889      be partly rectangular and partly curved.  The rectangular portions
890      will be drawn with XmeDrawShadows and the curved portions with
891      XFillPolygon.  The resulting shadow will be drawn just outside the
892      border highlight.  The shadow will have one open side and a 3D
893      appearance.  */
894 
895     if (SimpleShape(wid) == ExmSHAPE_RECTANGLE) {
896 
897 	/* Find the dimensions of the border highlight. */
898 	GetHighlightRectangle (tb, &hrect) ;
899 
900 	/* Apply a correction to the returned border highlight dimensions
901 	   since we're drawing the shadow this time around. */
902 	hrect.x -= tb->primitive.shadow_thickness ;
903 	hrect.y -= tb->primitive.shadow_thickness ;
904 	hrect.width += 2*tb->primitive.shadow_thickness ;
905 	hrect.height += 2*tb->primitive.shadow_thickness ;
906 
907 	/* Draw the rectangular portion of the shadows.
908 	   We're drawing a square shadow, but because the position are
909 	   negative on the open side, it'll look like an open shadow */
910 	XmeDrawShadows (XtDisplay (tb), XtWindow (tb),
911 			tb->primitive.top_shadow_GC,
912 			tb->primitive.bottom_shadow_GC,
913 			hrect.x, hrect.y, hrect.width, hrect.height,
914 			tb->primitive.shadow_thickness,
915 			VisualArmed(tb) ? XmSHADOW_IN: XmSHADOW_OUT);
916     } else {
917 	 FillOpenRoundedRectangle(XtDisplay(wid), XtWindow (wid),
918 				  VisualArmed(tb) ?
919 				  tb->primitive.bottom_shadow_GC:
920 				  tb->primitive.top_shadow_GC,
921 				  VisualArmed(tb) ?
922 				  tb->primitive.top_shadow_GC:
923 				  tb->primitive.bottom_shadow_GC,
924 				  NULL, 0, 0,
925 				  XtWidth(wid), XtHeight(wid),
926 				  tb->primitive.shadow_thickness,
927 				  CORNER_SIZE(wid), OpenSide(wid));
928      }
929 
930     /* Now draw the shadow join triangle */
931     /* Determine the dimensions of the curved portions of the shadow. */
932     if (OpenSide(wid) == XmLEFT) {
933 	pts[0].x = 0 ;
934 	pts[1].x = JoinShadowThickness(tb) ;
935 	pts[2].x = 0 ;
936 	if (!VisualArmed(tb)) {
937 	    /* top part starts with half bottom gc to join clean */
938 	    pts[0].y = 0 ;
939 	    pts[1].y = 0 ;
940 	    pts[2].y = tb->primitive.shadow_thickness ;
941 	} else {
942 	    /* bottom part starts with half bottom gc to join clean */
943 	    pts[0].y = tb->core.height - tb->primitive.shadow_thickness ;
944 	    pts[1].y = tb->core.height ;
945 	    pts[2].y = tb->core.height ;
946 	}
947 	tune_gc = tb->primitive.bottom_shadow_GC ;
948     } else
949     if (OpenSide(wid) == XmRIGHT) {
950 	pts[0].x = tb->core.width - JoinShadowThickness(tb) ;
951 	pts[1].x = tb->core.width ;
952 	pts[2].x = tb->core.width ;
953 	if (!VisualArmed(tb)) {
954 	    /* bottom part ends with half top gc to join clean */
955 	    pts[0].y = tb->core.height ;
956 	    pts[1].y = tb->core.height - tb->primitive.shadow_thickness ;
957 	    pts[2].y = tb->core.height ;
958 	} else {
959 	    /* top part ends with half top gc to join clean */
960 	    pts[0].y = 0 ;
961 	    pts[1].y = tb->primitive.shadow_thickness ;
962 	    pts[2].y = 0 ;
963 	}
964 	tune_gc = tb->primitive.top_shadow_GC ;
965     } else
966     if (OpenSide(wid) == XmTOP) {
967 	pts[0].y = 0 ;
968 	pts[1].y = 0 ;
969 	pts[2].y = JoinShadowThickness(tb) ;
970 	if (!VisualArmed(tb)) {
971 	    /* right part starts with half bottom gc to join clean */
972 	    pts[0].x = 0 ;
973 	    pts[1].x = tb->primitive.shadow_thickness ;
974 	    pts[2].x = 0 ;
975 	} else {
976 	    /* left part ends with half bottom gc to join clean */
977 	    pts[0].x = tb->core.width - tb->primitive.shadow_thickness ;
978 	    pts[1].x = tb->core.width ;
979 	    pts[2].x = tb->core.width ;
980 	}
981 	tune_gc = tb->primitive.bottom_shadow_GC ;
982     } else
983     if (OpenSide(wid) == XmBOTTOM) {
984 	pts[0].y = tb->core.height - JoinShadowThickness(tb) ;
985 	pts[1].y = tb->core.height ;
986 	pts[2].y = tb->core.height ;
987 	if (!VisualArmed(tb)) {
988 	    /* left part ends with half top gc to join clean */
989 	    pts[0].x = tb->core.width ;
990 	    pts[1].x = tb->core.width - tb->primitive.shadow_thickness ;
991 	    pts[2].x = tb->core.width ;
992 	} else {
993 	    /* right part ends with half top gc to join clean */
994 	    pts[0].x = 0 ;
995 	    pts[1].x = tb->primitive.shadow_thickness ;
996 	    pts[2].x = 0 ;
997 	}
998 	tune_gc = tb->primitive.top_shadow_GC ;
999     }
1000     /* else if (OpenSide(wid) == XmNONE) do nothing */
1001 
1002 
1003 
1004   /* Draw the curved portion of the shadows, unless join thickness is zero */
1005     if ((OpenSide(wid) != XmNONE) && (JoinShadowThickness(wid) > (Dimension)0))
1006 	XFillPolygon (XtDisplay (tb), XtWindow (tb),
1007 		      tune_gc, pts, 3, Convex, CoordModeOrigin);
1008 }
1009 
1010 
1011 
1012 /*****************************************************************************
1013  *
1014  *  GetHighlightRectangle
1015  *      Called by BorderHighlight, BorderUnhighlight, and DrawShadow. ,
1016  *      in the case a rectangular tab only.
1017  *
1018  ***************************************************************************/
1019 static void
GetHighlightRectangle(ExmTabButtonWidget tb,XRectangle * hrect)1020 GetHighlightRectangle (
1021     ExmTabButtonWidget tb,
1022     XRectangle *hrect)
1023 {
1024   /* This function writes the dimensions of the highlight rectangle
1025      into *hrect.  These dimensions depend on the value of ExmNopenSide. */
1026     if (OpenSide(tb) == XmLEFT) {
1027 	hrect->x = 0 ;
1028 	hrect->y = tb->primitive.shadow_thickness ;
1029 	hrect->width = XtWidth( tb) - tb->primitive.shadow_thickness ;
1030 	hrect->height = XtHeight(tb) - 2* tb->primitive.shadow_thickness ;
1031     } else
1032     if (OpenSide(tb) == XmRIGHT) {
1033 	hrect->x = tb->primitive.shadow_thickness ;
1034 	hrect->y = tb->primitive.shadow_thickness ;
1035 	hrect->width = XtWidth(tb) - tb->primitive.shadow_thickness ;
1036 	hrect->height = XtHeight(tb) - 2* tb->primitive.shadow_thickness ;
1037     } else
1038     if (OpenSide(tb) == XmTOP) {
1039 	hrect->x = tb->primitive.shadow_thickness ;
1040 	hrect->y = 0 ;
1041 	hrect->width = XtWidth(tb) - 2* tb->primitive.shadow_thickness ;
1042 	hrect->height = XtHeight(tb) - tb->primitive.shadow_thickness ;
1043     } else
1044     if (OpenSide(tb) == XmBOTTOM) {
1045 	hrect->x = tb->primitive.shadow_thickness ;
1046 	hrect->y = tb->primitive.shadow_thickness ;
1047 	hrect->width = XtWidth(tb) - 2* tb->primitive.shadow_thickness ;
1048 	hrect->height = XtHeight(tb) - tb->primitive.shadow_thickness ;
1049     } else
1050     if (OpenSide(tb) == XmNONE) {
1051 	hrect->x = tb->primitive.shadow_thickness ;
1052 	hrect->y = tb->primitive.shadow_thickness ;
1053 	hrect->width = XtWidth(tb) - 2* tb->primitive.shadow_thickness ;
1054 	hrect->height = XtHeight(tb) - 2*tb->primitive.shadow_thickness ;
1055     }
1056 }
1057 
1058 
1059 
1060 /***************************************************************
1061  * Trait methods for XmQTjoinSide trait
1062  ***************************************************************/
1063 
1064 /***************************************************************
1065  *
1066  * JoinSideSetValue:
1067  *      The setValue trait method of XmQTjoinSide.
1068  *
1069  ***************************************************************/
1070 static void
JoinSideSetValue(Widget tab,unsigned char join_side,Dimension join_thickness)1071 JoinSideSetValue(Widget tab,
1072 		 unsigned char join_side,
1073 		 Dimension join_thickness)
1074 {
1075    ExmTabButtonWidget tb = (ExmTabButtonWidget) tab;
1076    Arg args[6];
1077    int n = 0;
1078 
1079    if (OpenSide(tb) != join_side)
1080 	{
1081 	XtSetArg (args[n], ExmNopenSide, join_side), n++;
1082 	XtSetValues (tab, args, n);
1083 	}
1084 
1085    if (JoinShadowThickness(tb) != join_thickness)
1086 	{
1087 	JoinShadowThickness(tb) = join_thickness;
1088 	/* redraw, in the case XtSetValues will not */
1089 	if (XtIsRealized(tab))
1090 	   DrawShadow (tab);
1091 	}
1092 
1093 }
1094 
1095 
1096 /***************************************************************
1097  *
1098  * JoinSideGetValue:
1099  *      The getValue trait method of XmQTjoinSide.
1100  *
1101  ***************************************************************/
1102 static unsigned char
JoinSideGetValue(Widget tab,Dimension * join_thickness)1103 JoinSideGetValue(Widget tab,
1104 		 Dimension  * join_thickness)
1105 {
1106     ExmTabButtonWidget tb = (ExmTabButtonWidget) tab;
1107 
1108     *join_thickness = JoinShadowThickness(tb);
1109 
1110     return OpenSide(tb) ;
1111 }
1112 
1113 
1114 /************************************************************************
1115  *		Application Accessible External Functions
1116  ************************************************************************/
1117 
1118 /************************************************************************
1119  *
1120  *  ExmCreateTabButton
1121  *	Create an instance of a TabButton and return its widget id.
1122  *
1123  ************************************************************************/
1124 Widget
ExmCreateTabButton(Widget parent,char * name,ArgList arglist,Cardinal argcount)1125 ExmCreateTabButton(
1126         Widget parent,
1127         char *name,
1128         ArgList arglist,
1129         Cardinal argcount )
1130 {
1131    return (XtCreateWidget (name, exmTabButtonWidgetClass,
1132                            parent, arglist, argcount));
1133 }
1134