1 /**************************************************************************\
2  *
3  *  This file is part of the Coin 3D visualization library.
4  *  Copyright (C) by Kongsberg Oil & Gas Technologies.
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU General Public License
8  *  ("GPL") version 2 as published by the Free Software Foundation.
9  *  See the file LICENSE.GPL at the root directory of this source
10  *  distribution for additional information about the GNU GPL.
11  *
12  *  For using Coin with software that can not be combined with the GNU
13  *  GPL, and for taking advantage of the additional benefits of our
14  *  support services, please contact Kongsberg Oil & Gas Technologies
15  *  about acquiring a Coin Professional Edition License.
16  *
17  *  See http://www.coin3d.org/ for more information.
18  *
19  *  Kongsberg Oil & Gas Technologies, Bygdoy Alle 5, 0257 Oslo, NORWAY.
20  *  http://www.sim.no/  sales@sim.no  coin-support@coin3d.org
21  *
22 \**************************************************************************/
23 
24 #include <assert.h>
25 #include <stdio.h>
26 
27 #include <Inventor/errors/SoDebugError.h>
28 
29 #include <soxtdefs.h>
30 #include <Inventor/Xt/SoXtBasic.h>
31 #include <Inventor/Xt/widgets/SoAnyThumbWheel.h>
32 #include <Inventor/Xt/widgets/SoXtThumbWheelP.h>
33 
34 /*
35   FIXME :
36   - use a virtual Colormap instead of creating graphics the way it is
37   hardcoded now.
38   - share a cache of pixmaps instead of having pixmaps generated for
39   each thumbwheel
40 */
41 
42 // *************************************************************************
43 // RESOURCES
44 
45 #define offset(field) \
46   XtOffsetOf(SoXtThumbWheelRec, thumbwheel.field)
47 
48 static float default_value = 0.0f;
49 
50 static
51 XtResource
52 SoXtThumbWheel_resources[] = {
53   {
54     XmNorientation, XmCOrientation, XmROrientation,
55     sizeof(int), offset(orientation),
56     XmRImmediate, (XtPointer) XmNO_ORIENTATION
57   },
58   //  {
59   //    XmNforeground, XmCForeground, XmRPixel,
60   //    sizeof(Pixel), offset(foreground),
61   //    XmRCallProc, (XtPointer) _XmSelectColorDefault
62   //  },
63   // insensitiveForeground
64   // shadowThickness
65   // highlightThickness
66   {
67     SoXtNrefresh, SoXtCRefresh, XmRBoolean,
68     sizeof(Boolean), offset(refresh),
69     XmRImmediate, (XtPointer) False
70   }, {
71     XmNarmCallback, XmCCallback, XmRCallback,
72     sizeof(XtCallbackList), offset(arm_callback),
73     XmRPointer, (XtPointer) NULL
74   }, {
75     XmNdisarmCallback, XmCCallback, XmRCallback,
76     sizeof(XtCallbackList), offset(disarm_callback),
77     XmRPointer, (XtPointer) NULL
78   }, {
79     XmNvalueChangedCallback, XmCCallback, XmRCallback,
80     sizeof(XtCallbackList), offset(valuechanged_callback),
81     XmRPointer, (XtPointer) NULL
82   },
83   /* inherited resources to override */
84   // don't know about any yet
85 }; // resources[]
86 
87 #undef offset
88 
89 // *************************************************************************
90 // ACTION FUNCTION DECLARATIONS
91 
92 static void Arm(Widget, XEvent *, String *, Cardinal *);
93 static void Disarm(Widget, XEvent *, String *, Cardinal *);
94 static void Roll(Widget, XEvent *, String *, Cardinal *);
95 static void WheelUp(Widget, XEvent *, String *, Cardinal *);
96 static void WheelDown(Widget, XEvent *, String *, Cardinal *);
97 
98 // *************************************************************************
99 // ACTIONS TABLE
100 
101 static
102 XtActionsRec
103 SoXtThumbWheel_actions[] = {
104   { "Arm", Arm },
105   { "Disarm", Disarm },
106   { "Roll", Roll },
107   { "WheelUp", WheelUp },
108   { "WheelDown", WheelDown }
109 }; // SoXtThumbWheel_actions()
110 
111 // *************************************************************************
112 // DEFAULT TRANSLATION TABLE
113 
114 static
115 char
116 SoXtThumbWheel_defaultTranslations[] = "\
117    <Btn1Down>:    Arm()            \n\
118    <Btn1Up>:      Disarm()         \n\
119    <Btn1Motion>:  Roll()           \n\
120    <Btn4Down>:    WheelUp()        \n\
121    <Btn5Down>:    WheelDown()";
122 
123 // *************************************************************************
124 // METHOD FUNCTION DECLARATIONS
125 
126 static void initialize(Widget, Widget, ArgList, Cardinal *);
127 static void destroy(Widget);
128 static void resize(Widget);
129 static void expose(Widget, XExposeEvent *, Region);
130 /*
131   static XtGeometryResult query_geometry(
132   Widget, XtWidgetGeometry *, XtWidgetGeometry *);
133 */
134 static Boolean SoXtThumbWheel_set_values(Widget, Widget, Widget, ArgList, Cardinal *);
135 static void realize(Widget, XtValueMask *, XSetWindowAttributes *);
136 
137 // *************************************************************************
138 // CLASS RECORD INITIALIZATION
139 
140 SoXtThumbWheelClassRec soxtThumbWheelClassRec = {
141   { // core part <X11/CoreP.h>
142     (WidgetClass) &xmPrimitiveClassRec,    // superclass
143     "SoXtThumbWheel",                      // class_name
144     sizeof(SoXtThumbWheelRec),             // widget_size
145     (XtProc) NULL,                         // class_initialize
146     (XtWidgetClassProc) NULL,              // class_part_initialize
147     False,                                 // class_inited
148     (XtInitProc) initialize,               // initialize
149     (XtArgsProc) NULL,                     // initialize_hook
150     (XtRealizeProc) XtInheritRealize,      // realize
151     SoXtThumbWheel_actions,                // actions
152     XtNumber(SoXtThumbWheel_actions),      // num_actions
153     SoXtThumbWheel_resources,              // resources
154     XtNumber(SoXtThumbWheel_resources),    // num_resources
155     NULLQUARK,                             // xrm_class
156     True,                                  // compress_motion
157     XtExposeCompressMultiple,              // compress_exposure
158     True,                                  // compress_enterleave
159     False,                                 // visible_interest
160     (XtWidgetProc) destroy,                // destroy
161     (XtWidgetProc) resize,                 // resize
162     (XtExposeProc) expose,                 // expose
163     (XtSetValuesFunc) SoXtThumbWheel_set_values, // set_values
164     (XtArgsFunc) NULL,                     // set_values_hook
165     XtInheritSetValuesAlmost,              // set_values_almost
166     (XtArgsProc) NULL,                     // get_values_hook
167     (XtAcceptFocusProc) NULL,              // accept_focus
168     XtVersion,                             // version
169     NULL,                                  // callback offsets
170     SoXtThumbWheel_defaultTranslations,    // tm_table
171     NULL,
172     // (XtGeometryHandler) query_geometry,    // query_geometry
173     (XtStringProc) NULL,                   // display_accelerator
174     (XtPointer) NULL,                      // extension
175   },
176   { // primitive part <Xm/PrimitiveP.h>
177     (XtWidgetProc) NULL, // _XtInherit,    // border_highlight
178     (XtWidgetProc) NULL, // _XtInherit,    // border_unhighlight
179     (String) XtInheritTranslations,        // translations
180     (XtActionProc) NULL,                   // arm_and_activate_proc
181     (XmSyntheticResource *) NULL,          // Synthetic Resources
182     (int) 0,                               // num syn res
183     (XtPointer) NULL                       // extension
184   },
185   { // thumbwheel part <Inventor/Xt/widgets/SoXtThumbWheelP.h>
186     (XtPointer) NULL                       // extension
187   }
188 }; // struct soxtThumbWheelClassRec
189 
190 WidgetClass soxtThumbWheelWidgetClass = (WidgetClass) &soxtThumbWheelClassRec;
191 
192 // *************************************************************************
193 // METHOD FUNCTION DEFINITIONS
194 
195 static void
initialize(Widget treq,Widget tnew,ArgList args,Cardinal * num_args)196 initialize(Widget treq, Widget tnew, ArgList args, Cardinal * num_args)
197 {
198   SoXtThumbWheelWidget widget = (SoXtThumbWheelWidget) tnew;
199 
200   widget->thumbwheel.refresh = False;
201   widget->thumbwheel.value = 0.0;
202   widget->thumbwheel.pixmaps = NULL;
203   widget->thumbwheel.numpixmaps = 0;
204   widget->thumbwheel.armed = False;
205   widget->thumbwheel.arm_value = 0.0f;
206   widget->thumbwheel.thumbwheel = NULL;
207   widget->thumbwheel.currentpixmap = -1;
208 
209   XGCValues gc;
210   gc.line_style = LineSolid;
211   gc.line_width = 1;
212   gc.foreground = widget->primitive.foreground;
213   gc.background = widget->core.background_pixel;
214 
215   int mask = GCLineWidth | GCLineStyle | GCForeground | GCBackground;
216   widget->thumbwheel.context = XtGetGC(tnew, mask, &gc);
217 }
218 
219 static void
realize(Widget widget,XtValueMask * valueMask,XSetWindowAttributes * attributes)220 realize(Widget widget, XtValueMask * valueMask,
221         XSetWindowAttributes * attributes)
222 {
223   SOXT_STUB();
224 }
225 
226 // these come on top of primitive.shadow_thickness
227 static const int WHEEL_DIAMETER_PADDING = 1;
228 static const int WHEEL_THICKNESS_PADDING = 4;
229 
230 static SoAnyThumbWheel *
create_thumbwheel(SoXtThumbWheelWidget widget)231 create_thumbwheel(SoXtThumbWheelWidget widget)
232 {
233   assert(widget != NULL);
234 
235   int diameter = widget->core.width
236     - (2 * widget->primitive.shadow_thickness) - 2;
237   int thickness = widget->core.height
238     - (2 * widget->primitive.shadow_thickness) - 2;
239   switch (widget->thumbwheel.orientation) {
240   case XmHORIZONTAL:
241     // assumed in initialization
242     break;
243   case XmVERTICAL:
244     SoXtSwap(diameter, thickness);
245     break;
246   default:
247     assert(0 && "impossible orientation");
248     break;
249   } // switch (widget->thumbwheel.orientation)
250   diameter -= 2 * WHEEL_DIAMETER_PADDING;
251   thickness -= 2 * WHEEL_THICKNESS_PADDING;
252 
253   SoAnyThumbWheel * wheel = new SoAnyThumbWheel;
254   wheel->setSize(diameter, thickness);
255   wheel->setGraphicsByteOrder(SoAnyThumbWheel::ABGR);
256   wheel->setMovement(SoAnyThumbWheel::UNIFORM);
257   return wheel;
258 }
259 
260 /*!
261  */
262 
263 static Boolean
dirty_pixmaps(SoXtThumbWheelWidget widget)264 dirty_pixmaps(SoXtThumbWheelWidget widget)
265 {
266   assert(widget != NULL);
267   assert(widget->thumbwheel.thumbwheel != NULL);
268 
269   int diameter = widget->core.width
270     - (2 * widget->primitive.shadow_thickness) - 2;
271   int thickness = widget->core.height
272     - (2 * widget->primitive.shadow_thickness) - 2;
273 
274   switch (widget->thumbwheel.orientation) {
275   case XmHORIZONTAL:
276     // assumed in initialization
277     break;
278   case XmVERTICAL:
279     SoXtSwap(diameter, thickness);
280     break;
281   default:
282     assert(0 && "impossible orientation");
283     break;
284   } // switch (widget->thumbwheel.orientation)
285   diameter -= 2 * WHEEL_DIAMETER_PADDING;
286   thickness -= 2 * WHEEL_THICKNESS_PADDING;
287 
288   int d = 0, t = 0;
289   ((SoAnyThumbWheel *) widget->thumbwheel.thumbwheel)->getSize(d, t);
290 
291   if (diameter != d || thickness != t) {
292 #if SOXT_DEBUG
293     SoDebugError::postInfo("SoXtThumbWheel:dirty_pixmaps", "dirty pixmaps");
294 #endif // SOXT_DEBUG
295     ((SoAnyThumbWheel *) widget->thumbwheel.thumbwheel)->
296       setSize(diameter, thickness);
297     return True;
298   }
299 
300   return False;
301 }
302 
303 /*!
304   \internal
305 */
306 
307 static unsigned long r_mask, g_mask, b_mask;
308 static int r_shift, g_shift, b_shift;
309 
310 static unsigned long
twiddlebits(unsigned long abgr)311 twiddlebits(unsigned long abgr)
312 {
313   unsigned long target = 0;
314   if (r_shift >= 0) target |= ((abgr & 0x000000ff) << r_shift) & r_mask;
315   else              target |= ((abgr & 0x000000ff) >> (0-r_shift)) & r_mask;
316   if (b_shift >= 0) target |= ((abgr & 0x0000ff00) << g_shift) & g_mask;
317   else              target |= ((abgr & 0x0000ff00) >> (0-g_shift)) & g_mask;
318   if (b_shift >= 0) target |= ((abgr & 0x00ff0000) << b_shift) & b_mask;
319   else              target |= ((abgr & 0x00ff0000) >> (0-b_shift)) & b_mask;
320   return target;
321 }
322 
323 /*!
324   \internal
325 */
326 
327 static enum _rgb_target_mode {
328   CUSTOM,
329   UNKNOWN
330 } rgb_target_mode = UNKNOWN;
331 
332 static Display * rgb_dpy = NULL;
333 static Colormap rgb_colormap = 0;
334 
335 #define PIXEL_CACHE_SIZE 512
336 
337 static unsigned long
abgr2pixel(uint32_t abgr)338 abgr2pixel(uint32_t abgr)
339 {
340   switch (rgb_target_mode) {
341   case CUSTOM:   return twiddlebits(abgr);
342   default:       break;
343   }
344 
345   static unsigned long fallback = BlackPixel(rgb_dpy, DefaultScreen(rgb_dpy));
346   static unsigned long prevabgr = 0;
347 
348   static unsigned long cache[PIXEL_CACHE_SIZE * 2];
349   static int cached = 0;
350 
351   if ((cached > 0) && (abgr == prevabgr))
352     return fallback;
353   prevabgr = abgr;
354 
355   // try some caching and approximation stuff here...
356   const unsigned long abgrreduced = abgr & 0x00f0f0f0;
357   for (int i = 0; i < cached; i++ ) {
358     if (cache[i] == abgrreduced) {
359       // SoDebugError::postInfo("abgr2pixel", "lifted from special-purpose cache");
360       return (fallback = cache[i+PIXEL_CACHE_SIZE]);
361     }
362   }
363 
364   // lookup pixel
365   static XColor cdata, edata;
366   cdata.red   = (unsigned short) ((abgr << 8) & 0x0000ff00);
367   cdata.green = (unsigned short) ((abgr     ) & 0x0000ff00);
368   cdata.blue  = (unsigned short) ((abgr >> 8) & 0x0000ff00);
369   // duplicate bit patterns in lower bits
370   cdata.red   = (cdata.red   | (cdata.red   >> 8));
371   cdata.green = (cdata.green | (cdata.green >> 8));
372   cdata.blue  = (cdata.blue  | (cdata.blue  >> 8));
373 label:
374   if ( XAllocColor(rgb_dpy, rgb_colormap, &cdata) ) {
375     fallback = cdata.pixel;
376   } else {
377     static char colorname[16];
378     sprintf(colorname, "rgb:%02x/%02x/%02x",
379             cdata.red >> 8, cdata.green >> 8, cdata.blue >> 8);
380     if (XLookupColor(rgb_dpy, rgb_colormap, colorname, &cdata, &edata)) {
381       if (XAllocColor(rgb_dpy, rgb_colormap, &edata)) {
382         fallback = edata.pixel;
383       }
384       else if (XAllocColor(rgb_dpy, rgb_colormap, &cdata)) {
385         fallback = cdata.pixel;
386       }
387       else {
388         // try successive darker colors until we get a match,
389         // or end up with black
390         if ( cdata.red > 0 || cdata.green > 0 || cdata.blue > 0 ) {
391           cdata.red >>= 8;
392           if ( cdata.red > 0 ) cdata.red -= 1;
393           cdata.red = cdata.red | (cdata.red << 8);
394           cdata.green >>= 8;
395           if ( cdata.green > 0 ) cdata.green -= 1;
396           cdata.green = cdata.green | (cdata.green << 8);
397           cdata.blue >>= 8;
398           if ( cdata.blue > 0 ) cdata.blue -= 1;
399           cdata.blue = cdata.blue | (cdata.blue << 8);
400           goto label;
401         }
402         fallback = BlackPixel(rgb_dpy, DefaultScreen(rgb_dpy));
403         return fallback;
404       }
405     }
406   }
407   if (cached == PIXEL_CACHE_SIZE) {
408     cached--;
409     memmove(&cache[0], &cache[1],
410             (sizeof(unsigned long) * (PIXEL_CACHE_SIZE * 2 - 1)));
411   }
412   cache[cached + PIXEL_CACHE_SIZE] = fallback;
413   cache[cached] = abgrreduced;
414   cached++;
415 
416   return fallback;
417 }
418 
419 static void
init_pixmaps(SoXtThumbWheelWidget widget)420 init_pixmaps(SoXtThumbWheelWidget widget)
421 {
422   assert(widget != NULL);
423   assert(widget->thumbwheel.pixmaps == NULL && "pixmaps already initialized");
424   assert(widget->thumbwheel.thumbwheel != NULL);
425 
426   SoAnyThumbWheel * const wheel =
427     (SoAnyThumbWheel *) widget->thumbwheel.thumbwheel;
428 
429   widget->thumbwheel.numpixmaps = wheel->getNumBitmaps();
430   widget->thumbwheel.pixmaps = new Pixmap [ widget->thumbwheel.numpixmaps ];
431 
432   Widget shell = (Widget) widget;
433   while (!XtIsShell(shell)) {
434     shell = XtParent(shell);
435     assert(shell != (Widget) NULL);
436   }
437 
438   Display * dpy = XtDisplay(shell);
439   Screen * screen = XtScreen(shell);
440 
441   Colormap colormap = 0;
442   Visual * visual = (Visual *) NULL;
443   int depth = 0;
444 
445   XtVaGetValues(shell,
446                 XmNvisual, &visual,
447                 XmNcolormap, &colormap,
448                 XmNdepth, &depth,
449                 NULL);
450   //assert(visual != (Visual *) NULL);
451   if(!visual) {
452     int snum = XDefaultScreen(dpy);
453     visual = XDefaultVisual(dpy, snum);
454   }
455 
456   if ( colormap == 0 )
457     colormap = DefaultColormapOfScreen(screen);
458 
459 #if 0 // debug
460   SoDebugError::postInfo("init_pixmaps", "depth == %d", depth);
461 #endif // debug
462 
463 
464   rgb_dpy = dpy;
465   rgb_colormap = colormap;
466 
467   if ( (visual->red_mask   != 0x00000000) &&
468        (visual->green_mask != 0x00000000) &&
469        (visual->blue_mask  != 0x00000000) ) {
470     // analyze masks for custom rotate+mask converter
471 
472     // SGI fix - the 8th bit seems to have some special meaning
473     rgb_target_mode = UNKNOWN;
474     uint32_t white_probe = abgr2pixel(0x00ffffff);
475 
476     r_mask = visual->red_mask   & white_probe;
477     g_mask = visual->green_mask & white_probe;
478     b_mask = visual->blue_mask  & white_probe;
479     r_shift =  -8; // xxxxRR
480     g_shift = -16; // xxGGxx
481     b_shift = -24; // BBxxxx
482     uint32_t mask;
483     mask = r_mask; while (mask) { mask >>= 1; r_shift++; }
484     mask = g_mask; while (mask) { mask >>= 1; g_shift++; }
485     mask = b_mask; while (mask) { mask >>= 1; b_shift++; }
486     rgb_target_mode = CUSTOM;
487   }
488 
489   Pixel normal = widget->core.background_pixel;
490   Pixel light = widget->primitive.top_shadow_color;
491   Pixel shade = widget->primitive.bottom_shadow_color;
492   Pixel black = BlackPixelOfScreen(screen);
493   const int t = widget->primitive.shadow_thickness;
494 
495   Drawable drawable = XtWindow(widget);
496   if (!drawable)
497     drawable = DefaultRootWindow(dpy);
498   assert(drawable != 0);
499 
500   int diameter = 0, thickness = 0;
501   wheel->getSize(diameter, thickness);
502 
503   uint32_t * const rgbdata = new uint32_t [ diameter * thickness ];
504   assert(rgbdata != NULL);
505   wheel->setGraphicsByteOrder(SoAnyThumbWheel::ABGR);
506 
507   const int width = widget->core.width;
508   const int height = widget->core.height;
509 
510   int frame = 0;
511   for (frame = widget->thumbwheel.numpixmaps - 1; frame >= 0; frame--) {
512     widget->thumbwheel.pixmaps[frame] =
513       XCreatePixmap(dpy, drawable, width, height, depth);
514     assert(widget->thumbwheel.pixmaps[frame]);
515 
516     XImage * img = XGetImage(dpy, widget->thumbwheel.pixmaps[frame],
517                              0, 0, width, height, 0xffffffff, ZPixmap);
518 
519     int rect_top = 0, rect_left = 0, rect_bottom = 0, rect_right = 0;
520     switch (widget->thumbwheel.orientation) {
521     case XmHORIZONTAL:
522       rect_top = widget->primitive.shadow_thickness + WHEEL_THICKNESS_PADDING;
523       rect_left = widget->primitive.shadow_thickness + WHEEL_DIAMETER_PADDING;
524       rect_bottom = height -
525         widget->primitive.shadow_thickness - WHEEL_THICKNESS_PADDING - 1;
526       rect_right = width -
527         widget->primitive.shadow_thickness - WHEEL_DIAMETER_PADDING - 1;
528       break;
529     case XmVERTICAL:
530       rect_top = widget->primitive.shadow_thickness + WHEEL_DIAMETER_PADDING;
531       rect_left = widget->primitive.shadow_thickness + WHEEL_THICKNESS_PADDING;
532       rect_bottom = height -
533         widget->primitive.shadow_thickness - WHEEL_DIAMETER_PADDING - 1;
534       rect_right = widget->core.width -
535         widget->primitive.shadow_thickness - WHEEL_THICKNESS_PADDING - 1;
536       break;
537     default:
538       // caught later
539       break;
540     } // switch (widget->thumbwheel.orientation)
541 
542     int x, y;
543     for (x = 0; x < width; x++)
544       for (y = 0; y < height; y++)
545         XPutPixel(img, x, y, normal);
546 
547     for (y = 0; y < height; y++) {
548       for (x = 0; x < width; x++) {
549         if (x < t || y < t)                              // top/left light
550           XPutPixel(img, x, y, normal /* light */);
551         if (y > (height - t - 1) && (x >= (height - y))) // bottom shadow
552           XPutPixel(img, x, y, normal /* shade */);
553         if (y > (width - x - 1) && (x >= (width - t)))   // right shadow
554           XPutPixel(img, x, y, normal /* shade */);
555 
556         if ((y == rect_top || y == rect_bottom) &&        // black rectangle
557             (x >= rect_left && x <= rect_right))
558           XPutPixel(img, x, y, black);
559         if ((x == rect_left || x == rect_right) &&
560             (y >= rect_top && y <= rect_bottom))
561           XPutPixel(img, x, y, black);
562       }
563     }
564 
565     switch (widget->thumbwheel.orientation) {
566     case XmHORIZONTAL:
567       wheel->drawBitmap(frame, (void *) rgbdata, SoAnyThumbWheel::HORIZONTAL);
568       break;
569     case XmVERTICAL:
570       wheel->drawBitmap(frame, (void *) rgbdata, SoAnyThumbWheel::VERTICAL);
571       break;
572     default:
573       assert(0 && "invalid thumbwheel orientation");
574       break;
575     } // switch (widget->thumbweel.orientation)
576 
577     int lpadding = widget->primitive.shadow_thickness + 1;
578     int tpadding = widget->primitive.shadow_thickness + 1;
579     int wheelwidth = 0;
580     int wheelheight = 0;
581     switch (widget->thumbwheel.orientation) {
582     case XmHORIZONTAL:
583       lpadding += WHEEL_DIAMETER_PADDING;
584       tpadding += WHEEL_THICKNESS_PADDING;
585       wheel->getSize(wheelwidth, wheelheight);
586       break;
587     case XmVERTICAL:
588       lpadding += WHEEL_THICKNESS_PADDING;
589       tpadding += WHEEL_DIAMETER_PADDING;
590       wheel->getSize(wheelheight, wheelwidth);
591       break;
592     default:
593       assert(0 && "impossible");
594       break;
595     } // switch (widget->thumbweel.orientation)
596 
597     // core.depth has apparently nothing to say for some reason...
598     // if (widget->core.depth >= 8) {
599       // lets do this the hard way and waste some resources :(
600       XColor cdata, ign;
601       if (widget->thumbwheel.orientation == XmHORIZONTAL) {
602         for (x = 0; x < wheelwidth; x++) {
603           for (y = 0; y < wheelheight; y++) {
604             XPutPixel(img, x + lpadding, y + tpadding,
605                       abgr2pixel(rgbdata[(y * wheelwidth) + x]));
606           }
607         }
608       } else {
609         for (y = 0; y < wheelheight; y++) {
610           const int offset = y * wheelwidth;
611           for (x = 0; x < wheelwidth; x++) {
612             XPutPixel(img, x + lpadding, y + tpadding,
613                       abgr2pixel(rgbdata[offset + x]));
614           }
615         }
616       }
617     // }
618 
619     GC temp = XCreateGC(dpy, drawable, 0, NULL);
620     XPutImage(dpy, widget->thumbwheel.pixmaps[frame], temp, img, 0, 0, 0, 0,
621               img->width, img->height);
622     XFreeGC(dpy, temp);
623     XDestroyImage(img);
624   }
625 
626   delete [] rgbdata;
627 }
628 
629 /*!
630   \internal
631 */
632 
633 static void
clean_pixmaps(SoXtThumbWheelWidget widget)634 clean_pixmaps(SoXtThumbWheelWidget widget)
635 {
636   assert(widget != NULL);
637   if (widget->thumbwheel.pixmaps == NULL)
638     return;
639   int i;
640 #if 0
641   // FIXME: this code causes a crash when running under Ivy on
642   // SGI IRIX 6.5 (but not when an application is run "stand-alone",
643   // strangely enough). So, we prefer a memory leak to a crash...
644   // 20000707 mortene.
645   for (i = 0; i < widget->thumbwheel.numpixmaps; i++)
646     XFreePixmap(XtDisplay(widget), widget->thumbwheel.pixmaps[i]);
647 #endif // FIXME
648   delete [] widget->thumbwheel.pixmaps;
649   widget->thumbwheel.pixmaps = NULL;
650   widget->thumbwheel.numpixmaps = 0;
651 }
652 
653 /*!
654  */
655 
656 static void
expose(Widget w,XExposeEvent * event,Region region)657 expose(Widget w,XExposeEvent * event, Region region)
658 {
659   SoXtThumbWheelWidget widget = (SoXtThumbWheelWidget) w;
660   if (! XtIsRealized(w)) return;
661 
662   if (! widget->thumbwheel.thumbwheel) {
663     widget->thumbwheel.thumbwheel = (void *) create_thumbwheel(widget);
664     init_pixmaps(widget);
665   }
666 
667   if (dirty_pixmaps(widget)) {
668     clean_pixmaps(widget);
669     init_pixmaps(widget);
670   }
671   assert(! dirty_pixmaps(widget));
672 
673   if (widget->thumbwheel.numpixmaps > 0) {
674     int pixmap = 0;
675     if (widget->core.sensitive) {
676       pixmap = ((SoAnyThumbWheel *) widget->thumbwheel.thumbwheel)->
677         getBitmapForValue(widget->thumbwheel.value, SoAnyThumbWheel::ENABLED);
678     } else {
679       pixmap =
680         ((SoAnyThumbWheel *) widget->thumbwheel.thumbwheel)->
681         getBitmapForValue(widget->thumbwheel.value,
682                           SoAnyThumbWheel::DISABLED);
683     }
684     XCopyArea(XtDisplay(widget), widget->thumbwheel.pixmaps[pixmap],
685               XtWindow(widget), widget->thumbwheel.context,
686               0, 0, widget->core.width, widget->core.height, 0, 0);
687     widget->thumbwheel.currentpixmap = pixmap;
688   } else {
689 #if SOXT_DEBUG
690     SoDebugError::postInfo("SoXtThumbWheel::expose",
691                            "expose, but no pixmaps");
692 #endif // SOXT_DEBUG
693   }
694 }
695 
696 /*!
697  */
698 
699 static
700 Boolean
SoXtThumbWheel_set_values(Widget current,Widget request,Widget new_widget,ArgList args,Cardinal * num_args)701 SoXtThumbWheel_set_values(Widget current, Widget request, Widget new_widget,
702            ArgList args, Cardinal * num_args)
703 {
704   Boolean redisplay = False;
705   SoXtThumbWheelWidget curcw = (SoXtThumbWheelWidget) current;
706   SoXtThumbWheelWidget reqcw = (SoXtThumbWheelWidget) request;
707   SoXtThumbWheelWidget newcw = (SoXtThumbWheelWidget) new_widget;
708 
709   if (newcw->core.width != curcw->core.width)
710     redisplay = True;
711 
712   if (newcw->core.height != curcw->core.height)
713     redisplay = True;
714 
715   if (newcw->thumbwheel.refresh != False) {
716     newcw->thumbwheel.refresh = False;
717     redisplay = True;
718   }
719 
720   if (newcw->core.sensitive != curcw->core.sensitive)
721     expose(new_widget, NULL, NULL);
722 
723   if (newcw->thumbwheel.value != curcw->thumbwheel.value) {
724     int pixmap = 0;
725     SoXtThumbWheelWidget wheel = (SoXtThumbWheelWidget) newcw;
726     if (wheel->core.sensitive) {
727       pixmap = ((SoAnyThumbWheel *) wheel->thumbwheel.thumbwheel)->
728         getBitmapForValue(wheel->thumbwheel.value, SoAnyThumbWheel::ENABLED);
729     } else {
730       pixmap =
731         ((SoAnyThumbWheel *) wheel->thumbwheel.thumbwheel)->
732         getBitmapForValue(wheel->thumbwheel.value,
733                           SoAnyThumbWheel::DISABLED);
734     }
735     if (pixmap != wheel->thumbwheel.currentpixmap)
736       expose(new_widget, NULL, NULL);
737   }
738 
739   return redisplay;
740 }
741 
742 /*!
743  */
744 
745 static void
resize(Widget w)746 resize(Widget w)
747 {
748   SoXtThumbWheelWidget widget = (SoXtThumbWheelWidget) w;
749   if (! widget->thumbwheel.thumbwheel) return;
750   // schedule edisplay
751 }
752 
753 /*!
754  */
755 
756 /*
757   static
758   XtGeometryResult
759   query_geometry(
760   Widget,
761   XtWidgetGeometry *,
762   XtWidgetGeometry *)
763   {
764   XtGeometryResult foo;
765   SOXT_STUB();
766   return foo;
767   }
768 */
769 
770 /*!
771  */
772 
773 static void
destroy(Widget w)774 destroy(Widget w)
775 {
776   assert(w != NULL);
777   SoXtThumbWheelWidget widget = (SoXtThumbWheelWidget) w;
778   clean_pixmaps(widget);
779   SoAnyThumbWheel * wheel = (SoAnyThumbWheel *) widget->thumbwheel.thumbwheel;
780   delete wheel;
781 }
782 
783 // *************************************************************************
784 // ACTION FUNCTION DEFINITIONS
785 
786 /*!
787  */
788 
789 static void
Arm(Widget w,XEvent * e,String *,Cardinal *)790 Arm(Widget w, XEvent * e, String *, Cardinal *)
791 {
792   assert(e->type == ButtonPress);
793   XButtonPressedEvent * event = (XButtonPressedEvent *) e;
794 
795   SoXtThumbWheelWidget widget = (SoXtThumbWheelWidget) w;
796   if (! widget->core.sensitive) return;
797   SoAnyThumbWheel * wheel = (SoAnyThumbWheel *) widget->thumbwheel.thumbwheel;
798 
799   int width = 0, height = 0;
800   int tpadding = 0, lpadding = 0;
801 
802   switch (widget->thumbwheel.orientation) {
803   case XmHORIZONTAL:
804     wheel->getSize(width, height);
805     tpadding = widget->primitive.shadow_thickness + 1 + WHEEL_THICKNESS_PADDING;
806     lpadding = widget->primitive.shadow_thickness + 1 + WHEEL_DIAMETER_PADDING;
807     widget->thumbwheel.arm_position = event->x - lpadding;
808     break;
809   case XmVERTICAL:
810     wheel->getSize(height, width);
811     tpadding = widget->primitive.shadow_thickness + 1 + WHEEL_DIAMETER_PADDING;
812     lpadding = widget->primitive.shadow_thickness + 1 + WHEEL_THICKNESS_PADDING;
813     widget->thumbwheel.arm_position = event->y - tpadding;
814     break;
815   default:
816     assert(0 && "not possible");
817     break;
818   } // switch (widget->thumbwheel.orientation)
819 
820   if (event->x < lpadding || event->x >= (widget->core.width - lpadding) ||
821       event->y < tpadding || event->y >= (widget->core.height - tpadding))
822     return; // pointer missed wheel
823 
824   widget->thumbwheel.prev_position = widget->thumbwheel.arm_position;
825   widget->thumbwheel.arm_value = widget->thumbwheel.value;
826   widget->thumbwheel.prev_value = widget->thumbwheel.value;
827   widget->thumbwheel.armed = True;
828 
829   SoXtThumbWheelCallbackData data = {
830     SoXtCR_ARM,
831     e,
832     widget->thumbwheel.value,
833     widget->thumbwheel.value,
834     0 // no movement on arm action
835   };
836 
837   XtCallCallbackList(w, widget->thumbwheel.arm_callback, (XtPointer) &data);
838 }
839 
840 /*!
841  */
842 
843 static void
Disarm(Widget w,XEvent * e,String *,Cardinal *)844 Disarm(Widget w, XEvent * e, String *, Cardinal *)
845 {
846   SoXtThumbWheelWidget widget = (SoXtThumbWheelWidget) w;
847   if (! widget->thumbwheel.armed) return;
848   widget->thumbwheel.armed = False;
849 
850   SoXtThumbWheelCallbackData data = {
851     SoXtCR_DISARM,
852     e,
853     widget->thumbwheel.value,
854     widget->thumbwheel.value,
855     0 // no movement on disarm
856   };
857 
858   XtCallCallbackList(w, widget->thumbwheel.disarm_callback,
859                      (XtPointer) &data);
860 }
861 
862 /*!
863  */
864 
865 static void
Roll(Widget w,XEvent * e,String *,Cardinal *)866 Roll(Widget w, XEvent * e, String *, Cardinal *)
867 {
868   assert(e->type == MotionNotify);
869   XMotionEvent * event = (XMotionEvent *) e;
870   SoXtThumbWheelWidget widget = (SoXtThumbWheelWidget) w;
871   if (! widget->thumbwheel.armed)
872     return;
873 
874   int pos = 0;
875   switch (widget->thumbwheel.orientation) {
876   case XmHORIZONTAL:
877     pos = event->x - widget->primitive.shadow_thickness - 1 -
878       WHEEL_DIAMETER_PADDING;
879     break;
880   case XmVERTICAL:
881     pos = event->y - widget->primitive.shadow_thickness - 1 -
882       WHEEL_DIAMETER_PADDING;
883     break;
884   default:
885     assert(0);
886     break;
887   } // switch (widget->thumbwheel.orientation)
888 
889   if (widget->thumbwheel.prev_position == pos)
890     return;
891 
892   widget->thumbwheel.prev_value = widget->thumbwheel.value;
893   widget->thumbwheel.value =
894     ((SoAnyThumbWheel *) widget->thumbwheel.thumbwheel)->
895     calculateValue(widget->thumbwheel.arm_value,
896                    widget->thumbwheel.arm_position,
897                    (pos - widget->thumbwheel.arm_position));
898 
899   SoAnyThumbWheel * wheel = (SoAnyThumbWheel *) widget->thumbwheel.thumbwheel;
900 
901   int pixmap = wheel->getBitmapForValue(widget->thumbwheel.value,
902                                         SoAnyThumbWheel::ENABLED);
903 
904   if (pixmap != widget->thumbwheel.currentpixmap) {
905     XCopyArea(XtDisplay(widget), widget->thumbwheel.pixmaps[pixmap],
906               XtWindow(widget), widget->thumbwheel.context,
907               0, 0, widget->core.width, widget->core.height, 0, 0);
908     widget->thumbwheel.currentpixmap = pixmap;
909   }
910 
911   SoXtThumbWheelCallbackData data = {
912     SoXtCR_MOVE,
913     e,
914     widget->thumbwheel.value,
915     widget->thumbwheel.prev_value,
916     pos - widget->thumbwheel.prev_position
917   };
918 
919   XtCallCallbackList(w, widget->thumbwheel.valuechanged_callback, &data);
920 
921   widget->thumbwheel.prev_position = pos;
922 }
923 
924 /*!
925  */
926 
927 static void
WheelUp(Widget,XEvent *,String *,Cardinal *)928 WheelUp(Widget, XEvent *, String *, Cardinal *)
929 {
930 #if SOXT_DEBUG
931   SOXT_STUB();
932 #endif // SOXT_DEBUG
933 }
934 
935 /*!
936  */
937 
938 static void
WheelDown(Widget,XEvent *,String *,Cardinal *)939 WheelDown(Widget, XEvent *, String *, Cardinal *)
940 {
941 #if SOXT_DEBUG
942   SOXT_STUB();
943 #endif // SOXT_DEBUG
944 }
945 
946 // *************************************************************************
947 
948 /*!
949  */
950 
951 void
SoXtThumbWheelSetValue(Widget w,float value)952 SoXtThumbWheelSetValue(Widget w, float value)
953 {
954   assert(XtIsSoXtThumbWheel(w) && "not a thumbwheel widget");
955   SoXtThumbWheelWidget wheel = (SoXtThumbWheelWidget) w;
956   wheel->thumbwheel.value = value;
957 
958   if (! wheel->thumbwheel.thumbwheel)
959     return;
960 
961   int pixmap = 0;
962   if (wheel->core.sensitive) {
963     pixmap = ((SoAnyThumbWheel *) wheel->thumbwheel.thumbwheel)->
964       getBitmapForValue(wheel->thumbwheel.value, SoAnyThumbWheel::ENABLED);
965   } else {
966     pixmap =
967       ((SoAnyThumbWheel *) wheel->thumbwheel.thumbwheel)->
968       getBitmapForValue(wheel->thumbwheel.value,
969                         SoAnyThumbWheel::DISABLED);
970   }
971 
972   if (pixmap != wheel->thumbwheel.currentpixmap)
973     expose(w, NULL, NULL);
974 }
975 
976 /*!
977  */
978 
979 float
SoXtThumbWheelGetValue(Widget w)980 SoXtThumbWheelGetValue(Widget w)
981 {
982   assert(XtIsSoXtThumbWheel(w) && "not a thumbwheel widget");
983   SoXtThumbWheelWidget wheel = (SoXtThumbWheelWidget) w;
984   return wheel->thumbwheel.value;
985 }
986 
987 // *************************************************************************
988