/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2010-2012 University of Bonn. All rights reserved.
 * Copyright (C)  2013 Frederik Heber. All rights reserved.
 * 
 *
 *   This file is part of MoleCuilder.
 *
 *    MoleCuilder is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    MoleCuilder 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 General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with MoleCuilder.  If not, see .
 */
/*
 * GLWorldView.cpp
 *
 *  Created on: Aug 1, 2010
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "GLWorldView.hpp"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "GLWorldScene.hpp"
#include "CodePatterns/MemDebug.hpp"
#include "Atom/AtomObserver.hpp"
#include "Atom/atom_observable.hpp"
#include "Box.hpp"
#include "CodePatterns/Log.hpp"
#include "CodePatterns/Observer/Notification.hpp"
#include "CodePatterns/Observer/ObserverLog.hpp"
#include "Descriptors/MoleculeIdDescriptor.hpp"
#include "molecule.hpp"
#include "Shapes/ShapeRegistry.hpp"
#include "World.hpp"
#include "WorldTime.hpp"
GLWorldView::GLWorldView(
    QtObservedInstanceBoard * _board,
    QWidget *parent) :
  QGLView(parent),
  Observer("GLWorldView"),
  worldscene(NULL),
  changesPresent(false),
  needsRedraw(false)
{
  worldscene = new GLWorldScene(_board, this);
  setOption(QGLView::ObjectPicking, true);
  setOption(QGLView::CameraNavigation, false);
  setFocusPolicy(Qt::StrongFocus);
  setCameraControlMode(Rotate);
  defaultEyeSeparation = 4.0;
  createDomainBox();
  createDreiBein();
  //changeMaterials(false);
  qRegisterMetaType("atomId_t");
  qRegisterMetaType("moleculeId_t");
  connect(this, SIGNAL(ShapeAdded(const std::string &)), worldscene, SLOT(addShape(const std::string &)));
  connect(this, SIGNAL(ShapeRemoved(const std::string &)), worldscene, SLOT(removeShape(const std::string &)));
//  connect(this, SIGNAL(TimeChanged()), worldscene, SIGNAL(updated()));
  connect(worldscene, SIGNAL(changeOccured()), this, SLOT(changeSignalled()));
  connect(worldscene, SIGNAL(changed()), this, SIGNAL(changed()));
  connect(worldscene, SIGNAL(hoverChanged(const atomId_t)), this, SLOT(sceneHoverSignalled(const atomId_t)));
  connect(worldscene, SIGNAL(hoverChanged(const moleculeId_t, int)), this, SLOT(sceneHoverSignalled(const moleculeId_t, int)));
  //connect(this, SIGNAL(changed()), this, SLOT(updateGL()));
  connect(this, SIGNAL(changed()), this, SLOT(sceneChangeSignalled()));
  connect(this, SIGNAL(moleculesVisibilityChanged(const moleculeId_t,bool)), worldscene, SLOT(moleculesVisibilityChanged(const moleculeId_t,bool)));
  // sign on to changes in the world
  World::getInstance().signOn(this);
  World::getInstance().signOn(this, World::SelectionChanged);
//  WorldTime::getInstance().signOn(this, WorldTime::TimeChanged);
  AtomObserver::getInstance().signOn(this, AtomObservable::PositionChanged);
  ShapeRegistry::getInstance().signOn(this);
  ShapeRegistry::getInstance().signOn(this, ShapeRegistry::ShapeInserted);
  ShapeRegistry::getInstance().signOn(this, ShapeRegistry::ShapeRemoved);
  ShapeRegistry::getInstance().signOn(this, ShapeRegistry::SelectionChanged);
  redrawTimer = new QTimer(this);
}
GLWorldView::~GLWorldView()
{
  QSettings settings;
  settings.beginGroup("WorldView");
  settings.setValue("domainBoxEnabled", (meshDomainBox->options() & QGLSceneNode::HideNode) == 0);
  settings.setValue("dreiBeinEnabled", (meshDreiBein->options() & QGLSceneNode::HideNode) == 0);
  settings.endGroup();
  World::getInstance().signOff(this);
  World::getInstance().signOff(this, World::SelectionChanged);
//  WorldTime::getInstance().signOff(this, WorldTime::TimeChanged);
  AtomObserver::getInstance().signOff(this, AtomObservable::PositionChanged);
  ShapeRegistry::getInstance().signOff(this);
  ShapeRegistry::getInstance().signOff(this, ShapeRegistry::ShapeInserted);
  ShapeRegistry::getInstance().signOff(this, ShapeRegistry::ShapeRemoved);
  ShapeRegistry::getInstance().signOff(this, ShapeRegistry::SelectionChanged);
  delete worldscene;
  delete(domainBoxMaterial);
  for (int i=0;i<3;i++)
    delete(dreiBeinMaterial[i]);
}
/**
 * Add some widget specific actions to the toolbar:
 * - camera rotation/translation mode
 * - camera fit to domain
 */
