/**********************************************************************
NavigateTool - Navigation Tool for Avogadro
Copyright (C) 2007,2008 by Marcus D. Hanwell
Copyright (C) 2006,2007 by Benoit Jacob
This file is part of the Avogadro molecular editor project.
For more information, see
Avogadro 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.
Avogadro 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
**********************************************************************/
#include "navigatetool.h"
#include "eyecandy.h"
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace OpenBabel;
using namespace Eigen;
namespace Avogadro {
NavigateTool::NavigateTool(QObject *parent) : Tool(parent), m_clickedAtom(0),
m_leftButtonPressed(false), m_midButtonPressed(false),
m_rightButtonPressed(false), m_drawEyeCandy(false),
m_draggingInitialized(false), m_eyecandy(new Eyecandy)
{
QAction *action = activateAction();
action->setIcon(QIcon(QString::fromUtf8(":/navigate/navigate.png")));
action->setToolTip(tr("Navigation Tool (F9)\n\n"
"Left Mouse: Click and drag to rotate the view\n"
"Middle Mouse: Click and drag to zoom in or out\n"
"Right Mouse: Click and drag to move the view"));
action->setShortcut(Qt::Key_F9);
}
NavigateTool::~NavigateTool()
{
delete m_eyecandy;
}
int NavigateTool::usefulness() const
{
return 2500000;
}
void NavigateTool::computeReferencePoint(GLWidget *widget)
{
// Remember to account for the situation where no molecule is loaded or it is empty
if(!widget->molecule())
m_referencePoint = Vector3d(0., 0., 0.);
else if(!widget->molecule()->numAtoms())
m_referencePoint = Vector3d(0., 0., 0.);
else if(m_clickedAtom)
m_referencePoint = *m_clickedAtom->pos();
else {
// let's set m_referencePoint to be the center of the visible
// part of the molecule.
Vector3d atomsBarycenter(0., 0., 0.);
double sumOfWeights = 0.;
QList atoms = widget->molecule()->atoms();
foreach (Atom *atom, atoms) {
Vector3d transformedAtomPos = widget->camera()->modelview() * *atom->pos();
double atomDistance = transformedAtomPos.norm();
double dot = transformedAtomPos.z() / atomDistance;
double weight = exp(-30. * (1. + dot));
sumOfWeights += weight;
atomsBarycenter += weight * *atom->pos();
}
atomsBarycenter /= sumOfWeights;
m_referencePoint = atomsBarycenter;
}
}
QUndoCommand* NavigateTool::mousePressEvent(GLWidget *widget, QMouseEvent *event)
{
event->accept();
m_drawEyeCandy = false;
m_lastDraggingPosition = event->pos();
// Make sure there aren't modifier keys clicked with the left button
// If the user has a Mac and only a one-button mouse, everything
// looks like a left button
if (event->buttons() & Qt::LeftButton &&
event->modifiers() == Qt::NoModifier)
{
m_leftButtonPressed = true;
// Set the cursor - this needs to be reset to Qt::ArrowCursor after
widget->setCursor(Qt::ClosedHandCursor);
}
// On a Mac, click and hold the Shift key
if (event->buttons() & Qt::MidButton ||
(event->buttons() & Qt::LeftButton &&
event->modifiers() == Qt::ShiftModifier))
{
m_midButtonPressed = true;
// Set the cursor - this needs to be reset to Qt::ArrowCursor after
widget->setCursor(Qt::SizeVerCursor);
}
// On a Mac, click and hold either the Command or Control Keys
// (Control or Meta in Qt-speak)
if (event->buttons() & Qt::RightButton ||
(event->buttons() & Qt::LeftButton &&
(event->modifiers() == Qt::ControlModifier
|| event->modifiers() == Qt::MetaModifier)))
{
m_rightButtonPressed = true;
// Set the cursor - this needs to be reset to Qt::ArrowCursor after
// Currently, there's a Qt/Mac bug -- SizeAllCursor looks like a spreadsheet cursor
#ifdef Q_WS_MAC
widget->setCursor(Qt::CrossCursor);
#else
widget->setCursor(Qt::SizeAllCursor);
#endif
}
m_clickedAtom = widget->computeClickedAtom(event->pos());
computeReferencePoint(widget);
// Initialise the angle variables on any new mouse press
m_yAngleEyecandy = 0.;
m_xAngleEyecandy = 0.;
widget->update();
return 0;
}
QUndoCommand* NavigateTool::mouseReleaseEvent(GLWidget *widget, QMouseEvent *event)
{
event->accept();
m_leftButtonPressed = false;
m_midButtonPressed = false;
m_rightButtonPressed = false;
m_drawEyeCandy = false;
m_clickedAtom = 0;
m_draggingInitialized = false;
// Set the cursor back to the default cursor
widget->setCursor(Qt::ArrowCursor);
widget->update();
return 0;
}
QUndoCommand* NavigateTool::mouseMoveEvent(GLWidget *widget, QMouseEvent *event)
{
if(!widget->molecule()) {
return 0;
}
m_drawEyeCandy = true;
event->accept();
QPoint deltaDragging;
if (m_draggingInitialized) {
deltaDragging = event->pos() - m_lastDraggingPosition;
}
else {
m_lastDraggingPosition = event->pos();
m_draggingInitialized = true;
}
// Mouse navigation has two modes - atom centred when an atom is clicked
// and scene if no atom has been clicked. However we don't need two codepaths
// here because the m_referencePoint has already been computed in mousePress,
// and that is the only difference between the two modes.
// update eyecandy angle
m_xAngleEyecandy += deltaDragging.x() * ROTATION_SPEED;
m_yAngleEyecandy += deltaDragging.y() * ROTATION_SPEED;
if (event->buttons() & Qt::LeftButton && event->modifiers() == Qt::NoModifier)
{
Navigate::rotate(widget, m_referencePoint, deltaDragging.x(), deltaDragging.y());
}
// On the Mac, either use a three-button mouse
// or hold down the Shift key
else if ( (event->buttons() & Qt::MidButton) ||
(event->buttons() & Qt::LeftButton && event->modifiers() & Qt::ShiftModifier) )
{
// Perform the rotation
Navigate::tilt(widget, m_referencePoint, deltaDragging.x());
// Perform the zoom toward clicked atom
Navigate::zoom(widget, m_referencePoint, deltaDragging.y());
}
// On the Mac, either use a three-button mouse
// or hold down the Command key (ControlModifier in Qt notation)
else if ( (event->buttons() & Qt::RightButton) ||
(event->buttons() & Qt::LeftButton && (event->modifiers() == Qt::ControlModifier || event->modifiers() == Qt::MetaModifier) ) )
{
// translate the molecule following mouse movement
Navigate::translate(widget, m_referencePoint, m_lastDraggingPosition, event->pos());
}
m_lastDraggingPosition = event->pos();
widget->update();
return 0;
}
QUndoCommand* NavigateTool::wheelEvent(GLWidget *widget, QWheelEvent *event )
{
event->accept();
m_clickedAtom = 0; // no need for mouse wheel to detect exactly the atom,
// the referencePoint will be accurate enough, and
// on large molecules doing a gl selection on every
// mousewheel event is too expensive.
computeReferencePoint(widget); // needs m_clickedAtom to be set.
Navigate::zoom(widget, m_referencePoint, - MOUSE_WHEEL_SPEED * event->delta());
widget->update();
return 0;
}
QUndoCommand* NavigateTool::keyPressEvent(GLWidget *widget, QKeyEvent *event)
{
computeReferencePoint(widget);
switch (event->key()) {
case Qt::Key_Left: // Left arrow
if (event->modifiers() == Qt::NoModifier)
Navigate::rotate(widget, m_referencePoint, -5, 0);
else if (event->modifiers() & Qt::ShiftModifier)
Navigate::tilt(widget, m_referencePoint, -5);
else if (event->modifiers() & Qt::ControlModifier)
Navigate::translate(widget, m_referencePoint, -5, 0);
event->accept();
break;
case Qt::Key_Right: // Right arrow
if (event->modifiers() == Qt::NoModifier)
Navigate::rotate(widget, m_referencePoint, 5, 0);
else if ((event->modifiers() & Qt::ShiftModifier))
Navigate::tilt(widget, m_referencePoint, 5);
else if (event->modifiers() & Qt::ControlModifier)
Navigate::translate(widget, m_referencePoint, 5, 0);
event->accept();
break;
case Qt::Key_Up: // Up arrow
if (event->modifiers() == Qt::NoModifier)
Navigate::rotate(widget, m_referencePoint, 0, -5);
else if (event->modifiers() & Qt::ShiftModifier)
Navigate::zoom(widget, m_referencePoint, -2);
else if (event->modifiers() & Qt::ControlModifier)
Navigate::translate(widget, m_referencePoint, 0, -5);
event->accept();
break;
case Qt::Key_Down: // Down arrow
if (event->modifiers() == Qt::NoModifier)
Navigate::rotate(widget, m_referencePoint, 0, 5);
else if (event->modifiers() & Qt::ShiftModifier)
Navigate::zoom(widget, m_referencePoint, 2);
else if (event->modifiers() & Qt::ControlModifier)
Navigate::translate(widget, m_referencePoint, 0, 5);
event->accept();
break;
default:
return 0;
}
return 0;
}
QUndoCommand* NavigateTool::keyReleaseEvent(GLWidget *, QKeyEvent *)
{
return 0;
}
bool NavigateTool::paint(GLWidget *widget)
{
if (m_drawEyeCandy) {
if(m_leftButtonPressed)
m_eyecandy->drawRotation(widget, m_clickedAtom, m_xAngleEyecandy,
m_yAngleEyecandy, &m_referencePoint);
else if(m_midButtonPressed)
m_eyecandy->drawZoom(widget, m_clickedAtom, &m_referencePoint);
else if(m_rightButtonPressed)
m_eyecandy->drawTranslation(widget, m_clickedAtom, &m_referencePoint);
}
return true;
}
}
#include "navigatetool.moc"
Q_EXPORT_PLUGIN2(navigatetool, Avogadro::NavigateToolFactory)