Command.c revision 775e7de9
1/* $Xorg: Command.c,v 1.5 2001/02/09 02:03:43 xorgcvs Exp $ */
2
3/***********************************************************
4
5Copyright 1987, 1988, 1994, 1998  The Open Group
6
7Permission to use, copy, modify, distribute, and sell this software and its
8documentation for any purpose is hereby granted without fee, provided that
9the above copyright notice appear in all copies and that both that
10copyright notice and this permission notice appear in supporting
11documentation.
12
13The above copyright notice and this permission notice shall be included in
14all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
19OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
20AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23Except as contained in this notice, the name of The Open Group shall not be
24used in advertising or otherwise to promote the sale, use or other dealings
25in this Software without prior written authorization from The Open Group.
26
27
28Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
29
30                        All Rights Reserved
31
32Permission to use, copy, modify, and distribute this software and its
33documentation for any purpose and without fee is hereby granted,
34provided that the above copyright notice appear in all copies and that
35both that copyright notice and this permission notice appear in
36supporting documentation, and that the name of Digital not be
37used in advertising or publicity pertaining to distribution of the
38software without specific, written prior permission.
39
40DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
41ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
42DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
43ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
44WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
45ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
46SOFTWARE.
47
48******************************************************************/
49/* $XFree86: xc/lib/Xaw/Command.c,v 1.15tsi Exp $ */
50
51/*
52 * Command.c - Command button widget
53 */
54
55#ifdef HAVE_CONFIG_H
56#include <config.h>
57#endif
58#include <stdio.h>
59#include <X11/IntrinsicP.h>
60#include <X11/StringDefs.h>
61#include <X11/extensions/shape.h>
62#include <X11/Xmu/Converters.h>
63#include <X11/Xmu/Drawing.h>
64#include <X11/Xmu/Misc.h>
65#include <X11/Xaw/CommandP.h>
66#include <X11/Xaw/XawInit.h>
67#include "Private.h"
68
69#define DEFAULT_HIGHLIGHT_THICKNESS 2
70#define DEFAULT_SHAPE_HIGHLIGHT 32767
71#define STR_EQUAL(str1, str2)	(str1 == str2 || strcmp(str1, str2) == 0)
72
73/*
74 * Class Methods
75 */
76static void XawCommandClassInitialize(void);
77static void XawCommandDestroy(Widget);
78static void XawCommandInitialize(Widget, Widget, ArgList, Cardinal*);
79static void XawCommandRealize(Widget, Mask*, XSetWindowAttributes*);
80static void XawCommandResize(Widget);
81static void XawCommandRedisplay(Widget, XEvent*, Region);
82static Boolean XawCommandSetValues(Widget, Widget, Widget, ArgList, Cardinal*);
83static void XawCommandGetValuesHook(Widget, ArgList, Cardinal*);
84static Bool ChangeSensitive(Widget);
85
86/*
87 * Prototypes
88 */
89static GC Get_GC(CommandWidget, Pixel, Pixel);
90static void PaintCommandWidget(Widget, XEvent*, Region, Bool);
91static Region HighlightRegion(CommandWidget);
92static Bool ShapeButton(CommandWidget, Bool);
93static void XawCommandToggle(Widget);
94
95/*
96 * Actions
97 */
98static void Highlight(Widget, XEvent*, String*, Cardinal*);
99static void Notify(Widget, XEvent*, String*, Cardinal*);
100static void Reset(Widget, XEvent*, String*, Cardinal*);
101static void Set(Widget, XEvent*, String*, Cardinal*);
102static void Unhighlight(Widget, XEvent*, String*, Cardinal*);
103static void Unset(Widget, XEvent*, String*, Cardinal*);
104
105/*
106 * Initialization
107 */
108static char defaultTranslations[] =
109"<Enter>:"	"highlight()\n"
110"<Leave>:"	"reset()\n"
111"<Btn1Down>:"	"set()\n"
112"<Btn1Up>:"	"notify() unset()\n"
113;
114
115#define offset(field) XtOffsetOf(CommandRec, field)
116static XtResource resources[] = {
117  {
118    XtNcallback,
119    XtCCallback,
120    XtRCallback,
121    sizeof(XtPointer),
122    offset(command.callbacks),
123    XtRCallback,
124    NULL
125  },
126  {
127    XtNhighlightThickness,
128    XtCThickness,
129    XtRDimension,
130    sizeof(Dimension),
131    offset(command.highlight_thickness),
132    XtRImmediate,
133    (XtPointer)DEFAULT_SHAPE_HIGHLIGHT
134  },
135  {
136    XtNshapeStyle,
137    XtCShapeStyle,
138    XtRShapeStyle,
139    sizeof(int),
140    offset(command.shape_style),
141    XtRImmediate,
142    (XtPointer)XawShapeRectangle
143  },
144  {
145    XtNcornerRoundPercent,
146    XtCCornerRoundPercent,
147    XtRDimension,
148    sizeof(Dimension),
149    offset(command.corner_round),
150    XtRImmediate,
151    (XtPointer)25
152  },
153};
154#undef offset
155
156static XtActionsRec actionsList[] = {
157  {"set",		Set},
158  {"notify",		Notify},
159  {"highlight",		Highlight},
160  {"reset",		Reset},
161  {"unset",		Unset},
162  {"unhighlight",	Unhighlight}
163};
164
165#define SuperClass ((LabelWidgetClass)&labelClassRec)
166
167CommandClassRec commandClassRec = {
168  /* core */
169  {
170    (WidgetClass)SuperClass,		/* superclass		  */
171    "Command",				/* class_name		  */
172    sizeof(CommandRec),			/* size			  */
173    XawCommandClassInitialize,		/* class_initialize	  */
174    NULL,				/* class_part_initialize  */
175    False,				/* class_inited		  */
176    XawCommandInitialize,		/* initialize		  */
177    NULL,				/* initialize_hook	  */
178    XawCommandRealize,			/* realize		  */
179    actionsList,			/* actions		  */
180    XtNumber(actionsList),		/* num_actions		  */
181    resources,				/* resources		  */
182    XtNumber(resources),		/* num_resources	  */
183    NULLQUARK,				/* xrm_class		  */
184    False,				/* compress_motion	  */
185    True,				/* compress_exposure	  */
186    True,				/* compress_enterleave	  */
187    False,				/* visible_interest	  */
188    XawCommandDestroy,			/* destroy		  */
189    XawCommandResize,			/* resize		  */
190    XawCommandRedisplay,		/* expose		  */
191    XawCommandSetValues,		/* set_values		  */
192    NULL,				/* set_values_hook	  */
193    XtInheritSetValuesAlmost,		/* set_values_almost	  */
194    XawCommandGetValuesHook,		/* get_values_hook	  */
195    NULL,				/* accept_focus		  */
196    XtVersion,				/* version		  */
197    NULL,				/* callback_private	  */
198    defaultTranslations,		/* tm_table		  */
199    XtInheritQueryGeometry,		/* query_geometry	  */
200    XtInheritDisplayAccelerator,	/* display_accelerator	  */
201    NULL,				/* extension */
202  },
203  /* simple */
204  {
205    ChangeSensitive,			/* change_sensitive */
206  },
207  /* label */
208  {
209    NULL,				/* not used */
210  },
211  /* command */
212  {
213    NULL,				/* not used */
214  },
215};
216
217WidgetClass commandWidgetClass = (WidgetClass)&commandClassRec;
218
219/*
220 * Implementation
221 */
222static GC
223Get_GC(CommandWidget cbw, Pixel fg, Pixel bg)
224{
225    XGCValues	values;
226
227    values.foreground	= fg;
228    values.background	= bg;
229    values.font		= cbw->label.font->fid;
230    values.cap_style	= CapProjecting;
231
232    if (cbw->command.highlight_thickness > 1)
233	values.line_width = cbw->command.highlight_thickness;
234    else
235	values.line_width = 0;
236
237    if (cbw->simple.international == True)
238	return (XtAllocateGC((Widget)cbw, 0,
239			     GCForeground | GCBackground | GCLineWidth |
240			     GCCapStyle, &values, GCFont, 0));
241    else
242	return (XtGetGC((Widget)cbw,
243			GCForeground | GCBackground | GCFont | GCLineWidth |
244			GCCapStyle, &values));
245}
246
247/*ARGSUSED*/
248static void
249XawCommandInitialize(Widget request, Widget cnew,
250		     ArgList args, Cardinal *num_args)
251{
252    CommandWidget cbw = (CommandWidget)cnew;
253    int shape_event_base, shape_error_base;
254
255    if (!cbw->label.font) XtError("Aborting: no font found\n");
256
257    if (cbw->command.shape_style != XawShapeRectangle &&
258	!XShapeQueryExtension(XtDisplay(cnew), &shape_event_base,
259			      &shape_error_base))
260	cbw->command.shape_style = XawShapeRectangle;
261
262    if (cbw->command.highlight_thickness == DEFAULT_SHAPE_HIGHLIGHT) {
263	if (cbw->command.shape_style != XawShapeRectangle)
264	    cbw->command.highlight_thickness = 0;
265	else
266	    cbw->command.highlight_thickness = DEFAULT_HIGHLIGHT_THICKNESS;
267    }
268
269    cbw->command.normal_GC = Get_GC(cbw, cbw->label.foreground,
270				    cbw->core.background_pixel);
271    cbw->command.inverse_GC = Get_GC(cbw, cbw->core.background_pixel,
272				     cbw->label.foreground);
273    XtReleaseGC(cnew, cbw->label.normal_GC);
274    cbw->label.normal_GC = cbw->command.normal_GC;
275
276    cbw->command.set = False;
277    cbw->command.highlighted = HighlightNone;
278}
279
280static Region
281HighlightRegion(CommandWidget cbw)
282{
283    static Region outerRegion = NULL, innerRegion, emptyRegion;
284    XRectangle rect;
285
286    if (cbw->command.highlight_thickness == 0 ||
287        cbw->command.highlight_thickness > Min(XtWidth(cbw), XtHeight(cbw)) / 2)
288	return (NULL);
289
290    if (outerRegion == NULL) {
291	/* save time by allocating scratch regions only once. */
292	outerRegion = XCreateRegion();
293	innerRegion = XCreateRegion();
294	emptyRegion = XCreateRegion();
295    }
296
297    rect.x = rect.y = 0;
298    rect.width = XtWidth(cbw);
299    rect.height = XtHeight(cbw);
300    XUnionRectWithRegion(&rect, emptyRegion, outerRegion);
301    rect.x = rect.y = cbw->command.highlight_thickness;
302    rect.width -= cbw->command.highlight_thickness * 2;
303    rect.height -= cbw->command.highlight_thickness * 2;
304    XUnionRectWithRegion(&rect, emptyRegion, innerRegion);
305    XSubtractRegion(outerRegion, innerRegion, outerRegion);
306
307    return (outerRegion);
308}
309
310/***************************
311*  Action Procedures
312***************************/
313static void
314XawCommandToggle(Widget w)
315{
316    CommandWidget xaw = (CommandWidget)w;
317    Arg args[2];
318    Cardinal num_args;
319
320    num_args = 0;
321    XtSetArg(args[num_args], XtNbackground,
322	     xaw->label.foreground);		++num_args;
323    XtSetArg(args[num_args], XtNforeground,
324	     xaw->core.background_pixel);	++num_args;
325    XtSetValues(w, args, num_args);
326}
327
328/*ARGSUSED*/
329static void
330Set(Widget w, XEvent *event, String *params, Cardinal *num_params)
331{
332    CommandWidget cbw = (CommandWidget)w;
333
334    if (cbw->command.set)
335	return;
336
337    XawCommandToggle(w);
338    cbw->command.set= True;
339}
340
341/*ARGSUSED*/
342static void
343Unset(Widget w, XEvent *event, String *params, Cardinal *num_params)
344{
345    CommandWidget cbw = (CommandWidget)w;
346
347    if (!cbw->command.set)
348	return;
349
350    cbw->command.set = False;
351    XawCommandToggle(w);
352}
353
354/*ARGSUSED*/
355static void
356Reset(Widget w, XEvent *event, String *params, Cardinal *num_params)
357{
358    CommandWidget cbw = (CommandWidget)w;
359
360    if (cbw->command.set) {
361	cbw->command.highlighted = HighlightNone;
362	Unset(w, event, params, num_params);
363    }
364    else
365	Unhighlight(w, event, params, num_params);
366}
367
368/*ARGSUSED*/
369static void
370Highlight(Widget w, XEvent *event, String *params, Cardinal *num_params)
371{
372    CommandWidget cbw = (CommandWidget)w;
373
374    if (*num_params == (Cardinal)0)
375	cbw->command.highlighted = HighlightWhenUnset;
376    else {
377	if (*num_params != (Cardinal)1)
378	    XtWarning("Too many parameters passed to highlight action table.");
379	switch (params[0][0]) {
380	    case 'A':
381	    case 'a':
382		cbw->command.highlighted = HighlightAlways;
383		break;
384	    default:
385		cbw->command.highlighted = HighlightWhenUnset;
386		break;
387	}
388    }
389
390    if (XtIsRealized(w))
391	PaintCommandWidget(w, event, HighlightRegion(cbw), True);
392}
393
394/*ARGSUSED*/
395static void
396Unhighlight(Widget w, XEvent *event, String *params, Cardinal *num_params)
397{
398    CommandWidget cbw = (CommandWidget)w;
399
400    cbw->command.highlighted = HighlightNone;
401    if (XtIsRealized(w))
402	PaintCommandWidget(w, event, HighlightRegion(cbw), True);
403}
404
405/*ARGSUSED*/
406static void
407Notify(Widget w, XEvent *event, String *params, Cardinal *num_params)
408{
409    CommandWidget cbw = (CommandWidget)w;
410
411    /* check to be sure state is still Set so that user can cancel
412       the action (e.g. by moving outside the window, in the default
413       bindings.
414    */
415    if (cbw->command.set)
416	XtCallCallbackList(w, cbw->command.callbacks, (XtPointer) NULL);
417}
418
419static void
420XawCommandRedisplay(Widget w, XEvent *event, Region region)
421{
422    PaintCommandWidget(w, event, region, False);
423}
424
425/*
426 * Function:
427 *	PaintCommandWidget
428 * Parameters:
429 *	w      - command widget
430 *	region - region to paint (passed to the superclass)
431 *                 change - did it change either set or highlight state?
432 */
433static void
434PaintCommandWidget(Widget w, XEvent *event, Region region, Bool change)
435{
436    CommandWidget cbw = (CommandWidget)w;
437    Bool very_thick;
438    GC rev_gc;
439
440    very_thick = cbw->command.highlight_thickness
441		 > Min(XtWidth(cbw), XtHeight(cbw)) / 2;
442
443    if (cbw->command.highlight_thickness == 0) {
444	(*SuperClass->core_class.expose) (w, event, region);
445	return;
446    }
447
448    /*
449     * If we are set then use the same colors as if we are not highlighted
450     */
451
452    if (cbw->command.highlighted != HighlightNone) {
453	rev_gc = cbw->command.normal_GC;
454    }
455    else {
456	rev_gc = cbw->command.inverse_GC;
457    }
458
459    if (!((!change && cbw->command.highlighted == HighlightNone)
460	|| (cbw->command.highlighted == HighlightWhenUnset
461	    && cbw->command.set))) {
462	if (very_thick)
463	    XFillRectangle(XtDisplay(w),XtWindow(w), rev_gc,
464			   0, 0, XtWidth(cbw), XtHeight(cbw));
465	else {
466	    /* wide lines are centered on the path, so indent it */
467	    if (cbw->core.background_pixmap != XtUnspecifiedPixmap &&
468		rev_gc == cbw->command.inverse_GC) {
469		XClearArea(XtDisplay(w), XtWindow(w),
470			   0, 0, XtWidth(cbw), cbw->command.highlight_thickness,
471			   False);
472		XClearArea(XtDisplay(w), XtWindow(w),
473			   0, cbw->command.highlight_thickness,
474			   cbw->command.highlight_thickness,
475			   XtHeight(cbw) - (cbw->command.highlight_thickness<<1),
476			   False);
477		XClearArea(XtDisplay(w), XtWindow(w),
478			   XtWidth(cbw) - cbw->command.highlight_thickness,
479			   cbw->command.highlight_thickness,
480			   cbw->command.highlight_thickness,
481			   XtHeight(cbw) - (cbw->command.highlight_thickness<<1),
482			   False);
483		XClearArea(XtDisplay(w), XtWindow(w),
484			   0, XtHeight(cbw) - cbw->command.highlight_thickness,
485			   XtWidth(cbw), cbw->command.highlight_thickness,
486			   False);
487	    }
488	    else {
489		int offset = cbw->command.highlight_thickness / 2;
490
491		XDrawRectangle(XtDisplay(w),XtWindow(w), rev_gc, offset, offset,
492			       XtWidth(cbw) - cbw->command.highlight_thickness,
493			      XtHeight(cbw) - cbw->command.highlight_thickness);
494	   }
495	}
496    }
497
498    (*SuperClass->core_class.expose)(w, event, region);
499}
500
501static void
502XawCommandDestroy(Widget w)
503{
504    CommandWidget cbw = (CommandWidget)w;
505
506    /* Label will release cbw->command.normal_GC */
507    XtReleaseGC(w, cbw->command.inverse_GC);
508}
509
510/*ARGSUSED*/
511static Boolean
512XawCommandSetValues(Widget current, Widget request, Widget cnew,
513		    ArgList args, Cardinal *num_args)
514{
515    CommandWidget oldcbw = (CommandWidget)current;
516    CommandWidget cbw = (CommandWidget)cnew;
517    Boolean redisplay = False;
518
519    if (oldcbw->core.sensitive != cbw->core.sensitive && !cbw->core.sensitive) {
520	cbw->command.highlighted = HighlightNone;
521	redisplay = True;
522    }
523
524    if (cbw->command.set) {
525	unsigned int i;
526	Pixel foreground, background;
527
528	foreground = oldcbw->label.foreground;
529	background = oldcbw->core.background_pixel;
530	for (i = 0; i < *num_args; i++) {
531	    if (STR_EQUAL(args[i].name, XtNforeground))
532		background = cbw->label.foreground;
533	    else if (STR_EQUAL(args[i].name, XtNbackground))
534		foreground = cbw->core.background_pixel;
535	}
536	cbw->label.foreground = foreground;
537	cbw->core.background_pixel = background;
538    }
539
540    if (oldcbw->label.foreground != cbw->label.foreground
541	|| oldcbw->core.background_pixel != cbw->core.background_pixel
542	|| oldcbw->command.highlight_thickness
543	!= cbw->command.highlight_thickness
544	|| oldcbw->label.font != cbw->label.font) {
545	XtReleaseGC(cnew, cbw->command.inverse_GC);
546
547	cbw->command.normal_GC = Get_GC(cbw, cbw->label.foreground,
548					cbw->core.background_pixel);
549	cbw->command.inverse_GC = Get_GC(cbw, cbw->core.background_pixel,
550					 cbw->label.foreground);
551	XtReleaseGC(cnew, cbw->label.normal_GC);
552	cbw->label.normal_GC = cbw->command.normal_GC;
553
554	redisplay = True;
555    }
556
557    if (XtIsRealized(cnew)
558	&& oldcbw->command.shape_style != cbw->command.shape_style
559	&& !ShapeButton(cbw, True))
560	cbw->command.shape_style = oldcbw->command.shape_style;
561
562    return (redisplay);
563}
564
565static void
566XawCommandGetValuesHook(Widget w, ArgList args, Cardinal *num_args)
567{
568    CommandWidget cbw = (CommandWidget)w;
569    unsigned int i;
570
571    for (i = 0; i < *num_args; i++) {
572	if (STR_EQUAL(args[i].name, XtNforeground))
573	    *((String*)args[i].value) = cbw->command.set ?
574		(String)cbw->core.background_pixel : (String)cbw->label.foreground;
575	else if (STR_EQUAL(args[i].name, XtNbackground))
576	    *((String*)args[i].value) = cbw->command.set ?
577		(String)cbw->label.foreground : (String)cbw->core.background_pixel;
578    }
579}
580
581static void
582XawCommandClassInitialize(void)
583{
584    XawInitializeWidgetSet();
585    XtSetTypeConverter(XtRString, XtRShapeStyle, XmuCvtStringToShapeStyle,
586		       NULL, 0, XtCacheNone, NULL);
587    XtSetTypeConverter(XtRShapeStyle, XtRString, XmuCvtShapeStyleToString,
588		       NULL, 0, XtCacheNone, NULL);
589}
590
591static Bool
592ShapeButton(CommandWidget cbw, Bool checkRectangular)
593{
594    Dimension corner_size = 0;
595
596    if (cbw->command.shape_style == XawShapeRoundedRectangle) {
597	corner_size = XtWidth(cbw) < XtHeight(cbw) ?
598			XtWidth(cbw) : XtHeight(cbw);
599	corner_size = (corner_size * cbw->command.corner_round) / 100;
600    }
601
602    if (checkRectangular || cbw->command.shape_style != XawShapeRectangle) {
603	if (!XmuReshapeWidget((Widget)cbw, cbw->command.shape_style,
604			      corner_size, corner_size)) {
605	    cbw->command.shape_style = XawShapeRectangle;
606	    return (False);
607	}
608    }
609
610    return (True);
611}
612
613static void
614XawCommandRealize(Widget w, Mask *valueMask, XSetWindowAttributes *attributes)
615{
616    (*commandWidgetClass->core_class.superclass->core_class.realize)
617	(w, valueMask, attributes);
618
619    ShapeButton((CommandWidget)w, False);
620}
621
622static void
623XawCommandResize(Widget w)
624{
625    if (XtIsRealized(w))
626	ShapeButton((CommandWidget)w, False);
627
628    (*commandWidgetClass->core_class.superclass->core_class.resize)(w);
629}
630
631static Bool
632ChangeSensitive(Widget w)
633{
634    CommandWidget cbw = (CommandWidget)w;
635
636    if (XtIsRealized(w)) {
637	if (XtIsSensitive(w)) {
638	    if (w->core.border_pixmap != XtUnspecifiedPixmap)
639		XSetWindowBorderPixmap(XtDisplay(w), XtWindow(w),
640				       w->core.border_pixmap);
641	    else
642		XSetWindowBorder(XtDisplay(w), XtWindow(w),
643				 w->core.border_pixel);
644	}
645	else {
646	    if (cbw->simple.insensitive_border == None)
647		cbw->simple.insensitive_border =
648		    XmuCreateStippledPixmap(XtScreen(w),
649					    w->core.border_pixel,
650					    cbw->command.set ?
651						cbw->label.foreground :
652						w->core.background_pixel,
653					    w->core.depth);
654	    XSetWindowBorderPixmap(XtDisplay(w), XtWindow(w),
655				   cbw->simple.insensitive_border);
656	}
657    }
658
659    return (False);
660}
661