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 // ************************************************************************
34 
35 // Class is documented in common/viewers/SoGuiFullViewer.cpp.in.
36 
37 // ************************************************************************
38 
39 // FIXME: this sourcefile is way too large -- this reeks of bad
40 // design. Should try to factor out lots of stuff. 20010821 mortene.
41 
42 // FIXME: can hide more stuff from the public header file into
43 // SoQtFullViewerP -- most or all of the private methods, for
44 // instance. 20010821 mortene.
45 
46 //    FIXME, low priority:
47 //     * add tooltips to the widgets?
48 //     * trim length of edit fields
49 
50 #ifdef HAVE_CONFIG_H
51 #include <config.h>
52 #endif // HAVE_CONFIG_H
53 #include <qt-config.h>
54 
55 #include <qbuttongroup.h>
56 #include <qcheckbox.h>
57 #include <qframe.h>
58 #include <qlabel.h>
59 #include <qlayout.h>
60 #include <qlineedit.h>
61 #include <qmetaobject.h>
62 #include <qpixmap.h>
63 #include <qpushbutton.h>
64 #include <qradiobutton.h>
65 #include <qslider.h>
66 
67 #ifdef HAVE_QSTYLEFACTORY_H
68 #include <qstylefactory.h>
69 #endif // HAVE_QSTYLEFACTORY_H
70 
71 #include <Inventor/nodes/SoPerspectiveCamera.h>
72 #include <Inventor/nodes/SoOrthographicCamera.h>
73 #include <Inventor/errors/SoDebugError.h>
74 
75 #include <Inventor/Qt/viewers/SoQtFullViewerP.h>
76 #include <Inventor/Qt/viewers/moc_SoQtFullViewerP.icc>
77 
78 #include <soqtdefs.h>
79 #include <Inventor/Qt/SoQt.h>
80 #include <Inventor/Qt/widgets/SoQtThumbWheel.h>
81 #include <Inventor/Qt/widgets/SoQtPopupMenu.h>
82 #include <Inventor/Qt/viewers/SoQtFullViewer.h>
83 #include <Inventor/Qt/SoAny.h>
84 
85 // Button icons.
86 #include <Inventor/Qt/common/pixmaps/pick.xpm>
87 #include <Inventor/Qt/common/pixmaps/view.xpm>
88 #include <Inventor/Qt/common/pixmaps/home.xpm>
89 #include <Inventor/Qt/common/pixmaps/set_home.xpm>
90 #include <Inventor/Qt/common/pixmaps/view_all.xpm>
91 #include <Inventor/Qt/common/pixmaps/seek.xpm>
92 
93 
94 // ************************************************************************
95 
96 // Take care of namespace incompatibilities between Qt 3 and Qt 4.
97 
98 #if QT_VERSION < 0x040000 // pre Qt 4
99 #define QTWIDGET_NOFOCUS QWidget::NoFocus
100 #else // Qt 4.0.0+
101 #define QTWIDGET_NOFOCUS Qt::NoFocus
102 #endif // Qt 4.0.0+
103 
104 // *************************************************************************
105 
106 SOQT_OBJECT_ABSTRACT_SOURCE(SoQtFullViewer);
107 
108 // *************************************************************************
109 
110 static const int VIEWERBORDER = 2;
111 static const int ZOOMSLIDERRESOLUTION = 200;
112 
113 // *************************************************************************
114 
115 // Button index values.
116 enum {
117   INTERACT_BUTTON = 0,
118   EXAMINE_BUTTON,
119   HOME_BUTTON,
120   SET_HOME_BUTTON,
121   VIEW_ALL_BUTTON,
122   SEEK_BUTTON
123 };
124 
125 // *************************************************************************
126 
127 #define PUBLIC(o) (o->pub)
128 #define PRIVATE(o) (o->pimpl)
129 
130 // *************************************************************************
131 
132 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
SoQtFullViewer(QWidget * parent,const char * name,SbBool embed,SoQtFullViewer::BuildFlag buildFlag,SoQtViewer::Type type,SbBool build)133 SoQtFullViewer::SoQtFullViewer(QWidget * parent,
134                                const char * name,
135                                SbBool embed,
136                                SoQtFullViewer::BuildFlag buildFlag,
137                                SoQtViewer::Type type,
138                                SbBool build)
139   : inherited(parent, name, embed, type, FALSE)
140 {
141   PRIVATE(this) = new SoQtFullViewerP(this);
142 
143   PRIVATE(this)->viewerwidget = NULL;
144   PRIVATE(this)->canvas = NULL;
145 //  PRIVATE(this)->canvasparent = NULL;
146 
147   PRIVATE(this)->viewbutton = NULL;
148   PRIVATE(this)->interactbutton = NULL;
149 
150   this->leftDecoration = NULL;
151   this->bottomDecoration = NULL;
152   this->rightDecoration = NULL;
153 
154   this->leftWheel = NULL;
155   this->leftWheelLabel = NULL;
156   this->leftWheelStr = NULL;
157   this->leftWheelVal = 0.0f;
158 
159   this->bottomWheel = NULL;
160   this->bottomWheelLabel = NULL;
161   this->bottomWheelStr = NULL;
162   this->bottomWheelVal = 0.0f;
163 
164   this->rightWheel = NULL;
165   this->rightWheelLabel = NULL;
166   this->rightWheelStr = NULL;
167   this->rightWheelVal = 0.0f;
168 
169   this->setLeftWheelString("Motion X");
170   this->setBottomWheelString("Motion Y");
171   this->setRightWheelString("Dolly");
172 
173   PRIVATE(this)->mainlayout = NULL;
174   PRIVATE(this)->appbuttonlayout = NULL;
175 
176   PRIVATE(this)->menuenabled = buildFlag & SoQtFullViewer::BUILD_POPUP;
177   PRIVATE(this)->decorations =
178     (buildFlag & SoQtFullViewer::BUILD_DECORATION) ? TRUE : FALSE;
179 
180   this->prefmenu = NULL;
181   PRIVATE(this)->menutitle = "Viewer Menu";
182 
183   PRIVATE(this)->viewerbuttons = new SbPList;
184   PRIVATE(this)->appbuttonlist = new SbPList;
185   PRIVATE(this)->appbuttonform = NULL;
186 
187   this->setSize(SbVec2s(500, 390));
188 
189   if (! build) return;
190 
191   this->setClassName("SoQtFullViewer");
192   QWidget * viewer = this->buildWidget(this->getParentWidget());
193   this->setBaseWidget(viewer);
194 }
195 
196 // *************************************************************************
197 
198 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
~SoQtFullViewer()199 SoQtFullViewer::~SoQtFullViewer()
200 {
201   if (PRIVATE(this)->viewerwidget) {
202     this->unregisterWidget(PRIVATE(this)->viewerwidget);
203   }
204 
205   delete PRIVATE(this)->viewerbuttons;
206   delete PRIVATE(this)->appbuttonlist;
207 
208   delete [] this->leftWheelStr;
209   delete [] this->rightWheelStr;
210   delete [] this->bottomWheelStr;
211 
212   delete this->prefmenu;
213 
214   delete PRIVATE(this);
215 }
216 
217 // *************************************************************************
218 
219 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
220 void
setDecoration(const SbBool enable)221 SoQtFullViewer::setDecoration(const SbBool enable)
222 {
223 #if SOQT_DEBUG
224   if ((enable  && this->isDecoration()) ||
225        (!enable && !this->isDecoration())) {
226     SoDebugError::postWarning("SoQtFullViewer::setDecoration",
227                               "decorations already turned %s",
228                               enable ? "on" : "off");
229     return;
230   }
231 #endif // SOQT_DEBUG
232 
233   PRIVATE(this)->decorations = enable;
234   if (PRIVATE(this)->viewerwidget)
235     PRIVATE(this)->showDecorationWidgets(enable);
236 }
237 
238 // *************************************************************************
239 
240 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
241 SbBool
isDecoration(void) const242 SoQtFullViewer::isDecoration(void) const
243 {
244   return PRIVATE(this)->decorations;
245 }
246 
247 // *************************************************************************
248 
249 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
250 void
setPopupMenuEnabled(const SbBool enable)251 SoQtFullViewer::setPopupMenuEnabled(const SbBool enable)
252 {
253 #if SOQT_DEBUG
254   if ((enable && this->isPopupMenuEnabled()) ||
255        (!enable && !this->isPopupMenuEnabled())) {
256     SoDebugError::postWarning("SoQtFullViewer::setPopupMenuEnabled",
257                               "popup menu already turned %s",
258                               enable ? "on" : "off");
259     return;
260   }
261 #endif // SOQT_DEBUG
262   PRIVATE(this)->menuenabled = enable;
263 }
264 
265 // *************************************************************************
266 
267 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
268 SbBool
isPopupMenuEnabled(void) const269 SoQtFullViewer::isPopupMenuEnabled(void) const
270 {
271   return PRIVATE(this)->menuenabled;
272 }
273 
274 // *************************************************************************
275 
276 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
277 QWidget *
getAppPushButtonParent(void) const278 SoQtFullViewer::getAppPushButtonParent(void) const
279 {
280   return PRIVATE(this)->appbuttonform;
281 }
282 
283 // *************************************************************************
284 
285 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
286 void
addAppPushButton(QWidget * newButton)287 SoQtFullViewer::addAppPushButton(QWidget * newButton)
288 {
289   PRIVATE(this)->appbuttonlist->append(newButton);
290   PRIVATE(this)->layoutAppButtons(this->getAppPushButtonParent());
291 }
292 
293 // *************************************************************************
294 
295 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
296 void
insertAppPushButton(QWidget * newButton,int index)297 SoQtFullViewer::insertAppPushButton(QWidget * newButton, int index)
298 {
299 #if SOQT_DEBUG
300   if ((index < 0) || (index > this->lengthAppPushButton())) {
301     SoDebugError::postWarning("SoQtFullViewer::insertAppPushButton",
302                               "index %d out of bounds", index);
303     return;
304   }
305 #endif // SOQT_DEBUG
306   PRIVATE(this)->appbuttonlist->insert(newButton, index);
307   PRIVATE(this)->layoutAppButtons(this->getAppPushButtonParent());
308 }
309 
310 // *************************************************************************
311 
312 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
313 void
removeAppPushButton(QWidget * oldButton)314 SoQtFullViewer::removeAppPushButton(QWidget * oldButton)
315 {
316   int idx = PRIVATE(this)->appbuttonlist->find(oldButton);
317 
318 #if SOQT_DEBUG
319   if (idx == -1) {
320     SoDebugError::postWarning("SoQtFullViewer::removeAppPushButton",
321                               "tried to remove non-existant button");
322     return;
323   }
324 #endif // SOQT_DEBUG
325 
326   PRIVATE(this)->appbuttonlist->remove(idx);
327   PRIVATE(this)->layoutAppButtons(this->getAppPushButtonParent());
328 }
329 
330 // *************************************************************************
331 
332 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
333 int
findAppPushButton(QWidget * oldButton) const334 SoQtFullViewer::findAppPushButton(QWidget * oldButton) const
335 {
336   return PRIVATE(this)->appbuttonlist->find(oldButton);
337 }
338 
339 // *************************************************************************
340 
341 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
342 int
lengthAppPushButton(void) const343 SoQtFullViewer::lengthAppPushButton(void) const
344 {
345   return PRIVATE(this)->appbuttonlist->getLength();
346 }
347 
348 // *************************************************************************
349 
350 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
351 QWidget *
getRenderAreaWidget(void) const352 SoQtFullViewer::getRenderAreaWidget(void) const
353 {
354   return PRIVATE(this)->canvas;
355 }
356 
357 // *************************************************************************
358 
359 // Documented in superclass. Overridden from parent class to correctly
360 // set the user interface indicators on the current state, i.e. to
361 // flip the examine and interact pushbuttons and to grey out the seek
362 // mode activation button iff in interact mode.
363 void
setViewing(SbBool enable)364 SoQtFullViewer::setViewing(SbBool enable)
365 {
366   if ((enable && this->isViewing()) ||
367        (!enable && !this->isViewing())) {
368 #if SOQT_DEBUG && 0 // debug
369     SoDebugError::postWarning("SoQtFullViewer::setViewing",
370                               "view mode already %s", on ? "on" : "off");
371 #endif // debug
372     return;
373   }
374 
375   inherited::setViewing(enable);
376 
377   // Must check that buttons have been built, in case this viewer
378   // component was made without decorations.
379   if (PRIVATE(this)->viewerbuttons->getLength() > 0) {
380 #if QT_VERSION >= 0x040000
381     PRIVATE(this)->getViewerbutton(EXAMINE_BUTTON)->setChecked(enable);
382     PRIVATE(this)->getViewerbutton(INTERACT_BUTTON)->setChecked(enable ? FALSE : TRUE);
383 #else
384     PRIVATE(this)->getViewerbutton(EXAMINE_BUTTON)->setOn(enable);
385     PRIVATE(this)->getViewerbutton(INTERACT_BUTTON)->setOn(enable ? FALSE : TRUE);
386 #endif
387     PRIVATE(this)->getViewerbutton(SEEK_BUTTON)->setEnabled(enable);
388   }
389 }
390 
391 // *************************************************************************
392 
393 // Documented in superclass.
394 QWidget *
buildWidget(QWidget * parent)395 SoQtFullViewer::buildWidget(QWidget * parent)
396 {
397   // This will build the main view widgets, along with the decorations
398   // widgets and popup menu if they are enabled.
399 
400 #if SOQT_DEBUG && 0
401   SoDebugError::postInfo("SoQtFullViewer::buildWidget", "[invoked]");
402 #endif
403 
404   PRIVATE(this)->viewerwidget = new QWidget(parent);
405   this->registerWidget(PRIVATE(this)->viewerwidget);
406 
407   PRIVATE(this)->viewerwidget->move(0, 0);
408 
409 #if SOQT_DEBUG && 0
410   PRIVATE(this)->viewerwidget->setBackgroundColor(QColor(250, 0, 0));
411 #endif
412 
413   PRIVATE(this)->canvas = inherited::buildWidget(PRIVATE(this)->viewerwidget);
414 
415   QSize s(PRIVATE(this)->viewerwidget->size().width(),
416           PRIVATE(this)->viewerwidget->size().height());
417 
418   PRIVATE(this)->canvas->resize(s);
419 
420   this->buildDecoration( PRIVATE(this)->viewerwidget );
421   PRIVATE(this)->showDecorationWidgets( PRIVATE(this)->decorations );
422 
423   if (PRIVATE(this)->menuenabled)
424     this->buildPopupMenu();
425 
426   return PRIVATE(this)->viewerwidget;
427 }
428 
429 // *************************************************************************
430 
431 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
432 void
buildDecoration(QWidget * parent)433 SoQtFullViewer::buildDecoration(QWidget * parent)
434 {
435   this->leftDecoration = this->buildLeftTrim(parent);
436   this->bottomDecoration = this->buildBottomTrim(parent);
437   this->rightDecoration = this->buildRightTrim(parent);
438 }
439 
440 // *************************************************************************
441 
442 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
443 QWidget *
buildLeftTrim(QWidget * parent)444 SoQtFullViewer::buildLeftTrim(QWidget * parent)
445 {
446   QWidget * w = new QWidget(parent);
447   w->setFixedWidth(30);
448 
449   QGridLayout * gl = new QGridLayout(w);
450 #if QT_VERSION >= 0x040000
451   gl->setContentsMargins(0,0,0,0);
452 #endif
453   gl->addWidget(this->buildAppButtons(w), 0, 0);
454 
455   SoQtThumbWheel * t = new SoQtThumbWheel(SoQtThumbWheel::Vertical, w);
456   this->leftWheel = t;
457   t->setRangeBoundaryHandling(SoQtThumbWheel::ACCUMULATE);
458   this->leftWheelVal = t->value();
459 
460   QObject::connect(t, SIGNAL(wheelMoved(float)), PRIVATE(this), SLOT(leftWheelChanged(float)));
461   QObject::connect(t, SIGNAL(wheelPressed()), PRIVATE(this), SLOT(leftWheelPressed()));
462   QObject::connect(t, SIGNAL(wheelReleased()), PRIVATE(this), SLOT(leftWheelReleased()));
463 
464   gl->addWidget(t, 1, 0, Qt::AlignBottom | Qt::AlignHCenter);
465   gl->activate();
466 
467   return w;
468 }
469 
470 // *************************************************************************
471 
472 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
473 QWidget *
buildBottomTrim(QWidget * parent)474 SoQtFullViewer::buildBottomTrim(QWidget * parent)
475 {
476   QWidget * w = new QWidget(parent);
477   w->setFixedHeight(30);
478 
479   QLabel * label = new QLabel(this->leftWheelStr, w);
480   label->adjustSize();
481   label->setAlignment(Qt::AlignLeft | Qt::AlignTop);
482   label->setMargin(2);
483   this->leftWheelLabel = label;
484 
485   label = new QLabel(this->bottomWheelStr, w);
486   label->adjustSize();
487   label->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
488   label->setMargin(2);
489   this->bottomWheelLabel = label;
490 
491   label = new QLabel(this->rightWheelStr, w);
492   label->adjustSize();
493   label->setAlignment(Qt::AlignRight | Qt::AlignTop);
494   label->setMargin(2);
495   this->rightWheelLabel = label;
496 
497   SoQtThumbWheel * t = new SoQtThumbWheel(SoQtThumbWheel::Horizontal, w);
498   this->bottomWheel = t;
499   t->setRangeBoundaryHandling(SoQtThumbWheel::ACCUMULATE);
500 
501   QObject::connect(t, SIGNAL(wheelMoved(float)),
502                    PRIVATE(this), SLOT(bottomWheelChanged(float)));
503   QObject::connect(t, SIGNAL(wheelPressed()),
504                    PRIVATE(this), SLOT(bottomWheelPressed()));
505   QObject::connect(t, SIGNAL(wheelReleased()),
506                    PRIVATE(this), SLOT(bottomWheelReleased()));
507 
508   this->bottomWheelVal = t->value();
509 
510   QGridLayout * layout = new QGridLayout(w);
511 #if QT_VERSION >= 0x040000
512   layout->setContentsMargins(0,0,0,0);
513 #endif
514   layout->addWidget(this->leftWheelLabel, 0, 0, Qt::AlignVCenter | Qt::AlignHCenter);
515   layout->addWidget(this->bottomWheelLabel, 0,1,  Qt::AlignVCenter | Qt::AlignRight);
516   layout->addWidget(t, 0,2, Qt::AlignVCenter | Qt::AlignLeft);
517   layout->addItem(new QSpacerItem(0,0,QSizePolicy::MinimumExpanding,QSizePolicy::MinimumExpanding),0, 3);
518   layout->addWidget(this->rightWheelLabel, 0, 4,Qt::AlignVCenter | Qt::AlignRight);
519 
520   layout->activate();
521   return w;
522 }
523 
524 // *************************************************************************
525 
526 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
527 QWidget *
buildRightTrim(QWidget * parent)528 SoQtFullViewer::buildRightTrim(QWidget * parent)
529 {
530   QWidget * w = new QWidget(parent);
531   // FIXME: should be set according to width of viewer
532   // buttons. 20000424 mortene.
533   w->setFixedWidth(30);
534   // FIXME: nope, trims are actually guaranteed to be 30 pixels wide
535 
536   SoQtThumbWheel * t = new SoQtThumbWheel(SoQtThumbWheel::Vertical, w);
537   this->rightWheel = t;
538   t->setRangeBoundaryHandling(SoQtThumbWheel::ACCUMULATE);
539   this->rightWheelVal = t->value();
540 
541   QObject::connect(t, SIGNAL(wheelMoved(float)),
542                    PRIVATE(this), SLOT(rightWheelChanged(float)));
543   QObject::connect(t, SIGNAL(wheelPressed()),
544                    PRIVATE(this), SLOT(rightWheelPressed()));
545   QObject::connect(t, SIGNAL(wheelReleased()),
546                    PRIVATE(this), SLOT(rightWheelReleased()));
547 
548   QGridLayout * l = new QGridLayout(w);
549 #if QT_VERSION >= 0x040000
550   l->setContentsMargins(0,0,0,0);
551   l->setVerticalSpacing(0);
552 #endif
553   l->setMargin(0);
554   l->addWidget(this->buildViewerButtons(w), 0, 0);
555   l->addWidget(t, 2, 0, Qt::AlignBottom | Qt::AlignHCenter);
556   l->activate();
557 
558   return w;
559 }
560 
561 // *************************************************************************
562 
563 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
564 QWidget *
buildAppButtons(QWidget * parent)565 SoQtFullViewer::buildAppButtons(QWidget * parent)
566 {
567   PRIVATE(this)->appbuttonform = new QWidget(parent);
568   PRIVATE(this)->appbuttonform->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
569 
570   if (this->lengthAppPushButton() > 0)
571     PRIVATE(this)->layoutAppButtons(PRIVATE(this)->appbuttonform);
572 
573   return PRIVATE(this)->appbuttonform;
574 }
575 
576 // *************************************************************************
577 
578 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
579 QWidget *
buildViewerButtons(QWidget * parent)580 SoQtFullViewer::buildViewerButtons(QWidget * parent)
581 {
582   QWidget * w = new QWidget(parent);
583   this->createViewerButtons(w, PRIVATE(this)->viewerbuttons);
584   w->move(0, 0);
585   w->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
586 
587   // assert(PRIVATE(this)->viewerbuttons->getLength() != 0);
588   QGridLayout * l =
589     new QGridLayout(w);
590   l->setMargin(0);
591 #if QT_VERSION >= 0x040000
592   l->setVerticalSpacing(0);
593 #endif
594 
595   const int numViewerButtons = PRIVATE(this)->viewerbuttons->getLength();
596   for (int i = 0; i < numViewerButtons; i++) {
597     QPushButton * b = PRIVATE(this)->getViewerbutton(i);
598     b->setFixedSize(30, 30);
599     b->setFocusPolicy(QTWIDGET_NOFOCUS);
600     l->addWidget(b, i, 0);
601   }
602 
603   l->activate();
604   return w;
605 }
606 
607 // *************************************************************************
608 
609 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
610 void
createViewerButtons(QWidget * parent,SbPList * buttonlist)611 SoQtFullViewer::createViewerButtons(QWidget * parent, SbPList * buttonlist)
612 {
613   for (int i=0; i <= SEEK_BUTTON; i++) {
614     QPushButton * p = new QPushButton(parent);
615     // Button focus doesn't really make sense in the way we're using
616     // the pushbuttons.
617     p->setFocusPolicy(QTWIDGET_NOFOCUS);
618 
619 #if QT_VERSION >= 0x040000 // Qt 4.*
620     // In some GUI styles in Qt4, a default button is drawn with an
621     // extra frame around it, up to 3 pixels or more. This causes
622     // pixmaps on buttons to look tiny, which is not what we want.
623     p->setIconSize(QSize(24, 24));
624 #endif
625 
626 #if (defined Q_OS_MAC && QT_VERSION >= 0x030100) && defined(HAVE_QSTYLEFACTORY_H)
627     // Since Qt/Mac 3.1.x, all pushbuttons (even those < 32x32) are drawn
628     // using the Aqua style, i.e. with rounded edges and shading. This
629     // looks really ugly in the viewer decoration. Drawing the buttons
630     // in the Windows style gives us the flat, square buttons we want.
631     QStyle * style = QStyleFactory::create("windows");
632     if (style) { p->setStyle(style); }
633 #endif
634 
635     // FIXME: its wrong to cast xpm data to (const char **), it
636     // _should_ be (const char * const []), but this is not valid ANSI
637     // C++. Not sure what to do about this (and the wrong cast can
638     // (and has) in fact lead to bugs, so the situation should be
639     // resolved). 20000505 mortene.
640 
641     switch (i) {
642     case INTERACT_BUTTON:
643       PRIVATE(this)->interactbutton = p;
644 #if QT_VERSION >= 0x040000
645       p->setCheckable(TRUE);
646       p->setChecked(this->isViewing() ? FALSE : TRUE);
647       p->setIcon(QPixmap((const char **)pick_xpm));
648 #else
649       p->setToggleButton(TRUE);
650       p->setOn(this->isViewing() ? FALSE : TRUE);
651       p->setPixmap(QPixmap((const char **)pick_xpm));
652 #endif
653       QObject::connect(p, SIGNAL(clicked()),
654                        PRIVATE(this), SLOT(interactbuttonClicked()));
655       break;
656     case EXAMINE_BUTTON:
657       PRIVATE(this)->viewbutton = p;
658 #if QT_VERSION >= 0x040000
659       p->setCheckable(TRUE);
660       p->setChecked(this->isViewing());
661       p->setIcon(QPixmap((const char **)view_xpm));
662 #else
663       p->setToggleButton(TRUE);
664       p->setOn(this->isViewing());
665       p->setPixmap(QPixmap((const char **)view_xpm));
666 #endif
667       QObject::connect(p, SIGNAL(clicked()),
668                        PRIVATE(this), SLOT(viewbuttonClicked()));
669       break;
670     case HOME_BUTTON:
671       QObject::connect(p, SIGNAL(clicked()), PRIVATE(this), SLOT(homebuttonClicked()));
672 #if QT_VERSION >= 0x040000
673       p->setIcon(QPixmap((const char **)home_xpm));
674 #else
675       p->setPixmap(QPixmap((const char **)home_xpm));
676 #endif
677       break;
678     case SET_HOME_BUTTON:
679       QObject::connect(p, SIGNAL(clicked()),
680                        PRIVATE(this), SLOT(sethomebuttonClicked()));
681 #if QT_VERSION >= 0x040000
682       p->setIcon(QPixmap((const char **)set_home_xpm));
683 #else
684       p->setPixmap(QPixmap((const char **)set_home_xpm));
685 #endif
686       break;
687     case VIEW_ALL_BUTTON:
688       QObject::connect(p, SIGNAL(clicked()),
689                        PRIVATE(this), SLOT(viewallbuttonClicked()));
690 #if QT_VERSION >= 0x040000
691       p->setIcon(QPixmap((const char **)view_all_xpm));
692 #else
693       p->setPixmap(QPixmap((const char **)view_all_xpm));
694 #endif
695       break;
696     case SEEK_BUTTON:
697       QObject::connect(p, SIGNAL(clicked()), PRIVATE(this), SLOT(seekbuttonClicked()));
698 #if QT_VERSION >= 0x040000
699       p->setIcon(QPixmap((const char **)seek_xpm));
700 #else
701       p->setPixmap(QPixmap((const char **)seek_xpm));
702 #endif
703       break;
704     default:
705       assert(0);
706       break;
707     }
708 
709     p->adjustSize();
710     buttonlist->append(p);
711   }
712 }
713 
714 // *************************************************************************
715 
716 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
717 void
buildPopupMenu(void)718 SoQtFullViewer::buildPopupMenu(void)
719 {
720   this->prefmenu = PRIVATE(this)->setupStandardPopupMenu();
721 }
722 
723 // *************************************************************************
724 
725 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
726 void
openPopupMenu(const SbVec2s position)727 SoQtFullViewer::openPopupMenu(const SbVec2s position)
728 {
729   if (! this->isPopupMenuEnabled()) return;
730   if (this->prefmenu == NULL)
731     this->buildPopupMenu();
732   int x = 2 + position[0];
733   int y = 2 + this->getGLSize()[1] - position[1] - 1;
734 
735   PRIVATE(this)->prepareMenu(this->prefmenu);
736   this->prefmenu->popUp(this->getGLWidget(), x, y);
737 }
738 
739 // *************************************************************************
740 
741 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
742 void
setLeftWheelString(const char * const string)743 SoQtFullViewer::setLeftWheelString(const char * const string)
744 {
745   delete [] this->leftWheelStr;
746   this->leftWheelStr = NULL;
747 
748   if (string)
749     this->leftWheelStr = strcpy(new char [strlen(string)+1], string);
750   if (this->leftWheelLabel)
751     ((QLabel *)this->leftWheelLabel)->setText(string ? string : "");
752 }
753 
754 // *************************************************************************
755 
756 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
757 void
setBottomWheelString(const char * const string)758 SoQtFullViewer::setBottomWheelString(const char * const string)
759 {
760   delete [] this->bottomWheelStr;
761   this->bottomWheelStr = NULL;
762 
763   if (string)
764     this->bottomWheelStr = strcpy(new char [strlen(string)+1], string);
765   if (this->bottomWheelLabel)
766     ((QLabel *)this->bottomWheelLabel)->setText(string ? string : "");
767 }
768 
769 // *************************************************************************
770 
771 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
772 const char *
getRightWheelString() const773 SoQtFullViewer::getRightWheelString() const
774 {
775   return this->rightWheelStr;
776 }
777 
778 // Documented in common/viewers/SoGuiFullViewer.cpp.in.
779 void
setRightWheelString(const char * const string)780 SoQtFullViewer::setRightWheelString(const char * const string)
781 {
782   delete [] this->rightWheelStr;
783   this->rightWheelStr = NULL;
784 
785   if (string)
786     this->rightWheelStr = strcpy(new char [strlen(string)+1], string);
787   if (this->rightWheelLabel)
788     ((QLabel *)this->rightWheelLabel)->setText(string ? string : "");
789 }
790 
791 // *************************************************************************
792 
793 // doc in super
794 void
sizeChanged(const SbVec2s & size)795 SoQtFullViewer::sizeChanged(const SbVec2s & size)
796 {
797 
798 #if SOQT_DEBUG && 0
799   SoDebugError::postInfo("SoQtFullViewer::sizeChanged", "(%d, %d)",
800                          size[0], size[1]);
801 #endif
802 
803   SbVec2s newsize(size);
804   if (PRIVATE(this)->decorations) {
805     newsize[0] -= this->leftDecoration ? this->leftDecoration->width() : 0;
806     newsize[0] -= this->rightDecoration ? this->rightDecoration->width() : 0;
807     newsize[1] -= this->bottomDecoration ? this->bottomDecoration->height() : 0;
808   }
809 
810   // Workaround for a bug in Qt/Mac 3.1.0 and 3.1.1 (which has been
811   // confirmed fixed in 3.1.2):
812   //
813   // If the OpenGL context overlaps with the QSizeGrip widget
814   // (generated by default), resizing does not work any more. The
815   // workaround is to leave 15 pixels at the lower border of the
816   // window blank...
817 #if defined Q_OS_MAC && ((QT_VERSION == 0x030100) || (QT_VERSION == 0x030101))
818 
819   // Environment variable to override Qt/Mac 3.1.x workarounds.
820   const char * forcenoresizeworkaround =
821     SoAny::si()->getenv("FORCE_NO_QTMAC_31_RESIZE_WORKAROUND");
822   if (!forcenoresizeworkaround || (atoi(forcenoresizeworkaround) == 0)) {
823 
824     if (!PRIVATE(this)->decorations) { // GL context would fill whole window
825        newsize -= SbVec2s(0, 15);       // -> leave lowermost 15 px blank
826 
827       // spit out a warning that this is a Qt/Mac bug, not an SoQt problem
828       const char * env = SoAny::si()->getenv("SOQT_NO_QTMAC_BUG_WARNINGS");
829       if (!env || !atoi(env)) {
830         SoDebugError::postWarning("SoQtFullViewer::sizeChanged",
831                                   "\nThis version of Qt/Mac contains a bug "
832                                   "that makes it necessary to leave the\n"
833                                   "lowermost 15 pixels of the viewer window "
834                                   "blank. Set the environment variable\n"
835                                   "FORCE_NO_QTMAC_31_RESIZE_WORKAROUND=1 to "
836                                   "override this workaround. \n"
837                                   "You can turn off warnings about Qt/Mac "
838                                   "bugs permanently by setting \n"
839                                   "SOQT_NO_QTMAC_BUG_WARNINGS=1.\n");
840       }
841     }
842   }
843 
844 #endif
845 
846   newsize = SbVec2s(SoQtMax(newsize[0], (short)1),
847                     SoQtMax(newsize[1], (short)1));
848 
849   inherited::sizeChanged(newsize);
850 }
851 
852 // *************************************************************************
853 
854 #ifndef DOXYGEN_SKIP_THIS
855 
856 // Show or hide decorations. Will make and activate a Qt layout grid
857 // if we're turning the decorations on.
858 void
showDecorationWidgets(SbBool onOff)859 SoQtFullViewerP::showDecorationWidgets(SbBool onOff)
860 {
861 #if SOQT_DEBUG && 0
862   SoDebugError::postInfo("SoQtFullViewerP::showDecorationWidgets", "[invoked]");
863 #endif // SOQT_DEBUG
864 
865   if (this->mainlayout) delete this->mainlayout;
866 
867   assert(this->viewerwidget);
868 
869   assert(PUBLIC(this)->leftDecoration && PUBLIC(this)->bottomDecoration && PUBLIC(this)->rightDecoration);
870   if (onOff) {
871     PUBLIC(this)->leftDecoration->show();
872     PUBLIC(this)->bottomDecoration->show();
873     PUBLIC(this)->rightDecoration->show();
874 
875 #if QT_VERSION >= 0x040000
876     this->viewerwidget->setContentsMargins(0,0,0,0);
877 #endif
878 
879     QGridLayout * g = new QGridLayout(this->viewerwidget); // VIEWERBORDER);
880     g->setSpacing(0);
881 #if QT_VERSION >= 0x040000
882     g->setContentsMargins(0,0,0,0);
883 #endif
884 
885     g->addWidget(PUBLIC(this)->bottomDecoration, 1, 0);
886 
887     QGridLayout * subLayout = new QGridLayout();
888     g->addLayout(subLayout, 0, 0);
889 #if QT_VERSION >= 0x040000
890     subLayout->setVerticalSpacing(0);
891     subLayout->setContentsMargins(0,0,0,0);
892 #endif
893 
894     PUBLIC(this)->leftDecoration->setSizePolicy(QSizePolicy(QSizePolicy::Minimum,QSizePolicy::Minimum));
895 
896     subLayout->addWidget(PUBLIC(this)->leftDecoration, 0, 0);
897     this->canvas->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding));
898     subLayout->addWidget(this->canvas, 0, 1);
899     subLayout->addWidget(PUBLIC(this)->rightDecoration, 0, 2);
900 
901     this->mainlayout = g;
902   } else {
903     QGridLayout * g = new QGridLayout(this->viewerwidget);
904     g->addWidget(this->canvas, 0, 0);
905 #if QT_VERSION >= 0x040000
906     g->setContentsMargins(0,0,0,0);
907 #endif
908     this->mainlayout = g;
909 
910     PUBLIC(this)->leftDecoration->hide();
911     PUBLIC(this)->bottomDecoration->hide();
912     PUBLIC(this)->rightDecoration->hide();
913   }
914 
915   this->mainlayout->activate();
916   QSize size = this->viewerwidget->size();
917   SbVec2s rasize = SbVec2s(size.width(), size.height());
918   PUBLIC(this)->sizeChanged(rasize);
919 }
920 
921 // *************************************************************************
922 
923 // Layout application specified buttons.
924 void
layoutAppButtons(QWidget * form)925 SoQtFullViewerP::layoutAppButtons(QWidget * form)
926 {
927   delete this->appbuttonlayout; this->appbuttonlayout = NULL;
928 
929   int nrbuttons = this->appbuttonlist->getLength();
930   if (nrbuttons == 0) return;
931 
932   this->appbuttonlayout = new QGridLayout(form);
933 #if QT_VERSION >= 0x040000
934   this->appbuttonlayout->setContentsMargins(0,0,0,0);
935 #endif
936   this->appbuttonlayout->setSpacing(0);
937 
938   for (int i=0; i < nrbuttons; i++) {
939     QWidget * button = (QWidget *)((*(this->appbuttonlist))[i]);
940     ((QGridLayout *)this->appbuttonlayout)->addWidget(button, i, 0);
941     button->show();
942   }
943 
944   this->appbuttonlayout->activate();
945 }
946 
947 // *************************************************************************
948 
949 // Qt slot.
950 void
interactbuttonClicked(void)951 SoQtFullViewerP::interactbuttonClicked(void)
952 {
953   if (this->interactbutton)
954 #if QT_VERSION >= 0x040000
955     ((QPushButton *)this->interactbutton)->setChecked(TRUE);
956 #else
957     ((QPushButton *)this->interactbutton)->setOn(TRUE);
958 #endif
959   if (this->viewbutton)
960 #if QT_VERSION >= 0x040000
961     ((QPushButton *)this->viewbutton)->setChecked(FALSE);
962 #else
963     ((QPushButton *)this->viewbutton)->setOn(FALSE);
964 #endif
965   if (PUBLIC(this)->isViewing())
966     PUBLIC(this)->setViewing(FALSE);
967 }
968 
969 // *************************************************************************
970 
971 // Qt slot.
972 void
viewbuttonClicked(void)973 SoQtFullViewerP::viewbuttonClicked(void)
974 {
975   if (this->interactbutton)
976 #if QT_VERSION >= 0x040000
977     ((QPushButton *)this->interactbutton)->setChecked(FALSE);
978 #else
979     ((QPushButton *)this->interactbutton)->setOn(FALSE);
980 #endif
981   if (this->viewbutton)
982 #if QT_VERSION >= 0x040000
983     ((QPushButton *)this->viewbutton)->setChecked(TRUE);
984 #else
985     ((QPushButton *)this->viewbutton)->setOn(TRUE);
986 #endif
987   if (!PUBLIC(this)->isViewing())
988     PUBLIC(this)->setViewing(TRUE);
989 }
990 
991 // *************************************************************************
992 
993 // Qt slot.
994 void
homebuttonClicked()995 SoQtFullViewerP::homebuttonClicked()
996 {
997   PUBLIC(this)->resetToHomePosition();
998 }
999 
1000 // *************************************************************************
1001 
1002 // Qt slot.
1003 void
sethomebuttonClicked()1004 SoQtFullViewerP::sethomebuttonClicked()
1005 {
1006   PUBLIC(this)->saveHomePosition();
1007 }
1008 
1009 // *************************************************************************
1010 
1011 // Qt slot.
1012 void
viewallbuttonClicked()1013 SoQtFullViewerP::viewallbuttonClicked()
1014 {
1015   PUBLIC(this)->viewAll();
1016 }
1017 
1018 // *************************************************************************
1019 
1020 // Qt slot.
1021 void
seekbuttonClicked()1022 SoQtFullViewerP::seekbuttonClicked()
1023 {
1024   PUBLIC(this)->setSeekMode(PUBLIC(this)->isSeekMode() ? FALSE : TRUE);
1025 }
1026 
1027 // *************************************************************************
1028 
1029 // Qt slot.
1030 void
selectedViewing()1031 SoQtFullViewerP::selectedViewing()
1032 {
1033   PUBLIC(this)->setViewing(PUBLIC(this)->isViewing() ? FALSE : TRUE);
1034 }
1035 
1036 // *************************************************************************
1037 
1038 // Qt slot.
1039 void
selectedDecoration()1040 SoQtFullViewerP::selectedDecoration()
1041 {
1042   PUBLIC(this)->setDecoration(PUBLIC(this)->isDecoration() ? FALSE : TRUE);
1043 }
1044 
1045 // *************************************************************************
1046 
1047 // Qt slot.
1048 void
selectedHeadlight()1049 SoQtFullViewerP::selectedHeadlight()
1050 {
1051   PUBLIC(this)->setHeadlight(PUBLIC(this)->isHeadlight() ? FALSE : TRUE);
1052 }
1053 
1054 // *************************************************************************
1055 
1056 // Qt slot.
1057 void
increaseInteractiveCount()1058 SoQtFullViewerP::increaseInteractiveCount()
1059 {
1060   PUBLIC(this)->interactiveCountInc();
1061 }
1062 
1063 // *************************************************************************
1064 
1065 // Qt slot.
1066 void
decreaseInteractiveCount()1067 SoQtFullViewerP::decreaseInteractiveCount()
1068 {
1069   PUBLIC(this)->interactiveCountDec();
1070 }
1071 
1072 // *************************************************************************
1073 
SoQtFullViewerP(SoQtFullViewer * publ)1074 SoQtFullViewerP::SoQtFullViewerP(SoQtFullViewer * publ)
1075   : SoGuiFullViewerP(publ)
1076 {
1077 }
1078 
~SoQtFullViewerP()1079 SoQtFullViewerP::~SoQtFullViewerP()
1080 {
1081 }
1082 
1083 void
setThumbWheelValue(void * wheel,float val)1084 SoQtFullViewerP::setThumbWheelValue(void * wheel, float val)
1085 {
1086   ((SoQtThumbWheel *)wheel)->setValue(val);
1087 }
1088 
1089 // *************************************************************************
1090 
1091 // These are all private slots for catching Qt events.
leftWheelPressed(void)1092 void SoQtFullViewerP::leftWheelPressed(void) { PUBLIC(this)->leftWheelStart(); }
leftWheelChanged(float v)1093 void SoQtFullViewerP::leftWheelChanged(float v) { PUBLIC(this)->leftWheelMotion(v); }
leftWheelReleased(void)1094 void SoQtFullViewerP::leftWheelReleased(void) { PUBLIC(this)->leftWheelFinish(); }
bottomWheelPressed(void)1095 void SoQtFullViewerP::bottomWheelPressed(void) { PUBLIC(this)->bottomWheelStart(); }
bottomWheelChanged(float v)1096 void SoQtFullViewerP::bottomWheelChanged(float v) { PUBLIC(this)->bottomWheelMotion(v);}
bottomWheelReleased(void)1097 void SoQtFullViewerP::bottomWheelReleased(void) { PUBLIC(this)->bottomWheelFinish(); }
rightWheelPressed(void)1098 void SoQtFullViewerP::rightWheelPressed(void) { PUBLIC(this)->rightWheelStart(); }
rightWheelChanged(float v)1099 void SoQtFullViewerP::rightWheelChanged(float v) { PUBLIC(this)->rightWheelMotion(v); }
rightWheelReleased(void)1100 void SoQtFullViewerP::rightWheelReleased(void) { PUBLIC(this)->rightWheelFinish(); }
1101 
1102 // *************************************************************************
1103 
1104 #endif // DOXYGEN_SKIP_THIS
1105 
1106 #undef PUBLIC
1107 #undef PRIVATE
1108