void GLWorldView::addToolBarActions(QToolBar *toolbar)
{
  // camera control mode
  toolbar->addSeparator();
  QAction *transAction = new QAction(QIcon::fromTheme("forward"), tr("camera translation mode"), this);
  connect(transAction, SIGNAL(triggered()), this, SLOT(setCameraControlModeTranslation()));
  toolbar->addAction(transAction);
  QAction *rotAction = new QAction(QIcon::fromTheme("object-rotate-left"), tr("camera rotation mode"), this);
  connect(rotAction, SIGNAL(triggered()), this, SLOT(setCameraControlModeRotation()));
  toolbar->addAction(rotAction);
  QAction *fitAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("camera fit to domain"), this);
  connect(fitAction, SIGNAL(triggered()), this, SLOT(fitCameraToDomain()));
  toolbar->addAction(fitAction);
  // stereo mode
  QToolButton *stereoButton = new QToolButton(toolbar);
  QMenu *stereoMenu = new QMenu();
  QAction *stereoDisableAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("disable"), this);
  connect(stereoDisableAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeDisable()));
  stereoMenu->addAction(stereoDisableAction);
  QAction *stereoHardwareAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("hardware"), this);
  connect(stereoHardwareAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeHardware()));
  stereoMenu->addAction(stereoHardwareAction);
  QAction *stereoLeftRightAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("left right"), this);
  connect(stereoLeftRightAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeLeftRight()));
  stereoMenu->addAction(stereoLeftRightAction);
  QAction *stereoRightLeftAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("right left"), this);
  connect(stereoRightLeftAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeRightLeft()));
  stereoMenu->addAction(stereoRightLeftAction);
  QAction *stereoTopBottomAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("top bottom"), this);
  connect(stereoTopBottomAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeTopBottom()));
  stereoMenu->addAction(stereoTopBottomAction);
  QAction *stereoBottomTopAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("bottom top"), this);
  connect(stereoBottomTopAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeBottomTop()));
  stereoMenu->addAction(stereoBottomTopAction);
  QAction *stereoAnaglyphAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("anaglyph"), this);
  connect(stereoAnaglyphAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeAnaglyph()));
  stereoMenu->addAction(stereoAnaglyphAction);
  stereoButton->setMenu(stereoMenu);
  stereoButton->setIcon(QIcon(QPixmap(":/icon_view_stereo.png")));
  stereoButton->setPopupMode(QToolButton::InstantPopup);
  toolbar->addWidget(stereoButton);
  // selection mode
  toolbar->addSeparator();
  QAction *selAtomAction = new QAction(QIcon(QPixmap(":/icon_select_atom.png")), tr("select atom by clicking"), this);
  connect(selAtomAction, SIGNAL(triggered()), worldscene, SLOT(setSelectionModeAtom()));
  toolbar->addAction(selAtomAction);
  QAction *selMolAction = new QAction(QIcon(QPixmap(":/icon_select_molecule.png")), tr("select molecule by clicking"), this);
  connect(selMolAction, SIGNAL(triggered()), worldscene, SLOT(setSelectionModeMolecule()));
  toolbar->addAction(selMolAction);
  // dreiBein/domain enabler
  toolbar->addSeparator();
  QAction *seldreiBein = new QAction(QIcon(QPixmap(":/icon_dreiBein.png")), tr("enable/disable dreiBein"), this);
  connect(seldreiBein, SIGNAL(triggered()), this, SLOT(changeDreiBein()));
  toolbar->addAction(seldreiBein);
  QAction *seldomain = new QAction(QIcon(QPixmap(":/icon_domain.png")), tr("enable/disable domain box"), this);
  connect(seldomain, SIGNAL(triggered()), this, SLOT(changeDomain()));
  toolbar->addAction(seldomain);
}
void GLWorldView::createDomainBox()
{
  QSettings settings;
  settings.beginGroup("WorldView");
  QColor colorFrame = settings.value("domainBoxColorFrame", QColor(150,160,200,255)).value();
  QColor colorAmbient = settings.value("domainBoxColorAmbient", QColor(50,60,100,255)).value();
  QColor colorDiffuse = settings.value("domainBoxColorDiffuse", QColor(150,160,200,180)).value();
  settings.setValue("domainBoxColorFrame", colorFrame);
  settings.setValue("domainBoxColorAmbient", colorAmbient);
  settings.setValue("domainBoxColorDiffuse", colorDiffuse);
  const bool status = settings.value("domainBoxEnabled").toBool();
  settings.endGroup();
  domainBoxMaterial = new QGLMaterial;
  domainBoxMaterial->setAmbientColor(QColor(0,0,0,255));
  domainBoxMaterial->setDiffuseColor(QColor(0,0,0,255));
  domainBoxMaterial->setEmittedLight(colorFrame);
  QGLMaterial *material = new QGLMaterial;
  material->setAmbientColor(colorAmbient);
  material->setDiffuseColor(colorDiffuse);
  QGLBuilder builder;
  builder << QGL::Faceted;
  builder << QGLCube(-1.0); // "inverted" => inside faces are used as front.
  meshDomainBox = builder.finalizedSceneNode();
  QMatrix4x4 mat;
  mat.translate(0.5f, 0.5f, 0.5f);
  meshDomainBox->setLocalTransform(mat);
  meshDomainBox->setMaterial(material);
  setDomainStatus( status );
}
void GLWorldView::createDreiBein()
{
  QSettings settings;
  settings.beginGroup("WorldView");
  QColor colorX = settings.value("dreiBeinColorX", QColor(255,50,50,255)).value();
  QColor colorY = settings.value("dreiBeinColorY", QColor(50,255,50,255)).value();
  QColor colorZ = settings.value("dreiBeinColorZ", QColor(50,50,255,255)).value();
  settings.setValue("dreiBeinColorX", colorX);
  settings.setValue("dreiBeinColorY", colorY);
  settings.setValue("dreiBeinColorZ", colorZ);
  const bool status = settings.value("dreiBeinEnabled").toBool();
  settings.endGroup();
  // Create 3 color for the 3 axes.
  dreiBeinMaterial[0] = new QGLMaterial;
  dreiBeinMaterial[0]->setColor(colorX);
  dreiBeinMaterial[1] = new QGLMaterial;
  dreiBeinMaterial[1]->setColor(colorY);
  dreiBeinMaterial[2] = new QGLMaterial;
  dreiBeinMaterial[2]->setColor(colorZ);
  // Create the basic meshes (cylinder and cone).
  QGLBuilder builderCyl;
  builderCyl << QGLCylinder(.15,.15,1.6,16);
  QGLSceneNode *cyl = builderCyl.finalizedSceneNode();
  QGLBuilder builderCone;
  builderCone << QGLCylinder(0,.4,0.4,16);
  QGLSceneNode *cone = builderCone.finalizedSceneNode();
  {
    QMatrix4x4 mat;
    mat.translate(0.0f, 0.0f, 1.0f);
    cone->setLocalTransform(mat);
  }
  // Create a scene node from the 3 axes.
  meshDreiBein = new QGLSceneNode(this);
  // X-direction
  QGLSceneNode *node = new QGLSceneNode(meshDreiBein);
  node->setMaterial(dreiBeinMaterial[0]);
  node->addNode(cyl);
  node->setPosition(QVector3D(.8f, 0.f, 0.f));
  node->addNode(cone);
  {
    QMatrix4x4 mat;
    mat.rotate(90, 0.0f, 1.0f, 0.0f);
    node->setLocalTransform(mat);
  }
  // Y-direction
  node = new QGLSceneNode(meshDreiBein);
  node->setMaterial(dreiBeinMaterial[1]);
  node->addNode(cyl);
  node->addNode(cone);
  {
    QMatrix4x4 mat;
    mat.rotate(-90, 1.0f, 0.0f, 0.0f);
    node->setLocalTransform(mat);
  }
  node->setPosition(QVector3D(0.f, .8f, 0.f));
  // Z-direction
  node = new QGLSceneNode(meshDreiBein);
  node->setMaterial(dreiBeinMaterial[2]);
  node->addNode(cyl);
  node->addNode(cone);
  node->setPosition(QVector3D(0.f, 0.f, .8f));
  setdreiBeinStatus( status );
}
void GLWorldView::setSelectionChangedAgent(QtSelectionChangedAgent *agent)
{
  worldscene->setSelectionChangedAgent(agent);
}
/**
 * Update operation which can be invoked by the observable (which should be the
 * change tracker here).
 */
