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