1 /* Copyright (C) 1993 Nathan Sidwell */
2 /* RCS $Id: PixmapList.c,v 4.9 1995/12/14 13:53:27 nathan Exp $ */
3 /* Holds a list of pixmaps which can be selected or dragged like
4  * individual Icon widgets.
5  * I tried using a box widget with an insertPosition function
6  * but that didn't seem to work properly
7  * you can also attach a scroll bar to this widget
8  */
9 /*{{{  includes*/
10 #include "ansiknr.h"
11 #include <X11/X.h>
12 #include <X11/Xlib.h>
13 #include <X11/IntrinsicP.h>
14 #include <X11/StringDefs.h>
15 #include <X11/Xaw/SimpleP.h>
16 #include <X11/Xaw/Scrollbar.h>
17 #include <X11/Xmu/Drawing.h>
18 #include "PixmapList.h"
19 #include "Drag.h"
20 #include <math.h>
21 /*}}}*/
22 /*{{{  structs*/
23 /*{{{  typedef struct _PixmapListClass*/
24 typedef struct _PixmapListClass
25 {
26   int     ansi_compliance; /* not used */
27 } PixmapListClassPart;
28 /*}}}*/
29 /*{{{  typedef struct _PixmapListClassRec*/
30 typedef struct _PixmapListClassRec
31 {
32   CoreClassPart   core_class;
33   SimpleClassPart simple_class;
34   PixmapListClassPart   pixmap_list_class;
35 } PixmapListClassRec;
36 /*}}}*/
37 /*{{{  typedef _PixmapInfo*/
38 typedef struct _PixmapInfo
39 {
40   Pixmap    pixmap;   /* the pixmap */
41   int       width;    /* its width */
42   int       height;   /* its height */
43   Position  x;        /* its position */
44   Position  y;        /* its position */
45 } PixmapInfo;
46 /*}}}*/
47 /*{{{  typedef struct _PixmapListPart*/
48 typedef struct
49 {
50   /* resources */
51   XtCallbackList callbacks;   /* callbacks to notify */
52   Dimension drag_sensitivity; /* sensitivity of drag callback */
53   Dimension highlight_thickness;  /* thickness of highlight bar */
54   Pixel     foreground;       /* foreground pixel color */
55   int       flash_delay;      /* time to flash */
56   String    drag_name;        /* name of drag */
57   Dimension distance;         /* separation of pixmaps */
58   XtOrientation orient;       /* orientation */
59   Dimension border_width;     /* border width */
60   Pixel     border_color;     /* border color */
61 
62   /* private state */
63   PixmapInfo *pixmap_info;  /* list of pixmaps */
64   Cardinal  num_pixmaps;    /* their number */
65   Cardinal  limit;          /* malloc limit of pi */
66   GC        gc;             /* GC to draw foreground */
67   GC        border_gc;      /* GC to draw border */
68   Position  drag_x;         /* drag start x */
69   Position  drag_y;         /* drag start y */
70   int       drag_set;       /* drag selected */
71   Boolean   highlit;        /* is it highlit? */
72   int       selected;       /* selected pixmap */
73   int       was;            /* selected pixmap */
74   Position  x;              /* offset of start of list */
75   Position  y;              /* offset of start of list */
76   int       width;          /* pixmaps' widths or -1 for different */
77   int       height;         /* pixmaps' heights or -1 for different */
78   Dimension length;         /* length of list */
79   XtIntervalId timeout;     /* timout id */
80   Widget    scroll;         /* scrollbar widget */
81 } PixmapListPart;
82 /*}}}*/
83 /*{{{  typedef struct _PixmapListRec*/
84 typedef struct _PixmapListRec
85 {
86   CorePart    core;
87   SimplePart  simple;
88   PixmapListPart pixmap_list;
89 } PixmapListRec;
90 /*}}}*/
91 /*}}}*/
92 /*{{{  resources*/
93 static XtResource resources[] =
94 {
95   {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
96       XtOffsetOf(PixmapListRec, pixmap_list.foreground),
97       XtRString, (XtPointer)XtDefaultForeground},
98   {MredNhighlightThickness, XtCThickness, XtRDimension, sizeof(Dimension),
99       XtOffsetOf(PixmapListRec, pixmap_list.highlight_thickness),
100       XtRImmediate, (XtPointer)1},
101   {XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer),
102       XtOffsetOf(PixmapListRec, pixmap_list.callbacks),
103       XtRCallback, (XtPointer)NULL},
104   {MredNdistance, XtCThickness, XtRDimension, sizeof(Dimension),
105       XtOffsetOf(PixmapListRec, pixmap_list.distance),
106       XtRImmediate, (XtPointer)4},
107   {MredNdragSensitivity, XtCThickness, XtRDimension, sizeof(Dimension),
108       XtOffsetOf(PixmapListRec, pixmap_list.drag_sensitivity),
109       XtRImmediate, (XtPointer)4},
110   {MredNflashDelay, XtCInterval, XtRInt, sizeof(int),
111       XtOffsetOf(PixmapListRec, pixmap_list.flash_delay),
112       XtRImmediate, (XtPointer)100},
113   {MredNdragName, XtCLabel, XtRString, sizeof(String),
114       XtOffsetOf(PixmapListRec, pixmap_list.drag_name),
115       XtRImmediate, (XtPointer)"drag"},
116   {XtNinternalBorderWidth, XtCBorderWidth, XtRDimension, sizeof(Dimension),
117       XtOffsetOf(PixmapListRec, pixmap_list.border_width),
118       XtRImmediate, (XtPointer)1},
119   {XtNinternalBorderColor, XtCBorderColor, XtRPixel, sizeof(Pixel),
120       XtOffsetOf(PixmapListRec, pixmap_list.border_color),
121       XtRString, (XtPointer)XtDefaultForeground},
122   {XtNorientation, XtCOrientation, XtROrientation, sizeof(XtOrientation),
123       XtOffsetOf(PixmapListRec, pixmap_list.orient),
124       XtRImmediate, (XtPointer)XtorientVertical},
125 };
126 /*}}}*/
127 /*{{{  prototypes*/
128 static VOIDFUNC Drag PROTOARG((Widget, XEvent *, String *, Cardinal *));
129 static VOIDFUNC Highlight PROTOARG((Widget, XEvent *, String *, Cardinal *));
130 static VOIDFUNC Notify PROTOARG((Widget, XEvent *, String *, Cardinal *));
131 static VOIDFUNC Set PROTOARG((Widget, XEvent *, String *, Cardinal *));
132 static VOIDFUNC Unhighlight PROTOARG((Widget, XEvent *, String *, Cardinal *));
133 
134 static VOIDFUNC ClassInitialize PROTOARG((VOIDARG));
135 static VOIDFUNC Destroy PROTOARG((Widget));
136 static VOIDFUNC Initialize PROTOARG((Widget, Widget, ArgList, Cardinal *));
137 static XtGeometryResult QueryGeometry
138     PROTOARG((Widget, XtWidgetGeometry *, XtWidgetGeometry *));
139 static VOIDFUNC Redisplay PROTOARG((Widget, XEvent *, Region));
140 static VOIDFUNC Resize PROTOARG((Widget));
141 static Boolean SetValues
142     PROTOARG((Widget, Widget, Widget, ArgList, Cardinal *));
143 
144 static VOIDFUNC PaintPixmap
145     PROTOARG((PixmapListWidget, PixmapInfo *, unsigned));
146 static VOIDFUNC PaintRect PROTOARG((PixmapListWidget, Position, Position));
147 static VOIDFUNC GetGC PROTOARG((PixmapListWidget));
148 static VOIDFUNC GetPixmapSize PROTOARG((PixmapListWidget, PixmapInfo *));
149 static int GetPointerPosition
150     PROTOARG((PixmapListWidget, XEvent *, Position *, Position *));
151 static VOIDFUNC RemoveTimeOut PROTOARG((PixmapListWidget));
152 static VOIDFUNC ScrollJump PROTOARG((Widget, XtPointer, XtPointer));
153 static VOIDFUNC ScrollScroll PROTOARG((Widget, XtPointer, XtPointer));
154 static VOIDFUNC SetScrollbar PROTOARG((PixmapListWidget));
155 static VOIDFUNC TimeOut PROTOARG((XtPointer, XtIntervalId *));
156 /*}}}*/
157 /*{{{  translations*/
158 static char translations[] = "\
159 <BtnDown>:set()\n\
160 <BtnUp>:notify()\n\
161 <Motion>:highlight() drag()\n\
162 <LeaveNotify>:unhighlight()\n\
163 ";
164 /*}}}*/
165 /*{{{  actions*/
166 static XtActionsRec actions[] =
167 {
168   {"drag", Drag},
169   {"highlight", Highlight},
170   {"notify", Notify},
171   {"set", Set},
172   {"unhighlight", Unhighlight},
173 };
174 /*}}}*/
175 #define SuperClass (WidgetClass)&simpleClassRec
176 /*{{{  PixmapListClassRec pixmapListClassRec =*/
177 PixmapListClassRec pixmapListClassRec =
178 {
179   /*{{{  core class part*/
180   {
181     SuperClass,                       /* superclass */
182     "PixmapList",                     /* class_name */
183     sizeof(PixmapListRec),            /* size */
184     ClassInitialize,                  /* class_initialize */
185     NULL,                             /* class_part_initialize */
186     False,                            /* class_inited */
187     Initialize,                       /* initialize */
188     NULL,                             /* initialize_hook */
189     XtInheritRealize,                 /* realize */
190     actions,                          /* actions */
191     XtNumber(actions),                /* num_actions */
192     resources,                        /* resources */
193     XtNumber(resources),              /* num_resources */
194     NULLQUARK,                        /* xrm_class */
195     True,                             /* compress_motion */
196     XtExposeCompressMultiple,         /* compress_exposure */
197     True,                             /* compress_enterleave */
198     False,                            /* visible_interest */
199     Destroy,                          /* destroy */
200     Resize,                           /* resize */
201     Redisplay,                        /* expose */
202     SetValues,                        /* set_values */
203     NULL,                             /* set_values_hook */
204     XtInheritSetValuesAlmost,         /* set_values_almost */
205     NULL,                             /* get_values_hook */
206     NULL,                             /* accept_focus */
207     XtVersion,                        /* version */
208     NULL,                             /* callback_private */
209     translations,                     /* default_translations */
210     QueryGeometry,                    /* query_geometry */
211     XtInheritDisplayAccelerator,      /* display_accelerator */
212     NULL,                             /* extension */
213   },
214   /*}}}*/
215   /*{{{  simple class part*/
216   {
217     XtInheritChangeSensitive      /* change_sensitive */
218   },
219   /*}}}*/
220   /*{{{  pixmap list class part*/
221   {
222     0,        /* dummy */
223   },
224   /*}}}*/
225 };
226 /*}}}*/
227 WidgetClass pixmapListWidgetClass = (WidgetClass)&pixmapListClassRec;
228 /* actions */
229 /*{{{  void Drag(widget, event, params, num_params)*/
230 static VOIDFUNC Drag
231 FUNCARG((widget, event, params, num_params),
232 	Widget    widget
233 ARGSEP  XEvent    *event
234 ARGSEP  String    *params
235 ARGSEP  Cardinal  *num_params
236 )
237 /* determines if a drag sould start on the currently selected pixmap
238  * the drag widget is invoked the same as Icon does.
239  */
240 {
241   PixmapListWidget plw;
242 
243   plw = (PixmapListWidget)widget;
244   if(event->type != MotionNotify && event->type != ButtonPress)
245     /* EMPTY */;
246   else if(event->type == MotionNotify && !(event->xmotion.state &
247     (Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask)))
248     /* EMPTY */;
249   else
250     {
251       Position  x, y;
252       int       selected;
253 
254       selected = GetPointerPosition(plw, event, &x, &y);
255       if(selected != plw->pixmap_list.drag_set)
256 	{
257 	  plw->pixmap_list.drag_set = selected;
258 	  plw->pixmap_list.drag_x = x;
259 	  plw->pixmap_list.drag_y = y;
260 	}
261       else if(selected < 0)
262 	/* EMPTY */;
263       else if(!plw->pixmap_list.drag_sensitivity)
264 	/* EMPTY */;
265       else if(!plw->pixmap_list.drag_name)
266 	/* EMPTY */;
267       else if(plw->pixmap_list.drag_sensitivity *
268 	  plw->pixmap_list.drag_sensitivity <=
269 	  (x - plw->pixmap_list.drag_x) * (x - plw->pixmap_list.drag_x) +
270 	  (y - plw->pixmap_list.drag_y) * (y - plw->pixmap_list.drag_y))
271 	/*{{{  pop up drag*/
272 	{
273 	  Widget  drag;
274 	  Widget  parent;
275 
276 	  drag = NULL;
277 	  for(parent = (Widget)plw; parent;
278 	      parent = XtParent(parent))
279 	    {
280 	      drag = XtNameToWidget(parent, plw->pixmap_list.drag_name);
281 	      if(drag)
282 		break;
283 	    }
284 	  if(drag)
285 	    {
286 	      Position  rx, ry;
287 
288 	      plw->pixmap_list.highlit = False;
289 	      PaintPixmap(plw, NULL, selected);
290 	      XtTranslateCoords((Widget)plw,
291 		  plw->pixmap_list.pixmap_info[selected].x +
292 		    plw->pixmap_list.x,
293 		  plw->pixmap_list.pixmap_info[selected].y +
294 		    plw->pixmap_list.y, &rx, &ry);
295 	      DragPopup((Widget)plw, (Widget)drag,
296 		  plw->pixmap_list.pixmap_info[selected].pixmap,
297 		  plw->pixmap_list.drag_x, plw->pixmap_list.drag_y,
298 		  rx + x, ry + y, event->xmotion.time);
299 	    }
300 	}
301 	/*}}}*/
302     }
303   return;
304 }
305 /*}}}*/
306 /*{{{  void Highlight(widget, event, params, num_params)*/
307 static VOIDFUNC Highlight
308 FUNCARG((widget, event, params, num_params),
309 	Widget    widget
310 ARGSEP  XEvent    *event
311 ARGSEP  String    *params
312 ARGSEP  Cardinal  *num_params
313 )
314 /* highlight the pixmap we're on
315  */
316 {
317   PixmapListWidget plw;
318   Position  x, y;
319   int       selected;
320 
321   plw = (PixmapListWidget)widget;
322   selected = plw->pixmap_list.highlight_thickness ?
323       GetPointerPosition(plw, event, &x, &y) : -1;
324   if(selected != plw->pixmap_list.selected ||
325       (plw->pixmap_list.highlit == False && selected >= 0))
326     {
327       int       old;
328 
329       plw->pixmap_list.drag_x = x;
330       plw->pixmap_list.drag_y = y;
331       plw->pixmap_list.drag_set = selected + 1;
332       old = plw->pixmap_list.selected;
333       plw->pixmap_list.selected = selected;
334       plw->pixmap_list.highlit = selected < 0 ? False : True;
335       if(selected >= 0)
336 	{
337 	  int       was;
338 
339 	  RemoveTimeOut(plw);
340 	  was = plw->pixmap_list.was;
341 	  plw->pixmap_list.was = -1;
342 	  if(was >= 0 && was != old && was != selected)
343 	    PaintPixmap(plw, NULL, was);
344 	  PaintPixmap(plw, NULL, selected);
345 	}
346       if(old >= 0)
347 	PaintPixmap(plw, NULL, old);
348     }
349   return;
350 }
351 /*}}}*/
352 /*{{{  void Notify(widget, event, params, num_params)*/
353 static VOIDFUNC Notify
354 FUNCARG((widget, event, params, num_params),
355 	Widget    widget
356 ARGSEP  XEvent    *event
357 ARGSEP  String    *params
358 ARGSEP  Cardinal  *num_params
359 )
360 /* notify the callback of the currently selected pixmap
361  * flashes the pixmap too
362  * normally invoked on button release.
363  */
364 {
365   PixmapListWidget plw;
366 
367   plw = (PixmapListWidget)widget;
368   plw->pixmap_list.drag_set = -1;
369   if(plw->pixmap_list.selected >= 0)
370     {
371       PixmapListCallback data;
372 
373       if(plw->pixmap_list.flash_delay)
374 	{
375 	  plw->pixmap_list.was = plw->pixmap_list.selected;
376 	  PaintPixmap(plw, NULL, plw->pixmap_list.was);
377 	  plw->pixmap_list.timeout = XtAppAddTimeOut(
378 	      XtWidgetToApplicationContext((Widget)plw),
379 	      (unsigned long)plw->pixmap_list.flash_delay,
380 	      TimeOut, (XtPointer)plw);
381 	}
382       data.selection = plw->pixmap_list.selected;
383       if(event->type == ButtonPress || event->type == ButtonRelease)
384 	data.button = event->xbutton.button;
385       else
386 	data.button = -1;
387       XtCallCallbackList((Widget)plw, plw->pixmap_list.callbacks,
388 	  (XtPointer)&data);
389     }
390   return;
391 }
392 /*}}}*/
393 /*{{{  void Set(widget, event, params, num_params)*/
394 static VOIDFUNC Set
395 FUNCARG((widget, event, params, num_params),
396 	Widget    widget
397 ARGSEP  XEvent    *event
398 ARGSEP  String    *params
399 ARGSEP  Cardinal  *num_params
400 )
401 /* initialize the drag start point
402  */
403 {
404   PixmapListWidget plw;
405 
406   plw = (PixmapListWidget)widget;
407   plw->pixmap_list.drag_set = GetPointerPosition(plw, event,
408 	  &plw->pixmap_list.drag_x, &plw->pixmap_list.drag_y) + 1;
409   return;
410 }
411 /*}}}*/
412 /*{{{  void Unhighlight(widget, event, params, num_params)*/
413 static VOIDFUNC Unhighlight
414 FUNCARG((widget, event, params, num_params),
415 	Widget    widget
416 ARGSEP  XEvent    *event
417 ARGSEP  String    *params
418 ARGSEP  Cardinal  *num_params
419 )
420 /* deselect any selected widget
421  */
422 {
423   PixmapListWidget plw;
424 
425   plw = (PixmapListWidget)widget;
426   if(plw->pixmap_list.highlit != False)
427     {
428       plw->pixmap_list.highlit = False;
429       PaintPixmap(plw, NULL, plw->pixmap_list.selected);
430     }
431   return;
432 }
433 /*}}}*/
434 /* methods */
435 /*{{{  void ClassInitialize()*/
436 static VOIDFUNC ClassInitialize FUNCARGVOID
437 /* add the orientation type converter
438  */
439 {
440   XtAddConverter(XtRString, XtROrientation, XmuCvtStringToOrientation,
441       NULL, 0);
442   return;
443 }
444 /*}}}*/
445 /*{{{  void Destroy(widget)*/
446 static VOIDFUNC Destroy
447 FUNCARG((widget),
448 	Widget    widget
449 )
450 /* release our GCs and remove the flash timeout */
451 {
452   PixmapListWidget plw;
453 
454   plw = (PixmapListWidget)widget;
455   XtReleaseGC((Widget)plw, plw->pixmap_list.gc);
456   XtReleaseGC((Widget)plw, plw->pixmap_list.border_gc);
457   RemoveTimeOut(plw);
458   return;
459 }
460 /*}}}*/
461 /*{{{  void Initialize(treq, tnew, args, num_args)*/
462 static VOIDFUNC Initialize
463 FUNCARG((treq, tnew, args, num_args),
464 	Widget    treq
465 ARGSEP  Widget    tnew
466 ARGSEP  ArgList   args
467 ARGSEP  Cardinal  *num_args
468 )
469 /* initialize ourselves
470  */
471 {
472   PixmapListWidget nplw;
473 
474   nplw = (PixmapListWidget)tnew;
475   nplw->pixmap_list.selected = -1;
476   nplw->pixmap_list.was = -1;
477   nplw->pixmap_list.highlit = False;
478   nplw->pixmap_list.drag_set = -1;
479   nplw->pixmap_list.timeout = (XtIntervalId)0;
480   nplw->pixmap_list.pixmap_info = NULL;
481   nplw->pixmap_list.scroll = NULL;
482   nplw->pixmap_list.limit = 0;
483   nplw->pixmap_list.length = nplw->pixmap_list.distance;
484   nplw->pixmap_list.width = nplw->pixmap_list.height = 0;
485   nplw->pixmap_list.x = nplw->pixmap_list.y = 0;
486   nplw->pixmap_list.num_pixmaps = 0;
487   if(!nplw->core.width)
488     nplw->core.width = 16;
489   if(!nplw->core.height)
490     nplw->core.height = 16;
491   GetGC(nplw);
492   return;
493 }
494 /*}}}*/
495 /*{{{  XtGeometryResult QueryGeometry(widget, proposed, answer)*/
496 static XtGeometryResult QueryGeometry
497 FUNCARG((widget, proposed, answer),
498 	Widget    widget
499 ARGSEP  XtWidgetGeometry *proposed
500 ARGSEP  XtWidgetGeometry *answer
501 )
502 /* tell our parent how big we'd like to be.
503  * This is big enough to hold all the pixmaps,
504  * but we don't mind being bigger or smaller
505  */
506 {
507   PixmapListWidget plw;
508   unsigned  ix;
509   PixmapInfo *pi;
510   unsigned  max_width;
511   unsigned  max_height;
512 
513   plw = (PixmapListWidget)widget;
514   answer->request_mode = CWWidth | CWHeight;
515   max_width = max_height = 0;
516   for(ix = plw->pixmap_list.num_pixmaps,
517       pi = plw->pixmap_list.pixmap_info; ix--; pi++)
518     {
519       if(max_width < pi->width)
520 	max_width = pi->width;
521       if(max_height < pi->height)
522 	max_height = pi->height;
523     }
524   pi--;
525   if(plw->pixmap_list.orient == XtorientVertical)
526     {
527       answer->width = max_width + plw->pixmap_list.distance * 2 +
528 	  plw->pixmap_list.border_width * 2;
529       answer->height = plw->pixmap_list.length;
530     }
531   else
532     {
533       answer->width = plw->pixmap_list.length;
534       answer->height = max_height + plw->pixmap_list.distance * 2 +
535 	  plw->pixmap_list.border_width * 2;
536     }
537   if(!answer->width)
538     answer->width = 16;
539   if(!answer->height)
540     answer->height = 16;
541   if((proposed->request_mode & (CWWidth | CWHeight)) == (CWWidth | CWHeight) &&
542       proposed->width == answer->width && proposed->height == answer->height)
543     return XtGeometryYes;
544   else if(answer->width == plw->core.width &&
545       answer->height == plw->core.height)
546     return XtGeometryNo;
547   else
548     return XtGeometryAlmost;
549 }
550 /*}}}*/
551 /*{{{  void Redisplay(widget, event, region)*/
552 static VOIDFUNC Redisplay
553 FUNCARG((widget, event, region),
554 	Widget    widget
555 ARGSEP  XEvent    *event
556 ARGSEP  Region    region
557 )
558 /* repaint all the pixmaps.
559  * We use the expose region to only repaint those
560  * pixmaps falling partially or whole within it
561  */
562 {
563   PixmapListWidget plw;
564   unsigned  ix;
565   PixmapInfo *pi;
566 
567   if(!XtIsRealized(widget))
568     return;
569   plw = (PixmapListWidget)widget;
570   for(ix = 0, pi = plw->pixmap_list.pixmap_info;
571       ix != plw->pixmap_list.num_pixmaps; ix++, pi++)
572     if(XRectInRegion(region, pi->x + plw->pixmap_list.x -
573 	  (plw->pixmap_list.border_width + 1) / 2,
574 	pi->y + plw->pixmap_list.y -
575 	  (plw->pixmap_list.border_width + 1) / 2,
576 	pi->width + 2 * plw->pixmap_list.border_width,
577 	pi->height + 2 * plw->pixmap_list.border_width) != RectangleOut)
578       PaintPixmap(plw, pi, ix);
579   return;
580 }
581 /*}}}*/
582 /*{{{  void Resize(widget)*/
583 static VOIDFUNC Resize
584 FUNCARG((widget),
585 	Widget    widget
586 )
587 /* reset the starting point and reset the scrollbar scale */
588 {
589   PixmapListWidget plw;
590 
591   plw = (PixmapListWidget)widget;
592   if(plw->pixmap_list.orient == XtorientVertical)
593     plw->pixmap_list.x = 0;
594   else
595     plw->pixmap_list.y = 0;
596   SetScrollbar(plw);
597   return;
598 }
599 /*}}}*/
600 /*{{{  Boolean SetValues(cw, rw, nw, args, num_args)*/
601 static Boolean SetValues
602 FUNCARG((cw, rw, nw, args, num_args),
603 	Widget    cw
604 ARGSEP  Widget    rw
605 ARGSEP  Widget    nw
606 ARGSEP  ArgList   args
607 ARGSEP  Cardinal  *num_args
608 )
609 /* realloc the GCs if things change
610  * This generally involes a complete repaint
611  */
612 {
613   Boolean  redraw;
614   PixmapListWidget cplw;
615   PixmapListWidget nplw;
616 
617   redraw = False;
618   cplw = (PixmapListWidget)cw;
619   nplw = (PixmapListWidget)nw;
620   if(cplw->pixmap_list.foreground != nplw->pixmap_list.foreground ||
621       cplw->pixmap_list.highlight_thickness !=
622       nplw->pixmap_list.highlight_thickness ||
623       cplw->pixmap_list.border_color != nplw->pixmap_list.border_color ||
624       cplw->pixmap_list.border_width != nplw->pixmap_list.border_width)
625     {
626       redraw = True;
627       XtReleaseGC((Widget)cplw, cplw->pixmap_list.gc);
628       XtReleaseGC((Widget)cplw, cplw->pixmap_list.border_gc);
629       GetGC(nplw);
630     }
631   if(nplw->pixmap_list.border_width !=
632       cplw->pixmap_list.border_width ||
633       nplw->pixmap_list.distance != cplw->pixmap_list.distance ||
634       nplw->pixmap_list.orient != cplw->pixmap_list.orient)
635     {
636       PixmapInfo  *pi;
637       unsigned  count;
638       unsigned  spot;
639 
640       spot = nplw->pixmap_list.distance;
641       for(count = nplw->pixmap_list.num_pixmaps,
642 	  pi = nplw->pixmap_list.pixmap_info; count--; pi++)
643 	{
644 	  spot += nplw->pixmap_list.border_width;
645 	  if(nplw->pixmap_list.orient == XtorientVertical)
646 	    pi->x = spot;
647 	  else
648 	    pi->y = spot;
649 	  spot += nplw->pixmap_list.border_width +
650 	      nplw->pixmap_list.distance;
651 	}
652       nplw->pixmap_list.length = spot;
653       redraw = True;
654       SetScrollbar(nplw);
655     }
656   if(cplw->core.width != nplw->core.width ||
657       cplw->core.height != nplw->core.height)
658     redraw = False;
659   return redraw;
660 }
661 /*}}}*/
662 /* public routines */
663 /*{{{  void PixmapListRepaint(widget)*/
664 extern VOIDFUNC PixmapListRepaint
665 FUNCARG((widget, ix),
666 	Widget    widget
667 ARGSEP  Cardinal  ix      /* pixmap to repaint */
668 )
669 /* repaint a single pixmap, avoiding the annoying expose event flicker
670  */
671 {
672   if(!XtIsSubclass(widget, (WidgetClass)&pixmapListClassRec))
673     /* EMPTY */;
674   else if(!XtIsRealized(widget))
675     /* EMPTY */;
676   else if(ix >= ((PixmapListWidget)widget)->pixmap_list.num_pixmaps)
677     /* EMPTY */;
678   else
679     PaintPixmap((PixmapListWidget)widget, NULL, ix);
680   return;
681 }
682 /*}}}*/
683 /*{{{  void PixmapListInsert(widget, ix, pixmap)*/
684 extern VOIDFUNC PixmapListInsert
685 FUNCARG((widget, ix, pixmap),
686 	Widget    widget
687 ARGSEP  Cardinal  ix        /* insert at this position */
688 ARGSEP  Pixmap    pixmap    /* pixmap to insert */
689 )
690 /* insert a pixmap at a given position in the list
691  */
692 {
693   PixmapListWidget plw;
694   PixmapInfo  *pi;
695   Position  start;
696 
697   plw = (PixmapListWidget)widget;
698   if(!XtIsSubclass(widget, (WidgetClass)&pixmapListClassRec))
699     return;
700   if(ix > plw->pixmap_list.num_pixmaps)
701     ix = plw->pixmap_list.num_pixmaps;
702   if(plw->pixmap_list.drag_set >= (int)ix)
703     plw->pixmap_list.drag_set++;
704   if(plw->pixmap_list.selected >= (int)ix)
705     plw->pixmap_list.selected++;
706   if(plw->pixmap_list.was >= (int)ix)
707     plw->pixmap_list.was++;
708   /*{{{  insert it*/
709   {
710     unsigned  count;
711     unsigned  width, height;
712 
713     plw->pixmap_list.num_pixmaps++;
714     if(plw->pixmap_list.num_pixmaps > plw->pixmap_list.limit)
715       {
716 	plw->pixmap_list.limit += 128;
717 	plw->pixmap_list.pixmap_info =
718 	    (PixmapInfo *)XtRealloc((VOID *)plw-> pixmap_list.pixmap_info,
719 	    plw->pixmap_list.limit * sizeof(PixmapInfo));
720       }
721     for(pi = &plw->pixmap_list.pixmap_info[plw->pixmap_list.num_pixmaps - 1],
722 	count = plw->pixmap_list.num_pixmaps - ix - 1; count--; pi--)
723       memcpy(&pi[0], &pi[-1], sizeof(PixmapInfo));
724     pi->pixmap = pixmap;
725     GetPixmapSize(plw, pi);
726     if(!plw->pixmap_list.width)
727       plw->pixmap_list.width = pi->width;
728     else if(plw->pixmap_list.width != pi->width)
729       plw->pixmap_list.width = -1;
730     if(!plw->pixmap_list.height)
731       plw->pixmap_list.height = pi->height;
732     else if(plw->pixmap_list.height != pi->height)
733       plw->pixmap_list.height = -1;
734     if(ix == plw->pixmap_list.num_pixmaps - 1)
735       pi->x = pi->y = plw->pixmap_list.length +
736 	  plw->pixmap_list.border_width;
737     if(plw->pixmap_list.orient == XtorientVertical)
738       {
739 	pi->x = plw->pixmap_list.distance + plw->pixmap_list.border_width;
740 	width = 0;
741 	height = pi->height + plw->pixmap_list.distance +
742 	    plw->pixmap_list.border_width * 2;
743       }
744     else
745       {
746 	pi->y = plw->pixmap_list.distance + plw->pixmap_list.border_width;
747 	height = 0;
748 	width = pi->width + plw->pixmap_list.distance +
749 	    plw->pixmap_list.border_width * 2;
750       }
751     for(count = plw->pixmap_list.num_pixmaps - ix - 1; pi++, count--;)
752       {
753 	pi->x += width;
754 	pi->y += height;
755       }
756     plw->pixmap_list.length += width + height;
757   }
758   /*}}}*/
759   pi = &plw->pixmap_list.pixmap_info[ix];
760   start = plw->pixmap_list.orient == XtorientVertical ? pi->y : pi->x;
761   start -= plw->pixmap_list.border_width;
762   if(plw->pixmap_list.orient == XtorientVertical)
763     {
764       if(plw->core.height <= plw->pixmap_list.length)
765 	{
766 	  plw->pixmap_list.y -= plw->pixmap_list.border_width +
767 	      ((pi->height + plw->pixmap_list.distance + 1) >> 1);
768 	  start = -plw->pixmap_list.y;
769 	}
770     }
771   else
772     {
773       if(plw->core.width <= plw->pixmap_list.length)
774 	{
775 	  plw->pixmap_list.x -= plw->pixmap_list.border_width +
776 	      ((pi->width + plw->pixmap_list.distance + 1) >> 1);
777 	  start = -plw->pixmap_list.x;
778 	}
779     }
780   PaintRect(plw, start, plw->pixmap_list.length - start);
781   SetScrollbar(plw);
782   return;
783 }
784 /*}}}*/
785 /*{{{  void PixmapListRemove(widget, ix)*/
786 extern VOIDFUNC PixmapListRemove
787 FUNCARG((widget, ix),
788 	Widget    widget
789 ARGSEP  Cardinal  ix    /* pixmap to remove */
790 )
791 /* remove a pixmap from the list
792  * note that we don't free the pixmap, they belong to the widget operator
793  */
794 {
795   PixmapListWidget plw;
796   PixmapInfo  *pi;
797   Position  start;
798   unsigned  count;
799   unsigned  delta;
800 
801   plw = (PixmapListWidget)widget;
802   if(!XtIsSubclass(widget, (WidgetClass)&pixmapListClassRec))
803     return;
804   if(!plw->pixmap_list.num_pixmaps)
805     return;
806   if(ix >= plw->pixmap_list.num_pixmaps)
807     ix = plw->pixmap_list.num_pixmaps - 1;
808   if(plw->pixmap_list.drag_set == ix)
809     plw->pixmap_list.drag_set = -1;
810   if(plw->pixmap_list.selected == ix)
811     plw->pixmap_list.selected = -1;
812   if(plw->pixmap_list.selected == ix)
813     RemoveTimeOut(plw);
814   pi = &plw->pixmap_list.pixmap_info[ix];
815   plw->pixmap_list.num_pixmaps--;
816   if(plw->pixmap_list.orient == XtorientVertical)
817     {
818       delta = pi->height;
819       start = pi->y;
820     }
821   else
822     {
823       delta = pi->width;
824       start = pi->x;
825     }
826   delta += plw->pixmap_list.distance + plw->pixmap_list.border_width * 2;
827   plw->pixmap_list.length -= delta;
828   for(count = plw->pixmap_list.num_pixmaps - ix; count--; pi++)
829     {
830       memcpy(pi, pi + 1, sizeof(PixmapInfo));
831       if(plw->pixmap_list.orient == XtorientVertical)
832 	pi->y -= delta;
833       else
834 	pi->x -= delta;
835     }
836   /*{{{  set scroll point*/
837   if(plw->pixmap_list.orient == XtorientVertical)
838     {
839       if(plw->pixmap_list.length < plw->core.height)
840 	{
841 	  if(plw->pixmap_list.y)
842 	    {
843 	      plw->pixmap_list.y = 0;
844 	      start = 0;
845 	    }
846 	}
847       else
848 	{
849 	  plw->pixmap_list.y += delta / 2;
850 	  if(plw->pixmap_list.y > 0)
851 	    plw->pixmap_list.y = 0;
852 	  start = -plw->pixmap_list.y;
853 	}
854     }
855   else
856     {
857       if(plw->pixmap_list.length < plw->core.width)
858 	{
859 	  if(plw->pixmap_list.x)
860 	    {
861 	      plw->pixmap_list.x = 0;
862 	      start = 0;
863 	    }
864 	}
865       else
866 	{
867 	  plw->pixmap_list.x += delta / 2;
868 	  if(plw->pixmap_list.x > 0)
869 	    plw->pixmap_list.x = 0;
870 	  start = -plw->pixmap_list.x;
871 	}
872     }
873   /*}}}*/
874   PaintRect(plw, start - plw->pixmap_list.border_width,
875       plw->pixmap_list.length + delta - start);
876   SetScrollbar(plw);
877   /*{{{  check if width & height can be rechecked*/
878   if(!plw->pixmap_list.num_pixmaps)
879     plw->pixmap_list.width = plw->pixmap_list.height = 0;
880   else if(plw->pixmap_list.width < 0 || plw->pixmap_list.height < 0)
881     {
882       pi = plw->pixmap_list.pixmap_info;
883       plw->pixmap_list.width = pi->width;
884       plw->pixmap_list.height = pi->height;
885       for(count = plw->pixmap_list.num_pixmaps; count--; pi++)
886 	{
887 	  if(pi->width != plw->pixmap_list.width)
888 	    plw->pixmap_list.width = -1;
889 	  if(pi->height != plw->pixmap_list.height)
890 	    plw->pixmap_list.height = -1;
891 	}
892     }
893   /*}}}*/
894   return;
895 }
896 /*}}}*/
897 /*{{{  void PixmapListSetScroll(widget, scroll)*/
898 extern VOIDFUNC PixmapListSetScroll
899 FUNCARG((widget, scroll),
900 	Widget    widget    /* this widget */
901 ARGSEP  Widget    scroll    /* scrollbar widget */
902 )
903 /* inform of the scroll bar widget
904  * add callbacks to the scrollbar, so we can do the
905  * right thing
906  */
907 {
908   PixmapListWidget plw;
909 
910   plw = (PixmapListWidget)widget;
911   if(!XtIsSubclass(widget, (WidgetClass)&pixmapListClassRec))
912     return;
913   if(plw->pixmap_list.scroll)
914     return;
915   plw->pixmap_list.scroll = scroll;
916   SetScrollbar(plw);
917   XtAddCallback(scroll, XtNjumpProc, ScrollJump, (XtPointer)plw);
918   XtAddCallback(scroll, XtNscrollProc, ScrollScroll, (XtPointer)plw);
919   return;
920 }
921 /*}}}*/
922 /*{{{  int PixmapListQueryOffset(widget, x, y, flag)*/
923 extern int PixmapListQueryOffset
924 FUNCARG((widget, x, y, flag),
925 	Widget    widget
926 ARGSEP  Position  x     /* x position */
927 ARGSEP  Position  y     /* y position */
928 ARGSEP  Boolean   flag  /* center mode False:on pixmaps, True:on gaps  */
929 )
930 /* find out which pixmap is at this position
931  * The pixmaps can either be selected by centering on
932  * the pixmaps, or on the gaps.
933  * For selecting an insert position you probably want to center
934  * on the gaps. For selecting a delete position, you
935  * probably want to center on the pixmaps.
936  */
937 {
938   PixmapListWidget plw;
939   PixmapInfo *pi;
940   int       ix;
941   int       point;
942   int       count;
943   int       gap;
944 
945   plw = (PixmapListWidget)widget;
946   if(!XtIsSubclass(widget, (WidgetClass)&pixmapListClassRec))
947     return -1;
948   if(plw->pixmap_list.orient == XtorientVertical)
949     point = y - plw->pixmap_list.y;
950   else
951     point = x - plw->pixmap_list.x;
952   gap = plw->pixmap_list.border_width + ((plw->pixmap_list.distance + 1) >> 1);
953   for(ix = 0, pi = plw->pixmap_list.pixmap_info,
954       count = plw->pixmap_list.num_pixmaps; count; count--, ix++, pi++)
955     if(point < (flag != False ? (plw->pixmap_list.orient == XtorientVertical ?
956 	pi->y + (pi->height >> 1) : pi->x + (pi->width >> 1)) :
957     (plw->pixmap_list.orient == XtorientVertical ?
958 	pi->y + pi->height : pi->x + pi->width) + gap))
959       break;
960   return  ix;
961 }
962 /*}}}*/
963 /*{{{  int PixmapListQueryDrag(widget)*/
964 extern int PixmapListQueryDrag
965 FUNCARG((widget),
966 	Widget    widget
967 )
968 /* used to find out which pixmap was selected to initiate
969  * a drag
970  */
971 {
972   if(!XtIsSubclass(widget, (WidgetClass)&pixmapListClassRec))
973     return -1;
974   return ((PixmapListWidget)widget)->pixmap_list.drag_set;
975 }
976 /*}}}*/
977 /* private routines */
978 /*{{{  void PaintPixmap(plw, pi, ix)*/
979 static VOIDFUNC PaintPixmap
980 FUNCARG((plw, pi, ix),
981 	PixmapListWidget plw
982 ARGSEP  PixmapInfo *pi    /* pixmap info pointer */
983 ARGSEP  unsigned  ix      /* pixmap number */
984 )
985 /* repaint a specific pixmap
986  * We have to repaint the border too.
987  */
988 {
989   Position  x, y;
990 
991   if(!pi)
992     pi = &plw->pixmap_list.pixmap_info[ix];
993   x = pi->x + plw->pixmap_list.x;
994   y = pi->y + plw->pixmap_list.y;
995   if(x >= (int)plw->core.width || x + (int)pi->width <= 0 ||
996       y >= (int)plw->core.height || y + (int)pi->height <= 0)
997     return;
998   if(pi->pixmap != None)
999     XCopyArea(XtDisplay(plw), pi->pixmap, XtWindow(plw),
1000 	plw->pixmap_list.gc, 0, 0, pi->width, pi->height,
1001 	x, y);
1002   if(plw->pixmap_list.border_width)
1003     XDrawRectangle(XtDisplay(plw), XtWindow(plw), plw->pixmap_list.border_gc,
1004 	x - (plw->pixmap_list.border_width + 1) / 2,
1005 	y - (plw->pixmap_list.border_width + 1) / 2,
1006 	pi->width + plw->pixmap_list.border_width,
1007 	pi->height + plw->pixmap_list.border_width);
1008   if(ix == plw->pixmap_list.was)
1009     {
1010       XFillRectangle(XtDisplay(plw), XtWindow(plw), plw->pixmap_list.gc,
1011 	  x, y, pi->width, pi->height);
1012       return;
1013     }
1014   if(ix == plw->pixmap_list.selected &&
1015       plw->pixmap_list.highlit != False)
1016     XDrawRectangle(XtDisplay(plw), XtWindow(plw), plw->pixmap_list.gc,
1017 	x + (plw->pixmap_list.highlight_thickness >> 1),
1018 	y + (plw->pixmap_list.highlight_thickness >> 1),
1019 	pi->width - plw->pixmap_list.highlight_thickness,
1020 	pi->height - plw->pixmap_list.highlight_thickness);
1021   return;
1022 }
1023 /*}}}*/
1024 /*{{{  void PaintRect(plw, start, length)*/
1025 static VOIDFUNC PaintRect
1026 FUNCARG((plw, start, length),
1027 	PixmapListWidget plw
1028 ARGSEP  Position  start     /* starting offset */
1029 ARGSEP  Position  length    /* length of rectangle */
1030 )
1031 /* paint all the pixmaps which fall within the specified
1032  * distance allong the widget
1033  * clears to the background too.
1034  */
1035 {
1036   PixmapInfo *pi;
1037   unsigned  ix;
1038   Position  offset;
1039   Position  size;
1040   unsigned  count;
1041 
1042   /*{{{  set offset and size*/
1043   if(plw->pixmap_list.orient == XtorientVertical)
1044     {
1045       offset = plw->pixmap_list.y;
1046       size = plw->core.height;
1047     }
1048   else
1049     {
1050       offset = plw->pixmap_list.x;
1051       size = plw->core.width;
1052     }
1053   /*}}}*/
1054   /*{{{  clip start*/
1055   if(start + offset < 0)
1056     {
1057       length += start + offset;
1058       start = -offset;
1059     }
1060   /*}}}*/
1061   /*{{{  clip length*/
1062   if(offset + start + length > size)
1063     length = size - offset - start;
1064   /*}}}*/
1065   /*{{{  clear before first?*/
1066   if(start < 0)
1067     {
1068       if(plw->pixmap_list.orient == XtorientVertical)
1069 	XClearArea(XtDisplay((Widget)plw), XtWindow((Widget)plw),
1070 	    0, start + offset, plw->core.width, -start, False);
1071       else
1072 	XClearArea(XtDisplay((Widget)plw), XtWindow((Widget)plw),
1073 	    start + offset, 0, -start, plw->core.height, False);
1074       length += start;
1075       start = 0;
1076     }
1077   /*}}}*/
1078   /*{{{  find first pixmap*/
1079   for(ix = 0, pi = plw->pixmap_list.pixmap_info,
1080       count = plw->pixmap_list.num_pixmaps;
1081       count; ix++, pi++, count--)
1082     if(start < (plw->pixmap_list.orient == XtorientVertical ?
1083 	pi->y + pi->height : pi->x + pi->width) +
1084 	plw->pixmap_list.border_width)
1085       break;
1086   /*}}}*/
1087   for(; length > 0 && count; count--, ix++, pi++)
1088     if(plw->pixmap_list.orient == XtorientVertical)
1089       /*{{{  vertical*/
1090       {
1091 	XClearArea(XtDisplay((Widget)plw), XtWindow((Widget)plw),
1092 	    0, pi->y + plw->pixmap_list.y - plw->pixmap_list.border_width -
1093 	      plw->pixmap_list.distance, plw->core.width,
1094 	      plw->pixmap_list.distance, False);
1095 	if(plw->pixmap_list.width >= 0)
1096 	  {
1097 	    int       width;
1098 
1099 	    width = pi->x + plw->pixmap_list.x -
1100 		plw->pixmap_list.border_width;
1101 	    if(width > 0)
1102 	      XClearArea(XtDisplay((Widget)plw), XtWindow((Widget)plw),
1103 		  0, pi->y + plw->pixmap_list.y -
1104 		  plw->pixmap_list.border_width,
1105 		  width, pi->height + 2 * plw->pixmap_list.border_width,
1106 		  False);
1107 	    width = pi->x + plw->pixmap_list.x + pi->width +
1108 		plw->pixmap_list.border_width;
1109 	    if((int)plw->core.width - width > 0)
1110 	      XClearArea(XtDisplay((Widget)plw), XtWindow((Widget)plw),
1111 		  width, pi->y + plw->pixmap_list.y -
1112 		  plw->pixmap_list.border_width,
1113 		  plw->core.width - width,
1114 		  pi->height + 2 * plw->pixmap_list.border_width, False);
1115 	  }
1116 	PaintPixmap(plw, pi, ix);
1117 	length -= pi->y + pi->height + plw->pixmap_list.border_width - start;
1118 	start = pi->y + pi->height + plw->pixmap_list.border_width;
1119       }
1120       /*}}}*/
1121     else
1122       /*{{{  horizontal*/
1123       {
1124 	XClearArea(XtDisplay((Widget)plw), XtWindow((Widget)plw),
1125 	    pi->x + plw->pixmap_list.x - plw->pixmap_list.border_width -
1126 	      plw->pixmap_list.distance, 0, plw->pixmap_list.distance,
1127 	      plw->core.height, False);
1128 	if(plw->pixmap_list.height >= 0)
1129 	  {
1130 	    int       height;
1131 
1132 	    height = pi->y + plw->pixmap_list.y -
1133 		plw->pixmap_list.border_width;
1134 	    if(height > 0)
1135 	      XClearArea(XtDisplay((Widget)plw), XtWindow((Widget)plw),
1136 		  pi->x + plw->pixmap_list.x - plw->pixmap_list.border_width,
1137 		  0, pi->width + 2 * plw->pixmap_list.border_width,
1138 		  height, False);
1139 	    height = pi->y + plw->pixmap_list.y + pi->height +
1140 		plw->pixmap_list.border_width;
1141 	    if((int)plw->core.height - height > 0)
1142 	      XClearArea(XtDisplay((Widget)plw), XtWindow((Widget)plw),
1143 		  pi->x + plw->pixmap_list.x - plw->pixmap_list.border_width,
1144 		  height, pi->width + 2 * plw->pixmap_list.border_width,
1145 		  plw->core.height - height, False);
1146 	  }
1147 	PaintPixmap(plw, pi, ix);
1148 	length -= pi->x + pi->width + plw->pixmap_list.border_width - start;
1149 	start = pi->x + pi->width + plw->pixmap_list.border_width;
1150       }
1151       /*}}}*/
1152   /*{{{  clear after last?*/
1153   if(length > 0)
1154     {
1155       if(plw->pixmap_list.orient == XtorientVertical)
1156 	XClearArea(XtDisplay((Widget)plw), XtWindow((Widget)plw),
1157 	    0, start + offset, plw->core.width, length, False);
1158       else
1159 	XClearArea(XtDisplay((Widget)plw), XtWindow((Widget)plw),
1160 	    start + offset, 0, length, plw->core.height, False);
1161     }
1162   /*}}}*/
1163   return;
1164 }
1165 /*}}}*/
1166 /*{{{  void GetGC(plw)*/
1167 static VOIDFUNC GetGC
1168 FUNCARG((widget),
1169 	PixmapListWidget widget
1170 )
1171 /* get the two gcs which we need
1172  */
1173 {
1174   XGCValues     values;
1175 
1176   values.foreground = widget->pixmap_list.foreground;
1177   values.line_width = widget->pixmap_list.highlight_thickness;
1178   widget->pixmap_list.gc = XtGetGC((Widget)widget, GCForeground | GCLineWidth,
1179       &values);
1180   values.foreground = widget->pixmap_list.border_color;
1181   values.line_width = widget->pixmap_list.border_width;
1182   widget->pixmap_list.border_gc = XtGetGC((Widget)widget,
1183       GCForeground | GCLineWidth, &values);
1184   return;
1185 }
1186 /*}}}*/
1187 /*{{{  void GetPixmapSize(plw, pi)*/
1188 static VOIDFUNC GetPixmapSize
1189 FUNCARG((plw, pi),
1190 	PixmapListWidget plw
1191 ARGSEP  PixmapInfo *pi
1192 )
1193 /* find the size of a pixmap, returns 0 if None
1194  */
1195 {
1196   if(pi->pixmap != None)
1197     {
1198       Window  root;
1199       int x, y;
1200       unsigned border;
1201       unsigned depth;
1202       Status status;
1203 
1204       status = XGetGeometry(XtDisplay(plw), pi->pixmap,
1205 	  &root, &x, &y, (unsigned int *)&pi->width,
1206 	  (unsigned int *)&pi->height, &border, &depth);
1207     }
1208   else
1209     pi->width = pi->height = 0;
1210   return;
1211 }
1212 /*}}}*/
1213 /*{{{  int GetPointerPosition(plw, event, x, y)*/
1214 static int GetPointerPosition
1215 FUNCARG((plw, event, xp, yp),
1216 	PixmapListWidget plw
1217 ARGSEP  XEvent    *event
1218 ARGSEP  Position  *xp       /* x offset return */
1219 ARGSEP  Position  *yp       /* y offset return */
1220 )
1221 /* get the pointer position
1222  * returns the selected pixmap, or -1 for none
1223  * sets the pointer offset within that pixmap
1224  */
1225 {
1226   unsigned  got;
1227   Position  x;
1228   Position  y;
1229 
1230   /*{{{  get position from event*/
1231   switch(event->type)
1232   {
1233     case ButtonPress:
1234     case ButtonRelease:
1235       x = event->xbutton.x;
1236       y = event->xbutton.y;
1237       got = 0;
1238       break;
1239     case MotionNotify:
1240       x = event->xmotion.x;
1241       y = event->xmotion.y;
1242       got = 0;
1243       break;
1244     case EnterNotify:
1245     case LeaveNotify:
1246       x = event->xcrossing.x;
1247       y = event->xcrossing.y;
1248       got = 0;
1249       break;
1250     default:
1251       x = y = 0;
1252       got = -1;
1253   }
1254   /*}}}*/
1255   if(!got)
1256     {
1257       Cardinal  index;
1258       PixmapInfo *pi;
1259 
1260       x -= plw->pixmap_list.x;
1261       y -= plw->pixmap_list.y;
1262       got = -1;
1263       for(index = plw->pixmap_list.num_pixmaps,
1264 	  pi = plw->pixmap_list.pixmap_info;
1265 	  index--; pi++)
1266 	if(x >= pi->x && x < pi->x + pi->width &&
1267 	    y >= pi->y && y < pi->y + pi->height)
1268 	  {
1269 	    got = plw->pixmap_list.num_pixmaps - 1 - index;
1270 	    x -= pi->x;
1271 	    y -= pi->y;
1272 	    break;
1273 	  }
1274       *xp = x;
1275       *yp = y;
1276     }
1277   return got;
1278 }
1279 /*}}}*/
1280 /*{{{  void RemoveTimeOut(plw)*/
1281 static VOIDFUNC RemoveTimeOut
1282 FUNCARG((plw),
1283 	PixmapListWidget plw
1284 )
1285 /* remove the flash timeout, if set
1286  */
1287 {
1288   if(plw->pixmap_list.timeout != (XtIntervalId)0)
1289     {
1290       XtRemoveTimeOut(plw->pixmap_list.timeout);
1291       plw->pixmap_list.timeout = (XtIntervalId)0;
1292       plw->pixmap_list.was = -1;
1293     }
1294   return;
1295 }
1296 /*}}}*/
1297 /*{{{  void ScrollJump(widget, client, call)*/
1298 static VOIDFUNC ScrollJump
1299 FUNCARG((widget, client, call),
1300 	Widget    widget
1301 ARGSEP  XtPointer client
1302 ARGSEP  XtPointer call
1303 )
1304 /* jumpscroll callbac from the scrollbar
1305  * set the offset approriately, and redraw the widget
1306  */
1307 {
1308   PixmapListWidget plw;
1309   int       start;
1310   int       length;
1311   unsigned  reset;
1312 
1313   plw = (PixmapListWidget)client;
1314   start = (int)ceil(plw->pixmap_list.length * *(float *)call);
1315   length = plw->pixmap_list.orient == XtorientVertical ?
1316       plw->core.height : plw->core.width;
1317   reset = start + length > plw->pixmap_list.length;
1318   if(reset)
1319     start = plw->pixmap_list.length - length;
1320   if(start < 0)
1321     start = 0;
1322   length = 0;
1323   if(plw->pixmap_list.orient == XtorientVertical)
1324     {
1325       if(start != -plw->pixmap_list.y)
1326 	{
1327 	  plw->pixmap_list.y = -start;
1328 	  length = plw->core.height;
1329 	}
1330     }
1331   else
1332     {
1333       if(start != -plw->pixmap_list.x)
1334 	{
1335 	  plw->pixmap_list.x = -start;
1336 	  length = plw->core.width;
1337 	}
1338     }
1339   if(length)
1340     PaintRect(plw, start, length);
1341   if(reset)
1342     SetScrollbar(plw);
1343   return;
1344 }
1345 /*}}}*/
1346 /*{{{  void ScrollScroll(widget, client, call)*/
1347 static VOIDFUNC ScrollScroll
1348 FUNCARG((widget, client, call),
1349 	Widget    widget
1350 ARGSEP  XtPointer client
1351 ARGSEP  XtPointer call
1352 )
1353 /* scroll callback from the scrollbar.
1354  * adjust the offset, and redraw appropriately
1355  */
1356 {
1357   PixmapListWidget plw;
1358   int       start;
1359   int       length;
1360 
1361   plw = (PixmapListWidget)client;
1362   if(!(int)call)
1363     return;
1364   length = plw->pixmap_list.orient == XtorientVertical ?
1365       plw->core.height : plw->core.width;
1366   start = length / 2;
1367   if((int)call > 0)
1368     start = -start;
1369   start -= plw->pixmap_list.orient == XtorientVertical ?
1370       plw->pixmap_list.y : plw->pixmap_list.x;
1371   if(start < 0)
1372     start = 0;
1373   else if(start + length > plw->pixmap_list.length)
1374     start = plw->pixmap_list.length - length;
1375   length = 0;
1376   if(plw->pixmap_list.orient == XtorientVertical)
1377     {
1378       if(start != -plw->pixmap_list.y)
1379 	{
1380 	  plw->pixmap_list.y = -start;
1381 	  length = plw->core.height;
1382 	}
1383     }
1384   else
1385     {
1386       if(start != -plw->pixmap_list.x)
1387 	{
1388 	  plw->pixmap_list.x = -start;
1389 	  length = plw->core.width;
1390 	}
1391     }
1392   if(length)
1393     {
1394       PaintRect(plw, start, length);
1395       SetScrollbar(plw);
1396     }
1397   return;
1398 }
1399 /*}}}*/
1400 /*{{{  void SetScrollbar(plw)*/
1401 static VOIDFUNC SetScrollbar
1402 FUNCARG((plw),
1403 	PixmapListWidget plw
1404 )
1405 /* set the scrollbar scales appropriately
1406  * this needs to be done when
1407  *  we're resized
1408  *  we insert a pixmap
1409  *  we remove a pixmap
1410  *  our geometry changes
1411  */
1412 {
1413   float     shown, top;
1414   Arg       args[2];
1415 
1416   if(!plw->pixmap_list.scroll)
1417     return;
1418   shown = (float)(plw->pixmap_list.orient == XtorientVertical ?
1419       plw->core.height + 1 : plw->core.width + 1) /
1420       (float)plw->pixmap_list.length;
1421   if(shown > (float)1.0)
1422     shown = (float)1.0;
1423   top = (float)(plw->pixmap_list.orient == XtorientVertical ?
1424       -plw->pixmap_list.y : -plw->pixmap_list.x) /
1425       (float)plw->pixmap_list.length;
1426   XtSetArg(args[0], XtNtopOfThumb, sizeof(float) > sizeof(XtArgVal) ?
1427       (XtArgVal)&top : *(XtArgVal *)&top);
1428   XtSetArg(args[1], XtNshown, sizeof(float) > sizeof(XtArgVal) ?
1429       (XtArgVal)&shown : *(XtArgVal *)&shown);
1430   XtSetValues(plw->pixmap_list.scroll, args, 2);
1431   return;
1432 }
1433 /*}}}*/
1434 /*{{{  void TimeOut(data, id)*/
1435 static VOIDFUNC TimeOut
1436 FUNCARG((data, id),
1437 	XtPointer data
1438 ARGSEP  XtIntervalId *id
1439 )
1440 /* unflash the selected pixmap
1441  */
1442 {
1443   PixmapListWidget plw;
1444   int         ix;
1445 
1446   plw = (PixmapListWidget)data;
1447   plw->pixmap_list.timeout = (XtIntervalId)0;
1448   ix = plw->pixmap_list.was;
1449   plw->pixmap_list.was = -1;
1450   if(XtIsRealized((Widget)plw) && ix >= 0)
1451     PaintPixmap(plw, NULL, ix);
1452   return;
1453 }
1454 /*}}}*/
1455