void GLWorldView::update(Observable *publisher)
{
//  emit changed();
}
/**
 * The observable can tell when it dies.
 */
void GLWorldView::subjectKilled(Observable *publisher)
{
  // world never dies
}
/** Listen to specific changes to the world.
 *
 * @param publisher ref to observable.
 * @param notification type of notification
 */
void GLWorldView::recieveNotification(Observable *publisher, Notification_ptr notification)
{
  if (static_cast(publisher) == World::getPointer()) {
    switch (notification->getChannelNo()) {
      case World::SelectionChanged:
      {
  #ifdef LOG_OBSERVER
        observerLog().addMessage() << "++ Observer " << observerLog().getName(static_cast(this)) << " received notification that selection has changed.";
  #endif
        emit worldSelectionChanged();
        break;
      }
      default:
        ASSERT(0, "GLWorldView::recieveNotification() - we cannot get here for World.");
        break;
    }
  } else if (static_cast(publisher) == WorldTime::getPointer()) {
    switch (notification->getChannelNo()) {
      case WorldTime::TimeChanged:
      {
#ifdef LOG_OBSERVER
      observerLog().addMessage() << "++ Observer " << observerLog().getName(static_cast(this)) << " received notification that WorldTime's time has changed.";
#endif
        emit changed();
        emit TimeChanged();
        break;
      }
      default:
        ASSERT(0, "GLWorldView::recieveNotification() - we cannot get here for WorldTime.");
        break;
    }
  } else if (dynamic_cast(publisher) != NULL) {
    switch (notification->getChannelNo()) {
      case AtomObservable::PositionChanged:
      {
    #ifdef LOG_OBSERVER
        const atom *_atom = dynamic_cast(publisher);
        observerLog().addMessage() << "++ Observer " << observerLog().getName(static_cast(this)) << " received notification that atom "+toString(_atom->getId())+" has changed its position.";
    #endif
        emit changed();
        break;
      }
      default:
        ASSERT(0, "GLWorldView::recieveNotification() - we cannot get here for AtomObservable.");
        break;
    }
  } else if (static_cast(publisher) == ShapeRegistry::getPointer()) {
    switch (notification->getChannelNo()) {
      case ShapeRegistry::ShapeInserted:
      {
        emit ShapeAdded(ShapeRegistry::getInstance().lastChanged()->getName());
        break;
      }
      case ShapeRegistry::ShapeRemoved:
      {
        emit ShapeRemoved(ShapeRegistry::getInstance().lastChanged()->getName());
        break;
      }
      case ShapeRegistry::SelectionChanged:
      {
        worldscene->updateSelectedShapes();
        break;
      }
      default:
        ASSERT(0, "GLWorldView::recieveNotification() - we cannot get here for ShapeRegistry.");
        break;
    }
  }
}
void GLWorldView::checkChanges()
{
  updateGL();
  needsRedraw = false;
}
void GLWorldView::changeDreiBein()
{
  // invert to new status
  const bool status = ((meshDreiBein->options() & QGLSceneNode::HideNode) == 0);
  setdreiBeinStatus(!status);
  // realize
  updateGL();
  needsRedraw = true;
}
void GLWorldView::setdreiBeinStatus(const bool status)
{
  if (status)
    meshDreiBein->setOptions( meshDreiBein->options() & (255-QGLSceneNode::HideNode) );
  else
    meshDreiBein->setOptions( meshDreiBein->options() | QGLSceneNode::HideNode );
}
void GLWorldView::changeDomain()
{
  // invert to new status
  const bool status = ((meshDomainBox->options() & QGLSceneNode::HideNode) == 0);
  setDomainStatus(!status);
  // realize
  updateGL();
  needsRedraw = true;
}
void GLWorldView::setDomainStatus(const bool status)
{
  if (status)
    meshDomainBox->setOptions( meshDomainBox->options() & (255-QGLSceneNode::HideNode) );
  else
    meshDomainBox->setOptions( meshDomainBox->options() | QGLSceneNode::HideNode );
}
void GLWorldView::sceneChangeSignalled()
{
  if (!needsRedraw){
    redrawTimer->singleShot(0, this, SLOT(checkChanges()));
    needsRedraw = true;
    redrawTimer->start();
  }
}
void GLWorldView::initializeGL(QGLPainter *painter)
{
  worldscene->initialize(this, painter);
  changesPresent = false;
}
void GLWorldView::paintGL(QGLPainter *painter)
{
  if (changesPresent) {
    initializeGL(painter);
    changesPresent = false;
  }
  QVector3D cameraDir = camera()->center() - camera()->eye();
  cameraDir.normalize();
  QVector4D cameraPlane(cameraDir, QVector3D::dotProduct(cameraDir, camera()->eye()));
  worldscene->draw(painter, cameraPlane);
  drawDreiBein(painter);
  // Domain box has to be last because of its transparency.
  drawDomainBox(painter);
}
void GLWorldView::keyPressEvent(QKeyEvent *e)
{
  // Find the distance between the eye and focus point.
  QVector3D d = camera()->eye() - camera()->center();
//  LOG(1, "Distance vector eye and center is "
//      << d.x() << "," << d.y() << "," << d.z());
  // scale the move unit by the eye <-> domain center distance
  const double key_move_unit = 0.04*(d.length()/50.);
  if (e->key() == Qt::Key_Tab) {
    // The Tab key turns the ShowPicking option on and off,
    // which helps show what the pick buffer looks like.
    setOption(QGLView::ShowPicking, ((options() & QGLView::ShowPicking) == 0));
    updateGL();
  } else if ((e->key() == Qt::Key_Minus) || (e->key() == Qt::Key_Plus)) {
    // Scale the distance.
    if (e->key() == Qt::Key_Minus)
      d *= 1.2;
    else if (e->key() == Qt::Key_Plus)
      d /= 1.2;
    // Set new eye position.
    camera()->setEye(camera()->center() + d);
  } else if ((e->key() == Qt::Key_Left) || (e->key() == Qt::Key_Right)) {
    // Translate the camera.
    const double d = (e->key() == Qt::Key_Left) ? -key_move_unit : key_move_unit;
    camera()->translateCenter( d, 0., 0);
    camera()->translateEye( d, 0., 0);
  } else if ((e->key() == Qt::Key_Up) || (e->key() == Qt::Key_Down)) {
    // Translate the camera.
    const double d = (e->key() == Qt::Key_Up) ? -key_move_unit : key_move_unit;
    camera()->translateCenter( 0., d, 0);
    camera()->translateEye( 0., d, 0);
  } else if ((e->key() == Qt::Key_PageUp) || (e->key() == Qt::Key_PageDown)) {
    // Translate the camera.
    const double d = (e->key() == Qt::Key_PageUp) ? -key_move_unit : key_move_unit;
    camera()->translateCenter( 0., 0., d);
    camera()->translateEye( 0., 0., d);
  }
  QGLView::keyPressEvent(e);
}
void GLWorldView::changeSignalled()
{
  changesPresent = true;
}
/**
 * Set the current camera control mode.
 */
