1 /**************************************************************************\
2  * Copyright (c) Kongsberg Oil & Gas Technologies AS
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32 
33 // FIXME: should probably collect thumbwheel doc to a common
34 // class. 20031008 mortene.
35 
36 /*!
37   \class SoQtThumbWheel SoQtThumbWheel.h Inventor/Qt/widgets/SoQtThumbWheel.h
38   \brief The SoQtThumbWheel class is a UI component for fancy looking thumbwheel controls.
39 
40   \ingroup components
41 */
42 
43 // *************************************************************************
44 
45 #include <math.h>
46 #include <assert.h>
47 #include <stdio.h>
48 
49 #include <qpainter.h>
50 #include <qdrawutil.h>
51 #include <qimage.h>
52 #include <qpixmap.h>
53 #include <qmetaobject.h>
54 #include <qevent.h>
55 
56 #include <Inventor/SbBasic.h>
57 #include <Inventor/errors/SoDebugError.h>
58 
59 #include <Inventor/Qt/widgets/SoQtThumbWheel.h>
60 #include <Inventor/Qt/widgets/moc_SoQtThumbWheel.icc>
61 #include <Inventor/Qt/widgets/SoAnyThumbWheel.h>
62 
63 #include <soqtdefs.h>
64 
65 // *************************************************************************
66 
67 static const int SHADEBORDERWIDTH = 0;
68 
SoQtThumbWheel(QWidget * parent,const char * name)69 SoQtThumbWheel::SoQtThumbWheel(QWidget * parent,
70                                const char * name)
71   : QWidget(parent)
72 {
73 #if QT_VERSION >= 0x040000
74   this->setObjectName(name);
75 #else
76   this->setName(name);
77 #endif
78   this->constructor(SoQtThumbWheel::Vertical);
79 }
80 
SoQtThumbWheel(Orientation orientation,QWidget * parent,const char * name)81 SoQtThumbWheel::SoQtThumbWheel(Orientation orientation,
82                                QWidget * parent,
83                                const char * name)
84   : QWidget(parent)
85 {
86 #if QT_VERSION >= 0x040000
87   this->setObjectName(name);
88 #else
89   this->setName(name);
90 #endif
91   this->constructor(orientation);
92 }
93 
94 void
constructor(Orientation orientation)95 SoQtThumbWheel::constructor(Orientation orientation)
96 {
97   this->orient = orientation;
98   this->state = SoQtThumbWheel::Idle;
99   this->wheelValue = this->tempWheelValue = 0.0f;
100   this->wheel = new SoAnyThumbWheel;
101   this->wheel->setMovement(SoAnyThumbWheel::UNIFORM);
102   this->wheel->setGraphicsByteOrder(SoAnyThumbWheel::ARGB);
103   this->pixmaps = NULL;
104   this->numPixmaps = 0;
105   this->currentPixmap = -1;
106 }
107 
~SoQtThumbWheel()108 SoQtThumbWheel::~SoQtThumbWheel()
109 {
110   delete this->wheel;
111   if (this->pixmaps) {
112     for (int i = 0; i < this->numPixmaps; i++)
113       delete this->pixmaps[i];
114     delete [] this->pixmaps;
115   }
116 }
117 
118 void
setOrientation(Orientation orientation)119 SoQtThumbWheel::setOrientation(Orientation orientation)
120 {
121   this->orient = orientation;
122   this->repaint();
123 }
124 
125 void
paintEvent(QPaintEvent * event)126 SoQtThumbWheel::paintEvent(QPaintEvent * event)
127 {
128   QPainter p(this);
129   QRect paintRect = event->rect();
130   p.setClipRect(paintRect);
131 
132   int w, dval;
133   if (this->orient == SoQtThumbWheel::Vertical) {
134     w = this->width() - 12;
135     dval = this->height() - 6;
136   } else {
137     w = this->height() - 12;
138     dval = this->width() - 6;
139   }
140 
141   // Handle resizing to too small dimensions gracefully.
142   if ((dval <= 0) || (w <= 0)) return;
143 
144   this->initWheel(dval, w);
145 
146   int pixmap = this->wheel->getBitmapForValue(this->tempWheelValue,
147                                               (this->state == SoQtThumbWheel::Disabled) ?
148                                               SoAnyThumbWheel::DISABLED : SoAnyThumbWheel::ENABLED);
149 
150   QRect widgetrect(0, 0, this->width(), this->height());
151   QRect wheelrect(widgetrect);
152 
153   if (this->orient == Vertical) {
154     wheelrect.setTop(   wheelrect.top() + 2);
155     wheelrect.setBottom(wheelrect.bottom() - 2);
156     wheelrect.setLeft(  wheelrect.left() + 5);
157     wheelrect.setRight( wheelrect.right() - 5);
158   } else {
159     wheelrect.setTop(   wheelrect.top() + 5);
160     wheelrect.setBottom(wheelrect.bottom() - 5);
161     wheelrect.setLeft(  wheelrect.left() + 2);
162     wheelrect.setRight( wheelrect.right() - 2);
163   }
164 
165   qDrawPlainRect(&p, wheelrect.left(), wheelrect.top(), wheelrect.width(),
166                  wheelrect.height(), QColor(0, 0, 0), 1);
167 
168   wheelrect.setTop(   wheelrect.top() + 1);
169   wheelrect.setBottom(wheelrect.bottom() - 1);
170   wheelrect.setLeft(  wheelrect.left() + 1);
171   wheelrect.setRight( wheelrect.right() - 1);
172   // wheelrect is now wheel-only
173 
174   /* Somewhere deep inside Qt (inside the bitBlt functions, which are
175      used next), a second painter is created, which conflicts with the
176      one created and used here, unless we explicitly call
177      QPainter::end(). This leads to a warning message with the Qt
178      4.1.0 snapshots (for Qt/X11, I believe).
179 
180      -mortene, information supplied by Markus Grabner.
181   */
182   p.end();
183 
184   QPainter painter(this);
185   QRect sRect;
186   QRect dRect;
187   if (this->orient == Vertical) {
188     sRect = QRect(0,0,w,dval);
189     dRect = QRect(wheelrect.left(),wheelrect.top(),w,dval);
190   }
191   else {
192     sRect = QRect(0,0,dval,w);
193     dRect = QRect(wheelrect.left(),wheelrect.top(),dval,w);
194   }
195 #if QT_VERSION >= 0x040000
196   painter.drawPixmap(dRect,*this->pixmaps[pixmap],sRect);
197 #else
198   painter.drawPixmap(QPoint(dRect.x(), dRect.y()),*this->pixmaps[pixmap],sRect);
199 #endif
200   this->currentPixmap = pixmap;
201 }
202 
203 /*!
204   \internal
205 */
206 
207 void
mousePressEvent(QMouseEvent * event)208 SoQtThumbWheel::mousePressEvent(QMouseEvent * event)
209 {
210   if (this->state != SoQtThumbWheel::Idle)
211     return;
212 
213   if (event->button() != Qt::LeftButton)
214     return;
215 
216   QRect wheelrect;
217   if (this->orient == Vertical) {
218     wheelrect.setLeft(SHADEBORDERWIDTH + 3);
219     wheelrect.setTop(SHADEBORDERWIDTH + 6);
220     wheelrect.setRight(this->width() - SHADEBORDERWIDTH - 3);
221     wheelrect.setBottom(this->height() - SHADEBORDERWIDTH - 6);
222   } else {
223     wheelrect.setLeft(SHADEBORDERWIDTH + 6);
224     wheelrect.setTop(SHADEBORDERWIDTH + 3);
225     wheelrect.setRight(this->width() - SHADEBORDERWIDTH - 6);
226     wheelrect.setBottom(this->height() - SHADEBORDERWIDTH - 3);
227   }
228 
229   if (!wheelrect.contains(event->pos()))
230     return;
231 
232   this->state = SoQtThumbWheel::Dragging;
233 
234   if (this->orient == SoQtThumbWheel::Vertical)
235     this->mouseDownPos = event->pos().y() - SHADEBORDERWIDTH - 6;
236   else
237     this->mouseDownPos = event->pos().x() - SHADEBORDERWIDTH - 6;
238 
239   this->mouseLastPos = this->mouseDownPos;
240 
241   emit wheelPressed();
242 }
243 
244 /*!
245   \internal
246 */
247 
248 void
mouseMoveEvent(QMouseEvent * event)249 SoQtThumbWheel::mouseMoveEvent(QMouseEvent * event)
250 {
251   if (this->state != SoQtThumbWheel::Dragging)
252     return;
253 
254   if (this->orient == SoQtThumbWheel::Vertical)
255     this->mouseLastPos = event->pos().y() - SHADEBORDERWIDTH - 6;
256   else
257     this->mouseLastPos = event->pos().x() - SHADEBORDERWIDTH - 6;
258 
259 
260   this->tempWheelValue = this->wheel->calculateValue(this->wheelValue,
261       this->mouseDownPos, this->mouseLastPos - this->mouseDownPos);
262 
263   emit wheelMoved(this->tempWheelValue);
264 
265   this->repaint();
266 }
267 
268 /*!
269   \internal
270 */
271 
272 void
mouseReleaseEvent(QMouseEvent * event)273 SoQtThumbWheel::mouseReleaseEvent(QMouseEvent * event)
274 {
275   if (this->state != SoQtThumbWheel::Dragging)
276     return;
277 
278   if (event->button() != Qt::LeftButton)
279     return;
280 
281   this->wheelValue = this->tempWheelValue;
282   this->mouseLastPos = this->mouseDownPos;
283   this->state = SoQtThumbWheel::Idle;
284   emit wheelReleased();
285 }
286 
287 /*
288 float
289 SoQtThumbWheel::getNormalizedValue(int pos) const
290 {
291   int relativepos = pos - this->mouseDownPos;
292   return (float) relativepos / (float)this->getWheelLength() * 2.0f;
293 }
294 */
295 
296 /*
297 int
298 SoQtThumbWheel::getWheelLength(void) const
299 {
300   return this->orient == SoQtThumbWheel::Vertical ?
301     this->height() : this->width();
302 }
303 */
304 
305 /*
306 int
307 SoQtThumbWheel::orientedCoord(const QPoint &p) const
308 {
309   return (this->orient == SoQtThumbWheel::Horizontal) ?  p.x() : p.y();
310 }
311 */
312 
313 QSize
sizeHint(void) const314 SoQtThumbWheel::sizeHint(void) const
315 {
316   const int length = 122;
317   int thick = 24;
318 
319   if (this->orient == SoQtThumbWheel::Horizontal)
320     return QSize(length, thick);
321   else
322     return QSize(thick, length);
323 }
324 
325 SoQtThumbWheel::Orientation
orientation(void) const326 SoQtThumbWheel::orientation(void) const
327 {
328   return this->orient;
329 }
330 
331 float
value(void) const332 SoQtThumbWheel::value(void) const
333 {
334   return this->wheelValue;
335 }
336 
337 // *************************************************************************
338 
339 void
initWheel(int diameter,int width)340 SoQtThumbWheel::initWheel(int diameter, int width)
341 {
342   int dval, w;
343   this->wheel->getSize(dval, w);
344   if (dval == diameter && w == width) return;
345 
346   this->wheel->setSize(diameter, width);
347 
348   int pwidth = width;
349   int pheight = diameter;
350   if (this->orient == Horizontal) {
351     pwidth = diameter;
352     pheight = width;
353   }
354 
355   if (this->pixmaps != NULL) {
356     for (int i = 0; i < this->numPixmaps; i++)
357       delete this->pixmaps[i];
358     delete [] this->pixmaps;
359   }
360 
361   this->numPixmaps = this->wheel->getNumBitmaps();
362   this->pixmaps = new QPixmap * [this->numPixmaps];
363 #if QT_VERSION >= 0x040000
364   QImage image(pwidth, pheight, QImage::Format_RGB32);
365 #else
366   QImage image(pwidth, pheight, 32);
367 #endif
368   for (int i = 0; i < this->numPixmaps; i++) {
369     this->wheel->drawBitmap(i, image.bits(), (this->orient == Vertical) ?
370                             SoAnyThumbWheel::VERTICAL : SoAnyThumbWheel::HORIZONTAL);
371     this->pixmaps[i] = new QPixmap(QSize(pwidth, pheight));
372 #if QT_VERSION >= 0x040000
373     *this->pixmaps[i] = QPixmap::fromImage(image);
374 #else
375     *this->pixmaps[i] = QPixmap(image);
376 #endif
377   }
378 }
379 
380 // *************************************************************************
381 
382 void
setEnabled(bool enable)383 SoQtThumbWheel::setEnabled(bool enable)
384 {
385   if (enable)
386     this->state = SoQtThumbWheel::Idle;
387   else
388     this->state = SoQtThumbWheel::Disabled;
389   this->repaint();
390 }
391 
392 bool
isEnabled(void) const393 SoQtThumbWheel::isEnabled(void) const
394 {
395   return (this->state != SoQtThumbWheel::Disabled);
396 }
397 
398 void
setValue(float value)399 SoQtThumbWheel::setValue(float value)
400 {
401   this->wheelValue = this->tempWheelValue = value;
402   this->mouseDownPos = this->mouseLastPos;
403   this->repaint();
404 }
405 
406 // *************************************************************************
407 
408 void
setRangeBoundaryHandling(boundaryHandling handling)409 SoQtThumbWheel::setRangeBoundaryHandling(boundaryHandling handling)
410 {
411   switch (handling) {
412   case CLAMP:
413     this->wheel->setBoundaryHandling(SoAnyThumbWheel::CLAMP);
414     break;
415   case MODULATE:
416     this->wheel->setBoundaryHandling(SoAnyThumbWheel::MODULATE);
417     break;
418   case ACCUMULATE:
419     this->wheel->setBoundaryHandling(SoAnyThumbWheel::ACCUMULATE);
420     break;
421   default:
422     assert(0 && "impossible");
423   }
424 }
425 
426 // *************************************************************************
427 
428 SoQtThumbWheel::boundaryHandling
getRangeBoundaryHandling(void) const429 SoQtThumbWheel::getRangeBoundaryHandling(void) const
430 {
431   switch (this->wheel->getBoundaryHandling()) {
432   case SoAnyThumbWheel::CLAMP:
433     return CLAMP;
434   case SoAnyThumbWheel::MODULATE:
435     return MODULATE;
436   case SoAnyThumbWheel::ACCUMULATE:
437     return ACCUMULATE;
438   default:
439     assert(0 && "impossible");
440   }
441   return CLAMP; // never reached
442 }
443 
444 // *************************************************************************
445