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