void GLWorldView::setCameraControlMode(GLWorldView::CameraControlModeType mode)
{
  cameraControlMode = mode;
}
void GLWorldView::setCameraControlModeRotation()
{
  setCameraControlMode(Rotate);
}
void GLWorldView::setCameraControlModeTranslation()
{
  setCameraControlMode(Translate);
}
/**
 * Returns the current camera control mode.
 * This needs to be invertable (rotation - translation), if the shift key is pressed.
 */
GLWorldView::CameraControlModeType GLWorldView::getCameraControlMode(bool inverted)
{
  if (inverted){
    if (cameraControlMode == Rotate)
      return Translate;
    if (cameraControlMode == Translate)
      return Rotate;
    return Rotate;
  }else
    return cameraControlMode;
}
/**
 * Set the camera so it can oversee the whole domain.
 */
void GLWorldView::fitCameraToDomain()
{
  // Move the camera focus point to the center of the domain box.
  Vector v = World::getInstance().getDomain().translateIn(Vector(0.5, 0.5, 0.5));
  camera()->setCenter(QVector3D(v[0], v[1], v[2]));
  // Guess some eye distance.
  double dist = v.Norm() * 3;
  camera()->setEye(QVector3D(v[0], v[1], v[2] + dist));
  camera()->setUpVector(QVector3D(0, 1, 0));
}
void GLWorldView::setCameraStereoModeDisable()
{
  setStereoType(QGLView::Hardware);
  camera()->setEyeSeparation(0.0);
  updateGL();
}
void GLWorldView::setCameraStereoModeHardware()
{
  setStereoType(QGLView::Hardware);
  camera()->setEyeSeparation(defaultEyeSeparation);
  updateGL();
}
void GLWorldView::setCameraStereoModeLeftRight()
{
  setStereoType(QGLView::LeftRight);
  camera()->setEyeSeparation(defaultEyeSeparation);
  updateGL();
}
void GLWorldView::setCameraStereoModeRightLeft()
{
  setStereoType(QGLView::RightLeft);
  camera()->setEyeSeparation(defaultEyeSeparation);
  updateGL();
}
void GLWorldView::setCameraStereoModeTopBottom()
{
  setStereoType(QGLView::TopBottom);
  camera()->setEyeSeparation(defaultEyeSeparation);
  updateGL();
}
void GLWorldView::setCameraStereoModeBottomTop()
{
  setStereoType(QGLView::BottomTop);
  camera()->setEyeSeparation(defaultEyeSeparation);
  updateGL();
}
void GLWorldView::setCameraStereoModeAnaglyph()
{
  setStereoType(QGLView::RedCyanAnaglyph);
  camera()->setEyeSeparation(defaultEyeSeparation);
  updateGL();
}
void GLWorldView::mousePressEvent(QMouseEvent *event)
{
  QGLView::mousePressEvent(event);
  // Reset the saved mouse position.
  lastMousePos = event->posF();
}
/**
 * Handle a mouse move event.
 * This is used to control the camera (rotation and translation) when the left button is being pressed.
 */
