1 /*************************************<+>*************************************
2 *****************************************************************************
3 **
4 ** File: Button.c
5 **
6 ** Project: X Widgets
7 **
8 ** Description: Code/Definitions for Button widget meta class.
9 **
10 ****************************************************************************
11 **
12 ** Copyright (c) 1988 by Hewlett-Packard Company
13 ** Copyright (c) 1988 by the Massachusetts Institute of Technology
14 **
15 ** Permission to use, copy, modify, and distribute this software
16 ** and its documentation for any purpose and without fee is hereby
17 ** granted, provided that the above copyright notice appear in all
18 ** copies and that both that copyright notice and this permission
19 ** notice appear in supporting documentation, and that the names of
20 ** Hewlett-Packard or M.I.T. not be used in advertising or publicity
21 ** pertaining to distribution of the software without specific, written
22 ** prior permission.
23 **
24 *****************************************************************************
25 *************************************<+>************************************/
26
27
28 /*
29 * Include files & Static Routine Definitions
30 */
31
32 #include <stdio.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <X11/StringDefs.h>
36 #include <X11/IntrinsicP.h>
37 #include <X11/Intrinsic.h>
38 #include <X11/Xatom.h>
39 #include <Xw/Xw.h>
40 #include <Xw/XwP.h>
41
42 static Boolean SetValues();
43 static void ButtonDestroy();
44
45
46 /*************************************<->*************************************
47 *
48 *
49 * Description: resource list for class: Button
50 * -----------
51 *
52 * Provides default resource settings for instances of this class.
53 * To get full set of default settings, examine resouce list of super
54 * classes of this class.
55 *
56 *************************************<->***********************************/
57
58 static XtResource resources[] =
59 {
60
61 {
62 XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *),
63 XtOffset (XwButtonWidget, button.font), XtRString, "Fixed"
64 },
65
66 {
67 XtNlabel, XtCLabel, XtRString, sizeof (caddr_t),
68 XtOffset (XwButtonWidget, button.label), XtRString, NULL
69 },
70
71 {
72 XtNlabelLocation, XtCLabelLocation, XtRLabelLocation, sizeof(int),
73 XtOffset (XwButtonWidget, button.label_location), XtRString, "right"
74 },
75
76 {
77 XtNvSpace, XtCVSpace, XtRDimension, sizeof(Dimension),
78 XtOffset (XwButtonWidget, button.internal_height), XtRString, "2"
79 },
80
81 {
82 XtNhSpace, XtCHSpace, XtRDimension, sizeof(Dimension),
83 XtOffset (XwButtonWidget, button.internal_width), XtRString, "2"
84 },
85
86 {
87 XtNset, XtCSet, XtRBoolean, sizeof (Boolean),
88 XtOffset (XwButtonWidget, button.set), XtRString, "False"
89 },
90
91 {
92 XtNsensitiveTile, XtCSensitiveTile, XtRTileType, sizeof (int),
93 XtOffset(XwButtonWidget, button.sensitive_tile),
94 XtRString, "foreground"
95 },
96
97 {
98 XtNborderWidth, XtCBorderWidth, XtRDimension, sizeof(Dimension),
99 XtOffset(XwButtonWidget,core.border_width), XtRString, "0"
100 },
101 };
102
103
104
105
106 /*************************************<->*************************************
107 *
108 *
109 * Description: global class record for instances of class: Button
110 * -----------
111 *
112 * Defines default field settings for this class record. Note that
113 * while Button needs a special type converter, it doesn't need to
114 * register that converter since Primitive takes care of registering
115 * all of the converters for the X Widgets library and Button is a
116 * subclass of Primitive.
117 *
118 *************************************<->***********************************/
119
120 XwButtonClassRec XwbuttonClassRec =
121 {
122 {
123 (WidgetClass) &XwprimitiveClassRec, /* superclass */
124 "XwButton", /* class_name */
125 sizeof(XwButtonRec), /* widget_size */
126 NULL, /* class_initialize */
127 NULL, /* class part initialize */
128 FALSE, /* class_inited */
129 _XwInitializeButton, /* initialize */
130 NULL, /* initialize hook */
131 NULL, /* realize */
132 NULL, /* actions */
133 0, /* num_actions */
134 resources, /* resources */
135 XtNumber(resources), /* num_resources */
136 NULLQUARK, /* xrm_class */
137 TRUE, /* compress_motion */
138 TRUE, /* compress_exposure */
139 TRUE, /* compress_enterleave */
140 FALSE, /* visible_interest */
141 ButtonDestroy, /* destroy */
142 NULL, /* resize */
143 NULL, /* expose */
144 SetValues, /* set_values */
145 NULL, /* set_values_hook */
146 XtInheritSetValuesAlmost, /* set_values_almost */
147 NULL, /* get_values_hook */
148 NULL, /* accept_focus */
149 XtVersion, /* toolkit version */
150 NULL, /* PRIVATE callback */
151 NULL, /* tm_table */
152 NULL, /* query_geometry */
153 /* display_accelerator */ XtInheritDisplayAccelerator,
154 /* extension */ NULL
155 },
156
157 /* Inherit procedures for handling traversal and selection */
158 {
159 (XtWidgetProc) XtInheritBorderHighlight,
160 (XtWidgetProc) XtInheritBorderUnhighlight,
161 (XwEventProc) XtInheritSelectProc,
162 (XwEventProc) XtInheritReleaseProc,
163 (XwEventProc) XtInheritToggleProc,
164 },
165
166 {
167 0, /* Button class part - unused */
168 }
169 };
170
171 WidgetClass XwbuttonWidgetClass = (WidgetClass) &XwbuttonClassRec;
172
173
174
175 /*************************************<->*************************************
176 *
177 * ButtonDestroy (bw)
178 *
179 * Description:
180 * -----------
181 * Free up the space taken by the button's label and free up the
182 * button's graphic contexts.
183 *
184 *
185 * Inputs:
186 * ------
187 * bw = button (or subclass of button) to be destroyed.
188 *
189 *************************************<->***********************************/
ButtonDestroy(bw)190 static void ButtonDestroy (bw)
191 XwButtonWidget bw;
192 {
193 if (bw->button.label)
194 XtFree (bw->button.label);
195 XtDestroyGC (bw->button.normal_GC);
196 XtDestroyGC (bw->button.inverse_GC);
197 }
198
199 /*************************************<->*************************************
200 *
201 * SetValues(current, request, new, last)
202 *
203 * Description:
204 * -----------
205 * This is the set values procedure for the button class.
206 *
207 *
208 * Inputs:
209 * ------
210 * current = original widget;
211 * request = copy of current (?);
212 * new = copy of request which reflects changes made to it by
213 * set values procedures of its superclasses;
214 * last = TRUE if this is the last set values procedure to be called.
215 *
216 * Outputs:
217 * -------
218 *
219 * Procedures Called
220 * -----------------
221 *
222 *************************************<->***********************************/
223
SetValues(current,request,new)224 static Boolean SetValues(current, request, new)
225 Widget current, request, new;
226 {
227 XwButtonWidget curcbox = (XwButtonWidget) current;
228 XwButtonWidget newcbox = (XwButtonWidget) new;
229 Boolean flag = FALSE; /* our return value */
230 int GCflag = 0;
231
232 #define NewNormal 1
233 #define NewInverse 2
234
235
236 /************************************************************
237 * If the button setting has been changed then set flag so that
238 * button will be redisplayed. NOTE THAT IF THE APPLICATION
239 * WANTS THE CALLBACKS ASSOCIATED WITH THE BUTTON TO BE INVOKED
240 * IT WILL HAVE TO DIRECTLY CALL XTCALLCALLBACKS.
241 ************************************************************/
242 if (curcbox->button.set != newcbox->button.set) flag = TRUE;
243
244
245 /************************************************************
246 * If the font has been changed then we need to
247 * recompute the text width and height. We'll also have
248 * to change other things, see below...
249 ************************************************************/
250 if (curcbox->button.font != newcbox->button.font)
251 {
252 _XwSetTextWidthAndHeight(newcbox);
253 flag = TRUE;
254 }
255
256
257 /**************************************************************
258 * If a new label has been provided then we'll need to copy it
259 * into our data space.
260 * Free up any space previously allocated for the label name.
261 * NOTE: We allow them to specify a NULL label.
262 ***************************************************************/
263 if (curcbox->button.label != newcbox->button.label)
264 {
265 /* Set label length, height and width */
266 _XwSetTextWidthAndHeight(newcbox);
267
268 if (newcbox->button.label)
269 newcbox->button.label = strcpy(
270 XtMalloc((unsigned) newcbox->button.label_len + 1),
271 newcbox->button.label);
272
273 if (curcbox->button.label)
274 XtFree ((char *) curcbox->button.label);
275 flag = TRUE;
276 }
277
278
279
280 /**********************************************************************
281 * If the label location flag has changed set the redisplay flag TRUE
282 * and check that it has a valid setting. If its invalid, then issue
283 * a warning.
284 ********************************************************************/
285 if (curcbox->button.label_location != newcbox->button.label_location)
286 {
287 flag = TRUE;
288 if ((newcbox->button.label_location != XwLEFT) &&
289 (newcbox->button.label_location != XwRIGHT) &&
290 (newcbox->button.label_location != XwCENTER))
291 {
292 XtWarning("SetValues: Invalid Label Location Setting.");
293 newcbox->button.label_location =
294 curcbox->button.label_location;
295 }
296 }
297
298 /*********************************************************************
299 * If the sensitive tile has changed, validate it. Then, if the
300 * foreground or background or font or sensitive tile has changed
301 * then recompute/get a new (In)sensitive GC.
302 ********************************************************************/
303 if (newcbox->button.sensitive_tile != XwFOREGROUND)
304 {
305 newcbox->button.sensitive_tile = curcbox->button.sensitive_tile;
306 XtWarning("SetValues: Invalid sensitive tile setting.");
307 }
308
309 if (curcbox->primitive.foreground != newcbox->primitive.foreground ||
310 curcbox->core.background_pixel != newcbox->core.background_pixel)
311 {
312 /* GET ALL NEW GC's */
313 GCflag = NewNormal | NewInverse;
314 }
315
316 if (curcbox->button.font->fid != newcbox->button.font->fid)
317 {
318 /* GET NEW GC's FOR normal & inverse */
319 GCflag = NewNormal | NewInverse;
320 }
321
322 if ((curcbox->button.sensitive_tile != newcbox->button.sensitive_tile) &&
323 (! newcbox->core.sensitive || ! newcbox->core.ancestor_sensitive))
324 {
325 /* GET NEW normal GC */
326 GCflag |= NewNormal;
327 }
328
329 if ((newcbox->core.sensitive != curcbox->core.sensitive) &&
330 ((newcbox->core.sensitive && newcbox->core.ancestor_sensitive) ||
331 (!newcbox->core.sensitive && curcbox->core.ancestor_sensitive)))
332 {
333 /* GET NEW normal GC */
334 GCflag |= NewNormal;
335 }
336 else if (newcbox->core.sensitive &&
337 (curcbox->core.ancestor_sensitive != newcbox->core.ancestor_sensitive))
338 {
339 /* GET NEW normal GC */
340 GCflag |= NewNormal;
341 }
342
343
344
345 if (GCflag & NewNormal)
346 {
347 XtDestroyGC(curcbox->button.normal_GC);
348 _XwGetNormalGC(newcbox);
349 flag = TRUE;
350 }
351 if (GCflag & NewInverse)
352 {
353 XtDestroyGC(curcbox->button.inverse_GC);
354 _XwGetInverseGC(newcbox);
355 flag = TRUE;
356 }
357
358
359 /********************************************************************
360 * NOTE THAT SUBCLASSES OF BUTTON WILL HAVE TO CHECK FOR CHANGES IN
361 * internal_width, internal_height AND DETERMINE WHAT SHOULD BE DONE.
362 * WE'RE JUST BEING GOOD CITIZENS HERE AND FLAGING THAT IT WILL HAVE
363 * TO BE REDISPLAYED BECAUSE OF THE CHANGE.
364 *********************************************************************/
365 if ((curcbox->button.internal_width
366 != newcbox->button.internal_width) ||
367 (curcbox->button.internal_height
368 != newcbox->button.internal_height)) flag = TRUE;
369
370
371 return( flag );
372 }
373
374
375
376 /*************************************<->*************************************
377 *
378 * void _XwSetTextWidthAndHeight (aButton)
379 *
380 * Description:
381 * -----------
382 * Calculate width and height of displayed text in pixels.
383 * Sets the following fields in the button portion of a button
384 * instance record:
385 *
386 * label_len
387 * label_height
388 * label_width
389 *
390 * Inputs:
391 * ------
392 * aButton = XwButtonWidget
393 *
394 * Procedures Called
395 * -----------------
396 * XwStrlen() [a macro: #define XwStrlen(s) ((s) ? strlen(s) : 0) ]
397 * XTextWidth()
398 *************************************<->***********************************/
399
_XwSetTextWidthAndHeight(aButton)400 void _XwSetTextWidthAndHeight(aButton)
401 XwButtonWidget aButton;
402 {
403 register XFontStruct *fs = aButton->button.font;
404
405 aButton->button.label_height =
406 fs->max_bounds.ascent + fs->max_bounds.descent;
407
408 if (aButton->button.label)
409 {
410 aButton->button.label_len = XwStrlen(aButton->button.label);
411
412 aButton->button.label_width = XTextWidth(
413 fs, aButton->button.label,
414 (int) aButton->button.label_len);
415 }
416 else
417 {
418 aButton->button.label_len = 0;
419 aButton->button.label_width = 0;
420 }
421 }
422
423 /*************************************<->*************************************
424 *
425 * void _XwGetNormalGC(aButton)
426 * XwButtonWidget aButton;
427 *
428 * Description:
429 * -----------
430 * Uses the widget specific foreground to generate the "normal"
431 * graphic context. Note that this is a XToolkit sharable GC.
432 * Creates the needed GC and sets ptr. in instance record.
433 *
434 * Inputs:
435 * ------
436 * aButton = widget instance.
437 *
438 * Outputs:
439 * -------
440 *
441 * Procedures Called
442 * -----------------
443 * XtGetGC
444 *************************************<->***********************************/
445
_XwGetNormalGC(aButton)446 void _XwGetNormalGC(aButton)
447 XwButtonWidget aButton;
448 {
449 XGCValues values;
450
451 values.foreground = aButton->primitive.foreground;
452 values.background = aButton->core.background_pixel;
453 values.font = aButton->button.font->fid;
454
455 values.line_width = 1;
456 aButton->button.normal_GC = XtGetGC((Widget)aButton,
457 (unsigned) GCForeground |
458 (unsigned) GCBackground |
459 GCLineWidth |
460 GCFont,
461 &values);
462 }
463
464 /*************************************<->*************************************
465 *
466 * void _XwGetInverseGC(aButton)
467 * XwButtonWidget aButton;
468 *
469 * Description:
470 * -----------
471 *
472 * Inputs:
473 * ------
474 * aButton = widget instance.
475 *
476 * Outputs:
477 * -------
478 *
479 * Procedures Called
480 * -----------------
481 * XwGetGC
482 *************************************<->***********************************/
483
_XwGetInverseGC(aButton)484 void _XwGetInverseGC(aButton)
485 XwButtonWidget aButton;
486 {
487 XGCValues values;
488 values.foreground = aButton->core.background_pixel;
489 values.background = aButton->primitive.foreground;
490 values.font = aButton->button.font->fid;
491 values.line_width = 1;
492
493 aButton->button.inverse_GC = XtGetGC((Widget)aButton,
494 (unsigned) GCForeground | (unsigned) GCBackground |
495 GCLineWidth | GCFont, &values);
496 }
497
498 /*************************************<->*************************************
499 *
500 * _XwInitializeButton (request, new)
501 *
502 * Description:
503 * -----------
504 * This is the button instance initialize procedure. Make sure
505 * that the button has a label, compute its height and width fields;
506 * set the appropriate graphic contexts.
507 *
508 * Inputs:
509 * ------
510 * request = original instance record;
511 *
512 * new = instance record with modifications induced by
513 * other initialize routines, changes are made to this
514 * record;
515 *
516 * args = argument list specified in XtCreateWidget;
517 *
518 * num_args = argument count;
519 *
520 * Outputs:
521 * -------
522 *
523 * Procedures Called
524 * -----------------
525 *
526 *************************************<->***********************************/
527
_XwInitializeButton(request,new)528 void _XwInitializeButton (request, new)
529 Widget request, new;
530
531 {
532 XwButtonWidget aButton = (XwButtonWidget) new;
533
534 /*
535 * NOTE: we don't allow a NULL string for our button label.
536 * If we do find a NULL at initialize time then we will use the
537 * button name. The rationale for this is that this allows someone
538 * to change the button label in the .Xdefaults file, whereas if
539 * the application programmer hardwired the name into an arg list
540 * it would be impossible to change. However, if you really want
541 * a NULL string, do a SetValues and change the label to NULL.
542 *
543 * We need to malloc space for this string and copy it to our
544 * space. The toolkit simply copies the pointer to the string.
545 */
546 if (aButton->button.label == NULL) aButton->button.label =
547 aButton->core.name;
548
549 aButton->button.label = strcpy( XtMalloc((unsigned)
550 (XwStrlen(aButton->button.label)+1)),
551 aButton->button.label);
552
553
554 /*
555 * make sure that sensitive_tile is valid
556 */
557 if (aButton->button.sensitive_tile != XwFOREGROUND)
558 {
559 aButton->button.sensitive_tile = XwFOREGROUND;
560 XtWarning("Initialize: Invalid sensitive tile setting.");
561 }
562
563
564 _XwGetInverseGC(aButton);
565 _XwGetNormalGC(aButton);
566
567 _XwSetTextWidthAndHeight(aButton);
568
569 aButton->primitive.display_sensitive = FALSE;
570 aButton->primitive.highlighted = FALSE;
571
572
573 } /* _XwInitializeButton */
574
575
576 /*************************************<->*************************************
577 *
578 * _XwRealize
579 *
580 * Description:
581 * -----------
582 * This is a generalize routine used by both subclasses of Primitive
583 * and Manager. It sets bit gravity to NW for managers and to Forget
584 * for others, but for Buttons is sets it according to the setting of
585 * label location.
586 *
587 * Inputs
588 * ------
589 * w = widget to be realized.
590 *
591 * valueMask = contains event mask for this window/widget.
592 *
593 * attributes = window attributes for this window/widget.
594 *
595 * Outputs:
596 * -------
597 *
598 * Procedures Called
599 * -----------------
600 * XCreateWindow()
601 *************************************<->***********************************/
602
_XwRealize(w,p_valueMask,attributes)603 void _XwRealize(w, p_valueMask, attributes)
604 register Widget w;
605 Mask *p_valueMask;
606 XSetWindowAttributes *attributes;
607 {
608 Mask valueMask = *p_valueMask;
609 valueMask |= CWBitGravity;
610
611 if (XtIsSubclass(w, XwprimitiveWidgetClass)) {
612 register XwPrimitiveWidget pw = (XwPrimitiveWidget) w;
613 if (pw->primitive.cursor != None) valueMask |= CWCursor;
614 attributes->cursor = pw->primitive.cursor;
615 }
616
617 if (XtIsSubclass(w, XwmanagerWidgetClass))
618 attributes->bit_gravity = NorthWestGravity;
619 else
620 if (w->core.widget_class != XwbuttonWidgetClass)
621 attributes->bit_gravity = ForgetGravity;
622 else
623 switch (((XwButtonWidget)w)->button.label_location)
624 {
625 case XwRIGHT:
626 attributes->bit_gravity = EastGravity; break;
627 case XwLEFT:
628 attributes->bit_gravity = WestGravity; break;
629 case XwCENTER:
630 attributes->bit_gravity = CenterGravity; break;
631 }
632
633 XtCreateWindow(w, InputOutput, (Visual *)CopyFromParent,
634 valueMask, attributes);
635
636 _XwRegisterName(w); /* hang widget name as property on window */
637
638 } /* _XwRealize */
639
640
641 /*************************************<->*************************************
642 *
643 * _XwResizeButton(w)
644 *
645 * Description:
646 * -----------
647 * A resize event has been generated. Recompute location of button
648 * elements.
649 *
650 * USED BY CHECKBOX AND PUSHBUTTON CLASSES.
651 *
652 * Inputs:
653 * ------
654 * w = widget to be resized.
655 *
656 * Outputs:
657 * -------
658 *
659 * Procedures Called
660 * -----------------
661 *
662 *************************************<->***********************************/
663
664
_XwResizeButton(w)665 void _XwResizeButton(w)
666 Widget w;
667 {
668 register XwButtonWidget aButton = (XwButtonWidget) w;
669 int direction = aButton->button.label_location;
670
671 switch (direction)
672 {
673 case XwCENTER:
674 aButton->button.label_x = ((int)aButton->core.width + 1 -
675 (int)aButton->button.label_width) / 2;
676 break;
677
678 case XwRIGHT:
679 aButton->button.label_x = (int)aButton->core.width -
680 (int)aButton->button.internal_width -
681 aButton->primitive.highlight_thickness -
682 (int)aButton->button.label_width;
683 break;
684
685 default:
686 aButton->button.label_x = aButton->button.internal_width +
687 aButton->primitive.highlight_thickness;
688 }
689
690 aButton->button.label_y =
691 (((int)aButton->core.height - (int)aButton->button.label_height) >> 1)
692 + aButton->button.font->max_bounds.ascent;
693 }
694
695
696
697 /*************************************<->*************************************
698 *
699 * ProcedureName (parameters)
700 *
701 * Description:
702 * -----------
703 * This procedure is used to store the name of the widget as a
704 * property on the widget's window. The property name is the
705 * string "XW_NAME". Note that we do not malloc space for the
706 * widget's name, we merely use the pointer to this string which
707 * is already in core.name. This shouldn't be a problem since
708 * the name will remain as long as the widget exists.
709 *
710 * We only want to do this one time, thus the hokey method of
711 * initializing a static boolean which prevents us from registering
712 * this atom more than once on the server.
713 *
714 * If possible, we should put this together with other functions done
715 * once. Also, WILL THIS BE A PROBLEM WHEN THE TOOLKIT ALLOWS MORE
716 * THAN ONE DISPLAY TO BE OPENED?????
717 *
718 * Inputs:
719 * ------
720 * w = widget being created.
721 *
722 * Outputs:
723 * -------
724 *
725 * Procedures Called
726 * -----------------
727 *
728 *************************************<->***********************************/
729
730 static Atom widget_name;
731 static Boolean first=True;
732
_XwRegisterName(w)733 void _XwRegisterName(w)
734 Widget w;
735 {
736 if (first)
737 {
738 widget_name = XInternAtom (XtDisplay(w), "XW_NAME", False);
739 first = False;
740 }
741 XChangeProperty (XtDisplay(w), XtWindow(w), widget_name, XA_STRING, 8,
742 PropModeReplace, w->core.name,
743 XwStrlen(w->core.name)+1);
744 }
745
746
747 /*************************************<->*************************************
748 * Boolean
749 * _XwRecomputeSize(current, new)
750 *
751 * Description:
752 * -----------
753 * Used during SetValues for PButton and Toggle.
754 * If the font has changed OR the label has changed OR
755 * the internal spacing has changed OR the highlight
756 * thickness has changed AND the recompute flag is TRUE
757 * (in the new widget) return TRUE, else return FALSE.
758 *
759 *
760 * Inputs:
761 * ------
762 * current = current version of widget
763 * new = new version of widget
764 *
765 * Outputs:
766 * -------
767 * TRUE if resize is needed and okay, FALSE otherwise.
768 *
769 *************************************<->***********************************/
_XwRecomputeSize(current,new)770 Boolean _XwRecomputeSize(current, new)
771 XwButtonWidget current, new;
772 {
773 if (((new->button.font != current->button.font) ||
774 (new->button.label != current->button.label) ||
775 (new->primitive.highlight_thickness !=
776 current->primitive.highlight_thickness) ||
777 (new->button.internal_height != current->button.internal_height) ||
778 (new->button.internal_width != current->button.internal_width)) &&
779 (new->primitive.recompute_size == TRUE))
780 return(TRUE);
781 else
782 return(FALSE);
783 }
784
785
786
787