/*
*
* Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
*
* http://www.sgi.com
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/NoticeExplan/
*
*/
/*
* Copyright (C) 1990-95 Silicon Graphics, Inc.
*
_______________________________________________________________________
______________ S I L I C O N G R A P H I C S I N C . ____________
|
| $Revision: 1.1 $
|
| Classes : SoXtExaminerViewer
|
| Author(s) : Alain Dumesny
|
______________ S I L I C O N G R A P H I C S I N C . ____________
_______________________________________________________________________
*/
#include <inttypes.h>
#include <X11/Intrinsic.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <stdlib.h>
#ifndef __sgi
#define ftan tan
#endif
#include <Xm/LabelG.h>
#include <Xm/Text.h>
#include <Xm/ToggleB.h>
#include <Xm/ToggleBG.h>
#include <Xm/Form.h>
#include <Sgm/ThumbWheel.h>
#include <Inventor/SoDB.h>
#include <Inventor/SoInput.h>
#include <Inventor/sensors/SoFieldSensor.h>
#include <Inventor/fields/SoSFTime.h>
#include <Inventor/nodes/SoOrthographicCamera.h>
#include <Inventor/nodes/SoPerspectiveCamera.h>
#include <Inventor/nodes/SoScale.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoSwitch.h>
#include <Inventor/nodes/SoTranslation.h>
#include <Inventor/projectors/SbSphereSheetProjector.h>
#include <Inventor/actions/SoSearchAction.h>
#include <Inventor/Xt/SoXt.h>
#include <Inventor/Xt/SoXtCursors.h>
#include <Inventor/Xt/SoXtResource.h>
#include <Inventor/Xt/SoXtIcons.h>
#include <Inventor/Xt/viewers/SoXtExaminerViewer.h>
#include "SoXtBitmapButton.h"
#include <GL/gl.h>
#ifdef DEBUG
#include <Inventor/errors/SoDebugError.h>
#include <SoDebug.h>
#endif
/*
* Defines
*/
enum ViewerModes {
PICK_MODE,
VIEW_MODE,
SPIN_MODE_ACTIVE,
PAN_MODE,
PAN_MODE_ACTIVE,
DOLLY_MODE_ACTIVE,
SEEK_MODE
};
// list of custom push buttons
enum {
CAM_PUSH = 0,
PUSH_NUM
};
// size of the rotation buffer, which is used to animate the spinning ball.
#define ROT_BUFF_SIZE 3
// Resources for labels.
typedef struct {
char *examinViewer;
char *roty;
char *rotx;
char *preferenceSheet;
char *zoom;
char *dolly;
char *axesSizeLabel;
} RES_LABELS;
static RES_LABELS rl;
static char *defaultLabel[]={
"Examiner Viewer",
"Roty",
"Rotx",
"Examiner Viewer Preference Sheet",
"Zoom",
"Dolly",
"axes size:"
};
//
// The point of interest geometry description
//
char *SoXtExaminerViewer::geometryBuffer = "\
#Inventor V2.0 ascii\n\
\
Separator { \
PickStyle { style UNPICKABLE } \
LightModel { model BASE_COLOR } \
MaterialBinding { value PER_PART } \
DrawStyle { lineWidth 2 } \
Coordinate3 { point [0 0 0, 1 0 0, 0 1 0, 0 0 1] } \
BaseColor { rgb [1 0 0, 0 1 0, 0 0 1] } \
IndexedLineSet { coordIndex [1, 0, 2, -1, 0, 3] } \
\
LightModel { model PHONG } \
MaterialBinding { value OVERALL } \
Complexity { value .1 } \
Separator { \
Material { \
diffuseColor [ 0.5 0 0 ] \
emissiveColor [ 0.5 0 0 ] \
} \
Translation { translation 1 0 0 } \
RotationXYZ { axis Z angle -1.570796327 } \
Cone { bottomRadius .2 height .3 } \
} \
Separator { \
Material { \
diffuseColor [ 0 0.5 0 ] \
emissiveColor [ 0 0.5 0 ] \
} \
Translation { translation 0 1 0 } \
Cone { bottomRadius .2 height .3 } \
} \
Material { \
diffuseColor [ 0 0 0.5 ] \
emissiveColor [ 0 0 0.5 ] \
} \
Translation { translation 0 0 1 } \
RotationXYZ { axis X angle 1.570796327 } \
Cone { bottomRadius .2 height .3 } \
} ";
static char *thisClassName = "SoXtExaminerViewer";
////////////////////////////////////////////////////////////////////////
//
// Public constructor - build the widget right now
//
SoXtExaminerViewer::SoXtExaminerViewer(
Widget parent,
const char *name,
SbBool buildInsideParent,
SoXtFullViewer::BuildFlag b,
SoXtViewer::Type t)
: SoXtFullViewer(
parent,
name,
buildInsideParent,
b,
t,
FALSE) // tell GLWidget not to build just yet
//
////////////////////////////////////////////////////////////////////////
{
// In this case, render area is what the app wants, so buildNow = TRUE
constructorCommon(TRUE);
}
////////////////////////////////////////////////////////////////////////
//
// SoEXTENDER constructor - the subclass tells us whether to build or not
//
SoXtExaminerViewer::SoXtExaminerViewer(
Widget parent,
const char *name,
SbBool buildInsideParent,
SoXtFullViewer::BuildFlag b,
SoXtViewer::Type t,
SbBool buildNow)
: SoXtFullViewer(
parent,
name,
buildInsideParent,
b,
t,
FALSE) // tell GLWidget not to build just yet
//
////////////////////////////////////////////////////////////////////////
{
// In this case, render area may be what the app wants,
// or it may want a subclass of render area. Pass along buildNow
// as it was passed to us.
constructorCommon(buildNow);
}
////////////////////////////////////////////////////////////////////////
//
// Called by the constructors
//
// private
//
void
SoXtExaminerViewer::constructorCommon(SbBool buildNow)
//
////////////////////////////////////////////////////////////////////////
{
// init local vars
addVisibilityChangeCallback(visibilityChangeCB, this);
mode = isViewing() ? VIEW_MODE : PICK_MODE;
createdCursors = FALSE;
spinCursor = panCursor = dollyCursor = seekCursor = 0;
firstBuild = TRUE;
setSize( SbVec2s(500, 390) ); // default size
setClassName(thisClassName);
// axis of rotation feedback vars
feedbackFlag = FALSE;
feedbackRoot = NULL;
feedbackSwitch = NULL;
feedbackSize = 20.0;
feedbackSizeWheel = NULL;
// init animation variables
animationEnabled = TRUE;
animatingFlag = FALSE;
rotBuffer = new SbRotation[ROT_BUFF_SIZE];
lastMotionTime = 0;
animationSensor = new
SoFieldSensor(SoXtExaminerViewer::animationSensorCB, this);
#ifdef DEBUG
if (SoDebug::GetEnv("IV_DEBUG_SENSORS")) {
SoDebug::NamePtr("examinerSpinSensor", animationSensor);
}
#endif
// init the projector class
SbViewVolume vv;
vv.ortho(-1, 1, -1, 1, -10, 10);
sphereSheet = new SbSphereSheetProjector;
sphereSheet->setViewVolume( vv );
sphereSheet->setSphere( SbSphere( SbVec3f(0, 0, 0), .7) );
// Initialize buttonList.
for (int i=0; i<PUSH_NUM; i++)
buttonList[i] = NULL;
// Build the widget tree, and let SoXtComponent know about our base widget.
if (buildNow) {
Widget w = buildWidget(getParentWidget());
setBaseWidget(w);
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Destructor.
//
// Use: public
SoXtExaminerViewer::~SoXtExaminerViewer()
//
////////////////////////////////////////////////////////////////////////
{
delete animationSensor;
for (int i=0; i<PUSH_NUM; i++)
delete buttonList[i];
delete sphereSheet;
if (feedbackRoot)
feedbackRoot->unref();
// free the viewer cursors
if (getDisplay()) {
Display *display = getDisplay();
if (spinCursor) XFreeCursor(display, spinCursor);
if (panCursor) XFreeCursor(display, panCursor);
if (dollyCursor) XFreeCursor(display, dollyCursor);
if (seekCursor) XFreeCursor(display, seekCursor);
}
delete [] rotBuffer;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Shows/hide the point of rotation feedback geometry
//
// Use: public
void
SoXtExaminerViewer::setFeedbackVisibility(SbBool insertFlag)
//
////////////////////////////////////////////////////////////////////////
{
// check for trivial return
if (camera == NULL || feedbackFlag == insertFlag) {
feedbackFlag = insertFlag;
return;
}
//
// find the camera parent to insert/remove the feedback root
//
SoSearchAction sa;
if (insertFlag)
sa.setNode(camera);
else {
sa.setNode(feedbackRoot);
sa.setSearchingAll(TRUE); // find under OFF switches for removal
}
sa.apply(sceneRoot);
SoFullPath *fullPath = (SoFullPath *) sa.getPath();
if (!fullPath) {
#if DEBUG
SoDebugError::post("SoXtExaminerViewer::setFeedbackVisibility",
insertFlag ? "ERROR: cannot find camera in graph" :
"ERROR: cannot find axis feedback in graph");
#endif
return;
}
SoGroup *parent = (SoGroup *) fullPath->getNodeFromTail(1);
feedbackFlag = insertFlag;
// make sure the feedback has been built
if (!feedbackRoot)
createFeedbackNodes();
//
// inserts/remove the feedback axis group
//
if (feedbackFlag) {
int camIndex;
// check to make sure that the camera parent is not a switch node
// (VRML camera viewpoints are kept under a switch node). Otherwise
// we will insert the feedback after the switch node.
if (parent->isOfType(SoSwitch::getClassTypeId())) {
SoNode *switchNode = parent;
parent = (SoGroup *) fullPath->getNodeFromTail(2);
camIndex = parent->findChild(switchNode);
}
else
camIndex = parent->findChild(camera);
// return if feedback is already there (this should be an error !)
if (parent->findChild(feedbackRoot) >= 0)
return;
// insert the feedback right after the camera+headlight (index+2)
if (camIndex >= 0) {
if (isHeadlight())
parent->insertChild(feedbackRoot, camIndex+2);
else
parent->insertChild(feedbackRoot, camIndex+1);
}
// make sure the feedback switch is turned to the correct state now
// that the feedback root has been inserted in the scene
feedbackSwitch->whichChild.setValue(viewingFlag ? SO_SWITCH_ALL : SO_SWITCH_NONE);
}
else {
if (parent->findChild(feedbackRoot) >= 0)
parent->removeChild(feedbackRoot);
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Sets the feedback size.
//
// Use: public
void
SoXtExaminerViewer::setFeedbackSize(int newSize)
//
////////////////////////////////////////////////////////////////////////
{
if (feedbackSize == newSize)
return;
// assign new value and redraw (since it is not a field in the scene)
feedbackSize = newSize;
if (isFeedbackVisible() && isViewing())
scheduleRedraw();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Remove the extra geometry when doing a viewAll.
//
// Use: virtual public
void
SoXtExaminerViewer::viewAll()
//
////////////////////////////////////////////////////////////////////////
{
// stop spinning
if ( isAnimating() )
stopAnimating();
// temporarily remove the feedback geometry
if (feedbackFlag && isViewing() && feedbackSwitch)
feedbackSwitch->whichChild.setValue( SO_SWITCH_NONE );
// call the base class
SoXtFullViewer::viewAll();
// now add the geometry back in
if (feedbackFlag && isViewing() && feedbackSwitch)
feedbackSwitch->whichChild.setValue( SO_SWITCH_ALL );
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Call the parent class and stop animation if any
//
// Use: virtual public
void
SoXtExaminerViewer::resetToHomePosition()
//
////////////////////////////////////////////////////////////////////////
{
// stop spinning
if ( isAnimating() )
stopAnimating();
// call the base class
SoXtFullViewer::resetToHomePosition();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Call the parent class and insert/remove the feedback root
//
// Use: virtual public
void
SoXtExaminerViewer::setCamera(SoCamera *newCamera)
//
////////////////////////////////////////////////////////////////////////
{
if (camera == newCamera)
return;
// set the right thumbwheel label and toggle button image based on
// the camera type
if (newCamera != NULL && (camera == NULL ||
newCamera->getTypeId() != camera->getTypeId())) {
if (newCamera->isOfType(SoOrthographicCamera::getClassTypeId())) {
if (buttonList[CAM_PUSH])
buttonList[CAM_PUSH]->setIcon(so_xt_ortho_bits,
so_xt_icon_width, so_xt_icon_height);
setRightWheelString(rl.zoom);
}
else {
if (buttonList[CAM_PUSH])
buttonList[CAM_PUSH]->setIcon(so_xt_persp_bits,
so_xt_icon_width, so_xt_icon_height);
setRightWheelString(rl.dolly);
}
}
// detach feedback which depends on camera
if ( feedbackFlag ) {
setFeedbackVisibility(FALSE);
feedbackFlag = TRUE; // can later be turned on
}
// call parent class
SoXtFullViewer::setCamera(newCamera);
// attach feedback back on
if ( feedbackFlag ) {
feedbackFlag = FALSE; // enables routine to be called
setFeedbackVisibility(TRUE);
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Call the base class and sets the correct cursors on the window,
// plus insert/remove the feedback geometry.
//
// Use: virtual public
void
SoXtExaminerViewer::setViewing(SbBool flag)
//
////////////////////////////////////////////////////////////////////////
{
if (flag == viewingFlag)
return;
// call the parent class
SoXtFullViewer::setViewing(flag);
switchMode(isViewing() ? VIEW_MODE : PICK_MODE);
// show/hide the feedback geometry based on the viewing state
if (feedbackFlag && feedbackSwitch)
feedbackSwitch->whichChild.setValue(viewingFlag ? SO_SWITCH_ALL : SO_SWITCH_NONE);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Enables/Disable the viewer cursor on the window.
//
// Use: virtual public
void
SoXtExaminerViewer::setCursorEnabled(SbBool flag)
//
////////////////////////////////////////////////////////////////////////
{
if (flag == cursorEnabledFlag)
return;
cursorEnabledFlag = flag;
if (! isViewing())
return;
updateCursor();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Process the given event to do viewing stuff
//
// Use: virtual protected
void
SoXtExaminerViewer::processEvent(XAnyEvent *xe)
//
////////////////////////////////////////////////////////////////////////
{
if ( processCommonEvents(xe) )
return;
if (!createdCursors)
updateCursor();
XButtonEvent *be;
XMotionEvent *me;
XKeyEvent *ke;
XCrossingEvent *ce;
KeySym keysym;
SbVec2s raSize = getGlxSize();
switch(xe->type) {
case ButtonPress:
case ButtonRelease:
be = (XButtonEvent *)xe;
if (be->button != Button1 && be->button != Button2)
break;
locator[0] = be->x;
locator[1] = raSize[1] - be->y;
if (mode == SEEK_MODE) {
if (xe->type == ButtonPress)
seekToPoint(locator);
}
else {
if (xe->type == ButtonPress)
interactiveCountInc();
else { //... ButtonRelease
// check if we need to start spinning
if (mode == SPIN_MODE_ACTIVE && animationEnabled
&& lastMotionTime == be->time) {
animatingFlag = TRUE;
computeAverage = TRUE;
animationSensor->attach(viewerRealTime);
interactiveCountInc();
}
interactiveCountDec();
}
updateViewerMode(be->state);
}
if (xe->type == ButtonPress)
stopAnimating();
break;
case KeyPress:
case KeyRelease:
ke = (XKeyEvent *)xe;
keysym = XLookupKeysym(ke, 0);
locator[0] = ke->x;
locator[1] = raSize[1] - ke->y;
if (keysym == XK_Control_L || keysym == XK_Control_R)
updateViewerMode(ke->state);
break;
case MotionNotify:
me = (XMotionEvent *)xe;
switch (mode) {
case SPIN_MODE_ACTIVE:
lastMotionTime = me->time;
spinCamera(SbVec2f(me->x/float(raSize[0]), (raSize[1] - me->y)/float(raSize[1])));
break;
case PAN_MODE_ACTIVE:
panCamera(SbVec2f(me->x/float(raSize[0]), (raSize[1] - me->y)/float(raSize[1])));
break;
case DOLLY_MODE_ACTIVE:
dollyCamera( SbVec2s(me->x, raSize[1] - me->y) );
break;
}
break;
case LeaveNotify:
case EnterNotify:
ce = (XCrossingEvent *)xe;
//
// because the application might use Ctrl-key for motif menu
// accelerators we might not receive a key-up event, so make sure
// to reset any Ctrl mode if we loose focus, but don't do anything
// if Ctrl-key is not down (nothing to do) or if a mouse button
// is down (we will get another leaveNotify).
//
if (! (ce->state & ControlMask))
break;
if (ce->state & Button1Mask || ce->state & Button2Mask)
break;
if (xe->type == LeaveNotify)
switchMode(VIEW_MODE);
else
updateViewerMode(ce->state);
break;
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// sets the viewer mode based on what keys and buttons are being pressed
//
// Use: private
void
SoXtExaminerViewer::updateViewerMode(unsigned int state)
//
////////////////////////////////////////////////////////////////////////
{
// ??? WARNING - this routine ONLY works because of
// ??? SoXtViewer::updateEventState() which is called for us
// ??? by SoXtViewer::processCommonEvents().
// ??? (XEvent state for button and modifier keys is not updated
// ??? until after the event is received. WEIRD)
// LEFT+MIDDLE down
if (state & Button1Mask && state & Button2Mask) {
switchMode(DOLLY_MODE_ACTIVE);
}
// LEFT down
else if (state & Button1Mask) {
if (state & ControlMask)
switchMode(PAN_MODE_ACTIVE);
else
switchMode(SPIN_MODE_ACTIVE);
}
// MIDDLE DOWN
else if (state & Button2Mask) {
if (state & ControlMask)
switchMode(DOLLY_MODE_ACTIVE);
else
switchMode(PAN_MODE_ACTIVE);
}
// no buttons down...
else {
if (state & ControlMask)
switchMode(PAN_MODE);
else
switchMode(VIEW_MODE);
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// switches to the specified viewer mode
//
// Use: private
void
SoXtExaminerViewer::switchMode(int newMode)
//
////////////////////////////////////////////////////////////////////////
{
Widget raWidget = getRenderAreaWidget();
SbVec2s raSize = getGlxSize();
// assing new mode
int prevMode = mode;
mode = newMode;
// update the cursor
updateCursor();
// switch to new viewer mode
switch (newMode) {
case PICK_MODE:
if (raWidget && XtWindow(raWidget)) {
// ???? is if are going into PICK mode and some of our
// mouse buttons are still down, make sure to decrement
// interactive count correctly (correct draw style). One
// for the LEFT and one for MIDDLE mouse.
Window root_return, child_return;
int root_x_return, root_y_return;
int win_x_return, win_y_return;
unsigned int mask_return;
XQueryPointer(XtDisplay(raWidget), XtWindow(raWidget),
&root_return, &child_return,
&root_x_return, &root_y_return, &win_x_return,
&win_y_return, &mask_return);
if (mask_return & Button1Mask && prevMode != SEEK_MODE)
interactiveCountDec();
if (mask_return & Button2Mask && prevMode != SEEK_MODE)
interactiveCountDec();
}
stopAnimating();
break;
case SPIN_MODE_ACTIVE:
// set the sphere sheet starting point
sphereSheet->project(
SbVec2f(locator[0]/float(raSize[0]), locator[1]/float(raSize[1])) );
// reset the animation queue
firstIndex = 0;
lastIndex = -1;
break;
case PAN_MODE_ACTIVE:
{
// Figure out the focal plane
SbMatrix mx;
mx = camera->orientation.getValue();
SbVec3f forward(-mx[2][0], -mx[2][1], -mx[2][2]);
SbVec3f fp = camera->position.getValue() +
forward * camera->focalDistance.getValue();
focalplane = SbPlane(forward, fp);
// map mouse starting position onto the panning plane
SbViewVolume cameraVolume;
SbLine line;
cameraVolume = camera->getViewVolume(raSize[0]/float(raSize[1]));
cameraVolume.projectPointToLine(
SbVec2f(locator[0]/float(raSize[0]), locator[1]/float(raSize[1])), line);
focalplane.intersect(line, locator3D);
}
break;
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// updates the viewer cursor
//
// Use: private
void
SoXtExaminerViewer::updateCursor()
//
////////////////////////////////////////////////////////////////////////
{
Widget w = getRenderAreaWidget();
Display *display = w ? XtDisplay(w) : NULL;
Window window = w ? XtWindow(w) : 0;
if (! window)
return;
if (! createdCursors)
defineCursors();
// the viewer cursor are not enabled, then we don't set a new cursor.
// Instead erase the old viewer cursor.
if (! cursorEnabledFlag) {
XUndefineCursor(display, window);
return;
}
// ...else set the right cursor for the viewer mode....
switch(mode) {
case PICK_MODE:
XUndefineCursor(display, window);
break;
case VIEW_MODE:
case SPIN_MODE_ACTIVE:
XDefineCursor(display, window, spinCursor);
break;
case DOLLY_MODE_ACTIVE:
XDefineCursor(display, window, dollyCursor);
break;
case PAN_MODE:
case PAN_MODE_ACTIVE:
XDefineCursor(display, window, panCursor);
break;
case SEEK_MODE:
XDefineCursor(display, window, seekCursor);
break;
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// draws viewer feedback during a render area redraw of the scene.
//
// Use: virtual protected
void
SoXtExaminerViewer::actualRedraw()
//
////////////////////////////////////////////////////////////////////////
{
// place the feedback at the focal point
// ??? we really only need to do this when the camera changes
if (isViewing() && feedbackFlag && camera != NULL && feedbackRoot) {
// adjust the position to be at the focal point
SbMatrix mx;
mx = camera->orientation.getValue();
SbVec3f forward(-mx[2][0], -mx[2][1], -mx[2][2]);
feedbackTransNode->translation = camera->position.getValue() +
camera->focalDistance.getValue() * forward;
// adjust the size to be constant on the screen
float height;
if (camera->isOfType(SoPerspectiveCamera::getClassTypeId())) {
float angle = ((SoPerspectiveCamera *)camera)->heightAngle.getValue();
height = camera->focalDistance.getValue() * ftan(angle/2);
}
else if (camera->isOfType(SoOrthographicCamera::getClassTypeId()))
height = ((SoOrthographicCamera *)camera)->height.getValue() / 2;
// ??? getGlxSize[1] == 0 the very first time, so return in that case
// ??? else the redraws are 3 times slower from now on !! (alain)
if (getGlxSize()[1] != 0) {
float size = 2.0 * height * feedbackSize / float (getGlxSize()[1]);
feedbackScaleNode->scaleFactor.setValue(size, size, size);
}
}
// have the base class draw the scene
SoXtFullViewer::actualRedraw();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Enable/disable the animation feature of the Examiner
//
// Use: public
void
SoXtExaminerViewer::setAnimationEnabled(SbBool flag)
//
////////////////////////////////////////////////////////////////////////
{
if (animationEnabled == flag)
return;
animationEnabled = flag;
if ( !animationEnabled && isAnimating())
stopAnimating();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Stops ongoing animation (if any)
//
// Use: public
void
SoXtExaminerViewer::stopAnimating()
//
////////////////////////////////////////////////////////////////////////
{
if (animatingFlag) {
animatingFlag = FALSE;
animationSensor->detach();
animationSensor->unschedule();
interactiveCountDec();
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Call the base class and sets the correct cursors on the window
//
// Use: virtual protected
void
SoXtExaminerViewer::setSeekMode(SbBool flag)
//
////////////////////////////////////////////////////////////////////////
{
if ( !isViewing() )
return;
// stop spinning
if (isAnimating())
stopAnimating();
// call the base class
SoXtFullViewer::setSeekMode(flag);
mode = isSeekMode() ? SEEK_MODE : VIEW_MODE;
updateCursor();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Redefine this routine to add some viewer specific stuff.
//
// Use: virtual protected
void
SoXtExaminerViewer::createPrefSheet()
//
////////////////////////////////////////////////////////////////////////
{
// create the preference sheet shell and form widget
Widget shell, form;
createPrefSheetShellAndForm(shell, form);
// create all of the parts
Widget widgetList[20];
int num = 0;
createDefaultPrefSheetParts(widgetList, num, form);
widgetList[num++] = createExamPrefSheetGuts(form);
layoutPartsAndMapPrefSheet(widgetList, num, form, shell);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Creates the viewer extra pref sheet stuff
//
// Use: private
Widget
SoXtExaminerViewer::createExamPrefSheetGuts(Widget parent)
//
////////////////////////////////////////////////////////////////////////
{
Widget toggles[2], labels[2];
Arg args[12];
int n;
// create a form to hold everything together
Widget form = XtCreateWidget("", xmFormWidgetClass, parent, NULL, 0);
// create all the parts
n = 0;
XtSetArg(args[n], XmNset, animationEnabled); n++;
XtSetArg(args[n], XmNspacing, 0); n++;
XtSetArg(args[n], XmNhighlightThickness, 0); n++;
toggles[0] = XtCreateWidget("", xmToggleButtonGadgetClass, form, args, n);
labels[0] = XtCreateWidget("Enable spin animation",
xmLabelGadgetClass, form, NULL, 0);
XtAddCallback(toggles[0], XmNvalueChangedCallback,
(XtCallbackProc) SoXtExaminerViewer::animPrefSheetToggleCB,
(XtPointer) this);
n = 0;
XtSetArg(args[n], XmNsensitive, camera != NULL); n++;
XtSetArg(args[n], XmNset, feedbackFlag); n++;
XtSetArg(args[n], XmNspacing, 0); n++;
XtSetArg(args[n], XmNhighlightThickness, 0); n++;
toggles[1] = XtCreateWidget("", xmToggleButtonGadgetClass, form, args, n);
n = 0;
XtSetArg(args[n], XmNsensitive, camera != NULL); n++;
labels[1] = XtCreateWidget("Show point of rotation axes",
xmLabelGadgetClass, form, args, n);
XtAddCallback(toggles[1], XmNvalueChangedCallback,
(XtCallbackProc) SoXtExaminerViewer::feedbackPrefSheetToggleCB,
(XtPointer) this);
// layout
n = 0;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNleftWidget, toggles[0]); n++;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
XtSetArg(args[n], XmNtopWidget, toggles[0]); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
XtSetArg(args[n], XmNbottomWidget, toggles[0]); n++;
XtSetValues(labels[0], args, n);
n = 0;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNtopWidget, toggles[0]); n++;
XtSetArg(args[n], XmNtopOffset, 10); n++;
XtSetValues(toggles[1], args, n);
n = 0;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNleftWidget, toggles[1]); n++;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
XtSetArg(args[n], XmNtopWidget, toggles[1]); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
XtSetArg(args[n], XmNbottomWidget, toggles[1]); n++;
XtSetValues(labels[1], args, n);
// manage children
XtManageChildren(toggles, 2);
XtManageChildren(labels, 2);
if (feedbackFlag && camera)
toggleFeedbackWheelSize(toggles[1]);
return form;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Brings the viewer help card (called by "?" push button)
//
// Use: virtual protected
void
SoXtExaminerViewer::openViewerHelpCard()
//
////////////////////////////////////////////////////////////////////////
{
// tell the base class to open the file for us
openHelpCard("SoXtExaminerViewer.help");
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Call the base class and stop animating
//
// Use: virtual protected
void
SoXtExaminerViewer::bottomWheelStart()
//
////////////////////////////////////////////////////////////////////////
{
SoXtFullViewer::bottomWheelStart();
stopAnimating();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Call the base class and stop animating
//
// Use: virtual protected
void
SoXtExaminerViewer::leftWheelStart()
//
////////////////////////////////////////////////////////////////////////
{
SoXtFullViewer::bottomWheelStart();
stopAnimating();
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Rotates the object around the screen x axis (called by thumb wheel).
//
// Use: virtual protected
void
SoXtExaminerViewer::bottomWheelMotion(float newVal)
//
////////////////////////////////////////////////////////////////////////
{
// get rotation and apply to camera
SbVec3f axis(0, 1, 0);
SbRotation rot(axis, bottomWheelVal - newVal);
rotateCamera(rot);
bottomWheelVal = newVal;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Rotates the object around the screen y axis (called by thumb wheel).
//
// Use: virtual protected
void
SoXtExaminerViewer::leftWheelMotion(float newVal)
//
////////////////////////////////////////////////////////////////////////
{
// get rotation and apply to camera
SbVec3f axis(1, 0, 0);
SbRotation rot(axis, newVal - leftWheelVal);
rotateCamera(rot);
leftWheelVal = newVal;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Moves the camera closer/further away from the plane of interest
// (perspective camera case), else change the camera height (orthographic
// camera case).
//
// Use: virtual protected
void
SoXtExaminerViewer::rightWheelMotion(float newVal)
//
////////////////////////////////////////////////////////////////////////
{
if (camera == NULL)
return;
if (camera->isOfType(SoOrthographicCamera::getClassTypeId())) {
// change the ortho camera height
SoOrthographicCamera *cam = (SoOrthographicCamera *) camera;
cam->height = cam->height.getValue() * powf(2.0, newVal - rightWheelVal);
}
else {
// shorter/grow the focal distance given the wheel rotation
float focalDistance = camera->focalDistance.getValue();;
float newFocalDist = focalDistance;
newFocalDist *= powf(2.0, newVal - rightWheelVal);
// finally reposition the camera
SbMatrix mx;
mx = camera->orientation.getValue();
SbVec3f forward(-mx[2][0], -mx[2][1], -mx[2][2]);
camera->position = camera->position.getValue() +
(focalDistance - newFocalDist) * forward;
camera->focalDistance = newFocalDist;
}
rightWheelVal = newVal;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// This routine is used to define cursors (can only be called after
// window has been realized).
//
// Use: private
void
SoXtExaminerViewer::defineCursors()
//
////////////////////////////////////////////////////////////////////////
{
XColor foreground;
Pixmap source;
Display *display = getDisplay();
Drawable d = DefaultRootWindow(display);
// set color
foreground.red = 65535;
foreground.green = foreground.blue = 0;
// spin cursor
source = XCreateBitmapFromData(display, d,
so_xt_curved_hand_bits, so_xt_curved_hand_width, so_xt_curved_hand_height);
spinCursor = XCreatePixmapCursor(display, source, source,
&foreground, &foreground, so_xt_curved_hand_x_hot, so_xt_curved_hand_y_hot);
XFreePixmap(display, source);
// panning cursor
source = XCreateBitmapFromData(display, d,
so_xt_flat_hand_bits, so_xt_flat_hand_width, so_xt_flat_hand_height);
panCursor = XCreatePixmapCursor(display, source, source,
&foreground, &foreground, so_xt_flat_hand_x_hot, so_xt_flat_hand_y_hot);
XFreePixmap(display, source);
// dolly cursor
source = XCreateBitmapFromData(display, d,
so_xt_pointing_hand_bits, so_xt_pointing_hand_width, so_xt_pointing_hand_height);
dollyCursor = XCreatePixmapCursor(display, source, source,
&foreground, &foreground, so_xt_pointing_hand_x_hot, so_xt_pointing_hand_y_hot);
XFreePixmap(display, source);
// seek cursor
source = XCreateBitmapFromData(display, d,
so_xt_target_bits, so_xt_target_width, so_xt_target_height);
seekCursor = XCreatePixmapCursor(display, source, source,
&foreground, &foreground, so_xt_target_x_hot, so_xt_target_y_hot);
XFreePixmap(display, source);
createdCursors = TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Rotates the camera around pt of interest by given rotation
//
// Use: private
void
SoXtExaminerViewer::rotateCamera(const SbRotation &rot)
//
////////////////////////////////////////////////////////////////////////
{
if (camera == NULL)
return;
// get center of rotation
SbRotation camRot = camera->orientation.getValue();
float radius = camera->focalDistance.getValue();
SbMatrix mx;
mx = camRot;
SbVec3f forward( -mx[2][0], -mx[2][1], -mx[2][2]);
SbVec3f center = camera->position.getValue()
+ radius * forward;
// apply new rotation to the camera
camRot = rot * camRot;
camera->orientation = camRot;
// reposition camera to look at pt of interest
mx = camRot;
forward.setValue( -mx[2][0], -mx[2][1], -mx[2][2]);
camera->position = center - radius * forward;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Moves the camera into the plane defined by the camera forward vector
// and the focal point to follow the new mouse location.
//
// Use: private
void
SoXtExaminerViewer::panCamera(const SbVec2f &newLocator)
//
////////////////////////////////////////////////////////////////////////
{
if (camera == NULL)
return;
// map new mouse location into the camera focal plane
SbViewVolume cameraVolume;
SbLine line;
SbVec3f newLocator3D;
SbVec2s raSize = getGlxSize();
cameraVolume = camera->getViewVolume(raSize[0]/float(raSize[1]));
cameraVolume.projectPointToLine(newLocator, line);
focalplane.intersect(line, newLocator3D);
// move the camera by the delta 3D position amount
camera->position = camera->position.getValue() +
(locator3D - newLocator3D);
// You would think we would have to set locator3D to
// newLocator3D here. But we don't, because moving the camera
// essentially makes locator3D equal to newLocator3D in the
// transformed space, and we will project the next newLocator3D in
// this transformed space.
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// rotates the camera using the sheet sphere projector, given the new
// mouse location.
//
// Use: private
void
SoXtExaminerViewer::spinCamera(const SbVec2f &newLocator)
//
////////////////////////////////////////////////////////////////////////
{
// find rotation and rotate camera
SbRotation rot;
sphereSheet->projectAndGetRotation(newLocator, rot);
rot.invert();
rotateCamera(rot);
// save rotation for animation
lastIndex = ((lastIndex+1) % ROT_BUFF_SIZE);
rotBuffer[lastIndex] = rot;
// check if queue is full
if (((lastIndex+1) % ROT_BUFF_SIZE) == firstIndex)
firstIndex = ((firstIndex+1) % ROT_BUFF_SIZE);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Moves the camera forward/backward based on the new mouse position.
// (perspective camera), else change the camera height (orthographic
// camera case).
//
// Use: private
void
SoXtExaminerViewer::dollyCamera(const SbVec2s &newLocator)
//
////////////////////////////////////////////////////////////////////////
{
if (camera == NULL)
return;
// moving the mouse up/down will move the camera futher/closer.
// moving the camera sideway will not move the camera at all
float d = (newLocator[1] - locator[1]) / 40.0;
if (camera->isOfType(SoOrthographicCamera::getClassTypeId())) {
// change the ortho camera height
SoOrthographicCamera *cam = (SoOrthographicCamera *) camera;
cam->height = cam->height.getValue() * powf(2.0, d);
}
else {
// shorter/grow the focal distance given the mouse move
float focalDistance = camera->focalDistance.getValue();;
float newFocalDist = focalDistance * powf(2.0, d);
// finally reposition the camera
SbMatrix mx;
mx = camera->orientation.getValue();
SbVec3f forward(-mx[2][0], -mx[2][1], -mx[2][2]);
camera->position = camera->position.getValue() +
(focalDistance - newFocalDist) * forward;
camera->focalDistance = newFocalDist;
}
locator = newLocator;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Routine which animates the ball spinning (called by sensor).
//
// Use: private
void
SoXtExaminerViewer::doSpinAnimation()
//
////////////////////////////////////////////////////////////////////////
{
//
// check if average rotation needs to be computed
//
if (computeAverage) {
float averageAngle, angle;
SbVec3f averageAxis, axis;
// get number of samples
int num = (((lastIndex - firstIndex) + 1 +
ROT_BUFF_SIZE) % ROT_BUFF_SIZE);
// check for not enough samples
if (num < 2) {
stopAnimating();
return;
}
// get average axis of rotation
// ??? right now only take one sample
rotBuffer[firstIndex].getValue(averageAxis, angle);
// get average angle of rotation
averageAngle = 0;
for (int i=0; i<num; i++) {
int n = (firstIndex + i) % ROT_BUFF_SIZE;
rotBuffer[n].getValue(axis, angle);
averageAngle += angle;
}
averageAngle /= float(num);
averageRotation.setValue(averageAxis, averageAngle);
computeAverage = FALSE;
}
//
// rotate camera by average rotation
//
rotateCamera(averageRotation);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Show/hide the pref sheet feedback size wheel and label.
//
// Use: private
void
SoXtExaminerViewer::toggleFeedbackWheelSize(Widget toggle)
//
////////////////////////////////////////////////////////////////////////
{
if ( feedbackFlag ) {
Widget parent = XtParent(toggle);
Arg args[12];
int n;
// create the label/thumb/text/label in the toggle parent
feedbackLabel[0] = XtCreateWidget(rl.axesSizeLabel,
xmLabelGadgetClass, parent, NULL, 0);
n = 0;
XtSetArg(args[n], XmNvalue, 0); n++;
XtSetArg(args[n], SgNangleRange, 0); n++;
XtSetArg(args[n], SgNunitsPerRotation, 360); n++;
XtSetArg(args[n], SgNshowHomeButton, FALSE); n++;
XtSetArg(args[n], XmNhighlightThickness, 0); n++;
XtSetArg(args[n], XmNorientation, XmHORIZONTAL); n++;
feedbackSizeWheel = SgCreateThumbWheel(parent, NULL, args, n);
XtAddCallback(feedbackSizeWheel, XmNvalueChangedCallback,
(XtCallbackProc) SoXtExaminerViewer::feedbackSizeWheelCB, (XtPointer) this);
XtAddCallback(feedbackSizeWheel, XmNdragCallback,
(XtCallbackProc) SoXtExaminerViewer::feedbackSizeWheelCB, (XtPointer) this);
feedbackSizeWheelVal = 0;
n = 0;
char str[15];
sprintf(str, "%d", int(feedbackSize));
XtSetArg(args[n], XmNvalue, str); n++;
XtSetArg(args[n], XmNhighlightThickness, 1); n++;
XtSetArg(args[n], XmNcolumns, 3); n++;
feedbackField = XtCreateWidget("", xmTextWidgetClass,
parent, args, n);
XtAddCallback(feedbackField, XmNactivateCallback,
(XtCallbackProc) SoXtExaminerViewer::feedbackSizeFieldCB,
(XtPointer) this);
feedbackLabel[1] = XtCreateWidget("pixels",
xmLabelGadgetClass, parent, NULL, 0);
// layout
n = 0;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftOffset, 20); n++;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNtopWidget, toggle); n++;
XtSetArg(args[n], XmNtopOffset, 5); n++;
XtSetValues(feedbackLabel[0], args, n);
n = 0;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNleftWidget, feedbackLabel[0]); n++;
XtSetArg(args[n], XmNleftOffset, 5); n++;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
XtSetArg(args[n], XmNtopWidget, feedbackLabel[0]); n++;
XtSetValues(feedbackSizeWheel, args, n);
n = 0;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNleftWidget, feedbackSizeWheel); n++;
XtSetArg(args[n], XmNleftOffset, 3); n++;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
XtSetArg(args[n], XmNtopWidget, feedbackSizeWheel); n++;
XtSetArg(args[n], XmNtopOffset, -5); n++;
XtSetValues(feedbackField, args, n);
n = 0;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNleftWidget, feedbackField); n++;
XtSetArg(args[n], XmNleftOffset, 5); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
XtSetArg(args[n], XmNbottomWidget, feedbackLabel[0]); n++;
XtSetValues(feedbackLabel[1], args, n);
// manage children
XtManageChild(feedbackLabel[0]);
XtManageChild(feedbackSizeWheel);
XtManageChild(feedbackField);
XtManageChild(feedbackLabel[1]);
}
else {
// destroys the widgets
XtDestroyWidget(feedbackLabel[1]);
XtDestroyWidget(feedbackField);
XtDestroyWidget(feedbackSizeWheel);
XtDestroyWidget(feedbackLabel[0]);
}
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// Build the parent class widget, then register this widget.
//
// Use: protected
Widget
SoXtExaminerViewer::buildWidget(Widget parent)
//
////////////////////////////////////////////////////////////////////////
{
// Get a resource value for menu title.
if (firstBuild) {
SoXtResource xrp(parent);
if (!xrp.getResource( "examinViewer", "ExaminViewer", rl.examinViewer ))
rl.examinViewer = defaultLabel[0];
setPopupMenuString(rl.examinViewer);
}
// Create the root widget and register it with a class name
Widget w = SoXtFullViewer::buildWidget(parent);
// If first build, get resource values
if (firstBuild) {
// Full viewer registered the widget for us
SoXtResource xr(w);
SbBool flag;
short val;
if (xr.getResource("spinAnimation", "SpinAnimation", flag))
setAnimationEnabled(flag);
if (xr.getResource("pointOfRotationAxes", "PointOfRotationAxes", flag))
setFeedbackVisibility(flag);
if (xr.getResource("axesSize", "AxesSize", val))
feedbackSize = val;
//
if (!xr.getResource( "roty", "Roty", rl.roty ))
rl.roty = defaultLabel[1];
if (!xr.getResource( "rotx", "Rotx", rl.rotx ))
rl.rotx = defaultLabel[2];
if (!xr.getResource( "preferenceSheet","PreferenceSheet",rl.preferenceSheet ))
rl.preferenceSheet = defaultLabel[3];
if (!xr.getResource( "zoom", "Zoom", rl.zoom ))
rl.zoom = defaultLabel[4];
if (!xr.getResource( "dolly", "Dolly", rl.dolly ))
rl.dolly = defaultLabel[5];
if (!xr.getResource( "axesSizeLabel", "AxesSizeLabel", rl.axesSizeLabel ))
rl.axesSizeLabel = defaultLabel[6];
// assign decoration names
setBottomWheelString(rl.roty);
setLeftWheelString(rl.rotx);
setPrefSheetString(rl.preferenceSheet);
firstBuild = FALSE;
}
return w;
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// add our own button to the existing list
//
// Use: virtual protected
void
SoXtExaminerViewer::createViewerButtons(Widget parent)
//
////////////////////////////////////////////////////////////////////////
{
// get the default buttons
SoXtFullViewer::createViewerButtons(parent);
// allocate our buttons
buttonList[CAM_PUSH] = new SoXtBitmapButton(parent, FALSE);
buttonList[CAM_PUSH]->setIcon(so_xt_persp_bits, so_xt_icon_width, so_xt_icon_height);
Widget w = buttonList[CAM_PUSH]->getWidget();
XtAddCallback(w, XmNactivateCallback,
(XtCallbackProc) SoXtExaminerViewer::camPushCB, (XtPointer) this);
// add this button to the list...
viewerButtonWidgets->append(w);
}
////////////////////////////////////////////////////////////////////////
//
// Description:
// read the point of interest geometry, which will be placed
// right after the camera node (or right after the headlight
// if the headlight is turned on).
//
// Use: private
void
SoXtExaminerViewer::createFeedbackNodes()
//
////////////////////////////////////////////////////////////////////////
{
// make sure we havn't built this yet...
if (feedbackRoot)
return;
feedbackRoot = new SoSeparator(1);
feedbackSwitch = new SoSwitch(3);
feedbackTransNode = new SoTranslation;
feedbackScaleNode = new SoScale;
feedbackRoot->ref();
feedbackRoot->addChild( feedbackSwitch );
feedbackSwitch->addChild( feedbackTransNode );
feedbackSwitch->addChild( feedbackScaleNode );
SoInput in;
in.setBuffer((void *)geometryBuffer, (size_t) strlen(geometryBuffer));
SoNode *node;
SbBool ok = SoDB::read(&in, node);
if (ok && node != NULL)
feedbackSwitch->addChild(node);
#ifdef DEBUG
else
SoDebugError::post("SoXtExaminerViewer::createFeedbackNodes",
"couldn't read feedback axis geometry");
#endif
}
//
// redefine those generic virtual functions
//
const char *
SoXtExaminerViewer::getDefaultWidgetName() const
{ return thisClassName; }
const char *
SoXtExaminerViewer::getDefaultTitle() const
{ return rl.examinViewer; }
const char *
SoXtExaminerViewer::getDefaultIconTitle() const
{ return rl.examinViewer; }
//
////////////////////////////////////////////////////////////////////////
// static callbacks stubs
////////////////////////////////////////////////////////////////////////
//
void
SoXtExaminerViewer::camPushCB(Widget, SoXtExaminerViewer *v, void *)
{ v->toggleCameraType(); }
void
SoXtExaminerViewer::animationSensorCB(void *v, SoSensor *)
{ ((SoXtExaminerViewer *) v)->doSpinAnimation(); }
void
SoXtExaminerViewer::animPrefSheetToggleCB(Widget toggle,
SoXtExaminerViewer *v, void *)
{
v->setAnimationEnabled( XmToggleButtonGetState(toggle) );
}
void
SoXtExaminerViewer::feedbackPrefSheetToggleCB(Widget toggle,
SoXtExaminerViewer *v, void *)
{
// show/hide the feedback
v->setFeedbackVisibility( XmToggleButtonGetState(toggle) );
// show/hide the extra size setting
v->toggleFeedbackWheelSize(toggle);
}
void
SoXtExaminerViewer::feedbackSizeWheelCB(Widget, SoXtExaminerViewer *v, XtPointer *d)
{
static SbBool firstDrag = TRUE;
SgThumbWheelCallbackStruct *data = (SgThumbWheelCallbackStruct *) d;
if (data->reason == XmCR_DRAG) {
// for the first move, invoke the start callbacks
if (firstDrag) {
v->interactiveCountInc();
firstDrag = FALSE;
}
// grow/shrink the feedback based on the wheel rotation
v->feedbackSize *= powf(12, (data->value - v->feedbackSizeWheelVal) / 360.0);
v->feedbackSizeWheelVal = data->value;
// update the text field
char str[15];
sprintf(str, "%d", int(v->feedbackSize));
XmTextSetString(v->feedbackField, str);
// redraw since the wheel size isn't a field in the scene
if (v->isViewing())
v->scheduleRedraw();
}
else {
// reason = XmCR_VALUE_CHANGED, invoke the finish callbacks
v->interactiveCountDec();
firstDrag = TRUE;
}
}
void
SoXtExaminerViewer::feedbackSizeFieldCB(Widget field, SoXtExaminerViewer *v, void *)
{
// get text value from the field
char *str = XmTextGetString(field);
int val;
if ( sscanf(str, "%d", &val) && val > 0)
v->setFeedbackSize(val);
else
val = int(v->feedbackSize);
free(str);
// reformat text field
char valStr[15];
sprintf(valStr, "%d", val);
XmTextSetString(field, valStr);
// make the text field loose the focus
XmProcessTraversal(SoXt::getShellWidget(field), XmTRAVERSE_CURRENT);
}
// called when the viewer becomes visible/hidden - when hidden, make
// sure to temporary stop any ongoing animation (and restart it as soon
// as we become visible).
//
void
SoXtExaminerViewer::visibilityChangeCB(void *pt, SbBool visible)
{
SoXtExaminerViewer *p = (SoXtExaminerViewer *)pt;
// only do this if we are/were spinning....
if (! p->animatingFlag)
return;
if (visible) {
// we now are visible again so attach the field sensor
p->animationSensor->attach(viewerRealTime);
}
else {
// if hidden, detach the field sensor, but don't change the
// animatingFlag var to let us know we need to turn it back on
// when we become visible....
p->animationSensor->detach();
p->animationSensor->unschedule();
}
}