void GLWorldView::mouseMoveEvent(QMouseEvent *event)
{
  if (event->buttons() & Qt::LeftButton){
    // Find the mouse distance since the last event.
    QPointF d = event->posF() - lastMousePos;
    lastMousePos = event->posF();
    // Rotate or translate?   (inverted by shift key)
    CameraControlModeType mode = getCameraControlMode(event->modifiers() & Qt::ShiftModifier);
    if (mode == Rotate){
      // Rotate the camera.
      d *= 0.3;
      camera()->tiltPanRollCenter(- d.y(), - d.x(), 0);
    }else if (mode == Translate){
      // Translate the camera.
      d *= 0.02;
      camera()->translateCenter(- d.x(), d.y(), 0);
      camera()->translateEye(- d.x(), d.y(), 0);
    }
  }else{
    // Without this Qt would not test for hover events (i.e. mouse over an atom).
    QGLView::mouseMoveEvent(event);
  }
}
/**
 * When the mouse wheel is used, zoom in or out.
 */
void GLWorldView::wheelEvent(QWheelEvent *event)
{
  // Find the distance between the eye and focus point.
  QVector3D d = camera()->eye() - camera()->center();
  // Scale the distance.
  if (event->delta() < 0)
    d *= 1.2;
  else if (event->delta() > 0)
    d /= 1.2;
  // Set new eye position.
  camera()->setEye(camera()->center() + d);
}
/**
 * Draw a transparent cube representing the domain.
 */
