/**************************************************************************\
 *
 *  This file is part of the Coin 3D visualization library.
 *  Copyright (C) 1998-2006 by Systems in Motion.  All rights reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  ("GPL") version 2 as published by the Free Software Foundation.
 *  See the file LICENSE.GPL at the root directory of this source
 *  distribution for additional information about the GNU GPL.
 *
 *  For using Coin with software that can not be combined with the GNU
 *  GPL, and for taking advantage of the additional benefits of our
 *  support services, please contact Systems in Motion about acquiring
 *  a Coin Professional Edition License.
 *
 *  See http://www.coin3d.org/ for more information.
 *
 *  Systems in Motion, Postboks 1283, Pirsenteret, 7462 Trondheim, NORWAY.
 *  http://www.sim.no/  sales@sim.no  coin-support@coin3d.org
 *
\**************************************************************************/

/*!
  \class SoHandleBoxDragger SoHandleBoxDragger.h Inventor/draggers/SoHandleBoxDragger.h
  \brief The SoHandleBoxDragger class provides support for interactive scaling and translation.
  \ingroup draggers

  \DRAGGER_DEFAULT_SCREENSHOT

  <center>
  <img src="http://doc.coin3d.org/images/Coin/draggers/handlebox.png">
  </center>

  A handlebox dragger is convenient to use for letting the user
  interact with geometry that can be scaled (uniformly or in a
  non-uniform way) and translated in 3D.

  The dragger consists of a "cube" of interaction geometry. The
  end-user can click and drag any side of the cube to translate the
  dragger and click and drag any of the corner or edge markers for
  scaling operations.
*/
// FIXME: Should include an URL-link to the default geometry-file?
// Plus a small usage example.  20011113 mortene.

#include <string.h>

#include <Inventor/draggers/SoHandleBoxDragger.h>
#include <Inventor/nodekits/SoSubKitP.h>
#include <Inventor/nodes/SoDrawStyle.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoSurroundScale.h>
#include <Inventor/nodes/SoSwitch.h>
#include <Inventor/nodes/SoTranslation.h>
#include <Inventor/sensors/SoFieldSensor.h>
#include <Inventor/projectors/SbPlaneProjector.h>
#include <Inventor/projectors/SbLineProjector.h>
#include <Inventor/events/SoKeyboardEvent.h>
#include <Inventor/actions/SoGetMatrixAction.h>

#include <data/draggerDefaults/handleBoxDragger.h>

/*!
  \var SoSFVec3f SoHandleBoxDragger::scaleFactor

  Continuously updated to contain the current vector of scaling along
  the X, Y and Z axes.
*/

/*!
  \var SoSFVec3f SoHandleBoxDragger::translation

  Continuously updated to contain the current translation from the
  dragger's local origo position.

  The application programmer applying this dragger in his code should
  connect the relevant node fields in the scene to this field to make
  it follow the dragger.
*/


/*!
  \var SoFieldSensor * SoHandleBoxDragger::translFieldSensor
  \COININTERNAL
*/
/*!
  \var SoFieldSensor * SoHandleBoxDragger::scaleFieldSensor
  \COININTERNAL
*/

#define WHATKIND_NONE       0
#define WHATKIND_TRANSLATOR 1
#define WHATKIND_EXTRUDER   2
#define WHATKIND_UNIFORM    3

#define CONSTRAINT_OFF  0
#define CONSTRAINT_WAIT 1
#define CONSTRAINT_X    2
#define CONSTRAINT_Y    3
#define CONSTRAINT_Z    4

static int uniform_ctrl_lookup[8][6] = {
  { 1,4,5,2,3,6 },
  { 1,4,6,2,3,5 },
  { 2,4,5,1,3,6 },
  { 2,4,6,1,3,5 },
  { 1,3,5,2,4,6 },
  { 1,3,6,2,4,5 },
  { 2,3,5,1,4,6 },
  { 2,3,6,1,4,5 }
};

SO_KIT_SOURCE(SoHandleBoxDragger);


// doc in superclass
void
SoHandleBoxDragger::initClass(void)
{
  SO_KIT_INTERNAL_INIT_CLASS(SoHandleBoxDragger, SO_FROM_INVENTOR_1);
}

