From ebd4a35297e5c588119ea62c239882d08586a2f2 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 1 Jan 2012 12:44:08 +0000 Subject: [PATCH] 0000555: 3D navigation using a two-button laptop touchpad git-svn-id: https://free-cad.svn.sourceforge.net/svnroot/free-cad/trunk@5373 e8eeb9e2-ec13-0410-a4a9-efa5cf37419d --- src/Gui/CMakeLists.txt | 1 + src/Gui/Makefile.am | 1 + src/Gui/NavigationStyle.h | 14 ++ src/Gui/SoFCDB.cpp | 1 + src/Gui/TouchpadNavigationStyle.cpp | 346 ++++++++++++++++++++++++++++ 5 files changed, 363 insertions(+) create mode 100644 src/Gui/TouchpadNavigationStyle.cpp diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index 96d196a61..b0e8968b5 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -558,6 +558,7 @@ SET(View3D_CPP_SRCS InventorNavigationStyle.cpp CADNavigationStyle.cpp BlenderNavigationStyle.cpp + TouchpadNavigationStyle.cpp SplitView3DInventor.cpp View.cpp View3DInventor.cpp diff --git a/src/Gui/Makefile.am b/src/Gui/Makefile.am index 60d706664..1cf4fcc16 100644 --- a/src/Gui/Makefile.am +++ b/src/Gui/Makefile.am @@ -346,6 +346,7 @@ libFreeCADGui_la_SOURCES=\ ToolBarManager.cpp \ ToolBox.cpp \ ToolBoxManager.cpp \ + TouchpadNavigationStyle.cpp \ Transform.cpp \ Transform.h \ Tree.cpp \ diff --git a/src/Gui/NavigationStyle.h b/src/Gui/NavigationStyle.h index 4186d9e2f..5b7087ef1 100644 --- a/src/Gui/NavigationStyle.h +++ b/src/Gui/NavigationStyle.h @@ -279,6 +279,20 @@ private: SbBool lockButton1; }; +class GuiExport TouchpadNavigationStyle : public UserNavigationStyle { + typedef UserNavigationStyle inherited; + + TYPESYSTEM_HEADER(); + +public: + TouchpadNavigationStyle(); + ~TouchpadNavigationStyle(); + const char* mouseButtons(ViewerMode); + +protected: + SbBool processSoEvent(const SoEvent * const ev); +}; + } // namespace Gui #endif // GUI_NAVIGATIONSTYLE_H diff --git a/src/Gui/SoFCDB.cpp b/src/Gui/SoFCDB.cpp index c2dd99133..46435be06 100644 --- a/src/Gui/SoFCDB.cpp +++ b/src/Gui/SoFCDB.cpp @@ -123,6 +123,7 @@ void Gui::SoFCDB::init() InventorNavigationStyle ::init(); CADNavigationStyle ::init(); BlenderNavigationStyle ::init(); + TouchpadNavigationStyle ::init(); qRegisterMetaType("Base::Vector3f"); qRegisterMetaType("Base::Vector3d"); diff --git a/src/Gui/TouchpadNavigationStyle.cpp b/src/Gui/TouchpadNavigationStyle.cpp new file mode 100644 index 000000000..e192e11d6 --- /dev/null +++ b/src/Gui/TouchpadNavigationStyle.cpp @@ -0,0 +1,346 @@ +/*************************************************************************** + * Copyright (c) 2012 Werner Mayer * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + + +#include "PreCompiled.h" +#ifndef _PreComp_ +# include +# include "InventorAll.h" +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +#include + +#include +#include "NavigationStyle.h" +#include "View3DInventorViewer.h" +#include "Application.h" +#include "MenuManager.h" +#include "MouseSelection.h" + +using namespace Gui; + +// ---------------------------------------------------------------------------------- + +/* TRANSLATOR Gui::TouchpadNavigationStyle */ + +TYPESYSTEM_SOURCE(Gui::TouchpadNavigationStyle, Gui::UserNavigationStyle); + +TouchpadNavigationStyle::TouchpadNavigationStyle() +{ +} + +TouchpadNavigationStyle::~TouchpadNavigationStyle() +{ +} + +const char* TouchpadNavigationStyle::mouseButtons(ViewerMode mode) +{ + switch (mode) { + case NavigationStyle::SELECTION: + return QT_TR_NOOP("Press left mouse button"); + case NavigationStyle::PANNING: + return QT_TR_NOOP("Press SHIFT button"); + case NavigationStyle::DRAGGING: + return QT_TR_NOOP("Press ALT button"); + case NavigationStyle::ZOOMING: + return QT_TR_NOOP("Press PgUp/PgDown button"); + default: + return "No description"; + } +} + +SbBool TouchpadNavigationStyle::processSoEvent(const SoEvent * const ev) +{ + // Events when in "ready-to-seek" mode are ignored, except those + // which influence the seek mode itself -- these are handled further + // up the inheritance hierarchy. + if (this->isSeekMode()) { return inherited::processSoEvent(ev); } + + const SoType type(ev->getTypeId()); + + const SbViewportRegion & vp = viewer->getViewportRegion(); + const SbVec2s size(vp.getViewportSizePixels()); + const SbVec2f prevnormalized = this->lastmouseposition; + const SbVec2s pos(ev->getPosition()); + const SbVec2f posn((float) pos[0] / (float) SoQtMax((int)(size[0] - 1), 1), + (float) pos[1] / (float) SoQtMax((int)(size[1] - 1), 1)); + + this->lastmouseposition = posn; + + // Set to TRUE if any event processing happened. Note that it is not + // necessary to restrict ourselves to only do one "action" for an + // event, we only need this flag to see if any processing happened + // at all. + SbBool processed = FALSE; + + const ViewerMode curmode = this->currentmode; + ViewerMode newmode = curmode; + + // Mismatches in state of the modifier keys happens if the user + // presses or releases them outside the viewer window. + if (this->ctrldown != ev->wasCtrlDown()) { + this->ctrldown = ev->wasCtrlDown(); + } + if (this->shiftdown != ev->wasShiftDown()) { + this->shiftdown = ev->wasShiftDown(); + } + if (this->altdown != ev->wasAltDown()) { + this->altdown = ev->wasAltDown(); + } + + // give the nodes in the foreground root the chance to handle events (e.g color bar) + if (!processed && !viewer->isEditing()) { + processed = handleEventInForeground(ev); + if (processed) + return TRUE; + } + + // Keyboard handling + if (type.isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { + const SoKeyboardEvent * const event = (const SoKeyboardEvent *) ev; + const SbBool press = event->getState() == SoButtonEvent::DOWN ? TRUE : FALSE; + switch (event->getKey()) { + case SoKeyboardEvent::LEFT_CONTROL: + case SoKeyboardEvent::RIGHT_CONTROL: + this->ctrldown = press; + break; + case SoKeyboardEvent::LEFT_SHIFT: + case SoKeyboardEvent::RIGHT_SHIFT: + this->shiftdown = press; + break; + case SoKeyboardEvent::LEFT_ALT: + case SoKeyboardEvent::RIGHT_ALT: + this->altdown = press; + break; + case SoKeyboardEvent::H: + processed = TRUE; + viewer->saveHomePosition(); + break; + case SoKeyboardEvent::S: + case SoKeyboardEvent::HOME: + case SoKeyboardEvent::LEFT_ARROW: + case SoKeyboardEvent::UP_ARROW: + case SoKeyboardEvent::RIGHT_ARROW: + case SoKeyboardEvent::DOWN_ARROW: + if (!this->isViewing()) + this->setViewing(true); + break; + case SoKeyboardEvent::PAGE_UP: + if (this->invertZoom) + zoom(viewer->getCamera(), 0.05f); + else + zoom(viewer->getCamera(), -0.05f); + processed = TRUE; + break; + case SoKeyboardEvent::PAGE_DOWN: + if (this->invertZoom) + zoom(viewer->getCamera(), -0.05f); + else + zoom(viewer->getCamera(), 0.05f); + processed = TRUE; + break; + default: + break; + } + } + + // Mouse Button / Spaceball Button handling + if (type.isDerivedFrom(SoMouseButtonEvent::getClassTypeId())) { + const SoMouseButtonEvent * const event = (const SoMouseButtonEvent *) ev; + const int button = event->getButton(); + const SbBool press = event->getState() == SoButtonEvent::DOWN ? TRUE : FALSE; + + // SoDebugError::postInfo("processSoEvent", "button = %d", button); + switch (button) { + case SoMouseButtonEvent::BUTTON1: + this->lockrecenter = TRUE; + this->button1down = press; + if (press && (this->currentmode == NavigationStyle::SEEK_WAIT_MODE)) { + newmode = NavigationStyle::SEEK_MODE; + this->seekToPoint(pos); // implicitly calls interactiveCountInc() + processed = TRUE; + } + else if (press && (this->currentmode == NavigationStyle::PANNING || + this->currentmode == NavigationStyle::ZOOMING)) { + newmode = NavigationStyle::DRAGGING; + this->centerTime = ev->getTime(); + processed = TRUE; + } + else if (!press && (this->currentmode == NavigationStyle::DRAGGING)) { + SbTime tmp = (ev->getTime() - this->centerTime); + float dci = (float)QApplication::doubleClickInterval()/1000.0f; + if (tmp.getValue() < dci) { + newmode = NavigationStyle::ZOOMING; + } + processed = TRUE; + } + else if (!press && (this->currentmode == NavigationStyle::DRAGGING)) { + this->setViewing(false); + processed = TRUE; + } + else if (viewer->isEditing() && (this->currentmode == NavigationStyle::SPINNING)) { + processed = TRUE; + } + break; + case SoMouseButtonEvent::BUTTON2: + // If we are in edit mode then simply ignore the RMB events + // to pass the event to the base class. + this->lockrecenter = TRUE; + if (!viewer->isEditing()) { + // If we are in zoom or pan mode ignore RMB events otherwise + // the canvas doesn't get any release events + if (this->currentmode != NavigationStyle::ZOOMING && + this->currentmode != NavigationStyle::PANNING && + this->currentmode != NavigationStyle::DRAGGING) { + if (this->isPopupMenuEnabled()) { + if (!press) { // release right mouse button + this->openPopupMenu(event->getPosition()); + } + } + } + } + // Alternative way of rotating & zooming + if (press && (this->currentmode == NavigationStyle::PANNING || + this->currentmode == NavigationStyle::ZOOMING)) { + newmode = NavigationStyle::DRAGGING; + this->centerTime = ev->getTime(); + processed = TRUE; + } + else if (!press && (this->currentmode == NavigationStyle::DRAGGING)) { + SbTime tmp = (ev->getTime() - this->centerTime); + float dci = (float)QApplication::doubleClickInterval()/1000.0f; + if (tmp.getValue() < dci) { + newmode = NavigationStyle::ZOOMING; + } + processed = TRUE; + } + this->button2down = press; + break; + default: + break; + } + } + + // Mouse Movement handling + if (type.isDerivedFrom(SoLocation2Event::getClassTypeId())) { + this->lockrecenter = TRUE; + const SoLocation2Event * const event = (const SoLocation2Event *) ev; + if (this->currentmode == NavigationStyle::ZOOMING) { + this->zoomByCursor(posn, prevnormalized); + processed = TRUE; + } + else if (this->currentmode == NavigationStyle::PANNING) { + float ratio = vp.getViewportAspectRatio(); + panCamera(viewer->getCamera(), ratio, this->panningplane, posn, prevnormalized); + processed = TRUE; + } + else if (this->currentmode == NavigationStyle::DRAGGING) { + this->addToLog(event->getPosition(), event->getTime()); + this->spin(posn); + processed = TRUE; + } + } + + // Spaceball & Joystick handling + if (type.isDerivedFrom(SoMotion3Event::getClassTypeId())) { + SoMotion3Event * const event = (SoMotion3Event *) ev; + SoCamera * const camera = viewer->getCamera(); + + SbVec3f dir = event->getTranslation(); + if (camera->getTypeId().isDerivedFrom(SoOrthographicCamera::getClassTypeId())){ + static float zoomConstant(-.03f); + dir[2] = 0.0;//don't move the cam for z translation. + + SoOrthographicCamera *oCam = static_cast(camera); + oCam->scaleHeight(1.0-event->getTranslation()[2] * zoomConstant); + } + camera->orientation.getValue().multVec(dir,dir); + camera->position = camera->position.getValue() + dir; + camera->orientation = event->getRotation() * camera->orientation.getValue(); + processed = TRUE; + } + + enum { + BUTTON1DOWN = 1 << 0, + BUTTON2DOWN = 1 << 1, + CTRLDOWN = 1 << 2, + SHIFTDOWN = 1 << 3, + ALTDOWN = 1 << 4 + }; + unsigned int combo = + (this->button1down ? BUTTON1DOWN : 0) | + (this->button2down ? BUTTON2DOWN : 0) | + (this->ctrldown ? CTRLDOWN : 0) | + (this->shiftdown ? SHIFTDOWN : 0) | + (this->altdown ? ALTDOWN : 0); + + switch (combo) { + case 0: + if (curmode == NavigationStyle::SPINNING) { break; } + newmode = NavigationStyle::IDLE; + break; + case BUTTON1DOWN: + // make sure not to change the selection when stopping spinning + if (curmode == NavigationStyle::SPINNING) + newmode = NavigationStyle::IDLE; + else + newmode = NavigationStyle::SELECTION; + break; + case CTRLDOWN: + newmode = NavigationStyle::IDLE; + break; + case SHIFTDOWN: + newmode = NavigationStyle::PANNING; + break; + case ALTDOWN: + case CTRLDOWN|SHIFTDOWN: + newmode = NavigationStyle::DRAGGING; + break; + case CTRLDOWN|SHIFTDOWN|BUTTON1DOWN: + newmode = NavigationStyle::ZOOMING; + break; + default: + break; + } + + if (newmode != curmode) { + this->setViewingMode(newmode); + } + + // If not handled in this class, pass on upwards in the inheritance + // hierarchy. + if (!processed) + processed = inherited::processSoEvent(ev); + else + return TRUE; + + return processed; +}