1// @configure_input@
2
3/**************************************************************************\
4 * Copyright (c) Kongsberg Oil & Gas Technologies AS
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * Neither the name of the copyright holder nor the names of its
19 * contributors may be used to endorse or promote products derived from
20 * this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33\**************************************************************************/
34
35// This file contains the generic, "templatize-able" parts of the
36// So*FullViewer sourcecode.
37
38/*!
39  \class So@Gui@FullViewer So@Gui@FullViewer.h Inventor/@Gui@/viewers/So@Gui@FullViewer.h
40  \brief The So@Gui@FullViewer class adds some user interface components to the viewer canvas.
41
42  \ingroup components viewers
43
44  The So@Gui@FullViewer is an abstract viewer class which extends it's
45  superclass (the basic So@Gui@Viewer class) with two major user
46  interface additions:
47
48  <ul>
49
50  <li>decorations around the render canvas, with buttons and
51  thumbwheels to control camera interaction and various other aspects
52  of model / scene viewing</li>
53
54  <li>a popup menu, invoked with the right mouse button, which
55  controls yet more aspects of the rendering and the viewer</li>
56
57  </ul>
58
59  The user interface features listed above are the common parts of the
60  user interfaces of the So@Gui@FullViewer's non-abstract subclasses.
61
62  For "real" application programs, the viewers / components from this
63  class onwards and further down in the inheritance hierarchy is
64  usually not all that interesting, as they enforces too much extra
65  user interface cruft not compatible with the look and feel of the
66  other parts of the application user interface. So most "real"
67  applications will use either the So@Gui@Viewer or the
68  So@Gui@RenderArea as their base-level component, and then inherit
69  one of these down into one's own "proper" viewer classes.
70
71  The non-abstract subclasses of the So@Gui@FullViewer class are
72  viewer components often used as a test-bed for prototyping / rapid
73  application development when doing quick testing of scenes,
74  stand-alone 3D-models, animation techniques, etc etc.
75
76
77  \sa So@Gui@ConstrainedViewer, So@Gui@ExaminerViewer, So@Gui@PlaneViewer
78*/
79
80
81// *************************************************************************
82
83#include <float.h>
84
85#include <Inventor/@Gui@/viewers/So@Gui@FullViewer.h>
86#include <Inventor/@Gui@/viewers/So@Gui@FullViewerP.h>
87#include <Inventor/@Gui@/widgets/So@Gui@PopupMenu.h>
88#include <so@gui@defs.h>
89
90#include <Inventor/events/SoMouseButtonEvent.h>
91#include <Inventor/events/SoKeyboardEvent.h>
92#include <Inventor/errors/SoDebugError.h>
93#include <Inventor/nodes/SoOrthographicCamera.h>
94#include <Inventor/nodes/SoPerspectiveCamera.h>
95
96#ifdef HAVE_CONFIG_H
97#include <config.h>
98#endif // HAVE_CONFIG_H
99
100// ************************************************************************
101
102#define PRIVATE(o) (o->pimpl)
103#define PUBLIC(o) (o->pub)
104
105// ************************************************************************
106
107// Note: the following function documentation block will also be used
108// for all the miscellaneous viewer subclasses, so keep it general.
109/*!
110  \fn void So@Gui@FullViewer::createViewerButtons(@WIDGET@ parent, SbPList * buttonlist)
111
112  Set up the viewer buttons with pixmaps and event connections.
113
114
115  One important note if you want to override this method to add your
116  own buttons to the list (in the explanation below, it is assumed
117  that you know how virtual function table pointers are handled by
118  C++):
119
120  createViewerButtons() is called (indirectly) from the constructors
121  of the viewer classes. This means that you need to delay it's
122  invocation until your own subclass's constructor is run, because
123  otherwise your re-implementation won't have been added to the
124  internal virtual function table yet.
125
126  Delaying a superclass viewer from building it's decorations is done
127  by passing build==FALSE as the last argument of the protected
128  constructors. You will then have to explicitly trigger the building
129  in your own constructor.
130
131  Here's a minimal example that shows the steps needed to be able to
132  override createViewerButtons() from So@Gui@ExaminerViewer:
133
134  \code
135  class MyExaminerViewer : public So@Gui@ExaminerViewer {
136
137  public:
138    MyExaminerViewer(@WIDGET@ parent);
139
140  protected:
141    virtual void createViewerButtons(@WIDGET@ parent, SbPList * buttonlist);
142  };
143
144  MyExaminerViewer::MyExaminerViewer(@WIDGET@ parent)
145    : So@Gui@ExaminerViewer(parent, NULL, TRUE,
146                            So@Gui@FullViewer::BUILD_ALL, So@Gui@FullViewer::BROWSER,
147                            // build == FALSE, to delay creation of decorations
148                            FALSE)
149  {
150    // Explicitly trigger the construction of viewer decorations.
151    @WIDGET@ widget = this->buildWidget(this->getParentWidget());
152    this->setBaseWidget(widget);
153  }
154
155  void
156  MyExaminerViewer::createViewerButtons(@WIDGET@ parent, SbPList * buttonlist)
157  {
158    So@Gui@ExaminerViewer::createViewerButtons(parent, buttonlist);
159    // [now add your own button(s) to the buttonlist]
160  }
161  \endcode
162*/
163
164/*!
165  \enum So@Gui@FullViewer::BuildFlag
166
167  Valid values for the constructor argument that decides how much of
168  the user interface features to set up. The enum values are bitflags
169  that can be OR'ed together.
170*/
171/*!
172  \var So@Gui@FullViewer::BuildFlag So@Gui@FullViewer::BUILD_NONE
173
174  Do not add any of the user interface components.
175*/
176/*!
177  \var So@Gui@FullViewer::BuildFlag So@Gui@FullViewer::BUILD_DECORATION
178
179  Only build and set up the decorations around the OpenGL canvas.
180*/
181/*!
182  \var So@Gui@FullViewer::BuildFlag So@Gui@FullViewer::BUILD_POPUP
183
184  Only build the popup menu. (Activated with the right mousebutton.)
185*/
186/*!
187  \var So@Gui@FullViewer::BuildFlag So@Gui@FullViewer::BUILD_ALL
188
189  Build all user interface components of the So@Gui@FullViewer.
190*/
191
192/*!
193  \var @WIDGET@ So@Gui@FullViewer::leftDecoration
194  The root widget for the left-side decorations.
195*/
196
197/*!
198  \var @WIDGET@ So@Gui@FullViewer::leftWheel
199  The widget representing the thumbwheel left of the render canvas.
200*/
201
202/*!
203  \var @WIDGET@ So@Gui@FullViewer::leftWheelLabel
204  Label widget of left wheel.
205*/
206
207/*!
208  \var char * So@Gui@FullViewer::leftWheelStr
209  Text of left thumbwheel's label.
210*/
211
212/*!
213  \var float So@Gui@FullViewer::leftWheelVal
214  Current value of the left thumbwheel.
215*/
216
217/*!
218  \var @WIDGET@ So@Gui@FullViewer::bottomDecoration
219  The root widget for the bottom decorations.
220*/
221
222/*!
223  \var @WIDGET@ So@Gui@FullViewer::bottomWheel
224  The widget representing the thumbwheel below the render canvas.
225*/
226
227/*!
228  \var @WIDGET@ So@Gui@FullViewer::bottomWheelLabel
229  Label widget of bottom wheel.
230*/
231
232/*!
233  \var char * So@Gui@FullViewer::bottomWheelStr
234  Text of bottom thumbwheel's label.
235*/
236
237/*!
238  \var float So@Gui@FullViewer::bottomWheelVal
239  Current value of the bottom thumbwheel.
240*/
241
242/*!
243  \var @WIDGET@ So@Gui@FullViewer::rightDecoration
244  The root widget for the right-side decorations.
245*/
246
247/*!
248  \var @WIDGET@ So@Gui@FullViewer::rightWheel
249  The widget representing the thumbwheel on the right side of the render canvas.
250*/
251
252/*!
253  \var @WIDGET@ So@Gui@FullViewer::rightWheelLabel
254  Label widget of right wheel.
255*/
256
257/*!
258  \var char * So@Gui@FullViewer::rightWheelStr
259  Text of right thumbwheel's label.
260*/
261
262/*!
263  \var float So@Gui@FullViewer::rightWheelVal
264  Current value of the right-side thumbwheel.
265*/
266
267/*!
268  \var So@Gui@PopupMenu * So@Gui@FullViewer::prefmenu
269  Pointer to the popup menu instance.
270*/
271
272/*!
273  \fn So@Gui@FullViewer::So@Gui@FullViewer(@WIDGET@ parent, const char * name, SbBool embed, So@Gui@FullViewer::BuildFlag buildFlag, So@Gui@Viewer::Type type, SbBool build)
274
275  Constructor. See parent class for explanation of arguments.
276
277  Subclasses will probably usually call with the \a buildNow flag set
278  to \c FALSE to be able to do delayed building of the OpenGL canvas
279  after other setup tasks has been performed.
280*/
281
282/*!
283  \fn So@Gui@FullViewer::~So@Gui@FullViewer()
284
285  Destructor.
286*/
287
288/*!
289  \fn void So@Gui@FullViewer::setDecoration(const SbBool enable)
290
291  Turn the viewer decorations on or off.
292
293  \sa isDecoration()
294*/
295
296/*!
297  \fn SbBool So@Gui@FullViewer::isDecoration(void) const
298
299  Return \c TRUE if the viewer decorations are on, otherwise \c FALSE.
300
301  \sa setDecoration()
302*/
303
304/*!
305  \fn void So@Gui@FullViewer::setPopupMenuEnabled(const SbBool enable)
306
307  Decide whether or not if clicking with the right mouse button on
308  the OpenGL canvas should reveal a preferences popup menu when in
309  viewing mode.
310
311  \sa isPopupMenuEnabled()
312*/
313
314/*!
315  \fn SbBool So@Gui@FullViewer::isPopupMenuEnabled(void) const
316
317  Return \c TRUE if the popup preferences menu is enabled,
318  otherwise \c FALSE.
319
320  \sa setPopupMenuEnabled()
321*/
322
323/*!
324  \fn @WIDGET@ So@Gui@FullViewer::getAppPushButtonParent(void) const
325
326  Returns the widget which is used as the parent of application
327  specified buttons. The row of application buttons (if any) will be
328  placed in the upper left corner.
329
330  \sa addAppPushButton(), insertAppPushButton(), removeAppPushButton()
331*/
332
333/*!
334  \fn void So@Gui@FullViewer::addAppPushButton(@WIDGET@ newButton)
335
336  Add an application specific push button to the viewer decorations.
337  Application buttons will be laid out in a vertical row from the
338  upper left corner.
339
340  The button will be added bottom-most.
341
342  \sa insertAppPushButton(), removeAppPushButton(), getAppPushButtonParent()
343*/
344
345/*!
346  \fn void So@Gui@FullViewer::insertAppPushButton(@WIDGET@ newButton, int index)
347
348  Insert an application specific push button to the viewer decorations
349  at the given \c index.
350
351  \sa addAppPushButton(), removeAppPushButton(), getAppPushButtonParent()
352*/
353
354/*!
355  \fn void So@Gui@FullViewer::removeAppPushButton(@WIDGET@ oldButton)
356
357  Remove one of the application specific buttons.
358
359  \sa addAppPushButton(), insertAppPushButton()
360*/
361
362/*!
363  \fn int So@Gui@FullViewer::findAppPushButton(@WIDGET@ oldButton) const
364
365  Return the index of a particular button that has been specified by
366  the application, or -1 of the button has not been added.
367
368  \sa addAppPushButton()
369*/
370
371/*!
372  \fn int So@Gui@FullViewer::lengthAppPushButton(void) const
373
374  Return number of application specific buttons added.
375
376  \sa addAppPushButton(), insertAddAppPushButton()
377*/
378
379/*!
380  \fn @WIDGET@ So@Gui@FullViewer::getRenderAreaWidget(void) const
381
382  Returns the render area OpenGL canvas widget.
383*/
384
385/*!
386  \fn void So@Gui@FullViewer::buildDecoration(@WIDGET@ parent)
387
388  Build viewer decorations.
389*/
390
391/*!
392  \fn @WIDGET@ So@Gui@FullViewer::buildLeftTrim(@WIDGET@ parent)
393
394  Build decorations on the left of the render canvas.  Override this
395  method in subclasses if you want your own decorations on the viewer
396  window.
397
398  The decoration will be 30 pixels wide.
399*/
400
401/*!
402  \fn @WIDGET@ So@Gui@FullViewer::buildBottomTrim(@WIDGET@ parent)
403
404  Build decorations on the bottom of the render canvas. Override this
405  method in subclasses if you want your own decorations on the viewer window.
406*/
407
408/*!
409  \fn @WIDGET@ So@Gui@FullViewer::buildRightTrim(@WIDGET@ parent)
410
411  Build decorations on the right side of the render canvas. Override this
412  method in subclasses if you want your own decorations on the viewer window.
413*/
414
415/*!
416  \fn @WIDGET@ So@Gui@FullViewer::buildAppButtons(@WIDGET@ parent)
417
418  Build the application specified button row (if any buttons were
419  set up).
420*/
421
422/*!
423  \fn @WIDGET@ So@Gui@FullViewer::buildViewerButtons(@WIDGET@ parent)
424
425  Build and layout viewer specified button row.
426*/
427
428/*!
429  \fn void So@Gui@FullViewer::buildPopupMenu(void)
430
431  Make a popup menu with preferences settings.
432
433  One important note as for when you want to override this function in
434  a subclass: be aware that it will usually be invoked (indirectly)
435  from this class's constructor. That is \e before the table of
436  virtual function pointers has been set up for the subclass, and
437  because of this, the function will not be called in the subclass
438  even though it is virtual.
439
440  To make the overriding actually be in effect, you need to delay
441  building the widgets of this class. For information on how to do
442  that, see the documentation of
443  So@Gui@FullViewer::createViewerButtons().
444*/
445
446/*!
447  \fn void So@Gui@FullViewer::openPopupMenu(const SbVec2s position)
448
449  NOTE: This method is not part of the original InventorXt API.
450*/
451
452/*!
453  \fn void So@Gui@FullViewer::setLeftWheelString(const char * const string)
454
455  Set label of the left thumbwheel.
456*/
457
458/*!
459  \fn void So@Gui@FullViewer::setBottomWheelString(const char * const string)
460
461  Set label of the bottom thumbwheel.
462*/
463
464/*!
465  \fn void So@Gui@FullViewer::setRightWheelString(const char * const string)
466
467  Set label of the right thumbwheel.
468*/
469
470// ************************************************************************
471
472// Note: the following function documentation blocks for thumbwheel
473// handling will also be used for all the miscellaneous viewer
474// subclasses, so keep'em general.
475
476/*!
477  Called when the user start to drag the thumbwheel in the left
478  frame.  Override this method in subclassed viewers to provide your
479  own functionality on the thumbwheel.
480
481  \sa leftWheelMotion(), leftWheelFinish()
482  \sa bottomWheelStart(), rightWheelStart()
483*/
484void
485So@Gui@FullViewer::leftWheelStart(void)
486{
487  this->interactiveCountInc();
488}
489
490/*!
491  Called repeatedly as the user drags the thumbwheel in the left
492  frame.  Override this method in subclassed viewers to provide your
493  own functionality on the thumbwheel.
494
495  \sa leftWheelStart(), leftWheelFinish()
496  \sa bottomWheelStart(), rightWheelStart()
497*/
498void
499So@Gui@FullViewer::leftWheelMotion(float value)
500{
501  this->leftWheelVal = value;
502}
503
504/*!
505  Called as the user let go of the thumbwheel in the left frame
506  after a drag operation. Override this method in subclassed viewers
507  to provide your own functionality on the thumbwheel.
508
509  \sa leftWheelStart(), leftWheelMotion()
510  \sa bottomWheelStart(), rightWheelStart()
511*/
512void
513So@Gui@FullViewer::leftWheelFinish(void)
514{
515  this->interactiveCountDec();
516}
517
518/*!
519  Set a new value for the left thumbwheel.
520*/
521void
522So@Gui@FullViewer::setLeftWheelValue(const float value)
523{
524  this->leftWheelVal = value;
525  // Wheel may not be constructed yet, but we need to be robust, as
526  // set*WheelValue() is used from subclasses.
527  if (this->leftWheel) {
528    So@Gui@FullViewerP::setThumbWheelValue(this->leftWheel, value);
529  }
530}
531
532/*!
533  Get current value of the left thumbwheel.
534
535  \sa leftWheelMotion()
536*/
537float
538So@Gui@FullViewer::getLeftWheelValue(void) const
539{
540  return this->leftWheelVal;
541}
542
543/*!
544  Called when the user start to drag the thumbwheel in the bottom
545  frame.  Override this method in subclassed viewers to provide your
546  own functionality on the thumbwheel.
547
548  \sa bottomWheelMotion(), bottomWheelFinish()
549  \sa leftWheelStart(), rightWheelStart()
550*/
551void
552So@Gui@FullViewer::bottomWheelStart(void)
553{
554  this->interactiveCountInc();
555}
556
557/*!
558  Called repeatedly as the user drags the thumbwheel in the bottom
559  frame.  Override this method in subclassed viewers to provide your
560  own functionality on the thumbwheel.
561
562  \sa bottomWheelStart(), bottomWheelFinish()
563  \sa leftWheelStart(), rightWheelStart()
564*/
565void
566So@Gui@FullViewer::bottomWheelMotion(float value)
567{
568  this->bottomWheelVal = value;
569}
570
571/*!
572  Called as the user let go of the thumbwheel in the bottom frame
573  after a drag operation. Override this method in subclassed viewers
574  to provide your own functionality on the thumbwheel.
575
576  \sa bottomWheelStart(), bottomWheelMotion()
577  \sa leftWheelStart(), rightWheelStart()
578*/
579void
580So@Gui@FullViewer::bottomWheelFinish(void)
581{
582  this->interactiveCountDec();
583}
584
585/*!
586  Set a new value for the bottom thumbwheel.
587*/
588void
589So@Gui@FullViewer::setBottomWheelValue(const float value)
590{
591  this->bottomWheelVal = value;
592  // Wheel may not be constructed yet, but we need to be robust, as
593  // set*WheelValue() is used from subclasses.
594  if (this->bottomWheel) {
595    So@Gui@FullViewerP::setThumbWheelValue(this->bottomWheel, value);
596  }
597}
598
599/*!
600  Get current value of the bottom thumbwheel.
601
602  \sa bottomWheelMotion()
603*/
604float
605So@Gui@FullViewer::getBottomWheelValue(void) const
606{
607  return this->bottomWheelVal;
608}
609
610/*!
611  Called when the user start to drag the thumbwheel in the right
612  frame.  Override this method in subclassed viewers to provide your
613  own functionality on the thumbwheel.
614
615  \sa rightWheelMotion(), rightWheelFinish()
616  \sa leftWheelStart(), bottomWheelStart()
617*/
618void
619So@Gui@FullViewer::rightWheelStart(void)
620{
621  this->interactiveCountInc();
622}
623
624/*!
625  Called repeatedly as the user drags the thumbwheel in the right
626  frame.  Override this method in subclassed viewers to provide your
627  own functionality on the thumbwheel.
628
629  \sa rightWheelStart(), rightWheelFinish()
630  \sa leftWheelStart(), bottomWheelStart()
631*/
632void
633So@Gui@FullViewer::rightWheelMotion(float value)
634{
635  this->rightWheelVal = value;
636}
637
638/*!
639  Called as the user let go of the thumbwheel in the right frame
640  after a drag operation. Override this method in subclassed viewers
641  to provide your own functionality on the thumbwheel.
642
643  \sa rightWheelStart(), rightWheelMotion()
644  \sa leftWheelStart(), bottomWheelStart()
645*/
646void
647So@Gui@FullViewer::rightWheelFinish(void)
648{
649  this->interactiveCountDec();
650}
651
652/*!
653  Set a new value for the right thumbwheel.
654*/
655void
656So@Gui@FullViewer::setRightWheelValue(const float value)
657{
658  this->rightWheelVal = value;
659  // Wheel may not be constructed yet, but we need to be robust, as
660  // set*WheelValue() is used from subclasses.
661  if (this->rightWheel) {
662    So@Gui@FullViewerP::setThumbWheelValue(this->rightWheel, value);
663  }
664}
665
666/*!
667  Get current value of the right thumbwheel.
668
669  \sa rightWheelMotion()
670*/
671float
672So@Gui@FullViewer::getRightWheelValue(void) const
673{
674  return this->rightWheelVal;
675}
676
677// *************************************************************************
678
679/*!
680  This method returns the native widget for the label below the left
681  thumb wheel.
682*/
683
684@WIDGET@
685So@Gui@FullViewer::getLeftWheelLabelWidget(void) const
686{
687  return this->leftWheelLabel;
688}
689
690/*!
691  This method returns the native widget for the label below the right thumb
692  wheel.
693*/
694@WIDGET@
695So@Gui@FullViewer::getRightWheelLabelWidget(void) const
696{
697  return this->rightWheelLabel;
698}
699
700/*!
701  This method returns the native widget for the label beside the
702  bottom thumb wheel.
703*/
704@WIDGET@
705So@Gui@FullViewer::getBottomWheelLabelWidget(void) const
706{
707  return this->bottomWheelLabel;
708}
709
710// ************************************************************************
711
712/*!
713  Set title of popup menu.
714*/
715void
716So@Gui@FullViewer::setPopupMenuString(const char * name)
717{
718  PRIVATE(this)->popupmenutitle = name;
719  if (this->prefmenu) { this->prefmenu->setMenuTitle(SoGuiFullViewerP::ROOT_MENU, name); }
720}
721
722
723// Documented in superclass.
724SbBool
725So@Gui@FullViewer::processSoEvent(const SoEvent * const ev)
726{
727  // We're in "interact" mode (ie *not* the camera modification mode),
728  // so don't handle the event here. It should either be forwarded to
729  // the scenegraph, or caught by So@Gui@Viewer::processSoEvent() if
730  // it's an ESC press (to switch modes).
731  if (!this->isViewing()) { return inherited::processSoEvent(ev); }
732
733  // Note: with the original SGI InventorXt, the popup menu is also
734  // activated by RMB clicks in non-viewing mode. We want to allow RMB
735  // clicks to pass through to the scenegraph in non-viewing mode, so
736  // we don't copy that behavior.
737
738  if (ev->getTypeId().isDerivedFrom(SoMouseButtonEvent::getClassTypeId())) {
739    SoMouseButtonEvent * const e = (SoMouseButtonEvent *) ev;
740    if ((e->getButton() == SoMouseButtonEvent::BUTTON2)) {
741      if (this->isPopupMenuEnabled()) {
742        if (e->getState() == SoButtonEvent::DOWN) {
743          this->openPopupMenu(e->getPosition());
744        }
745        // Steal all RMB-events if the viewer uses the popup-menu.
746        return TRUE;
747      }
748    }
749  }
750
751  return inherited::processSoEvent(ev);
752}
753
754// *************************************************************************
755
756// FIXME: get rid of this hack and make this file truly
757// "generic". 20020109 mortene.
758#ifndef __COIN_SOWIN__
759
760// *************************************************************************
761
762// Doc in superclass.
763void
764So@Gui@FullViewer::setComponentCursor(const So@Gui@Cursor & cursor)
765{
766  // Overridden to apply the new cursor only for the rendering canvas
767  // widget. Otherwise, the default So@Gui@Component
768  // setComponentCursor() method will set the cursor for the top-most
769  // parent widget, which makes it affect all sub-widgets, like the
770  // decorations stuff.
771
772  So@Gui@Component::setWidgetCursor(this->getGLWidget(), cursor);
773}
774
775// *************************************************************************
776
777#endif // ! __COIN_SOWIN__
778
779// ************************************************************************
780
781#ifndef DOXYGEN_SKIP_THIS
782
783SoGuiFullViewerP::SoGuiFullViewerP(So@Gui@FullViewer * owner)
784{
785  this->pub = owner;
786}
787
788SoGuiFullViewerP::~SoGuiFullViewerP()
789{
790}
791
792So@Gui@PopupMenu *
793SoGuiFullViewerP::setupStandardPopupMenu(void)
794{
795  So@Gui@PopupMenu * menu = So@Gui@PopupMenu::createInstance();
796
797  menu->newMenu("rootmenu", ROOT_MENU);
798  // FIXME: none of the So*-toolkits so far uses the rootmenu title to
799  // set up a title string at the top of the root popupmenu. 20010810 mortene.
800  menu->setMenuTitle(ROOT_MENU, this->popupmenutitle.getString());
801
802#if 0 // OBSOLETED: this looks ugly and confusing for the user. 20010810 mortene.
803  // Simple hack for a common / generic way of setting up a title for
804  // the popupmenu.
805  menu->newMenuItem("rootmenutitle", ROOT_MENU_TITLE);
806  menu->setMenuItemTitle(ROOT_MENU_TITLE, this->popupmenutitle.getString());
807  menu->addMenuItem(ROOT_MENU, ROOT_MENU_TITLE);
808  menu->setMenuItemEnabled(ROOT_MENU_TITLE, FALSE);
809  menu->addSeparator(ROOT_MENU);
810#endif // OBSOLETED
811
812  menu->newMenu("functionsmenu", FUNCTIONS_MENU);
813  menu->setMenuTitle(FUNCTIONS_MENU, _("Functions"));
814  menu->newMenu("drawstylesmenu", DRAWSTYLES_MENU);
815  menu->setMenuTitle(DRAWSTYLES_MENU, _("Draw Styles"));
816  menu->newMenu("stereomenu", STEREO_MENU);
817  menu->setMenuTitle(STEREO_MENU, _("Stereo Viewing"));
818  menu->newMenuItem("examining", EXAMINING_ITEM);
819  menu->setMenuItemTitle(EXAMINING_ITEM, _("Viewing"));
820  menu->newMenuItem("decoration", DECORATION_ITEM);
821  menu->setMenuItemTitle(DECORATION_ITEM, _("Decorations"));
822  menu->newMenuItem("headlight", HEADLIGHT_ITEM);
823  menu->setMenuItemTitle(HEADLIGHT_ITEM, _("Headlight"));
824  menu->newMenuItem("fullscreen", FULLSCREEN_ITEM);
825  menu->setMenuItemTitle(FULLSCREEN_ITEM, _("Fullscreen"));
826
827  menu->addMenu(ROOT_MENU, FUNCTIONS_MENU);
828  menu->addMenu(ROOT_MENU, DRAWSTYLES_MENU);
829  menu->addMenu(ROOT_MENU, STEREO_MENU);
830  menu->addMenuItem(ROOT_MENU, EXAMINING_ITEM);
831  menu->addMenuItem(ROOT_MENU, DECORATION_ITEM);
832  menu->addMenuItem(ROOT_MENU, HEADLIGHT_ITEM);
833  menu->addMenuItem(ROOT_MENU, FULLSCREEN_ITEM);
834
835  menu->newMenuItem("home", HOME_ITEM);
836  menu->setMenuItemTitle(HOME_ITEM, _("Home"));
837  menu->newMenuItem("set_home", SET_HOME_ITEM);
838  menu->setMenuItemTitle(SET_HOME_ITEM, _("Set Home"));
839  menu->newMenuItem("view_all", VIEW_ALL_ITEM);
840  menu->setMenuItemTitle(VIEW_ALL_ITEM, _("View All"));
841  menu->newMenuItem("seek", SEEK_ITEM);
842  menu->setMenuItemTitle(SEEK_ITEM, _("Seek"));
843
844  menu->addMenuItem(FUNCTIONS_MENU, HOME_ITEM);
845  menu->addMenuItem(FUNCTIONS_MENU, SET_HOME_ITEM);
846  menu->addMenuItem(FUNCTIONS_MENU, VIEW_ALL_ITEM);
847  menu->addMenuItem(FUNCTIONS_MENU, SEEK_ITEM);
848
849  menu->newMenu("drawstylesstill", DRAWSTYLES_STILL_MENU);
850  menu->setMenuTitle(DRAWSTYLES_STILL_MENU, _("Still Drawstyle"));
851
852  menu->newMenuItem("as_is", AS_IS_ITEM);
853  menu->setMenuItemTitle(AS_IS_ITEM, _("as is"));
854  menu->newMenuItem("hidden_line", HIDDEN_LINE_ITEM);
855  menu->setMenuItemTitle(HIDDEN_LINE_ITEM, _("hidden line"));
856  menu->newMenuItem("wireframe_overlay", WIREFRAME_OVERLAY_ITEM);
857  menu->setMenuItemTitle(WIREFRAME_OVERLAY_ITEM, _("wireframe overlay"));
858  menu->newMenuItem("no_textures", NO_TEXTURE_ITEM);
859  menu->setMenuItemTitle(NO_TEXTURE_ITEM, _("no texture"));
860  menu->newMenuItem("low_resolution", LOW_RESOLUTION_ITEM);
861  menu->setMenuItemTitle(LOW_RESOLUTION_ITEM, _("low resolution"));
862  menu->newMenuItem("wireframe", WIREFRAME_ITEM);
863  menu->setMenuItemTitle(WIREFRAME_ITEM, _("wireframe"));
864  menu->newMenuItem("points", POINTS_ITEM);
865  menu->setMenuItemTitle(POINTS_ITEM, _("points"));
866  menu->newMenuItem("bounding_box", BOUNDING_BOX_ITEM);
867  menu->setMenuItemTitle(BOUNDING_BOX_ITEM, _("bounding box (no depth)"));
868
869  menu->newRadioGroup(STILL_GROUP);
870  menu->addRadioGroupItem(STILL_GROUP, AS_IS_ITEM);
871  menu->addRadioGroupItem(STILL_GROUP, HIDDEN_LINE_ITEM);
872  menu->addRadioGroupItem(STILL_GROUP, WIREFRAME_OVERLAY_ITEM);
873  menu->addRadioGroupItem(STILL_GROUP, NO_TEXTURE_ITEM);
874  menu->addRadioGroupItem(STILL_GROUP, LOW_RESOLUTION_ITEM);
875  menu->addRadioGroupItem(STILL_GROUP, WIREFRAME_ITEM);
876  menu->addRadioGroupItem(STILL_GROUP, POINTS_ITEM);
877  menu->addRadioGroupItem(STILL_GROUP, BOUNDING_BOX_ITEM);
878
879  menu->addMenuItem(DRAWSTYLES_STILL_MENU, AS_IS_ITEM);
880  menu->addMenuItem(DRAWSTYLES_STILL_MENU, HIDDEN_LINE_ITEM);
881  menu->addMenuItem(DRAWSTYLES_STILL_MENU, WIREFRAME_OVERLAY_ITEM);
882  menu->addMenuItem(DRAWSTYLES_STILL_MENU, NO_TEXTURE_ITEM);
883  menu->addMenuItem(DRAWSTYLES_STILL_MENU, LOW_RESOLUTION_ITEM);
884  menu->addMenuItem(DRAWSTYLES_STILL_MENU, WIREFRAME_ITEM);
885  menu->addMenuItem(DRAWSTYLES_STILL_MENU, POINTS_ITEM);
886  menu->addMenuItem(DRAWSTYLES_STILL_MENU, BOUNDING_BOX_ITEM);
887
888  menu->newMenu("drawstylesanimation", DRAWSTYLES_ANIMATING_MENU);
889  menu->setMenuTitle(DRAWSTYLES_ANIMATING_MENU, _("Animating Drawstyle"));
890
891  menu->newMenuItem("move_same_as_still", MOVE_SAME_AS_STILL_ITEM);
892  menu->setMenuItemTitle(MOVE_SAME_AS_STILL_ITEM, _("same as still"));
893  menu->newMenuItem("move_no_texture", MOVE_NO_TEXTURE_ITEM);
894  menu->setMenuItemTitle(MOVE_NO_TEXTURE_ITEM, _("no texture"));
895  menu->newMenuItem("move_low_res", MOVE_LOW_RES_ITEM);
896  menu->setMenuItemTitle(MOVE_LOW_RES_ITEM, _("low resolution"));
897  menu->newMenuItem("move_wireframe", MOVE_WIREFRAME_ITEM);
898  menu->setMenuItemTitle(MOVE_WIREFRAME_ITEM, _("wireframe"));
899  menu->newMenuItem("move_low_res_wireframe", MOVE_LOW_RES_WIREFRAME_ITEM);
900  menu->setMenuItemTitle(MOVE_LOW_RES_WIREFRAME_ITEM,
901    _("low res wireframe (no depth)"));
902  menu->newMenuItem("move_points", MOVE_POINTS_ITEM);
903  menu->setMenuItemTitle(MOVE_POINTS_ITEM, _("points"));
904  menu->newMenuItem("move_low_res_points", MOVE_LOW_RES_POINTS_ITEM);
905  menu->setMenuItemTitle(MOVE_LOW_RES_POINTS_ITEM,
906    _("low res points (no depth)"));
907  menu->newMenuItem("move_bounding_box", MOVE_BOUNDING_BOX_ITEM);
908  menu->setMenuItemTitle(MOVE_BOUNDING_BOX_ITEM, _("bounding box (no depth)"));
909
910  menu->newRadioGroup(MOVE_GROUP);
911  menu->addRadioGroupItem(MOVE_GROUP, MOVE_SAME_AS_STILL_ITEM);
912  menu->addRadioGroupItem(MOVE_GROUP, MOVE_NO_TEXTURE_ITEM);
913  menu->addRadioGroupItem(MOVE_GROUP, MOVE_LOW_RES_ITEM);
914  menu->addRadioGroupItem(MOVE_GROUP, MOVE_WIREFRAME_ITEM);
915  menu->addRadioGroupItem(MOVE_GROUP, MOVE_LOW_RES_WIREFRAME_ITEM);
916  menu->addRadioGroupItem(MOVE_GROUP, MOVE_POINTS_ITEM);
917  menu->addRadioGroupItem(MOVE_GROUP, MOVE_LOW_RES_POINTS_ITEM);
918  menu->addRadioGroupItem(MOVE_GROUP, MOVE_BOUNDING_BOX_ITEM);
919
920  menu->addMenuItem(DRAWSTYLES_ANIMATING_MENU, MOVE_SAME_AS_STILL_ITEM);
921  menu->addMenuItem(DRAWSTYLES_ANIMATING_MENU, MOVE_NO_TEXTURE_ITEM);
922  menu->addMenuItem(DRAWSTYLES_ANIMATING_MENU, MOVE_LOW_RES_ITEM);
923  menu->addMenuItem(DRAWSTYLES_ANIMATING_MENU, MOVE_WIREFRAME_ITEM);
924  menu->addMenuItem(DRAWSTYLES_ANIMATING_MENU, MOVE_LOW_RES_WIREFRAME_ITEM);
925  menu->addMenuItem(DRAWSTYLES_ANIMATING_MENU, MOVE_POINTS_ITEM);
926  menu->addMenuItem(DRAWSTYLES_ANIMATING_MENU, MOVE_LOW_RES_POINTS_ITEM);
927  menu->addMenuItem(DRAWSTYLES_ANIMATING_MENU, MOVE_BOUNDING_BOX_ITEM);
928
929  menu->newMenu("transparencytype", TRANSPARENCY_TYPE_MENU);
930  menu->setMenuTitle(TRANSPARENCY_TYPE_MENU, _("Transparency Type"));
931  menu->newRadioGroup(TRANSPARENCY_GROUP);
932
933  { // Fill in menu items in radiomenu with transparency selections.
934
935    struct menutransparencyitem {
936      const char * name, * title;
937      int id;
938    };
939
940    struct menutransparencyitem items[] = {
941#ifdef __COIN__
942      { "none", _("none"), NONE_TRANSPARENCY_ITEM },
943#endif // __COIN__
944      { "screen_door", _("screen door"), SCREEN_DOOR_TRANSPARENCY_ITEM },
945      { "add", _("add"), ADD_TRANSPARENCY_ITEM },
946      { "delayed_add", _("delayed add"), DELAYED_ADD_TRANSPARENCY_ITEM },
947      { "sorted_object_add", _("sorted object add"), SORTED_OBJECT_ADD_TRANSPARENCY_ITEM },
948      { "blend", _("blend"), BLEND_TRANSPARENCY_ITEM },
949      { "delayed_blend", _("delayed blend"), DELAYED_BLEND_TRANSPARENCY_ITEM },
950      { "sorted_object_blend", _("sorted object blend"), SORTED_OBJECT_BLEND_TRANSPARENCY_ITEM }
951#ifdef __COIN__ // Coin extensions
952      ,
953      { "sorted_object_sorted_triangle_add", _("sorted object sorted triangle add"), SORTED_OBJECT_SORTED_TRIANGLE_ADD_TRANSPARENCY_ITEM },
954      { "sorted_object_sorted_triangle_blend", _("sorted object sorted triangle blend"), SORTED_OBJECT_SORTED_TRIANGLE_BLEND_TRANSPARENCY_ITEM }
955#endif // __COIN__
956#ifdef HAVE_SORTED_LAYERS_BLEND
957      , { "sorted_layers_blend", _("sorted layers blend"), SORTED_LAYERS_BLEND_ITEM }
958#endif // HAVE_SORTED_LAYERS_BLEND
959    };
960
961    int nritems = sizeof(items) / sizeof(struct menutransparencyitem);
962    for (int i = 0; i < nritems; i++) {
963      menu->newMenuItem(items[i].name, items[i].id);
964      menu->setMenuItemTitle(items[i].id, items[i].title);
965      menu->addRadioGroupItem(TRANSPARENCY_GROUP, items[i].id);
966      menu->addMenuItem(TRANSPARENCY_TYPE_MENU, items[i].id);
967    }
968  }
969
970  menu->newMenuItem("stereooff", STEREO_OFF_ITEM);
971  menu->newMenuItem("stereoredcyan", STEREO_ANAGLYPH_ITEM);
972  menu->newMenuItem("stereoquadbuffer", STEREO_QUADBUFFER_ITEM);
973  menu->setMenuItemTitle(STEREO_OFF_ITEM, _("Off"));
974  menu->setMenuItemTitle(STEREO_ANAGLYPH_ITEM, _("Red/Cyan Anaglyph"));
975  menu->setMenuItemTitle(STEREO_QUADBUFFER_ITEM, _("Quad buffer"));
976
977  menu->newMenuItem("stereointerleavedrows", STEREO_INTERLEAVED_ROWS_ITEM);
978  menu->setMenuItemTitle(STEREO_INTERLEAVED_ROWS_ITEM, _("Interleaved Rows"));
979  menu->newMenuItem("stereointerleavedcolumns", STEREO_INTERLEAVED_COLUMNS_ITEM);
980  menu->setMenuItemTitle(STEREO_INTERLEAVED_COLUMNS_ITEM, _("Interleaved Columns"));
981
982  menu->newRadioGroup(STEREO_GROUP);
983  menu->addRadioGroupItem(STEREO_GROUP, STEREO_OFF_ITEM);
984  menu->addRadioGroupItem(STEREO_GROUP, STEREO_ANAGLYPH_ITEM);
985  menu->addRadioGroupItem(STEREO_GROUP, STEREO_QUADBUFFER_ITEM);
986  menu->addRadioGroupItem(STEREO_GROUP, STEREO_INTERLEAVED_ROWS_ITEM);
987  menu->addRadioGroupItem(STEREO_GROUP, STEREO_INTERLEAVED_COLUMNS_ITEM);
988
989  menu->addMenuItem(STEREO_MENU, STEREO_OFF_ITEM);
990  menu->addMenuItem(STEREO_MENU, STEREO_ANAGLYPH_ITEM);
991  menu->addMenuItem(STEREO_MENU, STEREO_QUADBUFFER_ITEM);
992  menu->addMenuItem(STEREO_MENU, STEREO_INTERLEAVED_ROWS_ITEM);
993  menu->addMenuItem(STEREO_MENU, STEREO_INTERLEAVED_COLUMNS_ITEM);
994
995  menu->newMenu("renderbuffertype", RENDER_BUFFER_TYPE_MENU);
996  menu->setMenuTitle(RENDER_BUFFER_TYPE_MENU, _("Render Buffer Type"));
997
998  menu->newMenuItem("single_buffer", SINGLE_BUFFER_ITEM);
999  menu->setMenuItemTitle(SINGLE_BUFFER_ITEM, _("single"));
1000  menu->newMenuItem("double_buffer", DOUBLE_BUFFER_ITEM);
1001  menu->setMenuItemTitle(DOUBLE_BUFFER_ITEM, _("double"));
1002  menu->newMenuItem("interactive_buffer", INTERACTIVE_BUFFER_ITEM);
1003  menu->setMenuItemTitle(INTERACTIVE_BUFFER_ITEM, _("interactive"));
1004
1005  menu->newRadioGroup(BUFFER_GROUP);
1006  menu->addRadioGroupItem(BUFFER_GROUP, SINGLE_BUFFER_ITEM);
1007  menu->addRadioGroupItem(BUFFER_GROUP, DOUBLE_BUFFER_ITEM);
1008  menu->addRadioGroupItem(BUFFER_GROUP, INTERACTIVE_BUFFER_ITEM);
1009
1010  menu->addMenuItem(RENDER_BUFFER_TYPE_MENU, SINGLE_BUFFER_ITEM);
1011  menu->addMenuItem(RENDER_BUFFER_TYPE_MENU, DOUBLE_BUFFER_ITEM);
1012  menu->addMenuItem(RENDER_BUFFER_TYPE_MENU, INTERACTIVE_BUFFER_ITEM);
1013
1014  menu->addMenu(DRAWSTYLES_MENU, DRAWSTYLES_STILL_MENU);
1015  menu->addMenu(DRAWSTYLES_MENU, DRAWSTYLES_ANIMATING_MENU);
1016  menu->addMenu(DRAWSTYLES_MENU, TRANSPARENCY_TYPE_MENU);
1017  menu->addMenu(DRAWSTYLES_MENU, RENDER_BUFFER_TYPE_MENU);
1018
1019  int toggle;
1020  toggle = menu->newRadioGroup();
1021  menu->addRadioGroupItem(toggle, EXAMINING_ITEM);
1022  toggle = menu->newRadioGroup();
1023  menu->addRadioGroupItem(toggle, DECORATION_ITEM);
1024  toggle = menu->newRadioGroup();
1025  menu->addRadioGroupItem(toggle, HEADLIGHT_ITEM);
1026  toggle = menu->newRadioGroup();
1027  menu->addRadioGroupItem(toggle, FULLSCREEN_ITEM);
1028
1029  menu->addMenuSelectionCallback(SoGuiFullViewerP::menuSelectionCallback,
1030                                 (void *) this);
1031
1032  // this instance is freed in the native viewer component
1033  PUBLIC(this)->prefmenu = menu;
1034  return menu;
1035}
1036
1037// ************************************************************************
1038
1039void
1040SoGuiFullViewerP::prepareMenu(So@Gui@PopupMenu * menu)
1041{
1042  //// Misc. //////////////////////////////////////////////////////////
1043  menu->setMenuItemMarked(DECORATION_ITEM, PUBLIC(this)->isDecoration());
1044  menu->setMenuItemMarked(EXAMINING_ITEM, PUBLIC(this)->isViewing());
1045  menu->setMenuItemMarked(HEADLIGHT_ITEM, PUBLIC(this)->isHeadlight());
1046  menu->setMenuItemMarked(FULLSCREEN_ITEM, PUBLIC(this)->isFullScreen());
1047
1048  //// Basic drawstyles. //////////////////////////////////////////////
1049  this->setDrawStyleMenuActivation(So@Gui@FullViewer::STILL,
1050                                   PUBLIC(this)->getDrawStyle(So@Gui@FullViewer::STILL));
1051  this->setDrawStyleMenuActivation(So@Gui@FullViewer::INTERACTIVE,
1052                                   PUBLIC(this)->getDrawStyle(So@Gui@FullViewer::INTERACTIVE));
1053
1054  //// Transparency setting. //////////////////////////////////////////
1055  switch (PUBLIC(this)->getTransparencyType()) {
1056  case SoGLRenderAction::SCREEN_DOOR:
1057    menu->setMenuItemMarked(SCREEN_DOOR_TRANSPARENCY_ITEM, TRUE);
1058    break;
1059  case SoGLRenderAction::ADD:
1060    menu->setMenuItemMarked(ADD_TRANSPARENCY_ITEM, TRUE);
1061    break;
1062  case SoGLRenderAction::DELAYED_ADD:
1063    menu->setMenuItemMarked(DELAYED_ADD_TRANSPARENCY_ITEM, TRUE);
1064    break;
1065  case SoGLRenderAction::SORTED_OBJECT_ADD:
1066    menu->setMenuItemMarked(SORTED_OBJECT_ADD_TRANSPARENCY_ITEM, TRUE);
1067    break;
1068  case SoGLRenderAction::BLEND:
1069    menu->setMenuItemMarked(BLEND_TRANSPARENCY_ITEM, TRUE);
1070    break;
1071  case SoGLRenderAction::DELAYED_BLEND:
1072    menu->setMenuItemMarked(DELAYED_BLEND_TRANSPARENCY_ITEM, TRUE);
1073    break;
1074  case SoGLRenderAction::SORTED_OBJECT_BLEND:
1075    menu->setMenuItemMarked(SORTED_OBJECT_BLEND_TRANSPARENCY_ITEM, TRUE);
1076    break;
1077#ifdef __COIN__  // Coin extensions
1078  case SoGLRenderAction::NONE:
1079    menu->setMenuItemMarked(NONE_TRANSPARENCY_ITEM, TRUE);
1080    break;
1081  case SoGLRenderAction::SORTED_OBJECT_SORTED_TRIANGLE_ADD:
1082    menu->setMenuItemMarked(SORTED_OBJECT_SORTED_TRIANGLE_ADD_TRANSPARENCY_ITEM, TRUE);
1083    break;
1084  case SoGLRenderAction::SORTED_OBJECT_SORTED_TRIANGLE_BLEND:
1085    menu->setMenuItemMarked(SORTED_OBJECT_SORTED_TRIANGLE_BLEND_TRANSPARENCY_ITEM, TRUE);
1086    break;
1087#endif // __COIN__
1088#ifdef HAVE_SORTED_LAYERS_BLEND
1089  case SoGLRenderAction::SORTED_LAYERS_BLEND:
1090    menu->setMenuItemMarked(SORTED_LAYERS_BLEND_ITEM, TRUE);
1091    break;
1092#endif // HAVE_SORTED_LAYERS_BLEND
1093  default:
1094#if SO@GUI@_DEBUG
1095    SoDebugError::postInfo("SoGuiFullViewerP::prepareMenu",
1096                           "Unknown transparency type");
1097#endif // SO@GUI@_DEBUG
1098    break;
1099  }
1100
1101  //// Stereo viewing. ////////////////////////////////////////////////
1102
1103  const So@Gui@Viewer::StereoType type = PUBLIC(this)->getStereoType();
1104  switch (type) {
1105  case So@Gui@Viewer::STEREO_NONE:
1106    menu->setMenuItemMarked(STEREO_OFF_ITEM, TRUE);
1107    break;
1108  case So@Gui@Viewer::STEREO_ANAGLYPH:
1109    menu->setMenuItemMarked(STEREO_ANAGLYPH_ITEM, TRUE);
1110    break;
1111  case So@Gui@Viewer::STEREO_QUADBUFFER:
1112    menu->setMenuItemMarked(STEREO_QUADBUFFER_ITEM, TRUE);
1113    break;
1114  case So@Gui@Viewer::STEREO_INTERLEAVED_ROWS:
1115    menu->setMenuItemMarked(STEREO_INTERLEAVED_ROWS_ITEM, TRUE);
1116    break;
1117  case So@Gui@Viewer::STEREO_INTERLEAVED_COLUMNS:
1118    menu->setMenuItemMarked(STEREO_INTERLEAVED_COLUMNS_ITEM, TRUE);
1119    break;
1120  default:
1121    assert(FALSE); break;
1122  }
1123
1124  //// GL canvas settings. ////////////////////////////////////////////
1125  switch (PUBLIC(this)->getBufferingType()) {
1126  case So@Gui@Viewer::BUFFER_SINGLE:
1127    menu->setMenuItemMarked(SINGLE_BUFFER_ITEM, TRUE);
1128    break;
1129  case So@Gui@Viewer::BUFFER_DOUBLE:
1130    menu->setMenuItemMarked(DOUBLE_BUFFER_ITEM, TRUE);
1131    break;
1132  case So@Gui@Viewer::BUFFER_INTERACTIVE:
1133    menu->setMenuItemMarked(INTERACTIVE_BUFFER_ITEM, TRUE);
1134    break;
1135  default:
1136#if SO@GUI@_DEBUG
1137    SoDebugError::postInfo("SoGuiFullViewerP::prepareMenu",
1138                           "Unknown buffer type");
1139#endif // SO@GUI@_DEBUG
1140    break;
1141  }
1142}
1143
1144// ************************************************************************
1145
1146void
1147SoGuiFullViewerP::menuSelection(int menuitemid)
1148{
1149  SbBool ok;
1150
1151  switch (menuitemid) {
1152  case -1:
1153    // means no item was selected
1154#if SO@GUI@_DEBUG
1155    SoDebugError::postInfo("SoGuiFullViewerP::menuSelection",
1156                           "-1 not appropriate on callback usage");
1157#endif // SO@GUI@_DEBUG
1158    break;
1159
1160  case EXAMINING_ITEM:
1161    PUBLIC(this)->setViewing(PUBLIC(this)->isViewing() ? FALSE : TRUE);
1162    break;
1163  case DECORATION_ITEM:
1164    PUBLIC(this)->setDecoration(PUBLIC(this)->isDecoration() ? FALSE : TRUE);
1165    break;
1166  case HEADLIGHT_ITEM:
1167    PUBLIC(this)->setHeadlight(PUBLIC(this)->isHeadlight() ? FALSE : TRUE);
1168    break;
1169  case FULLSCREEN_ITEM:
1170    (void)PUBLIC(this)->setFullScreen(PUBLIC(this)->isFullScreen() ? FALSE : TRUE);
1171    break;
1172
1173  case HOME_ITEM:
1174    PUBLIC(this)->resetToHomePosition();
1175    break;
1176  case SET_HOME_ITEM:
1177    PUBLIC(this)->saveHomePosition();
1178    break;
1179  case VIEW_ALL_ITEM:
1180    PUBLIC(this)->viewAll();
1181    break;
1182  case SEEK_ITEM:
1183    ((So@Gui@FullViewerP *)this)->seekbuttonClicked();
1184    break;
1185
1186  case AS_IS_ITEM:
1187  case HIDDEN_LINE_ITEM:
1188  case WIREFRAME_OVERLAY_ITEM:
1189  case NO_TEXTURE_ITEM:
1190  case LOW_RESOLUTION_ITEM:
1191  case WIREFRAME_ITEM:
1192  case POINTS_ITEM:
1193  case BOUNDING_BOX_ITEM:
1194    this->drawstyleActivated(menuitemid);
1195    break;
1196
1197  case MOVE_SAME_AS_STILL_ITEM:
1198  case MOVE_NO_TEXTURE_ITEM:
1199  case MOVE_LOW_RES_ITEM:
1200  case MOVE_WIREFRAME_ITEM:
1201  case MOVE_LOW_RES_WIREFRAME_ITEM:
1202  case MOVE_POINTS_ITEM:
1203  case MOVE_LOW_RES_POINTS_ITEM:
1204  case MOVE_BOUNDING_BOX_ITEM:
1205    this->drawstyleActivated(menuitemid);
1206    break;
1207
1208  case SCREEN_DOOR_TRANSPARENCY_ITEM:
1209  case ADD_TRANSPARENCY_ITEM:
1210  case DELAYED_ADD_TRANSPARENCY_ITEM:
1211  case SORTED_OBJECT_ADD_TRANSPARENCY_ITEM:
1212  case BLEND_TRANSPARENCY_ITEM:
1213  case DELAYED_BLEND_TRANSPARENCY_ITEM:
1214  case SORTED_OBJECT_BLEND_TRANSPARENCY_ITEM:
1215  case SORTED_OBJECT_SORTED_TRIANGLE_ADD_TRANSPARENCY_ITEM:
1216  case SORTED_OBJECT_SORTED_TRIANGLE_BLEND_TRANSPARENCY_ITEM:
1217  case NONE_TRANSPARENCY_ITEM:
1218  case SORTED_LAYERS_BLEND_ITEM: // define test is not needed. This enum is always defined
1219    this->drawstyleActivated(menuitemid);
1220    PUBLIC(this)->scheduleRedraw();
1221    break;
1222
1223  case SINGLE_BUFFER_ITEM:
1224  case DOUBLE_BUFFER_ITEM:
1225  case INTERACTIVE_BUFFER_ITEM:
1226    this->drawstyleActivated(menuitemid);
1227    break;
1228
1229  case STEREO_OFF_ITEM:
1230    ok = PUBLIC(this)->setStereoType(So@Gui@Viewer::STEREO_NONE);
1231    assert(ok);
1232    break;
1233  case STEREO_ANAGLYPH_ITEM:
1234    ok = PUBLIC(this)->setStereoType(So@Gui@Viewer::STEREO_ANAGLYPH);
1235    assert(ok && "anaglyph stereo should always be possible");
1236    break;
1237  case STEREO_QUADBUFFER_ITEM:
1238    ok = PUBLIC(this)->setStereoType(So@Gui@Viewer::STEREO_QUADBUFFER);
1239    if (!ok) { PUBLIC(this)->setStereoType(So@Gui@Viewer::STEREO_NONE); }
1240    break;
1241  case STEREO_INTERLEAVED_ROWS_ITEM:
1242    ok = PUBLIC(this)->setStereoType(So@Gui@Viewer::STEREO_INTERLEAVED_ROWS);
1243    if (!ok) { PUBLIC(this)->setStereoType(So@Gui@Viewer::STEREO_NONE); }
1244    break;
1245  case STEREO_INTERLEAVED_COLUMNS_ITEM:
1246    ok = PUBLIC(this)->setStereoType(So@Gui@Viewer::STEREO_INTERLEAVED_COLUMNS);
1247    if (!ok) { PUBLIC(this)->setStereoType(So@Gui@Viewer::STEREO_NONE); }
1248    break;
1249
1250  default:
1251    SoDebugError::postInfo("SoGuiFullViewerP::menuSelection",
1252                           "popup menu handling for item %d is not implemented",
1253                           menuitemid);
1254    break;
1255  }
1256}
1257
1258
1259// ************************************************************************
1260
1261void
1262SoGuiFullViewerP::menuSelectionCallback(int menuitemid,
1263                                        void * userdata)
1264{
1265  SoGuiFullViewerP * viewer = (SoGuiFullViewerP *) userdata;
1266  viewer->menuSelection(menuitemid);
1267}
1268
1269// ************************************************************************
1270
1271void
1272SoGuiFullViewerP::setDrawStyleMenuActivation(So@Gui@Viewer::DrawType type,
1273                                             So@Gui@Viewer::DrawStyle value)
1274{
1275  assert(PUBLIC(this)->prefmenu != NULL);
1276
1277  switch (type) {
1278  case So@Gui@Viewer::STILL:
1279    switch (value) {
1280    case So@Gui@Viewer::VIEW_AS_IS:
1281      PUBLIC(this)->prefmenu->setMenuItemMarked(AS_IS_ITEM, TRUE);
1282      break;
1283    case So@Gui@Viewer::VIEW_HIDDEN_LINE:
1284      PUBLIC(this)->prefmenu->setMenuItemMarked(HIDDEN_LINE_ITEM, TRUE);
1285      break;
1286    case So@Gui@Viewer::VIEW_WIREFRAME_OVERLAY:
1287      PUBLIC(this)->prefmenu->setMenuItemMarked(WIREFRAME_OVERLAY_ITEM, TRUE);
1288      break;
1289    case So@Gui@Viewer::VIEW_NO_TEXTURE:
1290      PUBLIC(this)->prefmenu->setMenuItemMarked(NO_TEXTURE_ITEM, TRUE);
1291      break;
1292    case So@Gui@Viewer::VIEW_LOW_COMPLEXITY:
1293      PUBLIC(this)->prefmenu->setMenuItemMarked(LOW_RESOLUTION_ITEM, TRUE);
1294      break;
1295    case So@Gui@Viewer::VIEW_LOW_RES_LINE:
1296#if SO@GUI@_DEBUG
1297      SoDebugError::postWarning("So@Gui@FullViewer::setDrawStyleMenuActivation",
1298                                "Use VIEW_LINE, not VIEW_LOW_RES_LINE for the STILL drawstyle.");
1299#endif // debug
1300    case So@Gui@Viewer::VIEW_LINE:
1301      PUBLIC(this)->prefmenu->setMenuItemMarked(WIREFRAME_ITEM, TRUE);
1302      break;
1303    case So@Gui@Viewer::VIEW_LOW_RES_POINT:
1304#if SO@GUI@_DEBUG
1305      SoDebugError::postWarning("So@Gui@FullViewer::setDrawStyleMenuActivation",
1306                                "Use VIEW_POINT, not VIEW_LOW_RES_POINT for the STILL drawstyle.");
1307#endif // debug
1308    case So@Gui@Viewer::VIEW_POINT:
1309      PUBLIC(this)->prefmenu->setMenuItemMarked(POINTS_ITEM, TRUE);
1310      break;
1311    case So@Gui@Viewer::VIEW_BBOX:
1312      PUBLIC(this)->prefmenu->setMenuItemMarked(BOUNDING_BOX_ITEM, TRUE);
1313      break;
1314    default:
1315#if SO@GUI@_DEBUG
1316      SoDebugError::postWarning("So@Gui@FullViewer::setDrawStyleMenuActivation",
1317                                "Unsupporter still-drawstyle");
1318#endif // debug
1319      break;
1320    }
1321    break;
1322
1323  case So@Gui@Viewer::INTERACTIVE:
1324    switch (value) {
1325    case So@Gui@Viewer::VIEW_SAME_AS_STILL:
1326      PUBLIC(this)->prefmenu->setMenuItemMarked(MOVE_SAME_AS_STILL_ITEM, TRUE);
1327      break;
1328    case So@Gui@Viewer::VIEW_NO_TEXTURE:
1329      PUBLIC(this)->prefmenu->setMenuItemMarked(MOVE_NO_TEXTURE_ITEM, TRUE);
1330      break;
1331    case So@Gui@Viewer::VIEW_LOW_COMPLEXITY:
1332      PUBLIC(this)->prefmenu->setMenuItemMarked(MOVE_LOW_RES_ITEM, TRUE);
1333      break;
1334    case So@Gui@Viewer::VIEW_LINE:
1335      PUBLIC(this)->prefmenu->setMenuItemMarked(MOVE_WIREFRAME_ITEM, TRUE);
1336      break;
1337    case So@Gui@Viewer::VIEW_LOW_RES_LINE:
1338      PUBLIC(this)->prefmenu->setMenuItemMarked(MOVE_LOW_RES_WIREFRAME_ITEM, TRUE);
1339      break;
1340    case So@Gui@Viewer::VIEW_POINT:
1341      PUBLIC(this)->prefmenu->setMenuItemMarked(MOVE_POINTS_ITEM, TRUE);
1342      break;
1343    case So@Gui@Viewer::VIEW_LOW_RES_POINT:
1344      PUBLIC(this)->prefmenu->setMenuItemMarked(MOVE_LOW_RES_POINTS_ITEM, TRUE);
1345      break;
1346    case So@Gui@Viewer::VIEW_BBOX:
1347      PUBLIC(this)->prefmenu->setMenuItemMarked(MOVE_BOUNDING_BOX_ITEM, TRUE);
1348      break;
1349    default:
1350#if SO@GUI@_DEBUG
1351      SoDebugError::postWarning("So@Gui@FullViewer::setDrawStyleMenuActivation",
1352                                "Unsupporter interactive drawstyle");
1353#endif // debug
1354      break;
1355    }
1356    break;
1357
1358  default:
1359#if SO@GUI@_DEBUG
1360    SoDebugError::postWarning("So@Gui@FullViewer::setDrawStyleMenuActivation",
1361                              "Unsupporter drawstyle type");
1362#endif // debug
1363    break;
1364  }
1365}
1366
1367// ************************************************************************
1368
1369void
1370SoGuiFullViewerP::drawstyleActivated(int menuitemid)
1371{
1372  switch (menuitemid) {
1373  case SINGLE_BUFFER_ITEM:
1374    PUBLIC(this)->setBufferingType(So@Gui@Viewer::BUFFER_SINGLE);
1375    return;
1376  case DOUBLE_BUFFER_ITEM:
1377    PUBLIC(this)->setBufferingType(So@Gui@Viewer::BUFFER_DOUBLE);
1378    return;
1379  case INTERACTIVE_BUFFER_ITEM:
1380    PUBLIC(this)->setBufferingType(So@Gui@Viewer::BUFFER_INTERACTIVE);
1381    return;
1382  default:
1383    break;
1384  }
1385
1386  // FIXME: should perhaps override transparency type to be screendoor
1387  // if we detect that the OpenGL canvas has 0 alpha bits available?
1388  // 20030626 mortene.
1389  switch (menuitemid) {
1390  case SCREEN_DOOR_TRANSPARENCY_ITEM:
1391    PUBLIC(this)->setTransparencyType(SoGLRenderAction::SCREEN_DOOR);
1392    return;
1393  case ADD_TRANSPARENCY_ITEM:
1394    PUBLIC(this)->setTransparencyType(SoGLRenderAction::ADD);
1395    return;
1396  case DELAYED_ADD_TRANSPARENCY_ITEM:
1397    PUBLIC(this)->setTransparencyType(SoGLRenderAction::DELAYED_ADD);
1398    return;
1399  case SORTED_OBJECT_ADD_TRANSPARENCY_ITEM:
1400    PUBLIC(this)->setTransparencyType(SoGLRenderAction::SORTED_OBJECT_ADD);
1401    return;
1402  case BLEND_TRANSPARENCY_ITEM:
1403    PUBLIC(this)->setTransparencyType(SoGLRenderAction::BLEND);
1404    return;
1405  case DELAYED_BLEND_TRANSPARENCY_ITEM:
1406    PUBLIC(this)->setTransparencyType(SoGLRenderAction::DELAYED_BLEND);
1407    return;
1408  case SORTED_OBJECT_BLEND_TRANSPARENCY_ITEM:
1409    PUBLIC(this)->setTransparencyType(SoGLRenderAction::SORTED_OBJECT_BLEND);
1410    return;
1411#ifdef __COIN__ // Coin extensions
1412  case NONE_TRANSPARENCY_ITEM:
1413    PUBLIC(this)->setTransparencyType(SoGLRenderAction::NONE);
1414    return;
1415  case SORTED_OBJECT_SORTED_TRIANGLE_ADD_TRANSPARENCY_ITEM:
1416    PUBLIC(this)->setTransparencyType(SoGLRenderAction::SORTED_OBJECT_SORTED_TRIANGLE_ADD);
1417    return;
1418  case SORTED_OBJECT_SORTED_TRIANGLE_BLEND_TRANSPARENCY_ITEM:
1419    PUBLIC(this)->setTransparencyType(SoGLRenderAction::SORTED_OBJECT_SORTED_TRIANGLE_BLEND);
1420    return;
1421#endif // __COIN__
1422#ifdef HAVE_SORTED_LAYERS_BLEND
1423  case SORTED_LAYERS_BLEND_ITEM:
1424    PUBLIC(this)->setTransparencyType(SoGLRenderAction::SORTED_LAYERS_BLEND);
1425    return;
1426#endif // HAVE_SORTED_LAYERS_BLEND
1427  default:
1428    // some other menu item, just continue
1429    break;
1430  }
1431
1432  So@Gui@Viewer::DrawStyle val = So@Gui@Viewer::VIEW_AS_IS;
1433  switch (menuitemid) {
1434  case AS_IS_ITEM:
1435    val = So@Gui@Viewer::VIEW_AS_IS;
1436    break;
1437  case HIDDEN_LINE_ITEM:
1438    val = So@Gui@Viewer::VIEW_HIDDEN_LINE;
1439    break;
1440  case WIREFRAME_OVERLAY_ITEM:
1441    val = So@Gui@Viewer::VIEW_WIREFRAME_OVERLAY;
1442    break;
1443  case NO_TEXTURE_ITEM:
1444    val = So@Gui@Viewer::VIEW_NO_TEXTURE;
1445    break;
1446  case LOW_RESOLUTION_ITEM:
1447    val = So@Gui@Viewer::VIEW_LOW_COMPLEXITY;
1448    break;
1449  case WIREFRAME_ITEM:
1450    val = So@Gui@Viewer::VIEW_LINE;
1451    break;
1452  case POINTS_ITEM:
1453    val = So@Gui@Viewer::VIEW_POINT;
1454    break;
1455  case BOUNDING_BOX_ITEM:
1456    val = So@Gui@Viewer::VIEW_BBOX;
1457    break;
1458
1459  case MOVE_SAME_AS_STILL_ITEM:
1460    val = So@Gui@Viewer::VIEW_SAME_AS_STILL;
1461    break;
1462  case MOVE_NO_TEXTURE_ITEM:
1463    val = So@Gui@Viewer::VIEW_NO_TEXTURE;
1464    break;
1465  case MOVE_LOW_RES_ITEM:
1466    val = So@Gui@Viewer::VIEW_LOW_COMPLEXITY;
1467    break;
1468  case MOVE_WIREFRAME_ITEM:
1469    val = So@Gui@Viewer::VIEW_LINE;
1470    break;
1471  case MOVE_LOW_RES_WIREFRAME_ITEM:
1472    val = So@Gui@Viewer::VIEW_LOW_RES_LINE;
1473    break;
1474  case MOVE_POINTS_ITEM:
1475    val = So@Gui@Viewer::VIEW_POINT;
1476    break;
1477  case MOVE_LOW_RES_POINTS_ITEM:
1478    val = So@Gui@Viewer::VIEW_LOW_RES_POINT;
1479    break;
1480  case MOVE_BOUNDING_BOX_ITEM:
1481    val = So@Gui@Viewer::VIEW_BBOX;
1482    break;
1483
1484  default:
1485    assert(0);
1486    break;
1487  }
1488
1489  So@Gui@Viewer::DrawType type = So@Gui@Viewer::STILL;
1490
1491  switch (menuitemid) {
1492  case AS_IS_ITEM:
1493  case HIDDEN_LINE_ITEM:
1494  case WIREFRAME_OVERLAY_ITEM:
1495  case NO_TEXTURE_ITEM:
1496  case LOW_RESOLUTION_ITEM:
1497  case WIREFRAME_ITEM:
1498  case POINTS_ITEM:
1499  case BOUNDING_BOX_ITEM:
1500    type = So@Gui@Viewer::STILL;
1501    break;
1502
1503  case MOVE_SAME_AS_STILL_ITEM:
1504  case MOVE_NO_TEXTURE_ITEM:
1505  case MOVE_LOW_RES_ITEM:
1506  case MOVE_WIREFRAME_ITEM:
1507  case MOVE_LOW_RES_WIREFRAME_ITEM:
1508  case MOVE_POINTS_ITEM:
1509  case MOVE_LOW_RES_POINTS_ITEM:
1510  case MOVE_BOUNDING_BOX_ITEM:
1511    type = So@Gui@Viewer::INTERACTIVE;
1512    break;
1513
1514  default:
1515    assert(0);
1516    break;
1517  }
1518
1519  PUBLIC(this)->setDrawStyle(type, val);
1520}
1521
1522// ************************************************************************
1523
1524// Move camera parallel with the plane orthogonal to the camera
1525// direction vector.
1526//
1527// Used from both SoGuiPlaneViewer and SoGuiExaminerViewer.
1528// Implemented in the SoGuiFullViewer private class to collect common
1529// code.
1530void
1531SoGuiFullViewerP::pan(SoCamera * cam,
1532                      float aspectratio, const SbPlane & panningplane,
1533                      const SbVec2f & currpos, const SbVec2f & prevpos)
1534{
1535  if (cam == NULL) return; // can happen for empty scenegraph
1536  if (currpos == prevpos) return; // useless invocation
1537
1538#if SO@GUI@_DEBUG && 0
1539  SoDebugError::postInfo("SoGuiFullViewerP::pan",
1540                         "was(%.3g, %.3g) -> now(%.3g, %.3g)",
1541                         prevpos[0], prevpos[1], currpos[0], currpos[1]);
1542#endif // SO@GUI@_DEBUG
1543
1544  // Find projection points for the last and current mouse coordinates.
1545  SbViewVolume vv = cam->getViewVolume(aspectratio);
1546  SbLine line;
1547  vv.projectPointToLine(currpos, line);
1548  SbVec3f current_planept;
1549  panningplane.intersect(line, current_planept);
1550  vv.projectPointToLine(prevpos, line);
1551  SbVec3f old_planept;
1552  panningplane.intersect(line, old_planept);
1553
1554  // Reposition camera according to the vector difference between the
1555  // projected points.
1556  cam->position = cam->position.getValue() - (current_planept - old_planept);
1557}
1558
1559// *************************************************************************
1560
1561// Dependent on the camera type this will either shrink or expand the
1562// height of the viewport (orthogonal camera) or move the camera
1563// closer or further away from the focal point in the scene.
1564//
1565// Used from both SoGuiPlaneViewer and SoGuiExaminerViewer.
1566// Implemented in the SoGuiFullViewer private class to collect common
1567// code.
1568void
1569SoGuiFullViewerP::zoom(SoCamera * cam, const float diffvalue)
1570{
1571  if (cam == NULL) return; // can happen for empty scenegraph
1572  SoType t = cam->getTypeId();
1573  SbName tname = t.getName();
1574
1575  // This will be in the range of <0, ->>.
1576  float multiplicator = float(exp(diffvalue));
1577
1578  if (t.isDerivedFrom(SoOrthographicCamera::getClassTypeId())) {
1579
1580    // Since there's no perspective, "zooming" in the original sense
1581    // of the word won't have any visible effect. So we just increase
1582    // or decrease the field-of-view values of the camera instead, to
1583    // "shrink" the projection size of the model / scene.
1584    SoOrthographicCamera * oc = (SoOrthographicCamera *)cam;
1585    oc->height = oc->height.getValue() * multiplicator;
1586
1587  }
1588  else {
1589    // FrustumCamera can be found in the SmallChange CVS module (it's
1590    // a camera that lets you specify (for instance) an off-center
1591    // frustum (similar to glFrustum())
1592    if (!t.isDerivedFrom(SoPerspectiveCamera::getClassTypeId()) &&
1593        tname != "FrustumCamera") {
1594      static SbBool first = TRUE;
1595      if (first) {
1596        SoDebugError::postWarning("SoGuiFullViewerP::zoom",
1597                                  "Unknown camera type, "
1598                                  "will zoom by moving position, but this might not be correct.");
1599        first = FALSE;
1600      }
1601    }
1602
1603    const float oldfocaldist = cam->focalDistance.getValue();
1604    const float newfocaldist = oldfocaldist * multiplicator;
1605
1606    SbVec3f direction;
1607    cam->orientation.getValue().multVec(SbVec3f(0, 0, -1), direction);
1608
1609    const SbVec3f oldpos = cam->position.getValue();
1610    const SbVec3f newpos = oldpos + (newfocaldist - oldfocaldist) * -direction;
1611
1612    // This catches a rather common user interface "buglet": if the
1613    // user zooms the camera out to a distance from origo larger than
1614    // what we still can safely do floating point calculations on
1615    // (i.e. without getting NaN or Inf values), the faulty floating
1616    // point values will propagate until we start to get debug error
1617    // messages and eventually an assert failure from core Coin code.
1618    //
1619    // With the below bounds check, this problem is avoided.
1620    //
1621    // (But note that we depend on the input argument ''diffvalue'' to
1622    // be small enough that zooming happens gradually. Ideally, we
1623    // should also check distorigo with isinf() and isnan() (or
1624    // inversely; isinfite()), but those only became standardized with
1625    // C99.)
1626    const float distorigo = newpos.length();
1627    // sqrt(FLT_MAX) == ~ 1e+19, which should be both safe for further
1628    // calculations and ok for the end-user and app-programmer.
1629    if (distorigo > float(sqrt(FLT_MAX))) {
1630#if SO@GUI@_DEBUG && 0 // debug
1631      SoDebugError::postWarning("SoGuiFullViewerP::zoom",
1632                                "zoomed too far (distance to origo==%f (%e))",
1633                                distorigo, distorigo);
1634#endif // debug
1635    }
1636    else {
1637      cam->position = newpos;
1638      cam->focalDistance = newfocaldist;
1639    }
1640  }
1641}
1642
1643// ************************************************************************
1644
1645#endif // DOXYGEN_SKIP_THIS
1646
1647#undef PRIVATE
1648#undef PUBLIC
1649
1650