1 /* Copyright (C) 1993 Nathan Sidwell */
2 /* RCS $Id: Icon.c,v 4.13 1995/12/14 13:53:27 nathan Exp $ */
3 /* this widget holds a pixmap which it can select a row/column from
4  * similar to a command widget, or initiate a drag on press and move
5  */
6 /*{{{  includes*/
7 #include "ansiknr.h"
8 #include <X11/X.h>
9 #include <X11/Xlib.h>
10 #include <X11/IntrinsicP.h>
11 #include <X11/StringDefs.h>
12 #include <X11/Xaw/SimpleP.h>
13 #include "Icon.h"
14 #include "Drag.h"
15 /*}}}*/
16 /*{{{  structs*/
17 /*{{{  typedef struct _IconClass*/
18 typedef struct _IconClass
19 {
20   int     ansi_compliance; /* not used */
21 } IconClassPart;
22 /*}}}*/
23 /*{{{  typedef struct _IconClassRec*/
24 typedef struct _IconClassRec
25 {
26   CoreClassPart   core_class;
27   SimpleClassPart simple_class;
28   IconClassPart   icon_class;
29 } IconClassRec;
30 /*}}}*/
31 /*{{{  typedef struct _IconPart*/
32 typedef struct
33 {
34   /* resources */
35   Pixmap    pixmap;           /* the icon to display */
36   XtCallbackList callbacks;   /* callbacks to notify */
37   Dimension columns;          /* cells across to highlight */
38   Dimension rows;             /* cells down to highlight */
39   Dimension drag_sensitivity; /* sensitivity of drag callback */
40   Dimension highlight_thickness;  /* thickness of highlight bar */
41   Pixel     foreground;       /* foreground pixel color */
42   int       flash_delay;      /* time to flash */
43   String    drag_name;        /* name of drag */
44 
45   /* private state */
46   unsigned  width;          /* pixmap width */
47   unsigned  height;         /* pixmap height */
48   GC        gc;             /* GC to draw */
49   Position  x;              /* corner of pixmap */
50   Position  y;              /* corner of pixmap */
51   Position  drag_x;         /* drag start x */
52   Position  drag_y;         /* drag start y */
53   Position  selected;       /* selected cell */
54   Boolean   drag_set;       /* drag started */
55   Boolean   highlit;        /* is it highlit? */
56   Boolean   solid;          /* solid foreground */
57   XtIntervalId timeout;     /* timout id */
58 } IconPart;
59 /*}}}*/
60 /*{{{  typedef struct _IconRec*/
61 typedef struct _IconRec
62 {
63   CorePart    core;
64   SimplePart  simple;
65   IconPart    icon;
66 } IconRec;
67 /*}}}*/
68 /*}}}*/
69 /*{{{  resources*/
70 static XtResource resources[] =
71 {
72   {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
73       XtOffsetOf(IconRec, icon.foreground),
74       XtRString, (XtPointer)XtDefaultForeground},
75   {MredNhighlightThickness, XtCThickness, XtRDimension, sizeof(Dimension),
76       XtOffsetOf(IconRec, icon.highlight_thickness),
77       XtRImmediate, (XtPointer)1},
78   {XtNpixmap,   XtCPixmap, XtRBitmap, sizeof(Pixmap),
79       XtOffsetOf(IconRec, icon.pixmap), XtRImmediate, (XtPointer)None},
80   {MredNcolumns,  XtCWidth, XtRDimension, sizeof(Dimension),
81       XtOffsetOf(IconRec, icon.columns), XtRImmediate, (XtPointer)1},
82   {MredNrows,    XtCHeight, XtRDimension, sizeof(Dimension),
83       XtOffsetOf(IconRec, icon.rows), XtRImmediate, (XtPointer)1},
84   {XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer),
85       XtOffsetOf(IconRec, icon.callbacks), XtRCallback, (XtPointer)NULL},
86   {MredNdragSensitivity, XtCThickness, XtRDimension, sizeof(Dimension),
87       XtOffsetOf(IconRec, icon.drag_sensitivity), XtRImmediate, (XtPointer)4},
88   {MredNflashDelay, XtCInterval, XtRInt, sizeof(int),
89       XtOffsetOf(IconRec, icon.flash_delay), XtRImmediate, (XtPointer)100},
90   {MredNdragName, XtCLabel, XtRString, sizeof(String),
91       XtOffsetOf(IconRec, icon.drag_name), XtRImmediate, (XtPointer)"drag"},
92 };
93 /*}}}*/
94 /*{{{  prototypes*/
95 static VOIDFUNC Drag PROTOARG((Widget, XEvent *, String *, Cardinal *));
96 static VOIDFUNC Highlight PROTOARG((Widget, XEvent *, String *, Cardinal *));
97 static VOIDFUNC Notify PROTOARG((Widget, XEvent *, String *, Cardinal *));
98 static VOIDFUNC Set PROTOARG((Widget, XEvent *, String *, Cardinal *));
99 static VOIDFUNC Unhighlight PROTOARG((Widget, XEvent *, String *, Cardinal *));
100 
101 static VOIDFUNC Destroy PROTOARG((Widget));
102 static VOIDFUNC Initialize PROTOARG((Widget, Widget, ArgList, Cardinal *));
103 static XtGeometryResult QueryGeometry
104     PROTOARG((Widget, XtWidgetGeometry *, XtWidgetGeometry *));
105 static VOIDFUNC Redisplay PROTOARG((Widget, XEvent *, Region));
106 static VOIDFUNC Resize PROTOARG((Widget));
107 static Boolean SetValues
108     PROTOARG((Widget, Widget, Widget, ArgList, Cardinal *));
109 
110 #define FakeExpose(iw)  Redisplay((Widget)iw, NULL, NULL)
111 static VOIDFUNC GetGC PROTOARG((IconWidget));
112 static VOIDFUNC GetPixmapSize PROTOARG((IconWidget));
113 static unsigned GetPointerPosition
114     PROTOARG((IconWidget, XEvent *, Position *, Position *));
115 static VOIDFUNC RemoveTimeOut PROTOARG((IconWidget));
116 static VOIDFUNC TimeOut PROTOARG((XtPointer, XtIntervalId *));
117 /*}}}*/
118 /*{{{  translations*/
119 static char translations[] = "\
120 <BtnDown>:set()\n\
121 <BtnUp>:notify()\n\
122 <Motion>:highlight() drag()\n\
123 <EnterWindow>:highlight()\n\
124 <LeaveWindow>:unhighlight()\
125 ";
126 /*}}}*/
127 /*{{{  actions*/
128 static XtActionsRec actions[] =
129 {
130   {"drag", Drag},
131   {"highlight", Highlight},
132   {"notify", Notify},
133   {"set", Set},
134   {"unhighlight", Unhighlight},
135 };
136 /*}}}*/
137 #define SuperClass (WidgetClass)&simpleClassRec
138 /*{{{  IconClassRec iconClassRec =*/
139 IconClassRec iconClassRec =
140 {
141   /*{{{  core class part*/
142   {
143     SuperClass,                       /* superclass */
144     "Icon",                           /* class_name */
145     sizeof(IconRec),                  /* size */
146     NULL,                             /* class_initialize */
147     NULL,                             /* class_part_initialize */
148     False,                            /* class_inited */
149     Initialize,                       /* initialize */
150     NULL,                             /* initialize_hook */
151     XtInheritRealize,                 /* realize */
152     actions,                          /* actions */
153     XtNumber(actions),                /* num_actions */
154     resources,                        /* resources */
155     XtNumber(resources),              /* num_resources */
156     NULLQUARK,                        /* xrm_class */
157     True,                             /* compress_motion */
158     XtExposeCompressMultiple,         /* compress_exposure */
159     True,                             /* compress_enterleave */
160     False,                            /* visible_interest */
161     Destroy,                          /* destroy */
162     Resize,                           /* resize */
163     Redisplay,                        /* expose */
164     SetValues,                        /* set_values */
165     NULL,                             /* set_values_hook */
166     XtInheritSetValuesAlmost,         /* set_values_almost */
167     NULL,                             /* get_values_hook */
168     NULL,                             /* accept_focus */
169     XtVersion,                        /* version */
170     NULL,                             /* callback_private */
171     translations,                     /* default_translations */
172     QueryGeometry,                    /* query_geometry */
173     XtInheritDisplayAccelerator,      /* display_accelerator */
174     NULL,                             /* extension */
175   },
176   /*}}}*/
177   /*{{{  simple class part*/
178   {
179     XtInheritChangeSensitive      /* change_sensitive */
180   },
181   /*}}}*/
182   /*{{{  icon class part*/
183   {
184     0,        /* dummy */
185   },
186   /*}}}*/
187 };
188 /*}}}*/
189 WidgetClass iconWidgetClass = (WidgetClass)&iconClassRec;
190 /* actions */
191 /*{{{  void Drag(widget, event, params, num_params)*/
192 static VOIDFUNC Drag
193 FUNCARG((widget, event, params, num_params),
194 	Widget    widget
195 ARGSEP  XEvent    *event
196 ARGSEP  String    *params
197 ARGSEP  Cardinal  *num_params
198 )
199 /* detect dragging
200  * if we moved >= drag sensitivity since the button press then
201  * invoke a drag widget
202  * the drag widget is found by cheking each level of the widget tree
203  * for a drag child, going up from this widget.
204  * if started, any current selection is cleared
205  * Should be called on button type events
206  */
207 {
208   IconWidget iw;
209   Position  x, y;
210 
211   iw = (IconWidget)widget;
212   x = y = 0;
213   if(event->type != MotionNotify && event->type != ButtonPress)
214     /* EMPTY */;
215   else if(event->type == MotionNotify && !(event->xmotion.state &
216     (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)))
217     /* EMPTY */;
218   else if(!GetPointerPosition(iw, event, &x, &y))
219     /* EMPTY */;
220   else if(iw->icon.drag_set == False)
221     {
222       iw->icon.drag_x = x;
223       iw->icon.drag_y = y;
224       iw->icon.drag_set = True;
225     }
226   else if(iw->icon.drag_sensitivity && iw->icon.drag_name &&
227       iw->icon.drag_sensitivity * iw->icon.drag_sensitivity <=
228       (x - iw->icon.drag_x) * (x - iw->icon.drag_x) +
229       (y - iw->icon.drag_y) * (y - iw->icon.drag_y))
230     /*{{{  popup drag*/
231     {
232       Widget  drag;
233       Widget  parent;
234 
235       drag = NULL;
236       for(parent = (Widget)iw; parent; parent = XtParent(parent))
237 	{
238 	  drag = XtNameToWidget(parent, iw->icon.drag_name);
239 	  if(drag)
240 	    break;
241 	}
242       if(drag)
243 	{
244 	  Position  rx, ry;
245 
246 	  iw->icon.highlit = False;
247 	  FakeExpose(iw);
248 	  XtTranslateCoords((Widget)iw, iw->icon.x, iw->icon.y, &rx, &ry);
249 	  DragPopup((Widget)iw, (Widget)drag, iw->icon.pixmap,
250 	      iw->icon.drag_x, iw->icon.drag_y,
251 	      rx + x, ry + y, event->xmotion.time);
252 	}
253     }
254     /*}}}*/
255   return;
256 }
257 /*}}}*/
258 /*{{{  void Highlight(widget, event, params, num_params)*/
259 static VOIDFUNC Highlight
260 FUNCARG((widget, event, params, num_params),
261 	Widget    widget
262 ARGSEP  XEvent    *event
263 ARGSEP  String    *params
264 ARGSEP  Cardinal  *num_params
265 )
266 /* Determines which cell of the widget is selected, and
267  * redraws if changed.
268  * The drag position is reset, if a new cell is selected.
269  * Should be invoked on pointer movement events.
270  */
271 {
272   IconWidget iw;
273   Position  x, y;
274   int       selected;
275 
276   iw = (IconWidget)widget;
277   selected = GetPointerPosition(iw, event, &x, &y);
278   if(selected)
279     {
280       if(selected > 1 || !iw->icon.highlight_thickness)
281 	selected = -1;
282       else
283 	selected = y / (iw->icon.height / iw->icon.rows) * iw->icon.columns +
284 	    x / (iw->icon.width / iw->icon.columns);
285       if(selected != iw->icon.selected ||
286 	  (iw->icon.highlit == False && selected >= 0))
287 	{
288 	  iw->icon.drag_x = x;
289 	  iw->icon.drag_y = y;
290 	  iw->icon.selected = selected;
291 	  iw->icon.highlit = selected < 0 ? False : True;
292 	  if(selected >= 0)
293 	    RemoveTimeOut(iw);
294 	  FakeExpose(iw);
295 	}
296     }
297   return;
298 }
299 /*}}}*/
300 /*{{{  void Notify(widget, event, params, num_params)*/
301 static VOIDFUNC Notify
302 FUNCARG((widget, event, params, num_params),
303 	Widget    widget
304 ARGSEP  XEvent    *event
305 ARGSEP  String    *params
306 ARGSEP  Cardinal  *num_params
307 )
308 /* notifies of the current selection.
309  * If none is selected nothing happens.
310  * otherwise the callback is called, and the widget flashed
311  * in its foreground color
312  * Normally called on button release
313  */
314 {
315   IconWidget iw;
316 
317   iw = (IconWidget)widget;
318   iw->icon.drag_set = False;
319   if(iw->icon.selected >= 0)
320     {
321       IconCallback data;
322 
323       if(iw->icon.flash_delay)
324 	{
325 	  iw->icon.solid = True;
326 	  FakeExpose(iw);
327 	  iw->icon.timeout =
328 	      XtAppAddTimeOut(XtWidgetToApplicationContext((Widget)iw),
329 	      (unsigned long)iw->icon.flash_delay, TimeOut, (XtPointer)iw);
330 	}
331       data.selection = iw->icon.selected;
332       if(event->type == ButtonPress || event->type == ButtonRelease)
333 	data.button = event->xbutton.button;
334       else
335 	data.button = -1;
336       XtCallCallbackList((Widget)iw, iw->icon.callbacks, (XtPointer)&data);
337     }
338   return;
339 }
340 /*}}}*/
341 /*{{{  void Set(widget, event, params, num_params)*/
342 static VOIDFUNC Set
343 FUNCARG((widget, event, params, num_params),
344 	Widget    widget
345 ARGSEP  XEvent    *event
346 ARGSEP  String    *params
347 ARGSEP  Cardinal  *num_params
348 )
349 /* initialize the drag position
350  * normally called on button press
351  */
352 {
353   IconWidget iw;
354 
355   iw = (IconWidget)widget;
356   GetPointerPosition(iw, event, &iw->icon.drag_x, &iw->icon.drag_y);
357   iw->icon.drag_set = True;
358   return;
359 }
360 /*}}}*/
361 /*{{{  void Unhighlight(widget, event, params, num_params)*/
362 static VOIDFUNC Unhighlight
363 FUNCARG((widget, event, params, num_params),
364 	Widget    widget
365 ARGSEP  XEvent    *event
366 ARGSEP  String    *params
367 ARGSEP  Cardinal  *num_params
368 )
369 /* deselect any selected area and reset the drag
370  * Normally called on LeaveWindow
371  */
372 {
373   IconWidget iw;
374 
375   iw = (IconWidget)widget;
376   if(iw->icon.highlit != False)
377     {
378       iw->icon.highlit = False;
379       FakeExpose(iw);
380     }
381   return;
382 }
383 /*}}}*/
384 /* methods */
385 /*{{{  void Destroy(widget)*/
386 static VOIDFUNC Destroy
387 FUNCARG((widget),
388 	Widget    widget
389 )
390 /* free the GC and remove the flash timeout
391  */
392 {
393   IconWidget iw;
394 
395   iw = (IconWidget)widget;
396   XtReleaseGC((Widget)iw, iw->icon.gc);
397   RemoveTimeOut(iw);
398   return;
399 }
400 /*}}}*/
401 /*{{{  void Initialize(treq, tnew, args, num_args)*/
402 static VOIDFUNC Initialize
403 FUNCARG((treq, tnew, args, num_args),
404 	Widget    treq
405 ARGSEP  Widget    tnew
406 ARGSEP  ArgList   args
407 ARGSEP  Cardinal  *num_args
408 )
409 /* allocate ourselves a GC, and set out default size
410  */
411 {
412   IconWidget niw;
413 
414   niw = (IconWidget)tnew;
415   niw->icon.selected = -1;
416   niw->icon.highlit = False;
417   niw->icon.drag_set = False;
418   niw->icon.solid = False;
419   niw->icon.timeout = (XtIntervalId)0;
420   GetPixmapSize(niw);
421   if(!niw->core.width)
422     niw->core.width = niw->icon.width ? niw->icon.width : 16;
423   if(!niw->core.height)
424     niw->core.height = niw->icon.height ? niw->icon.height : 16;
425   GetGC(niw);
426   if(!niw->icon.columns)
427     niw->icon.columns = 1;
428   if(!niw->icon.rows)
429     niw->icon.rows = 1;
430   return;
431 }
432 /*}}}*/
433 /*{{{  XtGeometryResult QueryGeometry(widget, proposed, answer)*/
434 static XtGeometryResult QueryGeometry
435 FUNCARG((widget, proposed, answer),
436 	Widget    widget
437 ARGSEP  XtWidgetGeometry *proposed
438 ARGSEP  XtWidgetGeometry *answer
439 )
440 /* tell out parent what size we'd like to be
441  */
442 {
443   IconWidget iw;
444 
445   iw = (IconWidget)widget;
446   answer->request_mode = CWWidth | CWHeight;
447   answer->height = iw->icon.width ? iw->icon.width : 16;
448   answer->width = iw->icon.height ? iw->icon.height : 16;
449   if((proposed->request_mode & (CWWidth | CWHeight)) == (CWWidth | CWHeight) &&
450       proposed->width == answer->width && proposed->height == answer->height)
451     return XtGeometryYes;
452   else if(answer->width == iw->core.width && answer->height == iw->core.height)
453     return XtGeometryNo;
454   else
455     return XtGeometryAlmost;
456 }
457 /*}}}*/
458 /*{{{  void Redisplay(widget, event, region)*/
459 static VOIDFUNC Redisplay
460 FUNCARG((widget, event, region),
461 	Widget    widget
462 ARGSEP  XEvent    *event
463 ARGSEP  Region    region
464 )
465 /* repaint ourselves
466  * We don't bother with a clip, just do the whole lot
467  * This involves blitting the pixmap
468  * drawing the select box
469  * Drawing the flash box
470  */
471 {
472   IconWidget  iw;
473 
474   if(!XtIsRealized(widget))
475     return;
476   iw = (IconWidget)widget;
477   if(iw->icon.pixmap != None)
478     XCopyArea(XtDisplay(iw), iw->icon.pixmap, XtWindow(iw), iw->icon.gc, 0, 0,
479       iw->icon.width, iw->icon.height, iw->icon.x, iw->icon.y);
480   if(iw->icon.solid != False)
481     XFillRectangle(XtDisplay(iw), XtWindow(iw), iw->icon.gc,
482 	iw->icon.x + iw->icon.selected % iw->icon.columns *
483 	  iw->icon.width / iw->icon.columns,
484 	iw->icon.y + iw->icon.selected / iw->icon.columns *
485 	iw->icon.height / iw->icon.rows,
486 	iw->icon.width / iw->icon.columns, iw->icon.height / iw->icon.rows);
487   else if(iw->icon.highlit != False)
488     XDrawRectangle(XtDisplay(iw), XtWindow(iw), iw->icon.gc,
489 	iw->icon.x + iw->icon.selected % iw->icon.columns *
490 	  iw->icon.width / iw->icon.columns +
491 	  (iw->icon.highlight_thickness >> 1),
492 	iw->icon.y + iw->icon.selected / iw->icon.columns *
493 	  iw->icon.height / iw->icon.rows +
494 	  (iw->icon.highlight_thickness >> 1),
495 	iw->icon.width / iw->icon.columns - iw->icon.highlight_thickness,
496 	iw->icon.height / iw->icon.rows - iw->icon.highlight_thickness);
497   return;
498 }
499 /*}}}*/
500 /*{{{  void Resize(widget)*/
501 static VOIDFUNC Resize
502 FUNCARG((widget),
503 	Widget    widget
504 )
505 /* recenter the pixmap, when w get resized
506  */
507 {
508   IconWidget iw;
509 
510   iw = (IconWidget)widget;
511   iw->icon.x = (iw->core.width - iw->icon.width) / 2;
512   iw->icon.y = (iw->core.height - iw->icon.height) / 2;
513   return;
514 }
515 /*}}}*/
516 /*{{{  Boolean SetValues(cw, rw, nw, args, num_args)*/
517 static Boolean SetValues
518 FUNCARG((cw, rw, nw, args, num_args),
519 	Widget    cw
520 ARGSEP  Widget    rw
521 ARGSEP  Widget    nw
522 ARGSEP  ArgList   args
523 ARGSEP  Cardinal  *num_args
524 )
525 /* need to get a new GC if color or highlight width changes
526  * need to calculate a new size if the pixmap changes
527  */
528 {
529   Boolean  redraw;
530   IconWidget ciw;
531   IconWidget niw;
532 
533   redraw = False;
534   ciw = (IconWidget)cw;
535   niw = (IconWidget)nw;
536   if(ciw->icon.foreground != niw->icon.foreground ||
537       ciw->icon.highlight_thickness != niw->icon.highlight_thickness)
538     {
539       redraw = True;
540       XtReleaseGC(cw, ciw->icon.gc);
541       GetGC(niw);
542     }
543   if(ciw->icon.pixmap != niw->icon.pixmap)
544     {
545       redraw = True;
546       GetPixmapSize(niw);
547       if(niw->icon.width)
548 	{
549 	  niw->core.width = niw->icon.width;
550 	  niw->icon.x = 0;
551 	}
552       if(niw->icon.height)
553 	{
554 	  niw->core.height = niw->icon.height;
555 	  niw->icon.y = 0;
556 	}
557     }
558   if(!niw->icon.columns)
559     niw->icon.columns = 1;
560   if(!niw->icon.rows)
561     niw->icon.rows = 1;
562   if(ciw->icon.columns != niw->icon.columns ||
563       ciw->icon.rows != niw->icon.rows)
564     {
565       niw->icon.selected = -1;
566       niw->icon.highlit = False;
567     }
568   if(ciw->core.width != niw->core.width ||
569       ciw->core.height != niw->core.height)
570     redraw = False;
571   return redraw;
572 }
573 /*}}}*/
574 /* public routines */
575 /*{{{  void IconRepaint(widget)*/
576 extern VOIDFUNC IconRepaint
577 FUNCARG((widget),
578 	Widget widget
579 )
580 /* repaint the widget, without the anoying flicker caused by an expose
581  * event
582  */
583 {
584   if(XtIsSubclass(widget, (WidgetClass)&iconClassRec))
585     FakeExpose((IconWidget)widget);
586   return;
587 }
588 /*}}}*/
589 /* private routines */
590 /*{{{  void GetGC(iw)*/
591 static VOIDFUNC GetGC
592 FUNCARG((widget),
593 	IconWidget    widget
594 )
595 /* get an approptiate GC
596  */
597 {
598   XGCValues     values;
599 
600   values.foreground = widget->icon.foreground;
601   values.line_width = widget->icon.highlight_thickness;
602   widget->icon.gc = XtGetGC((Widget)widget, GCForeground | GCLineWidth,
603       &values);
604   return;
605 }
606 /*}}}*/
607 /*{{{  void GetPixmapSize(iw)*/
608 static VOIDFUNC GetPixmapSize
609 FUNCARG((widget),
610 	IconWidget  widget
611 )
612 /* find out the pixmap size
613  * if pixmap is None then set to 0
614  * note this interrogates the server
615  */
616 {
617   if(widget->icon.pixmap != None)
618     {
619       Window  root;
620       int x, y;
621       unsigned border;
622       unsigned depth;
623       Status status;
624 
625       status = XGetGeometry(XtDisplay(widget), widget->icon.pixmap, &root,
626 	  &x, &y, &widget->icon.width, &widget->icon.height, &border, &depth);
627     }
628   else
629     widget->icon.width = widget->icon.height = 0;
630   return;
631 }
632 /*}}}*/
633 /*{{{  unsigned GetPointerPosition(iw, event, x, y)*/
634 static unsigned GetPointerPosition
635 FUNCARG((iw, event, xp, yp),
636 	IconWidget iw
637 ARGSEP  XEvent    *event    /* event to query */
638 ARGSEP  Position  *xp       /* x coordinate return */
639 ARGSEP  Position  *yp       /* y coordinate return */
640 )
641 /* find the pointer position on widget window.
642  * returns 0 if not a pointer event
643  * returns 1 if inside the pixmap
644  * returns 2 if outside pixmap && inside widget
645  * returns 3 if outisde widget
646  * clips to pixmap
647  */
648 {
649   unsigned  got;
650   Position  x;
651   Position  y;
652 
653   /*{{{  get position from event*/
654   switch(event->type)
655   {
656     case ButtonPress:
657     case ButtonRelease:
658       x = event->xbutton.x;
659       y = event->xbutton.y;
660       got = 1;
661       break;
662     case MotionNotify:
663       x = event->xmotion.x;
664       y = event->xmotion.y;
665       got = 1;
666       break;
667     case EnterNotify:
668     case LeaveNotify:
669       x = event->xcrossing.x;
670       y = event->xcrossing.y;
671       got = 1;
672       break;
673     default:
674       x = y = 0;
675       got = 0;
676   }
677   /*}}}*/
678   if(got)
679     /*{{{  convert to pixmap coord*/
680     {
681       unsigned  out;
682 
683       out = event->type != EnterNotify &&
684 	  (x < 0 || x >= (Position)iw->core.width ||
685 	  y < 0 || y >= (Position)iw->core.height);
686       x -= iw->icon.x;
687       y -= iw->icon.y;
688       if(x < 0)
689 	{
690 	  x = 0;
691 	  got = 2;
692 	}
693       else if(x >= iw->icon.width)
694 	{
695 	  x = iw->icon.width - 1;
696 	  got = 2;
697 	}
698       if(y < 0)
699 	{
700 	  y = 0;
701 	  got = 2;
702 	}
703       else if(y >= iw->icon.height)
704 	{
705 	  y = iw->icon.height - 1;
706 	  got = 2;
707 	}
708       if(out)
709 	got = 3;
710       *xp = x;
711       *yp = y;
712     }
713     /*}}}*/
714   return got;
715 }
716 /*}}}*/
717 /*{{{  void RemoveTimeOut(iw)*/
718 static VOIDFUNC RemoveTimeOut
719 FUNCARG((iw),
720 	IconWidget iw
721 )
722 /* remove the flash timeout, if its set
723  */
724 {
725   if(iw->icon.timeout != (XtIntervalId)0)
726     {
727       XtRemoveTimeOut(iw->icon.timeout);
728       iw->icon.timeout = (XtIntervalId)0;
729       iw->icon.solid = False;
730     }
731   return;
732 }
733 /*}}}*/
734 /*{{{  void TimeOut(data, id)*/
735 static VOIDFUNC TimeOut
736 FUNCARG((data, id),
737 	XtPointer data
738 ARGSEP  XtIntervalId *id
739 )
740 /* unflashes the widget
741  */
742 {
743   IconWidget iw;
744 
745   iw = (IconWidget)data;
746   iw->icon.solid = False;
747   iw->icon.timeout = (XtIntervalId)0;
748   FakeExpose(iw);
749   return;
750 }
751 /*}}}*/
752