Release 0.4.7
[wine] / controls / WinCommand.c
1 /***********************************************************
2 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
3 and the Massachusetts Institute of Technology, Cambridge, Massachusetts.
4
5                         All Rights Reserved
6
7 Permission to use, copy, modify, and distribute this software and its 
8 documentation for any purpose and without fee is hereby granted, 
9 provided that the above copyright notice appear in all copies and that
10 both that copyright notice and this permission notice appear in 
11 supporting documentation, and that the names of Digital or MIT not be
12 used in advertising or publicity pertaining to distribution of the
13 software without specific, written prior permission.  
14
15 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
16 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
17 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
18 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
19 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
20 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
21 SOFTWARE.
22
23 ******************************************************************/
24
25 /*
26  * Modifications for Wine
27  *
28  * 8/27/93  David Metcalfe (david@prism.demon.co.uk)
29  *          Converted to WinCommand
30  */
31
32 /*
33  * WinCommand.c - WinCommand button widget
34  */
35
36 #include <stdio.h>
37 #include <X11/IntrinsicP.h>
38 #include <X11/StringDefs.h>
39 #include <X11/Xmu/Misc.h>
40 #include <X11/Xaw/XawInit.h>
41 #include "WinCommandP.h"
42 #include <X11/Xmu/Converters.h>
43
44 #define DEFAULT_HIGHLIGHT_THICKNESS 2
45 #define DEFAULT_SHAPE_HIGHLIGHT 32767
46
47 /****************************************************************
48  *
49  * Full class record constant
50  *
51  ****************************************************************/
52
53 /* Private Data */
54
55 static char defaultTranslations[] =
56     "<EnterWindow>:     highlight()             \n\
57      <LeaveWindow>:     reset()                 \n\
58      <Btn1Down>:        set()                   \n\
59      <Btn1Up>:          notify() unset()        ";
60
61 #define offset(field) XtOffsetOf(WinCommandRec, field)
62 static XtResource resources[] = { 
63    {XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer), 
64       offset(wincommand.callbacks), XtRCallback, (XtPointer)NULL},
65    {XtNhighlightThickness, XtCThickness, XtRDimension, sizeof(Dimension),
66       offset(wincommand.highlight_thickness), XtRImmediate,
67       (XtPointer) DEFAULT_SHAPE_HIGHLIGHT},
68    {XtNshapeStyle, XtCShapeStyle, XtRShapeStyle, sizeof(int),
69       offset(wincommand.shape_style), XtRImmediate, 
70             (XtPointer)XawShapeRectangle},
71    {XtNcornerRoundPercent, XtCCornerRoundPercent, 
72         XtRDimension, sizeof(Dimension),
73         offset(wincommand.corner_round), XtRImmediate, (XtPointer) 25},
74 };
75 #undef offset
76
77 static Boolean SetValues();
78 static void Initialize(), Redisplay(), Set(), Reset(), Notify(), Unset();
79 static void Highlight(), Unhighlight(), Destroy(), PaintWinCommandWidget();
80 static void ClassInitialize();
81 static Boolean ShapeButton();
82 static void Realize(), Resize();
83
84 static XtActionsRec actionsList[] = {
85   {"set",               Set},
86   {"notify",            Notify},
87   {"highlight",         Highlight},
88   {"reset",             Reset},
89   {"unset",             Unset},
90   {"unhighlight",       Unhighlight}
91 };
92
93 #define SuperClass ((WinLabelWidgetClass)&winLabelClassRec)
94
95 WinCommandClassRec winCommandClassRec = {
96   {
97     (WidgetClass) SuperClass,           /* superclass             */    
98     "WinCommand",                       /* class_name             */
99     sizeof(WinCommandRec),              /* size                   */
100     ClassInitialize,                    /* class_initialize       */
101     NULL,                               /* class_part_initialize  */
102     FALSE,                              /* class_inited           */
103     Initialize,                         /* initialize             */
104     NULL,                               /* initialize_hook        */
105     Realize,                            /* realize                */
106     actionsList,                        /* actions                */
107     XtNumber(actionsList),              /* num_actions            */
108     resources,                          /* resources              */
109     XtNumber(resources),                /* resource_count         */
110     NULLQUARK,                          /* xrm_class              */
111     FALSE,                              /* compress_motion        */
112     TRUE,                               /* compress_exposure      */
113     TRUE,                               /* compress_enterleave    */
114     FALSE,                              /* visible_interest       */
115     Destroy,                            /* destroy                */
116     Resize,                             /* resize                 */
117     Redisplay,                          /* expose                 */
118     SetValues,                          /* set_values             */
119     NULL,                               /* set_values_hook        */
120     XtInheritSetValuesAlmost,           /* set_values_almost      */
121     NULL,                               /* get_values_hook        */
122     NULL,                               /* accept_focus           */
123     XtVersion,                          /* version                */
124     NULL,                               /* callback_private       */
125     defaultTranslations,                /* tm_table               */
126     XtInheritQueryGeometry,             /* query_geometry         */
127     XtInheritDisplayAccelerator,        /* display_accelerator    */
128     NULL                                /* extension              */
129   },  /* CoreClass fields initialization */
130   {
131     XtInheritChangeSensitive            /* change_sensitive     */
132   },  /* SimpleClass fields initialization */
133   {
134     0,                                     /* field not used    */
135   },  /* WinLabelClass fields initialization */
136   {
137     0,                                     /* field not used    */
138   },  /* WinCommandClass fields initialization */
139 };
140
141   /* for public consumption */
142 WidgetClass winCommandWidgetClass = (WidgetClass) &winCommandClassRec;
143
144 /****************************************************************
145  *
146  * Private Procedures
147  *
148  ****************************************************************/
149
150 static GC 
151 Get_GC(cbw, fg, bg)
152 WinCommandWidget cbw;
153 Pixel fg, bg;
154 {
155   XGCValues     values;
156   
157   values.foreground   = fg;
158   values.background     = bg;
159   values.font           = cbw->winlabel.font->fid;
160   values.cap_style = CapProjecting;
161   
162   if (cbw->wincommand.highlight_thickness > 1 )
163     values.line_width   = cbw->wincommand.highlight_thickness;
164   else 
165     values.line_width   = 0;
166   
167   return XtGetGC((Widget)cbw,
168                  (GCForeground|GCBackground|GCFont|GCLineWidth|GCCapStyle),
169                  &values);
170 }
171
172
173 /* ARGSUSED */
174 static void 
175 Initialize(request, new, args, num_args)
176 Widget request, new;
177 ArgList args;                   /* unused */
178 Cardinal *num_args;             /* unused */
179 {
180   WinCommandWidget cbw = (WinCommandWidget) new;
181   int shape_event_base, shape_error_base;
182
183   if (cbw->wincommand.shape_style != XawShapeRectangle
184       && !XShapeQueryExtension(XtDisplay(new), &shape_event_base, 
185                                &shape_error_base))
186       cbw->wincommand.shape_style = XawShapeRectangle;
187   if (cbw->wincommand.highlight_thickness == DEFAULT_SHAPE_HIGHLIGHT) {
188       if (cbw->wincommand.shape_style != XawShapeRectangle)
189           cbw->wincommand.highlight_thickness = 0;
190       else
191           cbw->wincommand.highlight_thickness = DEFAULT_HIGHLIGHT_THICKNESS;
192   }
193
194   cbw->wincommand.normal_GC = Get_GC(cbw, cbw->winlabel.foreground, 
195                                   cbw->core.background_pixel);
196   cbw->wincommand.inverse_GC = Get_GC(cbw, cbw->core.background_pixel, 
197                                    cbw->winlabel.foreground);
198   XtReleaseGC(new, cbw->winlabel.normal_GC);
199   cbw->winlabel.normal_GC = cbw->wincommand.normal_GC;
200
201   cbw->wincommand.set = FALSE;
202   cbw->wincommand.highlighted = HighlightNone;
203 }
204
205 static Region 
206 HighlightRegion(cbw)
207 WinCommandWidget cbw;
208 {
209   static Region outerRegion = NULL, innerRegion, emptyRegion;
210   XRectangle rect;
211
212   if (cbw->wincommand.highlight_thickness == 0 ||
213       cbw->wincommand.highlight_thickness >
214       (Dimension) ((Dimension) Min(cbw->core.width, cbw->core.height)/2))
215     return(NULL);
216
217   if (outerRegion == NULL) {
218     /* save time by allocating scratch regions only once. */
219     outerRegion = XCreateRegion();
220     innerRegion = XCreateRegion();
221     emptyRegion = XCreateRegion();
222   }
223
224   rect.x = rect.y = 0;
225   rect.width = cbw->core.width;
226   rect.height = cbw->core.height;
227   XUnionRectWithRegion( &rect, emptyRegion, outerRegion );
228   rect.x = rect.y = cbw->wincommand.highlight_thickness;
229   rect.width -= cbw->wincommand.highlight_thickness * 2;
230   rect.height -= cbw->wincommand.highlight_thickness * 2;
231   XUnionRectWithRegion( &rect, emptyRegion, innerRegion );
232   XSubtractRegion( outerRegion, innerRegion, outerRegion );
233   return outerRegion;
234 }
235
236 /***************************
237 *
238 *  Action Procedures
239 *
240 ***************************/
241
242 /* ARGSUSED */
243 static void 
244 Set(w,event,params,num_params)
245 Widget w;
246 XEvent *event;
247 String *params;         /* unused */
248 Cardinal *num_params;   /* unused */
249 {
250   WinCommandWidget cbw = (WinCommandWidget)w;
251
252   if (cbw->wincommand.set)
253     return;
254
255   cbw->wincommand.set= TRUE;
256   if (XtIsRealized(w))
257     PaintWinCommandWidget(w, (Region) NULL, TRUE);
258 }
259
260 /* ARGSUSED */
261 static void
262 Unset(w,event,params,num_params)
263 Widget w;
264 XEvent *event;
265 String *params;         /* unused */
266 Cardinal *num_params;
267 {
268   WinCommandWidget cbw = (WinCommandWidget)w;
269
270   if (!cbw->wincommand.set)
271     return;
272
273   cbw->wincommand.set = FALSE;
274   if (XtIsRealized(w)) {
275     XClearWindow(XtDisplay(w), XtWindow(w));
276     PaintWinCommandWidget(w, (Region) NULL, TRUE);
277   }
278 }
279
280 /* ARGSUSED */
281 static void 
282 Reset(w,event,params,num_params)
283 Widget w;
284 XEvent *event;
285 String *params;         /* unused */
286 Cardinal *num_params;   /* unused */
287 {
288   WinCommandWidget cbw = (WinCommandWidget)w;
289
290   if (cbw->wincommand.set) {
291     cbw->wincommand.highlighted = HighlightNone;
292     Unset(w, event, params, num_params);
293   }
294   else
295     Unhighlight(w, event, params, num_params);
296 }
297
298 /* ARGSUSED */
299 static void 
300 Highlight(w,event,params,num_params)
301 Widget w;
302 XEvent *event;
303 String *params;         
304 Cardinal *num_params;   
305 {
306   WinCommandWidget cbw = (WinCommandWidget)w;
307
308   if ( *num_params == (Cardinal) 0) 
309     cbw->wincommand.highlighted = HighlightWhenUnset;
310   else {
311     if ( *num_params != (Cardinal) 1) 
312       XtWarning("Too many parameters passed to highlight action table.");
313     switch (params[0][0]) {
314     case 'A':
315     case 'a':
316       cbw->wincommand.highlighted = HighlightAlways;
317       break;
318     default:
319       cbw->wincommand.highlighted = HighlightWhenUnset;
320       break;
321     }
322   }
323
324   if (XtIsRealized(w))
325     PaintWinCommandWidget(w, HighlightRegion(cbw), TRUE);
326 }
327
328 /* ARGSUSED */
329 static void 
330 Unhighlight(w,event,params,num_params)
331 Widget w;
332 XEvent *event;
333 String *params;         /* unused */
334 Cardinal *num_params;   /* unused */
335 {
336   WinCommandWidget cbw = (WinCommandWidget)w;
337
338   cbw->wincommand.highlighted = HighlightNone;
339   if (XtIsRealized(w))
340     PaintWinCommandWidget(w, HighlightRegion(cbw), TRUE);
341 }
342
343 /* ARGSUSED */
344 static void 
345 Notify(w,event,params,num_params)
346 Widget w;
347 XEvent *event;
348 String *params;         /* unused */
349 Cardinal *num_params;   /* unused */
350 {
351   WinCommandWidget cbw = (WinCommandWidget)w; 
352
353   /* check to be sure state is still Set so that user can cancel
354      the action (e.g. by moving outside the window, in the default
355      bindings.
356   */
357   if (cbw->wincommand.set)
358     XtCallCallbackList(w, cbw->wincommand.callbacks, NULL);
359 }
360
361 /*
362  * Repaint the widget window
363  */
364
365 /************************
366 *
367 *  REDISPLAY (DRAW)
368 *
369 ************************/
370
371 /* ARGSUSED */
372 static void 
373 Redisplay(w, event, region)
374 Widget w;
375 XEvent *event;
376 Region region;
377 {
378   PaintWinCommandWidget(w, region, FALSE);
379 }
380
381 /*      Function Name: PaintWinCommandWidget
382  *      Description: Paints the wincommand widget.
383  *      Arguments: w - the wincommand widget.
384  *                 region - region to paint (passed to the superclass).
385  *                 change - did it change either set or highlight state?
386  *      Returns: none
387  */
388
389 static void 
390 PaintWinCommandWidget(w, region, change)
391 Widget w;
392 Region region;
393 Boolean change;
394 {
395   WinCommandWidget cbw = (WinCommandWidget) w;
396   Boolean very_thick;
397   GC norm_gc, rev_gc;
398    
399   very_thick = cbw->wincommand.highlight_thickness >
400                (Dimension)((Dimension) Min(cbw->core.width, 
401                                            cbw->core.height)/2);
402
403   if (cbw->wincommand.set) {
404     cbw->winlabel.normal_GC = cbw->wincommand.inverse_GC;
405     XFillRectangle(XtDisplay(w), XtWindow(w), cbw->wincommand.normal_GC,
406                    0, 0, cbw->core.width, cbw->core.height);
407     region = NULL;              /* Force label to repaint text. */
408   }
409   else
410       cbw->winlabel.normal_GC = cbw->wincommand.normal_GC;
411
412   if (cbw->wincommand.highlight_thickness <= 0)
413   {
414     (*SuperClass->core_class.expose) (w, (XEvent *) NULL, region);
415     return;
416   }
417
418 /*
419  * If we are set then use the same colors as if we are not highlighted. 
420  */
421
422   if (cbw->wincommand.set == (cbw->wincommand.highlighted == HighlightNone)) {
423     norm_gc = cbw->wincommand.inverse_GC;
424     rev_gc = cbw->wincommand.normal_GC;
425   }
426   else {
427     norm_gc = cbw->wincommand.normal_GC;
428     rev_gc = cbw->wincommand.inverse_GC;
429   }
430
431   if ( !( (!change && (cbw->wincommand.highlighted == HighlightNone)) ||
432           ((cbw->wincommand.highlighted == HighlightWhenUnset) &&
433            (cbw->wincommand.set))) ) {
434     if (very_thick) {
435       cbw->winlabel.normal_GC = norm_gc; /* Give the label the right GC. */
436       XFillRectangle(XtDisplay(w),XtWindow(w), rev_gc,
437                      0, 0, cbw->core.width, cbw->core.height);
438     }
439     else {
440       /* wide lines are centered on the path, so indent it */
441       int offset = cbw->wincommand.highlight_thickness/2;
442       XDrawRectangle(XtDisplay(w),XtWindow(w), rev_gc, offset, offset, 
443                      cbw->core.width - cbw->wincommand.highlight_thickness,
444                      cbw->core.height - cbw->wincommand.highlight_thickness);
445     }
446   }
447   (*SuperClass->core_class.expose) (w, (XEvent *) NULL, region);
448 }
449
450 static void 
451 Destroy(w)
452 Widget w;
453 {
454   WinCommandWidget cbw = (WinCommandWidget) w;
455
456   /* so WinLabel can release it */
457   if (cbw->winlabel.normal_GC == cbw->wincommand.normal_GC)
458     XtReleaseGC( w, cbw->wincommand.inverse_GC );
459   else
460     XtReleaseGC( w, cbw->wincommand.normal_GC );
461 }
462
463 /*
464  * Set specified arguments into widget
465  */
466
467 /* ARGSUSED */
468 static Boolean 
469 SetValues (current, request, new)
470 Widget current, request, new;
471 {
472   WinCommandWidget oldcbw = (WinCommandWidget) current;
473   WinCommandWidget cbw = (WinCommandWidget) new;
474   Boolean redisplay = False;
475
476   if ( oldcbw->core.sensitive != cbw->core.sensitive && !cbw->core.sensitive) {
477     /* about to become insensitive */
478     cbw->wincommand.set = FALSE;
479     cbw->wincommand.highlighted = HighlightNone;
480     redisplay = TRUE;
481   }
482   
483   if ( (oldcbw->winlabel.foreground != cbw->winlabel.foreground)           ||
484        (oldcbw->core.background_pixel != cbw->core.background_pixel) ||
485        (oldcbw->wincommand.highlight_thickness != 
486                                    cbw->wincommand.highlight_thickness) ||
487        (oldcbw->winlabel.font != cbw->winlabel.font) ) 
488   {
489     if (oldcbw->winlabel.normal_GC == oldcbw->wincommand.normal_GC)
490         /* WinLabel has release one of these */
491       XtReleaseGC(new, cbw->wincommand.inverse_GC);
492     else
493       XtReleaseGC(new, cbw->wincommand.normal_GC);
494
495     cbw->wincommand.normal_GC = Get_GC(cbw, cbw->winlabel.foreground, 
496                                     cbw->core.background_pixel);
497     cbw->wincommand.inverse_GC = Get_GC(cbw, cbw->core.background_pixel, 
498                                      cbw->winlabel.foreground);
499     XtReleaseGC(new, cbw->winlabel.normal_GC);
500     cbw->winlabel.normal_GC = (cbw->wincommand.set
501                             ? cbw->wincommand.inverse_GC
502                             : cbw->wincommand.normal_GC);
503     
504     redisplay = True;
505   }
506
507   if ( XtIsRealized(new)
508        && oldcbw->wincommand.shape_style != cbw->wincommand.shape_style
509        && !ShapeButton(cbw, TRUE))
510   {
511       cbw->wincommand.shape_style = oldcbw->wincommand.shape_style;
512   }
513
514   return (redisplay);
515 }
516
517 static void ClassInitialize()
518 {
519     XawInitializeWidgetSet();
520     XtSetTypeConverter( XtRString, XtRShapeStyle, XmuCvtStringToShapeStyle,
521                         NULL, 0, XtCacheNone, NULL );
522 }
523
524
525 static Boolean
526 ShapeButton(cbw, checkRectangular)
527 WinCommandWidget cbw;
528 Boolean checkRectangular;
529 {
530     Dimension corner_size;
531
532     if ( (cbw->wincommand.shape_style == XawShapeRoundedRectangle) ) {
533         corner_size = (cbw->core.width < cbw->core.height) ? cbw->core.width 
534                                                            : cbw->core.height;
535         corner_size = (int) (corner_size * cbw->wincommand.corner_round) / 100;
536     }
537
538     if (checkRectangular || cbw->wincommand.shape_style != XawShapeRectangle) {
539         if (!XmuReshapeWidget((Widget) cbw, cbw->wincommand.shape_style,
540                               corner_size, corner_size)) {
541             cbw->wincommand.shape_style = XawShapeRectangle;
542             return(False);
543         }
544     }
545     return(TRUE);
546 }
547
548 static void Realize(w, valueMask, attributes)
549     Widget w;
550     Mask *valueMask;
551     XSetWindowAttributes *attributes;
552 {
553     (*winCommandWidgetClass->core_class.superclass->core_class.realize)
554         (w, valueMask, attributes);
555
556     ShapeButton( (WinCommandWidget) w, FALSE);
557 }
558
559 static void Resize(w)
560     Widget w;
561 {
562     if (XtIsRealized(w)) 
563         ShapeButton( (WinCommandWidget) w, FALSE);
564
565     (*winCommandWidgetClass->core_class.superclass->core_class.resize)(w);
566 }
567