// FIXME: document which parts need to be present in the geometry
// scenegraph, and what role they play in the dragger. 20010913 mortene.
/*!
  \DRAGGER_CONSTRUCTOR

  \NODEKIT_PRE_DIAGRAM

  \verbatim
  CLASS SoHandleBoxDragger
  -->"this"
        "callbackList"
        "topSeparator"
           "motionMatrix"
  -->      "surroundScale"
           "geomSeparator"
  -->         "drawStyle"
  -->         "translator1Switch"
  -->            "translator1"
  -->            "translator1Active"
  -->         "translator2Switch"
  -->            "translator2"
  -->            "translator2Active"
  -->         "translator3Switch"
  -->            "translator3"
  -->            "translator3Active"
  -->         "translator4Switch"
  -->            "translator4"
  -->            "translator4Active"
  -->         "translator5Switch"
  -->            "translator5"
  -->            "translator5Active"
  -->         "translator6Switch"
  -->            "translator6"
  -->            "translator6Active"
  -->         "extruder1Switch"
  -->            "extruder1"
  -->            "extruder1Active"
  -->         "extruder2Switch"
  -->            "extruder2"
  -->            "extruder2Active"
  -->         "extruder3Switch"
  -->            "extruder3"
  -->            "extruder3Active"
  -->         "extruder4Switch"
  -->            "extruder4"
  -->            "extruder4Active"
  -->         "extruder5Switch"
  -->            "extruder5"
  -->            "extruder5Active"
  -->         "extruder6Switch"
  -->            "extruder6"
  -->            "extruder6Active"
  -->         "uniform1Switch"
  -->            "uniform1"
  -->            "uniform1Active"
  -->         "uniform2Switch"
  -->            "uniform2"
  -->            "uniform2Active"
  -->         "uniform3Switch"
  -->            "uniform3"
  -->            "uniform3Active"
  -->         "uniform4Switch"
  -->            "uniform4"
  -->            "uniform4Active"
  -->         "uniform5Switch"
  -->            "uniform5"
  -->            "uniform5Active"
  -->         "uniform6Switch"
  -->            "uniform6"
  -->            "uniform6Active"
  -->         "uniform7Switch"
  -->            "uniform7"
  -->            "uniform7Active"
  -->         "uniform8Switch"
  -->            "uniform8"
  -->            "uniform8Active"
  -->         "arrowTranslation"
  -->         "arrow1Switch"
  -->            "arrow1"
  -->         "arrow2Switch"
  -->            "arrow2"
  -->         "arrow3Switch"
  -->            "arrow3"
  -->         "arrow4Switch"
  -->            "arrow4"
  -->         "arrow5Switch"
  -->            "arrow5"
  -->         "arrow6Switch"
  -->            "arrow6"
  \endverbatim

  \NODEKIT_POST_DIAGRAM


  \NODEKIT_PRE_TABLE

  \verbatim
  CLASS SoHandleBoxDragger
  PVT   "this",  SoHandleBoxDragger  --- 
        "callbackList",  SoNodeKitListPart [ SoCallback, SoEventCallback ] 
  PVT   "topSeparator",  SoSeparator  --- 
  PVT   "motionMatrix",  SoMatrixTransform  --- 
        "surroundScale",  SoSurroundScale  --- 
  PVT   "geomSeparator",  SoSeparator  --- 
  PVT   "drawStyle",  SoDrawStyle  --- 
  PVT   "translator1Switch",  SoSwitch  --- 
        "translator1",  SoSeparator  --- 
        "translator1Active",  SoSeparator  --- 
  PVT   "translator2Switch",  SoSwitch  --- 
        "translator2",  SoSeparator  --- 
        "translator2Active",  SoSeparator  --- 
  PVT   "translator3Switch",  SoSwitch  --- 
        "translator3",  SoSeparator  --- 
        "translator3Active",  SoSeparator  --- 
  PVT   "translator4Switch",  SoSwitch  --- 
        "translator4",  SoSeparator  --- 
        "translator4Active",  SoSeparator  --- 
  PVT   "translator5Switch",  SoSwitch  --- 
        "translator5",  SoSeparator  --- 
        "translator5Active",  SoSeparator  --- 
  PVT   "translator6Switch",  SoSwitch  --- 
        "translator6",  SoSeparator  --- 
        "translator6Active",  SoSeparator  --- 
  PVT   "extruder1Switch",  SoSwitch  --- 
        "extruder1",  SoSeparator  --- 
        "extruder1Active",  SoSeparator  --- 
  PVT   "extruder2Switch",  SoSwitch  --- 
        "extruder2",  SoSeparator  --- 
        "extruder2Active",  SoSeparator  --- 
  PVT   "extruder3Switch",  SoSwitch  --- 
        "extruder3",  SoSeparator  --- 
        "extruder3Active",  SoSeparator  --- 
  PVT   "extruder4Switch",  SoSwitch  --- 
        "extruder4",  SoSeparator  --- 
        "extruder4Active",  SoSeparator  --- 
  PVT   "extruder5Switch",  SoSwitch  --- 
        "extruder5",  SoSeparator  --- 
        "extruder5Active",  SoSeparator  --- 
  PVT   "extruder6Switch",  SoSwitch  --- 
        "extruder6",  SoSeparator  --- 
        "extruder6Active",  SoSeparator  --- 
  PVT   "uniform1Switch",  SoSwitch  --- 
        "uniform1",  SoSeparator  --- 
        "uniform1Active",  SoSeparator  --- 
  PVT   "uniform2Switch",  SoSwitch  --- 
        "uniform2",  SoSeparator  --- 
        "uniform2Active",  SoSeparator  --- 
  PVT   "uniform3Switch",  SoSwitch  --- 
        "uniform3",  SoSeparator  --- 
        "uniform3Active",  SoSeparator  --- 
  PVT   "uniform4Switch",  SoSwitch  --- 
        "uniform4",  SoSeparator  --- 
        "uniform4Active",  SoSeparator  --- 
  PVT   "uniform5Switch",  SoSwitch  --- 
        "uniform5",  SoSeparator  --- 
        "uniform5Active",  SoSeparator  --- 
  PVT   "uniform6Switch",  SoSwitch  --- 
        "uniform6",  SoSeparator  --- 
        "uniform6Active",  SoSeparator  --- 
  PVT   "uniform7Switch",  SoSwitch  --- 
        "uniform7",  SoSeparator  --- 
        "uniform7Active",  SoSeparator  --- 
  PVT   "uniform8Switch",  SoSwitch  --- 
        "uniform8",  SoSeparator  --- 
        "uniform8Active",  SoSeparator  --- 
  PVT   "arrowTranslation",  SoTranslation  --- 
  PVT   "arrow1Switch",  SoSwitch  --- 
        "arrow1",  SoSeparator  --- 
  PVT   "arrow2Switch",  SoSwitch  --- 
        "arrow2",  SoSeparator  --- 
  PVT   "arrow3Switch",  SoSwitch  --- 
        "arrow3",  SoSeparator  --- 
  PVT   "arrow4Switch",  SoSwitch  --- 
        "arrow4",  SoSeparator  --- 
  PVT   "arrow5Switch",  SoSwitch  --- 
        "arrow5",  SoSeparator  --- 
  PVT   "arrow6Switch",  SoSwitch  --- 
        "arrow6",  SoSeparator  --- 
  \endverbatim

  \NODEKIT_POST_TABLE
*/
SoHandleBoxDragger::SoHandleBoxDragger(void)
{
  SO_KIT_INTERNAL_CONSTRUCTOR(SoHandleBoxDragger);

  SO_KIT_ADD_CATALOG_ENTRY(surroundScale, SoSurroundScale, TRUE, topSeparator, geomSeparator, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(drawStyle, SoDrawStyle, TRUE, geomSeparator, translator1Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(translator1Switch, SoSwitch, TRUE, geomSeparator, translator2Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(translator1, SoSeparator, TRUE, translator1Switch, translator1Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(translator1Active, SoSeparator, TRUE, translator1Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(translator2Switch, SoSwitch, TRUE, geomSeparator, translator3Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(translator2, SoSeparator, TRUE, translator2Switch, translator2Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(translator2Active, SoSeparator, TRUE, translator2Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(translator3Switch, SoSwitch, TRUE, geomSeparator, translator4Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(translator3, SoSeparator, TRUE, translator3Switch, translator3Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(translator3Active, SoSeparator, TRUE, translator3Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(translator4Switch, SoSwitch, TRUE, geomSeparator, translator5Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(translator4, SoSeparator, TRUE, translator4Switch, translator4Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(translator4Active, SoSeparator, TRUE, translator4Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(translator5Switch, SoSwitch, TRUE, geomSeparator, translator6Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(translator5, SoSeparator, TRUE, translator5Switch, translator5Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(translator5Active, SoSeparator, TRUE, translator5Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(translator6Switch, SoSwitch, TRUE, geomSeparator, extruder1Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(translator6, SoSeparator, TRUE, translator6Switch, translator6Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(translator6Active, SoSeparator, TRUE, translator6Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(extruder1Switch, SoSwitch, TRUE, geomSeparator, extruder2Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(extruder1, SoSeparator, TRUE, extruder1Switch, extruder1Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(extruder1Active, SoSeparator, TRUE, extruder1Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(extruder2Switch, SoSwitch, TRUE, geomSeparator, extruder3Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(extruder2, SoSeparator, TRUE, extruder2Switch, extruder2Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(extruder2Active, SoSeparator, TRUE, extruder2Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(extruder3Switch, SoSwitch, TRUE, geomSeparator, extruder4Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(extruder3, SoSeparator, TRUE, extruder3Switch, extruder3Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(extruder3Active, SoSeparator, TRUE, extruder3Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(extruder4Switch, SoSwitch, TRUE, geomSeparator, extruder5Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(extruder4, SoSeparator, TRUE, extruder4Switch, extruder4Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(extruder4Active, SoSeparator, TRUE, extruder4Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(extruder5Switch, SoSwitch, TRUE, geomSeparator, extruder6Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(extruder5, SoSeparator, TRUE, extruder5Switch, extruder5Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(extruder5Active, SoSeparator, TRUE, extruder5Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(extruder6Switch, SoSwitch, TRUE, geomSeparator, uniform1Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(extruder6, SoSeparator, TRUE, extruder6Switch, extruder6Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(extruder6Active, SoSeparator, TRUE, extruder6Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform1Switch, SoSwitch, TRUE, geomSeparator, uniform2Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform1, SoSeparator, TRUE, uniform1Switch, uniform1Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform1Active, SoSeparator, TRUE, uniform1Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform2Switch, SoSwitch, TRUE, geomSeparator, uniform3Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform2, SoSeparator, TRUE, uniform2Switch, uniform2Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform2Active, SoSeparator, TRUE, uniform2Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform3Switch, SoSwitch, TRUE, geomSeparator, uniform4Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform3, SoSeparator, TRUE, uniform3Switch, uniform3Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform3Active, SoSeparator, TRUE, uniform3Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform4Switch, SoSwitch, TRUE, geomSeparator, uniform5Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform4, SoSeparator, TRUE, uniform4Switch, uniform4Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform4Active, SoSeparator, TRUE, uniform4Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform5Switch, SoSwitch, TRUE, geomSeparator, uniform6Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform5, SoSeparator, TRUE, uniform5Switch, uniform5Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform5Active, SoSeparator, TRUE, uniform5Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform6Switch, SoSwitch, TRUE, geomSeparator, uniform7Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform6, SoSeparator, TRUE, uniform6Switch, uniform6Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform6Active, SoSeparator, TRUE, uniform6Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform7Switch, SoSwitch, TRUE, geomSeparator, uniform8Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform7, SoSeparator, TRUE, uniform7Switch, uniform7Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform7Active, SoSeparator, TRUE, uniform7Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform8Switch, SoSwitch, TRUE, geomSeparator, arrowTranslation, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform8, SoSeparator, TRUE, uniform8Switch, uniform8Active, TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(uniform8Active, SoSeparator, TRUE, uniform8Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(arrowTranslation, SoTranslation, TRUE, geomSeparator, arrow1Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(arrow1Switch, SoSwitch, TRUE, geomSeparator, arrow2Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(arrow1, SoSeparator, TRUE, arrow1Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(arrow2Switch, SoSwitch, TRUE, geomSeparator, arrow3Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(arrow2, SoSeparator, TRUE, arrow2Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(arrow3Switch, SoSwitch, TRUE, geomSeparator, arrow4Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(arrow3, SoSeparator, TRUE, arrow3Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(arrow4Switch, SoSwitch, TRUE, geomSeparator, arrow5Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(arrow4, SoSeparator, TRUE, arrow4Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(arrow5Switch, SoSwitch, TRUE, geomSeparator, arrow6Switch, FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(arrow5, SoSeparator, TRUE, arrow5Switch, "", TRUE);
  SO_KIT_ADD_CATALOG_ENTRY(arrow6Switch, SoSwitch, TRUE, geomSeparator, "", FALSE);
  SO_KIT_ADD_CATALOG_ENTRY(arrow6, SoSeparator, TRUE, arrow6Switch, "", TRUE);

  if (SO_KIT_IS_FIRST_INSTANCE()) {
    SoInteractionKit::readDefaultParts("handleBoxDragger.iv",
                                       HANDLEBOXDRAGGER_draggergeometry,
                                       (int)strlen(HANDLEBOXDRAGGER_draggergeometry));
  }

  SO_KIT_ADD_FIELD(translation, (0.0f, 0.0f, 0.0f));
  SO_KIT_ADD_FIELD(scaleFactor, (1.0f, 1.0f, 1.0f));

  SO_KIT_INIT_INSTANCE();

  this->setPartAsDefault("translator1", "handleBoxTranslator1");
  this->setPartAsDefault("translator2", "handleBoxTranslator2");
  this->setPartAsDefault("translator3", "handleBoxTranslator3");
  this->setPartAsDefault("translator4", "handleBoxTranslator4");
  this->setPartAsDefault("translator5", "handleBoxTranslator5");
  this->setPartAsDefault("translator6", "handleBoxTranslator6");

  this->setPartAsDefault("translator1Active", "handleBoxTranslator1Active");
  this->setPartAsDefault("translator2Active", "handleBoxTranslator2Active");
  this->setPartAsDefault("translator3Active", "handleBoxTranslator3Active");
  this->setPartAsDefault("translator4Active", "handleBoxTranslator4Active");
  this->setPartAsDefault("translator5Active", "handleBoxTranslator5Active");
  this->setPartAsDefault("translator6Active", "handleBoxTranslator6Active");

  this->setPartAsDefault("extruder1", "handleBoxExtruder1");
  this->setPartAsDefault("extruder2", "handleBoxExtruder2");
  this->setPartAsDefault("extruder3", "handleBoxExtruder3");
  this->setPartAsDefault("extruder4", "handleBoxExtruder4");
  this->setPartAsDefault("extruder5", "handleBoxExtruder5");
  this->setPartAsDefault("extruder6", "handleBoxExtruder6");

  this->setPartAsDefault("extruder1Active", "handleBoxExtruder1Active");
  this->setPartAsDefault("extruder2Active", "handleBoxExtruder2Active");
  this->setPartAsDefault("extruder3Active", "handleBoxExtruder3Active");
  this->setPartAsDefault("extruder4Active", "handleBoxExtruder4Active");
  this->setPartAsDefault("extruder5Active", "handleBoxExtruder5Active");
  this->setPartAsDefault("extruder6Active", "handleBoxExtruder6Active");

  this->setPartAsDefault("uniform1", "handleBoxUniform1");
  this->setPartAsDefault("uniform2", "handleBoxUniform2");
  this->setPartAsDefault("uniform3", "handleBoxUniform3");
  this->setPartAsDefault("uniform4", "handleBoxUniform4");
  this->setPartAsDefault("uniform5", "handleBoxUniform5");
  this->setPartAsDefault("uniform6", "handleBoxUniform6");
  this->setPartAsDefault("uniform7", "handleBoxUniform7");
  this->setPartAsDefault("uniform8", "handleBoxUniform8");

  this->setPartAsDefault("uniform1Active", "handleBoxUniform1Active");
  this->setPartAsDefault("uniform2Active", "handleBoxUniform2Active");
  this->setPartAsDefault("uniform3Active", "handleBoxUniform3Active");
  this->setPartAsDefault("uniform4Active", "handleBoxUniform4Active");
  this->setPartAsDefault("uniform5Active", "handleBoxUniform5Active");
  this->setPartAsDefault("uniform6Active", "handleBoxUniform6Active");
  this->setPartAsDefault("uniform7Active", "handleBoxUniform7Active");
  this->setPartAsDefault("uniform8Active", "handleBoxUniform8Active");

  this->setPartAsDefault("arrow1", "handleBoxArrow1");
  this->setPartAsDefault("arrow2", "handleBoxArrow2");
  this->setPartAsDefault("arrow3", "handleBoxArrow3");
  this->setPartAsDefault("arrow4", "handleBoxArrow4");
  this->setPartAsDefault("arrow5", "handleBoxArrow5");
  this->setPartAsDefault("arrow6", "handleBoxArrow6");

  this->constraintState = CONSTRAINT_OFF;
  this->whatkind = WHATKIND_NONE;

  this->setAllPartsActive(FALSE);

  this->planeProj = new SbPlaneProjector;
  this->lineProj = new SbLineProjector;

  this->addStartCallback(SoHandleBoxDragger::startCB);
  this->addMotionCallback(SoHandleBoxDragger::motionCB);
  this->addFinishCallback(SoHandleBoxDragger::finishCB);
  this->addValueChangedCallback(SoHandleBoxDragger::valueChangedCB);
  this->addOtherEventCallback(SoHandleBoxDragger::metaKeyChangeCB);

  this->translFieldSensor = new SoFieldSensor(SoHandleBoxDragger::fieldSensorCB, this);
  this->translFieldSensor->setPriority(0);
  this->scaleFieldSensor = new SoFieldSensor(SoHandleBoxDragger::fieldSensorCB, this);
  this->scaleFieldSensor->setPriority(0);

  this->setUpConnections(TRUE, TRUE);
}

/*!
  Protected destructor.

  (Dragger classes are derived from SoBase, so they are reference
  counted and automatically destroyed when their reference count goes
  to 0.)
 */
SoHandleBoxDragger::~SoHandleBoxDragger()
{
  delete this->lineProj;
  delete this->planeProj;
  delete this->translFieldSensor;
  delete this->scaleFieldSensor;
}

// Doc in superclass.
SbBool
SoHandleBoxDragger::setUpConnections(SbBool onoff, SbBool doitalways)
{
  if (!doitalways && this->connectionsSetUp == onoff) return onoff;

  if (onoff) {
    inherited::setUpConnections(onoff, doitalways);

    SoHandleBoxDragger::fieldSensorCB(this, NULL);

    if (this->translFieldSensor->getAttachedField() != &this->translation) {
      this->translFieldSensor->attach(&this->translation);
    }
    if (this->scaleFieldSensor->getAttachedField() != &this->scaleFactor) {
      this->scaleFieldSensor->attach(&this->scaleFactor);
    }

  }
  else {
    if (this->translFieldSensor->getAttachedField() != NULL) {
      this->translFieldSensor->detach();
    }
    if (this->scaleFieldSensor->getAttachedField() != NULL) {
      this->scaleFieldSensor->detach();
    }
    inherited::setUpConnections(onoff, doitalways);
  }
  return !(this->connectionsSetUp = onoff);
}

// Doc in superclass.
void
SoHandleBoxDragger::setDefaultOnNonWritingFields(void)
{
  this->surroundScale.setDefault(TRUE);
  this->arrowTranslation.setDefault(TRUE);
  this->drawStyle.setDefault(TRUE);

  inherited::setDefaultOnNonWritingFields();
}

/*! \COININTERNAL */
void
SoHandleBoxDragger::fieldSensorCB(void * d, SoSensor *)
{
  SoHandleBoxDragger *thisp = (SoHandleBoxDragger*)d;
  SbMatrix matrix = thisp->getMotionMatrix();
  thisp->workFieldsIntoTransform(matrix);
  thisp->setMotionMatrix(matrix);
}

/*! \COININTERNAL */
void
SoHandleBoxDragger::valueChangedCB(void * f, SoDragger * d)
{
  SoHandleBoxDragger *thisp = (SoHandleBoxDragger*)d;
  SbMatrix matrix = thisp->getMotionMatrix();
  SbVec3f trans, scale;
  SbRotation rot, scaleOrient;
  matrix.getTransform(trans, rot, scale, scaleOrient);

  thisp->translFieldSensor->detach();
  if (thisp->translation.getValue() != trans)
    thisp->translation = trans;
  thisp->translFieldSensor->attach(&thisp->translation);

  thisp->scaleFieldSensor->detach();
  if (thisp->scaleFactor.getValue() != scale)
    thisp->scaleFactor = scale;
  thisp->scaleFieldSensor->attach(&thisp->scaleFactor);
}

/*! \COININTERNAL */
void
SoHandleBoxDragger::startCB(void *, SoDragger * d)
{
  SoHandleBoxDragger *thisp = (SoHandleBoxDragger*)d;
  thisp->dragStart();
}

/*! \COININTERNAL */
void
SoHandleBoxDragger::motionCB(void *, SoDragger * d)
{
  SoHandleBoxDragger *thisp = (SoHandleBoxDragger*)d;
  thisp->drag();
}

/*! \COININTERNAL */
void
SoHandleBoxDragger::finishCB(void *, SoDragger * d)
{
  SoHandleBoxDragger *thisp = (SoHandleBoxDragger*)d;
  thisp->dragFinish();
}

/*! \COININTERNAL */
void
SoHandleBoxDragger::metaKeyChangeCB(void *, SoDragger * d)
{
  SoHandleBoxDragger *thisp = (SoHandleBoxDragger*)d;
  if (!thisp->isActive.getValue()) return;

  const SoEvent *event = thisp->getEvent();
  if (SO_KEY_RELEASE_EVENT(event, LEFT_SHIFT) ||
      SO_KEY_RELEASE_EVENT(event, RIGHT_SHIFT)) {
    if (thisp->constraintState != CONSTRAINT_OFF) thisp->drag();
  }
  else if (thisp->ctrlDown != event->wasCtrlDown()) {
    thisp->ctrlDown = !thisp->ctrlDown;
    thisp->updateSwitches();
  }
}

// Invalidate surround scale node, if it exists.
//
// Note: keep the function name prefix to avoid name clashes with
// other dragger .cpp files for "--enable-compact" builds.
//
// FIXME: should collect these methods in a common method visible to
// all draggers implementing the exact same functionality. 20010826 mortene.
static void 
SoHandleBoxDragger_invalidate_surroundscale(SoBaseKit * kit)
{
  SoSurroundScale * ss = (SoSurroundScale*) 
    kit->getPart("surroundScale", FALSE);
  if (ss) ss->invalidate();
}

/*! \COININTERNAL
  Called when dragger is selected (picked) by the user.
*/
void
SoHandleBoxDragger::dragStart(void)
{
  SoHandleBoxDragger_invalidate_surroundscale(this);

  static const char translatorname[] = "translator";
  static const char extrudername[] = "extruder";
  static const char uniformname[] = "uniform";

  const SoPath *pickpath = this->getPickPath();

  SbBool found = FALSE;
  this->whatkind = WHATKIND_NONE;
  this->whatnum = 0;

  int i;
  SbString str;
  if (!found) {
    for (i = 1; i <= 6; i++) {
      str.sprintf("%s%d", translatorname, i);
      if (pickpath->findNode(this->getNodeFieldNode(str.getString())) >= 0||
          this->getSurrogatePartPickedName() == str.getString()) break;
    }
    if (i <= 6) {
      found = TRUE;
      this->whatkind = WHATKIND_TRANSLATOR;
      this->whatnum = i;
    }
  }

  if (!found) {
    for (i = 1; i <= 6; i++) {
      str.sprintf("%s%d", extrudername, i);
      if (pickpath->findNode(this->getNodeFieldNode(str.getString()))>= 0 ||
          this->getSurrogatePartPickedName() == str.getString()) break;
    }
    if (i <= 6) {
      found = TRUE;
      this->whatkind = WHATKIND_EXTRUDER;
      this->whatnum = i;
    }
  }
  if (!found) {
    for (i = 1; i <= 8; i++) {
      str.sprintf("%s%d", uniformname, i);
      if (pickpath->findNode(this->getNodeFieldNode(str.getString()))>= 0 ||
          this->getSurrogatePartPickedName() == str.getString()) break;
    }
    if (i <= 8) {
      found = TRUE;
      this->whatkind = WHATKIND_UNIFORM;
      this->whatnum = i;
    }
  }
  assert(found);
  if (!found) return;

  SbVec3f startPt = this->getLocalStartingPoint();

  switch(this->whatkind) {
  case WHATKIND_TRANSLATOR:
    {
      SbVec3f n;
      if (this->whatnum <= 2) {
        n = SbVec3f(0.0f, 1.0f, 0.0f);
      }
      else if (this->whatnum <= 4) {
        n = SbVec3f(1.0f, 0.0f, 0.0f);
      }
      else {
        n = SbVec3f(0.0f, 0.0f, 1.0f);
      }
      SbVec3f localPt;
      {
        SbMatrix mat, inv;
        this->getSurroundScaleMatrices(mat, inv);
        inv.multVecMatrix(startPt, localPt);
      }
      this->planeProj->setPlane(SbPlane(n, startPt));
      SbLine myline(SbVec3f(0.0f, 0.0f, 0.0f), n);
      SoTranslation *t = SO_GET_ANY_PART(this, "arrowTranslation", SoTranslation);
      t->translation = myline.getClosestPoint(localPt);
      if (this->getEvent()->wasShiftDown()) {
        this->getLocalToWorldMatrix().multVecMatrix(startPt, this->worldRestartPt);
        this->constraintState = CONSTRAINT_WAIT;
      }
    }
    break;
  case WHATKIND_EXTRUDER:
    this->lineProj->setLine(SbLine(this->getDraggerCenter(), startPt));
    this->ctrlOffset = this->calcCtrlOffset(startPt);
    break;
  case WHATKIND_UNIFORM:
    this->lineProj->setLine(SbLine(this->getDraggerCenter(), startPt));
    this->ctrlOffset = this->calcCtrlOffset(startPt);
    break;
  }
  this->ctrlDown = this->getEvent()->wasCtrlDown();
  this->updateSwitches();
}

/*! \COININTERNAL
  Called when user drags the mouse after picking the dragger.
*/
void
SoHandleBoxDragger::drag(void)
{
  SbVec3f startPt = this->getLocalStartingPoint();

  if (this->whatkind == WHATKIND_TRANSLATOR) {
    this->planeProj->setViewVolume(this->getViewVolume());
    this->planeProj->setWorkingSpace(this->getLocalToWorldMatrix());
    SbVec3f projPt = this->planeProj->project(this->getNormalizedLocaterPosition());

    const SoEvent *event = this->getEvent();
    if (event->wasShiftDown() && this->constraintState == CONSTRAINT_OFF) {
      this->constraintState = CONSTRAINT_WAIT;
      this->setStartLocaterPosition(event->getPosition());
      this->getLocalToWorldMatrix().multVecMatrix(projPt, this->worldRestartPt);
    }
    else if (!event->wasShiftDown() && this->constraintState != CONSTRAINT_OFF) {
      this->constraintState = CONSTRAINT_OFF;
      this->updateArrows();
    }

    SbVec3f motion, localrestartpt;
    if (this->constraintState != CONSTRAINT_OFF) {
      this->getWorldToLocalMatrix().multVecMatrix(this->worldRestartPt,
                                                  localrestartpt);
      motion = localrestartpt - startPt;
    }
    else motion = projPt - startPt;
    switch(this->constraintState) {
    case CONSTRAINT_OFF:
      break;
    case CONSTRAINT_WAIT:
      if (this->isAdequateConstraintMotion()) {
        SbVec3f newmotion = projPt - localrestartpt;
        int biggest = 0;
        double bigval = fabs(newmotion[0]);
        if (fabs(newmotion[1]) > bigval) {
          biggest = 1;
          bigval = fabs(newmotion[1]);
        }
        if (fabs(newmotion[2]) > bigval) {
          biggest = 2;
        }
        motion[biggest] += newmotion[biggest];
        this->constraintState = CONSTRAINT_X + biggest;
        this->updateArrows();
      }
      else {
        return;
      }
      break;
    case CONSTRAINT_X:
      motion[0] += projPt[0] - localrestartpt[0];
      break;
    case CONSTRAINT_Y:
      motion[1] += projPt[1] - localrestartpt[1];
      break;
    case CONSTRAINT_Z:
      motion[2] += projPt[2] - localrestartpt[2];
    }
    this->setMotionMatrix(this->appendTranslation(this->getStartMotionMatrix(), motion));
  }
  else {
    this->lineProj->setViewVolume(this->getViewVolume());
    this->lineProj->setWorkingSpace(this->getLocalToWorldMatrix());
    SbVec3f projPt = this->lineProj->project(this->getNormalizedLocaterPosition());
    SbVec3f center = this->getDraggerCenter();
    if (this->getEvent()->wasCtrlDown()) {
      center += this->ctrlOffset;
    }

    float orglen = (startPt-center).length();
    float currlen = (projPt-center).length();
    float scale = 0.0f;

    if (orglen > 0.0f) scale = currlen / orglen;
    if (scale > 0.0f && (startPt-center).dot(projPt-center) <= 0.0f) scale = 0.0f;

    SbVec3f scalevec(scale, scale, scale);
    if (this->whatkind == WHATKIND_EXTRUDER) {
      if (this->whatnum <= 2) scalevec[0] = scalevec[2] = 1.0f;
      else if (this->whatnum <= 4) scalevec[1] = scalevec[2] = 1.0f;
      else scalevec[0] = scalevec[1] = 1.0f;
    }

    this->setMotionMatrix(this->appendScale(this->getStartMotionMatrix(),
                                            scalevec,
                                            center));
  }
}

/*! \COININTERNAL
  Called when mouse button is released after picking and interacting
  with the dragger.
*/
void
SoHandleBoxDragger::dragFinish(void)
{
  this->constraintState = CONSTRAINT_OFF;
  this->whatkind = WHATKIND_NONE;
  this->setAllPartsActive(FALSE);

  SoHandleBoxDragger_invalidate_surroundscale(this);
}

/*!
  Activate or deactive all dragger geometry parts.
*/
void
SoHandleBoxDragger::setAllPartsActive(SbBool onoroff)
{
  int i;
  int val = onoroff ? 1 : 0;
  SoSwitch *sw;
  SbString str;
  for (i = 1; i <= 6; i++) {
    str.sprintf("translator%dSwitch", i);
    sw = SO_GET_ANY_PART(this, str.getString(), SoSwitch);
    SoInteractionKit::setSwitchValue(sw, val);
  }
  for (i = 1; i <= 6; i++) {
    str.sprintf("extruder%dSwitch", i);
    sw = SO_GET_ANY_PART(this, str.getString(), SoSwitch);
    SoInteractionKit::setSwitchValue(sw, val);
  }
  for (i = 1; i <= 8; i++) {
    str.sprintf("uniform%dSwitch", i);
    sw = SO_GET_ANY_PART(this, str.getString(), SoSwitch);
    SoInteractionKit::setSwitchValue(sw, val);
  }
  this->updateArrows();
}

// Return node pointer from a SoSFNode field. Does misc sanity
// checking for robustness.
SoNode *
SoHandleBoxDragger::getNodeFieldNode(const char * fieldname)
{
  SoField *field = this->getField(fieldname);
  assert(field != NULL);
  assert(field->isOfType(SoSFNode::getClassTypeId()));
  assert(((SoSFNode*)field)->getValue() != NULL);
  return ((SoSFNode*)field)->getValue();
}

void
SoHandleBoxDragger::updateSwitches(void)
{
  int i;
  SbString str;
  SoSwitch *sw;

  if (this->whatkind == WHATKIND_UNIFORM) {
    if (this->ctrlDown) {
      const int *ptr = uniform_ctrl_lookup[this->whatnum-1];
      for (i = 0; i < 6; i++) {
        str.sprintf("extruder%dSwitch", ptr[i]);
        sw = SO_GET_ANY_PART(this, str.getString(), SoSwitch);
        SoInteractionKit::setSwitchValue(sw, i < 3 ? 1 : 0);
      }
    }
    else {
      for (i = 1; i <= 6; i++) {
        str.sprintf("extruder%dSwitch", i);
        sw = SO_GET_ANY_PART(this, str.getString(), SoSwitch);
        SoInteractionKit::setSwitchValue(sw, 1);
      }
    }
    str.sprintf("uniform%dSwitch", this->whatnum);
    sw = SO_GET_ANY_PART(this, str.getString(), SoSwitch);
    SoInteractionKit::setSwitchValue(sw, 1);
  }
  else if (this->whatkind == WHATKIND_EXTRUDER) {
    int othernum = ((this->whatnum-1) & ~1) + 1;
    if (othernum == this->whatnum) othernum++;

    str.sprintf("extruder%dSwitch", this->whatnum);
    sw = SO_GET_ANY_PART(this, str.getString(), SoSwitch);
    SoInteractionKit::setSwitchValue(sw, 1);
    str.sprintf("extruder%dSwitch", othernum);
    sw = SO_GET_ANY_PART(this, str.getString(), SoSwitch);
    SoInteractionKit::setSwitchValue(sw, this->ctrlDown ? 0 : 1);
  }
  else {
    this->setAllPartsActive(TRUE);
    this->updateArrows();
  }
}

void
SoHandleBoxDragger::updateArrows(void)
{
  int i;
  SbString str;
  SoSwitch *sw;

  if (this->constraintState >= CONSTRAINT_X) {
    int onval = -1;
    switch (this->constraintState) {
    case CONSTRAINT_X:
      onval = 3;
      break;
    case CONSTRAINT_Y:
      onval = 1;
      break;
    case CONSTRAINT_Z:
      onval = 5;
      break;
    }
    for (i = 1; i <= 6; i++) {
      str.sprintf("arrow%dSwitch", i);
      sw = SO_GET_ANY_PART(this, str.getString(), SoSwitch);
      if (i == onval || i == onval + 1) {
        SoInteractionKit::setSwitchValue(sw, 0);
      }
      else {
        SoInteractionKit::setSwitchValue(sw, SO_SWITCH_NONE);
      }
    }
  }
  else if (this->whatkind == WHATKIND_TRANSLATOR) {
    int num = (this->whatnum-1) & ~1;
    for (i = 0; i < 6; i++) {
      str.sprintf("arrow%dSwitch", i+1);
      sw = SO_GET_ANY_PART(this, str.getString(), SoSwitch);
      if (i == num || i == num+1) {
        SoInteractionKit::setSwitchValue(sw, SO_SWITCH_NONE);
      }
      else {
        SoInteractionKit::setSwitchValue(sw, 0);
      }
    }
  }
  else {
    for (i = 1; i <= 6; i++) {
      str.sprintf("arrow%dSwitch", i);
      sw = SO_GET_ANY_PART(this, str.getString(), SoSwitch);
      SoInteractionKit::setSwitchValue(sw, SO_SWITCH_NONE);
    }
  }
}

void
SoHandleBoxDragger::getSurroundScaleMatrices(SbMatrix &mat, SbMatrix &inv)
{
  if (this->surroundScale.getValue()) {
    this->getPartToLocalMatrix("surroundScale", mat, inv);
  }
  else {
    mat = inv = SbMatrix::identity();
  }
}

SbVec3f
SoHandleBoxDragger::getDraggerCenter(void)
{
  SbMatrix mat, inv;
  this->getSurroundScaleMatrices(mat, inv);
  return SbVec3f(mat[3][0], mat[3][1], mat[3][2]);
}

SbVec3f
SoHandleBoxDragger::calcCtrlOffset(const SbVec3f startpt)
{
  SbMatrix m, inv;
  this->getSurroundScaleMatrices(m, inv);
  SbVec3f v = SbVec3f(m[3][0], m[3][1], m[3][2]) - startpt;

  for (int i = 0; i < 3; i++) {
    v[i] *= inv[i][i];
    if (v[i] < -0.95) v[i] = -1.0f;
    else if (v[i] > 0.95) v[i] = 1.0f;
    else v[i] = 0.0f;
    v[i] *= m[i][i];
  }
  return v;
}
