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