void GLWorldView::drawDomainBox(QGLPainter *painter) const
{
  // Apply the domain matrix.
  RealSpaceMatrix m = World::getInstance().getDomain().getM();
  painter->modelViewMatrix().push();
  painter->modelViewMatrix() *= QMatrix4x4(m.at(0,0), m.at(0,1), m.at(0,2), 0.0,
                                           m.at(1,0), m.at(1,1), m.at(1,2), 0.0,
                                           m.at(2,0), m.at(2,1), m.at(2,2), 0.0,
                                           0.0,       0.0,       0.0,       1.0);
  // Draw the transparent cube.
  painter->setStandardEffect(QGL::LitMaterial);
  glCullFace(GL_BACK);
  glEnable(GL_CULL_FACE);
  glEnable(GL_BLEND);
  glDepthMask(0);
  //glDisable(GL_DEPTH_TEST);
  meshDomainBox->draw(painter);
  //glEnable(GL_DEPTH_TEST);
  glDepthMask(1);
  glDisable(GL_BLEND);
  glDisable(GL_CULL_FACE);
  // Draw the outlines (if we have drawn the box itself)
  if ((meshDomainBox->options() & QGLSceneNode::HideNode) == 0) {
    painter->setFaceMaterial(QGL::AllFaces, domainBoxMaterial);
    //glEnable(GL_LINE_SMOOTH);
    QVector3DArray array;
    array.append(0, 0, 0); array.append(1, 0, 0);
    array.append(1, 0, 0); array.append(1, 1, 0);
    array.append(1, 1, 0); array.append(0, 1, 0);
    array.append(0, 1, 0); array.append(0, 0, 0);
    array.append(0, 0, 1); array.append(1, 0, 1);
    array.append(1, 0, 1); array.append(1, 1, 1);
    array.append(1, 1, 1); array.append(0, 1, 1);
    array.append(0, 1, 1); array.append(0, 0, 1);
    array.append(0, 0, 0); array.append(0, 0, 1);
    array.append(1, 0, 0); array.append(1, 0, 1);
    array.append(0, 1, 0); array.append(0, 1, 1);
    array.append(1, 1, 0); array.append(1, 1, 1);
    painter->clearAttributes();
    painter->setVertexAttribute(QGL::Position, array);
    painter->draw(QGL::Lines, 24);
  }
  painter->modelViewMatrix().pop();
}
void GLWorldView::drawDreiBein(QGLPainter *painter)
{
  painter->modelViewMatrix().push();
  painter->modelViewMatrix().translate(camera()->center());
  painter->setStandardEffect(QGL::LitMaterial);
  painter->setFaceMaterial(QGL::FrontFaces, NULL);
  meshDreiBein->draw(painter);
  painter->modelViewMatrix().pop();
}
void GLWorldView::sceneHoverSignalled(const atomId_t _id)
{
  emit hoverChanged(_id);
}
void GLWorldView::sceneHoverSignalled(const moleculeId_t _id, int _i)
{
  emit hoverChanged(_id, _i);
}