0030507: Visualization - introduce AIS_ViewController
authorkgv <kgv@opencascade.com>
Mon, 10 Jun 2019 18:03:41 +0000 (21:03 +0300)
committerbugmaster <bugmaster@opencascade.com>
Thu, 13 Jun 2019 15:50:12 +0000 (18:50 +0300)
ViewerTest_EventManager now inherits AIS_ViewController.
Platform-dependent user input handling within ViewerTest has been revised
to process events in common way through AIS_ViewController.
The mouse navigation has been changed, so that left mouse clicked
without modifers now rotates View.
The rubber-band selection can be activated via Alt+LeftMouseButton.
Selection is now done on mouse unclick and keyboard short-cuts take effect on unclick.

Aspect_Window::SetTitle() - added new method configuring Window title.
Introduced new types Aspect_Touch, Aspect_VKey, Aspect_ScrollDelta
for processing window events in platform-independent way.

40 files changed:
src/AIS/AIS_DragAction.hxx [copied from src/ViewerTest/ViewerTest_EventManager.lxx with 56% similarity]
src/AIS/AIS_InteractiveContext.cxx
src/AIS/AIS_InteractiveContext.hxx
src/AIS/AIS_InteractiveContext_1.cxx
src/AIS/AIS_MouseGesture.hxx [new file with mode: 0644]
src/AIS/AIS_NavigationMode.hxx [copied from src/ViewerTest/ViewerTest_EventManager.lxx with 52% similarity]
src/AIS/AIS_RotationMode.hxx [new file with mode: 0644]
src/AIS/AIS_ViewController.cxx [new file with mode: 0644]
src/AIS/AIS_ViewController.hxx [new file with mode: 0644]
src/AIS/AIS_ViewInputBuffer.hxx [new file with mode: 0644]
src/AIS/AIS_WalkDelta.hxx [new file with mode: 0644]
src/AIS/FILES
src/Aspect/Aspect_ScrollDelta.hxx [new file with mode: 0644]
src/Aspect/Aspect_Touch.hxx [new file with mode: 0644]
src/Aspect/Aspect_TouchMap.hxx [moved from src/ViewerTest/ViewerTest_EventManager.lxx with 65% similarity]
src/Aspect/Aspect_VKey.hxx [new file with mode: 0644]
src/Aspect/Aspect_VKeyFlags.hxx [new file with mode: 0644]
src/Aspect/Aspect_VKeySet.cxx [new file with mode: 0644]
src/Aspect/Aspect_VKeySet.hxx [new file with mode: 0644]
src/Aspect/Aspect_Window.hxx
src/Aspect/FILES
src/Cocoa/Cocoa_Window.hxx
src/Cocoa/Cocoa_Window.mm
src/QABugs/QABugs_1.cxx
src/V3d/V3d_View.cxx
src/V3d/V3d_View.hxx
src/ViewerTest/FILES
src/ViewerTest/ViewerTest.cxx
src/ViewerTest/ViewerTest_EventManager.cxx
src/ViewerTest/ViewerTest_EventManager.hxx
src/ViewerTest/ViewerTest_ObjectCommands.cxx
src/ViewerTest/ViewerTest_V3dView.cxx [new file with mode: 0644]
src/ViewerTest/ViewerTest_V3dView.hxx [new file with mode: 0644]
src/ViewerTest/ViewerTest_ViewerCommands.cxx
src/ViewerTest/ViewerTest_ViewerCommands_1.mm
src/WNT/WNT_Window.cxx
src/WNT/WNT_Window.hxx
src/Xw/Xw_Window.cxx
src/Xw/Xw_Window.hxx
tests/bugs/vis/bug26147

similarity index 56%
copy from src/ViewerTest/ViewerTest_EventManager.lxx
copy to src/AIS/AIS_DragAction.hxx
index 88a8ae0..cf3bf23 100644 (file)
@@ -1,7 +1,4 @@
-// Created on: 1998-08-27
-// Created by: Robert COUBLANC
-// Copyright (c) 1998-1999 Matra Datavision
-// Copyright (c) 1999-2014 OPEN CASCADE SAS
+// Copyright (c) 2018-2019 OPEN CASCADE SAS
 //
 // This file is part of Open CASCADE Technology software library.
 //
 // Alternatively, this file may be used under the terms of Open CASCADE
 // commercial license or contractual agreement.
 
-inline const Handle(AIS_InteractiveContext)& ViewerTest_EventManager::Context() const
-{return myCtx;}
+#ifndef _AIS_DragAction_HeaderFile
+#define _AIS_DragAction_HeaderFile
+
+//! Dragging action.
+enum AIS_DragAction
+{
+  AIS_DragAction_Start,  //!< (try) start dragging object
+  AIS_DragAction_Update, //!< perform dragging (update position)
+  AIS_DragAction_Stop,   //!< stop dragging (save position)
+  AIS_DragAction_Abort,  //!< abort dragging (restore initial position)
+};
+
+#endif // _AIS_DragAction_HeaderFile
index 3ce8af6..2d2d2d5 100644 (file)
@@ -2524,3 +2524,12 @@ void AIS_InteractiveContext::SetTransformPersistence (const Handle(AIS_Interacti
     anActiveViewIter.Value()->View()->InvalidateZLayerBoundingBox (aLayerId);
   }
 }
+
+//=======================================================================
+//function : GravityPoint
+//purpose  :
+//=======================================================================
+gp_Pnt AIS_InteractiveContext::GravityPoint (const Handle(V3d_View)& theView) const
+{
+  return theView->GravityPoint();
+}
index 43da8b2..659fb55 100644 (file)
@@ -557,6 +557,14 @@ public: //! @name Selection management
   Standard_EXPORT void AddOrRemoveSelected (const Handle(AIS_InteractiveObject)& theObject,
                                             const Standard_Boolean               theToUpdateViewer);
 
+  //! Updates Selected state of specified owner without calling HilightSelected().
+  //! Has no effect if Selected state is not changed, and redirects to AddOrRemoveSelected() otherwise.
+  //! @param theOwner owner object to set selected state
+  //! @param theIsSelected new selected state
+  //! @return TRUE if Selected state has been changed
+  Standard_EXPORT Standard_Boolean SetSelectedState (const Handle(SelectMgr_EntityOwner)& theOwner,
+                                                     const Standard_Boolean               theIsSelected);
+
   //! Highlights selected objects.
   Standard_EXPORT void HilightSelected (const Standard_Boolean theToUpdateViewer);
 
@@ -816,6 +824,9 @@ public: //! @name common properties
   //! returns the number of removed  structures from the viewers.
   Standard_EXPORT Standard_Integer PurgeDisplay();
 
+  //! Return rotation gravity point.
+  Standard_EXPORT virtual gp_Pnt GravityPoint (const Handle(V3d_View)& theView) const;
+
 public: //! @name debug visualization
 
   //! Visualization of sensitives - for debugging purposes!
index 2d6b9d9..caeee2d 100644 (file)
@@ -1004,6 +1004,43 @@ void AIS_InteractiveContext::AddOrRemoveSelected (const Handle(SelectMgr_EntityO
     UpdateCurrentViewer();
 }
 
+// =======================================================================
+// function : SetSelectedState
+// purpose  :
+// =======================================================================
+Standard_Boolean AIS_InteractiveContext::SetSelectedState (const Handle(SelectMgr_EntityOwner)& theEntity,
+                                                           const Standard_Boolean theIsSelected)
+{
+  if (theEntity.IsNull())
+  {
+    throw Standard_ProgramError ("Internal error: AIS_InteractiveContext::SetSelectedState() called with NO object");
+  }
+
+  if (!theEntity->HasSelectable()
+    || mySelection->IsSelected (theEntity) == theIsSelected)
+  {
+    return false;
+  }
+
+  if (theEntity->IsAutoHilight())
+  {
+    AddOrRemoveSelected (theEntity, false);
+    return true;
+  }
+
+  if (theIsSelected)
+  {
+    const AIS_SelectStatus aSelStatus = mySelection->AddSelect (theEntity);
+    theEntity->SetSelected (true);
+    return aSelStatus == AIS_SS_Added;
+  }
+  else
+  {
+    const AIS_SelectStatus aSelStatus = mySelection->Select (theEntity);
+    theEntity->SetSelected (false);
+    return aSelStatus == AIS_SS_Removed;
+  }
+}
 
 //=======================================================================
 //function : IsSelected
diff --git a/src/AIS/AIS_MouseGesture.hxx b/src/AIS/AIS_MouseGesture.hxx
new file mode 100644 (file)
index 0000000..ab28f13
--- /dev/null
@@ -0,0 +1,36 @@
+// Copyright (c) 2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _AIS_MouseGesture_HeaderFile
+#define _AIS_MouseGesture_HeaderFile
+
+#include <NCollection_DataMap.hxx>
+
+//! Mouse gesture - only one can be active at one moment.
+enum AIS_MouseGesture
+{
+  AIS_MouseGesture_NONE,            //!< no active gesture
+  //
+  AIS_MouseGesture_SelectRectangle, //!< rectangular selection
+  AIS_MouseGesture_SelectLasso,     //!< polygonal   selection
+  //
+  AIS_MouseGesture_Zoom,            //!< view zoom gesture
+  AIS_MouseGesture_Pan,             //!< view panning gesture
+  AIS_MouseGesture_RotateOrbit,     //!< orbit rotation gesture
+  AIS_MouseGesture_RotateView,      //!< view  rotation gesture
+};
+
+//! Map defining mouse gestures.
+typedef NCollection_DataMap<unsigned int, AIS_MouseGesture> AIS_MouseGestureMap;
+
+#endif // _AIS_MouseGesture_HeaderFile
similarity index 52%
copy from src/ViewerTest/ViewerTest_EventManager.lxx
copy to src/AIS/AIS_NavigationMode.hxx
index 88a8ae0..b21cbe0 100644 (file)
@@ -1,7 +1,4 @@
-// Created on: 1998-08-27
-// Created by: Robert COUBLANC
-// Copyright (c) 1998-1999 Matra Datavision
-// Copyright (c) 1999-2014 OPEN CASCADE SAS
+// Copyright (c) 2019 OPEN CASCADE SAS
 //
 // This file is part of Open CASCADE Technology software library.
 //
 // Alternatively, this file may be used under the terms of Open CASCADE
 // commercial license or contractual agreement.
 
-inline const Handle(AIS_InteractiveContext)& ViewerTest_EventManager::Context() const
-{return myCtx;}
+#ifndef _AIS_NavigationMode_HeaderFile
+#define _AIS_NavigationMode_HeaderFile
+
+//! Camera navigation mode.
+enum AIS_NavigationMode
+{
+  AIS_NavigationMode_Orbit,             //!< orbit rotation
+  AIS_NavigationMode_FirstPersonFlight, //!< flight rotation (first person)
+  AIS_NavigationMode_FirstPersonWalk,   //!< walking mode (first person)
+};
+
+enum
+{
+  AIS_NavigationMode_LOWER = 0,
+  AIS_NavigationMode_UPPER = AIS_NavigationMode_FirstPersonWalk
+};
+
+#endif // _V3d_NavigationMode_HeaderFile
diff --git a/src/AIS/AIS_RotationMode.hxx b/src/AIS/AIS_RotationMode.hxx
new file mode 100644 (file)
index 0000000..7fe55dc
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright (c) 2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _AIS_RotationMode_HeaderFile
+#define _AIS_RotationMode_HeaderFile
+
+//! Camera rotation mode.
+enum AIS_RotationMode
+{
+  AIS_RotationMode_BndBoxActive, //!< default OCCT rotation
+  AIS_RotationMode_PickLast,     //!< rotate around last picked point
+  AIS_RotationMode_PickCenter,   //!< rotate around point at the center of window
+  AIS_RotationMode_CameraAt,     //!< rotate around camera center
+  AIS_RotationMode_BndBoxScene,  //!< rotate around scene center
+};
+
+enum
+{
+  AIS_RotationMode_LOWER = 0,
+  AIS_RotationMode_UPPER = AIS_RotationMode_BndBoxScene,
+};
+
+#endif // _AIS_RotationMode_HeaderFile
diff --git a/src/AIS/AIS_ViewController.cxx b/src/AIS/AIS_ViewController.cxx
new file mode 100644 (file)
index 0000000..e440330
--- /dev/null
@@ -0,0 +1,2321 @@
+// Copyright (c) 2016-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include "AIS_ViewController.hxx"
+
+#include <AIS_InteractiveContext.hxx>
+#include <AIS_Manipulator.hxx>
+#include <AIS_Point.hxx>
+#include <AIS_RubberBand.hxx>
+#include <Aspect_Grid.hxx>
+#include <Geom_CartesianPoint.hxx>
+#include <Message.hxx>
+#include <Message_Messenger.hxx>
+#include <gp_Quaternion.hxx>
+#include <V3d_View.hxx>
+
+// =======================================================================
+// function : AIS_ViewController
+// purpose  :
+// =======================================================================
+AIS_ViewController::AIS_ViewController()
+: myLastEventsTime    (0.0),
+  myToAskNextFrame    (false),
+  myMinCamDistance    (1.0),
+  myRotationMode      (AIS_RotationMode_BndBoxActive),
+  myNavigationMode    (AIS_NavigationMode_Orbit),
+  myMouseAccel           (1.0f),
+  myOrbitAccel           (1.0f),
+  myToShowPanAnchorPoint (true),
+  myToShowRotateCenter   (true),
+  myToLockOrbitZUp       (false),
+  myToInvertPitch        (false),
+  myToAllowTouchZRotation(false),
+  myToAllowRotation      (true),
+  myToAllowPanning       (true),
+  myToAllowZooming       (true),
+  myToAllowZFocus        (true),
+  myToAllowHighlight     (true),
+  myToAllowDragging      (true),
+  //
+  myWalkSpeedAbsolute (1.5f),
+  myWalkSpeedRelative (0.1f),
+  myThrustSpeed (0.0f),
+  myHasThrust (false),
+  //
+  myPrevMoveTo (-1, -1),
+  myHasHlrOnBeforeRotation (false),
+  //
+  myMouseClickThreshold (3.0),
+  myMouseDoubleClickInt (0.4),
+  myScrollZoomRatio     (15.0f),
+  myMouseActiveGesture  (AIS_MouseGesture_NONE),
+  myMouseActiveIdleRotation (false),
+  myMouseClickCounter   (0),
+  myMousePressed        (Aspect_VKeyMouse_NONE),
+  myMouseModifiers      (Aspect_VKeyFlags_NONE),
+  myMouseSingleButton   (-1),
+  //
+  myTouchToleranceScale      (1.0f),
+  myTouchRotationThresholdPx (6.0f),
+  myTouchZRotationThreshold  (float(2.0 * M_PI / 180.0)),
+  myTouchPanThresholdPx      (4.0f),
+  myTouchZoomThresholdPx     (6.0f),
+  myTouchZoomRatio           (0.13f),
+  //
+  myNbTouchesLast (0),
+  myUpdateStartPointPan  (true),
+  myUpdateStartPointRot  (true),
+  myUpdateStartPointZRot (true),
+  //
+  myPanPnt3d (Precision::Infinite(), 0.0, 0.0)
+{
+  myEventTimer.Start();
+
+  myAnchorPointPrs1 = new AIS_Point (new Geom_CartesianPoint (0.0, 0.0, 0.0));
+  myAnchorPointPrs1->SetZLayer (Graphic3d_ZLayerId_Top);
+  myAnchorPointPrs1->SetMutable (true);
+
+  myAnchorPointPrs2 = new AIS_Point (new Geom_CartesianPoint (0.0, 0.0, 0.0));
+  myAnchorPointPrs2->SetZLayer (Graphic3d_ZLayerId_Topmost);
+  myAnchorPointPrs2->SetMutable (true);
+
+  myRubberBand = new AIS_RubberBand (Quantity_NOC_LIGHTBLUE, Aspect_TOL_SOLID, Quantity_NOC_LIGHTBLUE, 0.4, 1.0);
+  myRubberBand->SetZLayer (Graphic3d_ZLayerId_TopOSD);
+  myRubberBand->SetTransformPersistence (new Graphic3d_TransformPers (Graphic3d_TMF_2d, Aspect_TOTP_LEFT_UPPER));
+  myRubberBand->SetDisplayMode (0);
+  myRubberBand->SetMutable (true);
+
+  myMouseGestureMap.Bind (Aspect_VKeyMouse_LeftButton,                           AIS_MouseGesture_RotateOrbit);
+  myMouseGestureMap.Bind (Aspect_VKeyMouse_LeftButton | Aspect_VKeyFlags_CTRL,   AIS_MouseGesture_Zoom);
+  myMouseGestureMap.Bind (Aspect_VKeyMouse_LeftButton | Aspect_VKeyFlags_SHIFT,  AIS_MouseGesture_Pan);
+  myMouseGestureMap.Bind (Aspect_VKeyMouse_LeftButton | Aspect_VKeyFlags_ALT,    AIS_MouseGesture_SelectRectangle);
+
+  myMouseGestureMap.Bind (Aspect_VKeyMouse_RightButton,                          AIS_MouseGesture_Zoom);
+  myMouseGestureMap.Bind (Aspect_VKeyMouse_RightButton | Aspect_VKeyFlags_CTRL,  AIS_MouseGesture_RotateOrbit);
+
+  myMouseGestureMap.Bind (Aspect_VKeyMouse_MiddleButton,                         AIS_MouseGesture_Pan);
+  myMouseGestureMap.Bind (Aspect_VKeyMouse_MiddleButton | Aspect_VKeyFlags_CTRL, AIS_MouseGesture_Pan);
+}
+
+// =======================================================================
+// function : ResetViewInput
+// purpose  :
+// =======================================================================
+void AIS_ViewController::ResetViewInput()
+{
+  myKeys.Reset();
+  myMousePressed      = Aspect_VKeyMouse_NONE;
+  myMouseModifiers    = Aspect_VKeyFlags_NONE;
+  myMouseSingleButton = -1;
+  myUI.Dragging.ToAbort = true;
+  myMouseActiveGesture = AIS_MouseGesture_NONE;
+  myMouseClickTimer.Stop();
+  myMouseClickCounter = 0;
+}
+
+// =======================================================================
+// function : FlushViewEvents
+// purpose  :
+// =======================================================================
+void AIS_ViewController::FlushViewEvents (const Handle(AIS_InteractiveContext)& theCtx,
+                                          const Handle(V3d_View)& theView,
+                                          Standard_Boolean theToHandle)
+{
+  flushBuffers (theCtx, theView);
+  flushGestures(theCtx, theView);
+  if (theToHandle)
+  {
+    HandleViewEvents (theCtx, theView);
+  }
+}
+
+// =======================================================================
+// function : flushBuffers
+// purpose  :
+// =======================================================================
+void AIS_ViewController::flushBuffers (const Handle(AIS_InteractiveContext)& ,
+                                       const Handle(V3d_View)& )
+{
+  myToAskNextFrame = false;
+
+  myGL.IsNewGesture = myUI.IsNewGesture;
+  myUI.IsNewGesture = false;
+
+  myGL.ZoomActions.Clear();
+  myGL.ZoomActions.Append (myUI.ZoomActions);
+  myUI.ZoomActions.Clear();
+
+  myGL.Orientation.ToFitAll = myUI.Orientation.ToFitAll;
+  myUI.Orientation.ToFitAll = false;
+  if (myUI.Orientation.ToSetViewOrient)
+  {
+    myUI.Orientation.ToSetViewOrient = false;
+    myGL.Orientation.ToSetViewOrient = true;
+    myGL.Orientation.ViewOrient      = myUI.Orientation.ViewOrient;
+  }
+
+  if (myUI.MoveTo.ToHilight)
+  {
+    myUI.MoveTo.ToHilight = false;
+    myGL.MoveTo.ToHilight = true;
+    myGL.MoveTo.Point     = myUI.MoveTo.Point;
+  }
+
+  {
+    myGL.Selection.Tool   = myUI.Selection.Tool;
+    myGL.Selection.IsXOR  = myUI.Selection.IsXOR;
+    myGL.Selection.Points = myUI.Selection.Points;
+    myUI.Selection.IsXOR  = false;
+    if (myUI.Selection.Tool == AIS_ViewSelectionTool_Picking)
+    {
+      myUI.Selection.Points.Clear();
+    }
+  }
+
+  if (myUI.Selection.ToApplyTool)
+  {
+    myGL.Selection.ToApplyTool = true;
+    myUI.Selection.ToApplyTool = false;
+    myUI.Selection.Points.Clear();
+  }
+
+  if (myUI.Panning.ToStart)
+  {
+    myUI.Panning.ToStart = false;
+    myGL.Panning.ToStart = true;
+    myGL.Panning.PointStart = myUI.Panning.PointStart;
+  }
+
+  if (myUI.Panning.ToPan)
+  {
+    myUI.Panning.ToPan = false;
+    myGL.Panning.ToPan = true;
+    myGL.Panning.Delta = myUI.Panning.Delta;
+  }
+
+  if (myUI.Dragging.ToAbort)
+  {
+    myUI.Dragging.ToAbort = false;
+    myGL.Dragging.ToAbort = true;
+  }
+  else if (myUI.Dragging.ToStop)
+  {
+    myUI.Dragging.ToStop = false;
+    myGL.Dragging.ToStop = true;
+  }
+  else if (myUI.Dragging.ToStart)
+  {
+    myUI.Dragging.ToStart = false;
+    myGL.Dragging.ToStart = true;
+    myGL.Dragging.PointStart = myUI.Dragging.PointStart;
+  }
+  myGL.Dragging.PointTo = myUI.Dragging.PointTo;
+
+  if (myUI.OrbitRotation.ToStart)
+  {
+    myUI.OrbitRotation.ToStart    = false;
+    myGL.OrbitRotation.ToStart    = true;
+    myGL.OrbitRotation.PointStart = myUI.OrbitRotation.PointStart;
+  }
+
+  if (myUI.OrbitRotation.ToRotate)
+  {
+    myUI.OrbitRotation.ToRotate = false;
+    myGL.OrbitRotation.ToRotate = true;
+    myGL.OrbitRotation.PointTo  = myUI.OrbitRotation.PointTo;
+  }
+
+  if (myUI.ViewRotation.ToStart)
+  {
+    myUI.ViewRotation.ToStart    = false;
+    myGL.ViewRotation.ToStart    = true;
+    myGL.ViewRotation.PointStart = myUI.ViewRotation.PointStart;
+  }
+
+  if (myUI.ViewRotation.ToRotate)
+  {
+    myUI.ViewRotation.ToRotate = false;
+    myGL.ViewRotation.ToRotate = true;
+    myGL.ViewRotation.PointTo  = myUI.ViewRotation.PointTo;
+  }
+
+  if (myUI.ZRotate.ToRotate)
+  {
+    myGL.ZRotate = myUI.ZRotate;
+    myUI.ZRotate.ToRotate = false;
+  }
+}
+
+// =======================================================================
+// function : flushGestures
+// purpose  :
+// =======================================================================
+void AIS_ViewController::flushGestures (const Handle(AIS_InteractiveContext)& ,
+                                        const Handle(V3d_View)& theView)
+{
+  const Standard_Real    aTolScale = myTouchToleranceScale;
+  const Standard_Integer aTouchNb  = myTouchPoints.Extent();
+  if (myNbTouchesLast != aTouchNb)
+  {
+    myNbTouchesLast = aTouchNb;
+    myGL.IsNewGesture = true;
+  }
+  if (aTouchNb == 1) // touch
+  {
+    Aspect_Touch& aTouch = myTouchPoints.ChangeFromIndex (1);
+    if (myUpdateStartPointRot)
+    {
+      // skip rotation if have active dragged object
+      if (myNavigationMode == AIS_NavigationMode_Orbit)
+      {
+        myGL.OrbitRotation.ToStart = true;
+        myGL.OrbitRotation.PointStart = myStartRotCoord;
+      }
+      else
+      {
+        myGL.ViewRotation.ToStart = true;
+        myGL.ViewRotation.PointStart = myStartRotCoord;
+      }
+
+      myUpdateStartPointRot = false;
+      theView->Invalidate();
+    }
+
+    // rotation
+    const Standard_Real aRotTouchTol = !aTouch.IsPreciseDevice
+                                     ? aTolScale * myTouchRotationThresholdPx
+                                     : gp::Resolution();
+    if (Abs (aTouch.Delta().x()) + Abs(aTouch.Delta().y()) > aRotTouchTol)
+    {
+      const Standard_Real aRotAccel = myNavigationMode == AIS_NavigationMode_FirstPersonWalk ? myMouseAccel : myOrbitAccel;
+      if (myNavigationMode == AIS_NavigationMode_Orbit)
+      {
+        const Graphic3d_Vec2d aRotDelta = aTouch.To - myGL.OrbitRotation.PointStart;
+        myGL.OrbitRotation.ToRotate = true;
+        myGL.OrbitRotation.PointTo  = myGL.OrbitRotation.PointStart + aRotDelta * aRotAccel;
+        myGL.Dragging.PointTo.SetValues ((int )aTouch.To.x(), (int )aTouch.To.y());
+      }
+      else
+      {
+        const Graphic3d_Vec2d aRotDelta = aTouch.To - myGL.ViewRotation.PointStart;
+        myGL.ViewRotation.ToRotate = true;
+        myGL.ViewRotation.PointTo = myGL.ViewRotation.PointStart + aRotDelta * aRotAccel;
+        myGL.Dragging.PointTo.SetValues ((int )aTouch.To.x(), (int )aTouch.To.y());
+      }
+
+      aTouch.From = aTouch.To;
+    }
+  }
+  else if (aTouchNb == 2) // pinch
+  {
+    Aspect_Touch& aFirstTouch = myTouchPoints.ChangeFromIndex (1);
+    Aspect_Touch& aLastTouch  = myTouchPoints.ChangeFromIndex (2);
+    const Graphic3d_Vec2d aFrom[2] = { aFirstTouch.From, aLastTouch.From };
+    const Graphic3d_Vec2d aTo[2]   = { aFirstTouch.To,   aLastTouch.To   };
+
+    Graphic3d_Vec2d aPinchCenterStart ((aFrom[0].x() + aFrom[1].x()) / 2.0,
+                                       (aFrom[0].y() + aFrom[1].y()) / 2.0);
+
+    Standard_Real aPinchCenterXEnd = (aTo[0].x() + aTo[1].x()) / 2.0;
+    Standard_Real aPinchCenterYEnd = (aTo[0].y() + aTo[1].y()) / 2.0;
+
+    Standard_Real aPinchCenterXDev = aPinchCenterXEnd - aPinchCenterStart.x();
+    Standard_Real aPinchCenterYDev = aPinchCenterYEnd - aPinchCenterStart.y();
+
+    Standard_Real aStartSize = (aFrom[0] - aFrom[1]).Modulus();
+    Standard_Real anEndSize  = (  aTo[0] -   aTo[1]).Modulus();
+
+    Standard_Real aDeltaSize = anEndSize - aStartSize;
+
+    bool anIsClearDev = false;
+
+    if (myToAllowTouchZRotation)
+    {
+      Standard_Real A1 = aFrom[0].y() - aFrom[1].y();
+      Standard_Real B1 = aFrom[1].x() - aFrom[0].x();
+
+      Standard_Real A2 = aTo[0].y() - aTo[1].y();
+      Standard_Real B2 = aTo[1].x() - aTo[0].x();
+
+      Standard_Real aRotAngle = 0.0;
+
+      Standard_Real aDenomenator = A1*A2 + B1*B2;
+      if (aDenomenator <= Precision::Confusion())
+      {
+        aRotAngle = 0.0;
+      }
+      else
+      {
+        Standard_Real aNumerator = A1*B2 - A2*B1;
+        aRotAngle = ATan (aNumerator / aDenomenator);
+      }
+
+      if (Abs(aRotAngle) > Standard_Real(myTouchZRotationThreshold))
+      {
+        myGL.ZRotate.ToRotate = true;
+        myGL.ZRotate.Angle = aRotAngle;
+        anIsClearDev = true;
+      }
+    }
+
+    if (Abs(aDeltaSize) > aTolScale * myTouchZoomThresholdPx)
+    {
+      // zoom
+      aDeltaSize *= Standard_Real(myTouchZoomRatio);
+      Aspect_ScrollDelta aParams (Graphic3d_Vec2i (aPinchCenterStart), aDeltaSize);
+      myGL.ZoomActions.Append (aParams);
+      anIsClearDev = true;
+    }
+
+    const Standard_Real aPanTouchTol = !aFirstTouch.IsPreciseDevice
+                                     ? aTolScale * myTouchPanThresholdPx
+                                     : gp::Resolution();
+    if (Abs(aPinchCenterXDev) + Abs(aPinchCenterYDev) > aPanTouchTol)
+    {
+      // pan
+      if (myUpdateStartPointPan)
+      {
+        myGL.Panning.ToStart = true;
+        myGL.Panning.PointStart = Graphic3d_Vec2i (myStartPanCoord);
+        myUpdateStartPointPan = false;
+        theView->Invalidate();
+      }
+
+      myGL.Panning.ToPan = true;
+      myGL.Panning.Delta.x() = int( aPinchCenterXDev);
+      myGL.Panning.Delta.y() = int(-aPinchCenterYDev);
+      anIsClearDev = true;
+    }
+
+    if (anIsClearDev)
+    {
+      aFirstTouch.From = aFirstTouch.To;
+      aLastTouch .From = aLastTouch.To;
+    }
+  }
+}
+
+// =======================================================================
+// function : UpdateViewOrientation
+// purpose  :
+// =======================================================================
+void AIS_ViewController::UpdateViewOrientation (V3d_TypeOfOrientation theOrientation,
+                                                bool theToFitAll)
+{
+  myUI.Orientation.ToFitAll = theToFitAll;
+  myUI.Orientation.ToSetViewOrient = true;
+  myUI.Orientation.ViewOrient = theOrientation;
+}
+
+// =======================================================================
+// function : SelectInViewer
+// purpose  :
+// =======================================================================
+void AIS_ViewController::SelectInViewer (const Graphic3d_Vec2i& thePnt,
+                                         const bool theIsXOR)
+{
+  if (myUI.Selection.Tool != AIS_ViewSelectionTool_Picking)
+  {
+    myUI.Selection.Tool = AIS_ViewSelectionTool_Picking;
+    myUI.Selection.Points.Clear();
+  }
+
+  myUI.Selection.IsXOR = theIsXOR;
+  myUI.Selection.Points.Append (thePnt);
+}
+
+// =======================================================================
+// function : SelectInViewer
+// purpose  :
+// =======================================================================
+void AIS_ViewController::SelectInViewer (const NCollection_Sequence<Graphic3d_Vec2i>& thePnts,
+                                         const bool theIsXOR)
+{
+  myUI.Selection.IsXOR = theIsXOR;
+  myUI.Selection.Points = thePnts;
+  myUI.Selection.ToApplyTool = true;
+  if (thePnts.Length() == 1)
+  {
+    myUI.Selection.Tool = AIS_ViewSelectionTool_Picking;
+  }
+  else if (thePnts.Length() == 2)
+  {
+    myUI.Selection.Tool = AIS_ViewSelectionTool_RubberBand;
+  }
+  else
+  {
+    myUI.Selection.Tool = AIS_ViewSelectionTool_Polygon;
+  }
+}
+
+// =======================================================================
+// function : UpdateRubberBand
+// purpose  :
+// =======================================================================
+void AIS_ViewController::UpdateRubberBand (const Graphic3d_Vec2i& thePntFrom,
+                                           const Graphic3d_Vec2i& thePntTo,
+                                           const bool theIsXOR)
+{
+  myUI.Selection.Tool = AIS_ViewSelectionTool_RubberBand;
+  myUI.Selection.IsXOR = theIsXOR;
+  myUI.Selection.Points.Clear();
+  myUI.Selection.Points.Append (thePntFrom);
+  myUI.Selection.Points.Append (thePntTo);
+}
+
+// =======================================================================
+// function : UpdatePolySelection
+// purpose  :
+// =======================================================================
+void AIS_ViewController::UpdatePolySelection (const Graphic3d_Vec2i& thePnt,
+                                              bool theToAppend)
+{
+  if (myUI.Selection.Tool != AIS_ViewSelectionTool_Polygon)
+  {
+    myUI.Selection.Tool = AIS_ViewSelectionTool_Polygon;
+    myUI.Selection.Points.Clear();
+  }
+
+  if (myUI.Selection.Points.IsEmpty())
+  {
+    myUI.Selection.Points.Append (thePnt);
+  }
+  else if (theToAppend
+        && myUI.Selection.Points.Last() != thePnt)
+  {
+    myUI.Selection.Points.Append (thePnt);
+  }
+  else
+  {
+    myUI.Selection.Points.ChangeLast() = thePnt;
+  }
+}
+
+// =======================================================================
+// function : UpdateZoom
+// purpose  :
+// =======================================================================
+bool AIS_ViewController::UpdateZoom (const Aspect_ScrollDelta& theDelta)
+{
+  if (!myUI.ZoomActions.IsEmpty())
+  {
+    if (myUI.ZoomActions.ChangeLast().Point == theDelta.Point)
+    {
+      myUI.ZoomActions.ChangeLast().Delta += theDelta.Delta;
+      return false;
+    }
+  }
+
+  myUI.ZoomActions.Append (theDelta);
+  return true;
+}
+
+// =======================================================================
+// function : UpdateZRotation
+// purpose  :
+// =======================================================================
+bool AIS_ViewController::UpdateZRotation (double theAngle)
+{
+  if (!ToAllowTouchZRotation())
+  {
+    return false;
+  }
+
+  myUI.ZRotate.Angle = myUI.ZRotate.ToRotate
+                     ? myUI.ZRotate.Angle + theAngle
+                     : theAngle;
+  if (myUI.ZRotate.ToRotate)
+  {
+    return false;
+  }
+  myUI.ZRotate.ToRotate = true;
+  return true;
+}
+
+// =======================================================================
+// function : UpdateMouseScroll
+// purpose  :
+// =======================================================================
+bool AIS_ViewController::UpdateMouseScroll (const Aspect_ScrollDelta& theDelta)
+{
+  Aspect_ScrollDelta aDelta = theDelta;
+  aDelta.Delta *= myScrollZoomRatio;
+  return UpdateZoom (aDelta);
+}
+
+// =======================================================================
+// function : UpdateMouseClick
+// purpose  :
+// =======================================================================
+bool AIS_ViewController::UpdateMouseClick (const Graphic3d_Vec2i& thePoint,
+                                           Aspect_VKeyMouse theButton,
+                                           Aspect_VKeyFlags theModifiers,
+                                           bool theIsDoubleClick)
+{
+  (void )theIsDoubleClick;
+  if (theButton == Aspect_VKeyMouse_LeftButton)
+  {
+    SelectInViewer (thePoint, (theModifiers & Aspect_VKeyFlags_SHIFT) != 0);
+    return true;
+  }
+  return false;
+}
+
+// =======================================================================
+// function : UpdateMouseButtons
+// purpose  :
+// =======================================================================
+bool AIS_ViewController::UpdateMouseButtons (const Graphic3d_Vec2i& thePoint,
+                                             Aspect_VKeyMouse theButtons,
+                                             Aspect_VKeyFlags theModifiers,
+                                             bool theIsEmulated)
+{
+  bool toUpdateView = false;
+  const double aTolClick = (theIsEmulated ? myTouchToleranceScale : 1.0) * myMouseClickThreshold;
+  if (theButtons == Aspect_VKeyMouse_NONE
+   && myMouseSingleButton > 0)
+  {
+    const Graphic3d_Vec2i aDelta = thePoint - myMousePressPoint;
+    if (double(aDelta.cwiseAbs().maxComp()) < aTolClick)
+    {
+      ++myMouseClickCounter;
+      const bool isDoubleClick = myMouseClickCounter == 2
+                              && myMouseClickTimer.IsStarted()
+                              && myMouseClickTimer.ElapsedTime() <= myMouseDoubleClickInt;
+
+      myMouseClickTimer.Stop();
+      myMouseClickTimer.Reset();
+      myMouseClickTimer.Start();
+      if (isDoubleClick)
+      {
+        myMouseClickCounter = 0;
+      }
+      toUpdateView = UpdateMouseClick (thePoint, (Aspect_VKeyMouse )myMouseSingleButton, theModifiers, isDoubleClick) || toUpdateView;
+    }
+    else
+    {
+      myMouseClickTimer.Stop();
+      myMouseClickCounter = 0;
+      myUI.Dragging.ToStop = true;
+      toUpdateView = true;
+    }
+    myMouseSingleButton = -1;
+  }
+  else if (theButtons == Aspect_VKeyMouse_NONE)
+  {
+    myMouseSingleButton = -1;
+  }
+  else if (myMouseSingleButton == -1)
+  {
+    if ((theButtons & Aspect_VKeyMouse_LeftButton) == Aspect_VKeyMouse_LeftButton)
+    {
+      myMouseSingleButton = Aspect_VKeyMouse_LeftButton;
+    }
+    else if ((theButtons & Aspect_VKeyMouse_RightButton) == Aspect_VKeyMouse_RightButton)
+    {
+      myMouseSingleButton = Aspect_VKeyMouse_RightButton;
+    }
+    else if ((theButtons & Aspect_VKeyMouse_MiddleButton) == Aspect_VKeyMouse_MiddleButton)
+    {
+      myMouseSingleButton = Aspect_VKeyMouse_MiddleButton;
+    }
+    else
+    {
+      myMouseSingleButton = 0;
+    }
+    if (myMouseSingleButton != 0)
+    {
+      if (myMouseClickCounter == 1)
+      {
+        const Graphic3d_Vec2i aDelta = thePoint - myMousePressPoint;
+        if (double(aDelta.cwiseAbs().maxComp()) >= aTolClick)
+        {
+          myMouseClickTimer.Stop();
+          myMouseClickCounter = 0;
+        }
+      }
+      myMousePressPoint = thePoint;
+    }
+  }
+  else
+  {
+    myMouseSingleButton = 0;
+
+    myUI.Dragging.ToAbort = true;
+    toUpdateView = true;
+  }
+
+  const AIS_MouseGesture aPrevGesture = myMouseActiveGesture;
+  myMouseModifiers = theModifiers;
+  myMousePressed   = theButtons;
+  if (theIsEmulated
+   || myNavigationMode != AIS_NavigationMode_FirstPersonWalk)
+  {
+    myMouseActiveIdleRotation = false;
+    myMouseActiveGesture = AIS_MouseGesture_NONE;
+    if (theButtons != 0)
+    {
+      myMousePressPoint    = thePoint;
+      myMouseProgressPoint = myMousePressPoint;
+    }
+
+    if (myMouseGestureMap.Find (theButtons | theModifiers, myMouseActiveGesture))
+    {
+      switch (myMouseActiveGesture)
+      {
+        case AIS_MouseGesture_RotateView:
+        case AIS_MouseGesture_RotateOrbit:
+        {
+          if (myToAllowRotation)
+          {
+            myUpdateStartPointRot = true;
+          }
+          else
+          {
+            myMouseActiveGesture = AIS_MouseGesture_NONE;
+          }
+          break;
+        }
+        case AIS_MouseGesture_Pan:
+        {
+          if (myToAllowPanning)
+          {
+            myUpdateStartPointPan = true;
+          }
+          else
+          {
+            myMouseActiveGesture = AIS_MouseGesture_NONE;
+          }
+          break;
+        }
+        case AIS_MouseGesture_Zoom:
+        {
+          if (!myToAllowZooming)
+          {
+            myMouseActiveGesture = AIS_MouseGesture_NONE;
+          }
+          break;
+        }
+        case AIS_MouseGesture_SelectRectangle:
+        {
+          break;
+        }
+        case AIS_MouseGesture_SelectLasso:
+        {
+          UpdatePolySelection (thePoint, true);
+          break;
+        }
+        case AIS_MouseGesture_NONE:
+        {
+          break;
+        }
+      }
+    }
+
+    if (theButtons == Aspect_VKeyMouse_LeftButton
+     && theModifiers == Aspect_VKeyFlags_NONE
+     && myToAllowDragging)
+    {
+      myUI.Dragging.ToStart = true;
+      myUI.Dragging.PointStart = thePoint;
+    }
+  }
+
+  if (aPrevGesture != myMouseActiveGesture)
+  {
+    if (aPrevGesture == AIS_MouseGesture_SelectRectangle
+     || aPrevGesture == AIS_MouseGesture_SelectLasso)
+    {
+      myUI.Selection.ToApplyTool = true;
+    }
+
+    myUI.IsNewGesture = true;
+    toUpdateView = true;
+  }
+  return toUpdateView;
+}
+
+// =======================================================================
+// function : UpdateMousePosition
+// purpose  :
+// =======================================================================
+bool AIS_ViewController::UpdateMousePosition (const Graphic3d_Vec2i& thePoint,
+                                              Aspect_VKeyMouse theButtons,
+                                              Aspect_VKeyFlags theModifiers,
+                                              bool theIsEmulated)
+{
+  myMousePositionLast = thePoint;
+  if (myMouseSingleButton > 0)
+  {
+    const double aTolClick = (theIsEmulated ? myTouchToleranceScale : 1.0) * myMouseClickThreshold;
+    const Graphic3d_Vec2i aPressDelta = thePoint - myMousePressPoint;
+    if (double(aPressDelta.cwiseAbs().maxComp()) >= aTolClick)
+    {
+      myMouseClickTimer.Stop();
+      myMouseClickCounter = 0;
+      myMouseSingleButton = -1;
+    }
+  }
+
+  bool toUpdateView = false;
+  Graphic3d_Vec2i aDelta = thePoint - myMouseProgressPoint;
+  if (!theIsEmulated
+    && myNavigationMode == AIS_NavigationMode_FirstPersonWalk)
+  {
+    if (!myMouseActiveIdleRotation
+      || myMouseActiveGesture != AIS_MouseGesture_RotateView)
+    {
+      myMouseActiveIdleRotation = true;
+      myMouseActiveGesture = AIS_MouseGesture_RotateView;
+      myMousePressPoint     = thePoint;
+      myMouseProgressPoint  = thePoint;
+      myUpdateStartPointRot = false;
+      myUI.ViewRotation.ToStart = true;
+      myUI.ViewRotation.PointStart.SetValues (thePoint.x(), thePoint.y());
+      myUI.ViewRotation.ToRotate = false;
+      aDelta.SetValues (0, 0);
+    }
+  }
+  else
+  {
+    if (myMouseActiveIdleRotation
+     && myMouseActiveGesture == AIS_MouseGesture_RotateView)
+    {
+      myMouseActiveGesture = AIS_MouseGesture_NONE;
+    }
+    myMouseActiveIdleRotation = false;
+  }
+
+  if (myMouseModifiers != theModifiers
+   && UpdateMouseButtons (thePoint, theButtons, theModifiers, theIsEmulated))
+  {
+    toUpdateView = true;
+  }
+
+  switch (myMouseActiveGesture)
+  {
+    case AIS_MouseGesture_SelectRectangle:
+    {
+      UpdateRubberBand (myMousePressPoint, thePoint);
+      toUpdateView = true;
+      break;
+    }
+    case AIS_MouseGesture_SelectLasso:
+    {
+      UpdatePolySelection (thePoint, true);
+      toUpdateView = true;
+      break;
+    }
+    case AIS_MouseGesture_RotateOrbit:
+    case AIS_MouseGesture_RotateView:
+    {
+      if (!myToAllowRotation)
+      {
+        break;
+      }
+      if (myUpdateStartPointRot)
+      {
+        if (myMouseActiveGesture == AIS_MouseGesture_RotateOrbit)
+        {
+          myUI.OrbitRotation.ToStart = true;
+          myUI.OrbitRotation.PointStart.SetValues (myMousePressPoint.x(), myMousePressPoint.y());
+        }
+        else
+        {
+          myUI.ViewRotation.ToStart = true;
+          myUI.ViewRotation.PointStart.SetValues (myMousePressPoint.x(), myMousePressPoint.y());
+        }
+        myUpdateStartPointRot = false;
+      }
+
+      const double aRotTol = theIsEmulated
+                           ? double(myTouchToleranceScale) * myTouchRotationThresholdPx
+                           : 0.0;
+      if (double (Abs (aDelta.x()) + Abs (aDelta.y())) > aRotTol)
+      {
+        const double aRotAccel = myNavigationMode == AIS_NavigationMode_FirstPersonWalk ? myMouseAccel : myOrbitAccel;
+        const Graphic3d_Vec2i aRotDelta = thePoint - myMousePressPoint;
+        if (myMouseActiveGesture == AIS_MouseGesture_RotateOrbit)
+        {
+          myUI.OrbitRotation.ToRotate = true;
+          myUI.OrbitRotation.PointTo = Graphic3d_Vec2d (myMousePressPoint.x(), myMousePressPoint.y())
+                                     + Graphic3d_Vec2d (aRotDelta.x(), aRotDelta.y()) * aRotAccel;
+        }
+        else
+        {
+          myUI.ViewRotation.ToRotate = true;
+          myUI.ViewRotation.PointTo = Graphic3d_Vec2d (myMousePressPoint.x(), myMousePressPoint.y())
+                                    + Graphic3d_Vec2d (aRotDelta.x(), aRotDelta.y()) * aRotAccel;
+        }
+        myUI.Dragging.PointTo = thePoint;
+
+        myMouseProgressPoint = thePoint;
+        toUpdateView = true;
+      }
+      break;
+    }
+    case AIS_MouseGesture_Zoom:
+    {
+      if (!myToAllowZooming)
+      {
+        break;
+      }
+      const double aZoomTol = theIsEmulated
+                            ? double(myTouchToleranceScale) * myTouchZoomThresholdPx
+                            : 0.0;
+      if (double (Abs (aDelta.x())) > aZoomTol)
+      {
+        if (UpdateZoom (Aspect_ScrollDelta (aDelta.x())))
+        {
+          toUpdateView = true;
+        }
+        myMouseProgressPoint = thePoint;
+      }
+      break;
+    }
+    case AIS_MouseGesture_Pan:
+    {
+      if (!myToAllowPanning)
+      {
+        break;
+      }
+      const double aPanTol = theIsEmulated
+                           ? double(myTouchToleranceScale) * myTouchPanThresholdPx
+                           : 0.0;
+      if (double (Abs (aDelta.x()) + Abs (aDelta.y())) > aPanTol)
+      {
+        if (myUpdateStartPointPan)
+        {
+          myUI.Panning.ToStart = true;
+          myUI.Panning.PointStart.SetValues (myMousePressPoint.x(), myMousePressPoint.y());
+          myUpdateStartPointPan = false;
+        }
+
+        aDelta.y() = -aDelta.y();
+        myMouseProgressPoint = thePoint;
+        if (myUI.Panning.ToPan)
+        {
+          myUI.Panning.Delta += aDelta;
+        }
+        else
+        {
+          myUI.Panning.ToPan = true;
+          myUI.Panning.Delta = aDelta;
+        }
+        toUpdateView = true;
+      }
+      break;
+    }
+    default:
+    {
+      break;
+    }
+  }
+
+  if (theButtons == Aspect_VKeyMouse_NONE
+  &&  myNavigationMode != AIS_NavigationMode_FirstPersonWalk
+  && !theIsEmulated
+  && !HasTouchPoints()
+  &&  myToAllowHighlight)
+  {
+    myUI.MoveTo.ToHilight = true;
+    myUI.MoveTo.Point = thePoint;
+    toUpdateView = true;
+  }
+  return toUpdateView;
+}
+
+// =======================================================================
+// function : AddTouchPoint
+// purpose  :
+// =======================================================================
+void AIS_ViewController::AddTouchPoint (Standard_Size theId,
+                                        const Graphic3d_Vec2d& thePnt,
+                                        Standard_Boolean theClearBefore)
+{
+  myUI.MoveTo.ToHilight = false;
+  if (theClearBefore)
+  {
+    RemoveTouchPoint ((Standard_Size )-1);
+  }
+
+  myTouchPoints.Add (theId, Aspect_Touch (thePnt, false));
+  if (myTouchPoints.Extent() == 1)
+  {
+    myUpdateStartPointRot = true;
+    myStartRotCoord = thePnt;
+    if (myToAllowDragging)
+    {
+      myUI.Dragging.ToStart = true;
+      myUI.Dragging.PointStart.SetValues ((int )thePnt.x(), (int )thePnt.y());
+    }
+  }
+  else if (myTouchPoints.Extent() == 2)
+  {
+    myUI.Dragging.ToAbort = true;
+
+    myUpdateStartPointPan = true;
+    myStartPanCoord = thePnt;
+  }
+  myUI.IsNewGesture = true;
+}
+
+// =======================================================================
+// function : RemoveTouchPoint
+// purpose  :
+// =======================================================================
+bool AIS_ViewController::RemoveTouchPoint (Standard_Size theId,
+                                           Standard_Boolean theClearSelectPnts)
+{
+  if (theId == (Standard_Size )-1)
+  {
+    myTouchPoints.Clear (false);
+  }
+  else
+  {
+    const Standard_Integer anOldExtent = myTouchPoints.Extent();
+    myTouchPoints.RemoveKey (theId);
+    if (myTouchPoints.Extent() == anOldExtent)
+    {
+      return false;
+    }
+  }
+
+  if (myTouchPoints.Extent() == 1)
+  {
+    // avoid incorrect transition from pinch to one finger
+    Aspect_Touch& aFirstTouch = myTouchPoints.ChangeFromIndex (1);
+    aFirstTouch.To = aFirstTouch.From;
+
+    myStartRotCoord = aFirstTouch.To;
+    myUpdateStartPointRot = true;
+  }
+  else if (myTouchPoints.Extent() == 2)
+  {
+    myStartPanCoord = myTouchPoints.FindFromIndex (1).To;
+    myUpdateStartPointPan = true;
+  }
+  else if (myTouchPoints.IsEmpty())
+  {
+    if (theClearSelectPnts)
+    {
+      myUI.Selection.ToApplyTool = true;
+    }
+
+    myUI.Dragging.ToStop = true;
+  }
+  myUI.IsNewGesture = true;
+  return true;
+}
+
+// =======================================================================
+// function : UpdateTouchPoint
+// purpose  :
+// =======================================================================
+void AIS_ViewController::UpdateTouchPoint (Standard_Size theId,
+                                           const Graphic3d_Vec2d& thePnt)
+{
+  if (Aspect_Touch* aTouch = myTouchPoints.ChangeSeek (theId))
+  {
+    aTouch->To = thePnt;
+  }
+  else
+  {
+    AddTouchPoint (theId, thePnt);
+  }
+}
+
+// =======================================================================
+// function : SetNavigationMode
+// purpose  :
+// =======================================================================
+void AIS_ViewController::SetNavigationMode (AIS_NavigationMode theMode)
+{
+  myNavigationMode = theMode;
+
+  // abort rotation
+  myUI.OrbitRotation.ToStart  = false;
+  myUI.OrbitRotation.ToRotate = false;
+  myUI.ViewRotation.ToStart   = false;
+  myUI.ViewRotation.ToRotate  = false;
+}
+
+// =======================================================================
+// function : KeyDown
+// purpose  :
+// =======================================================================
+void AIS_ViewController::KeyDown (Aspect_VKey theKey,
+                                  double theTime,
+                                  double thePressure)
+{
+  myKeys.KeyDown (theKey, theTime, thePressure);
+}
+
+// =======================================================================
+// function : KeyUp
+// purpose  :
+// =======================================================================
+void AIS_ViewController::KeyUp (Aspect_VKey theKey,
+                                double theTime)
+{
+  myKeys.KeyUp (theKey, theTime);
+}
+
+// =======================================================================
+// function : KeyFromAxis
+// purpose  :
+// =======================================================================
+void AIS_ViewController::KeyFromAxis (Aspect_VKey theNegative,
+                                      Aspect_VKey thePositive,
+                                      double theTime,
+                                      double thePressure)
+{
+  myKeys.KeyFromAxis (theNegative, thePositive, theTime, thePressure);
+}
+
+// =======================================================================
+// function : FetchNavigationKeys
+// purpose  :
+// =======================================================================
+AIS_WalkDelta AIS_ViewController::FetchNavigationKeys (Standard_Real theCrouchRatio,
+                                                       Standard_Real theRunRatio)
+{
+  AIS_WalkDelta aWalk;
+
+  // navigation keys
+  double aPrevEventTime = 0.0, aNewEventTime = 0.0;
+  updateEventsTime (aPrevEventTime, aNewEventTime);
+
+  double aDuration = 0.0, aPressure = 1.0;
+  if (Abs (myThrustSpeed) > gp::Resolution())
+  {
+    if (myHasThrust)
+    {
+      aWalk[AIS_WalkTranslation_Forward].Value = myThrustSpeed * (aNewEventTime - aPrevEventTime);
+    }
+    myHasThrust = true;
+    myToAskNextFrame = true;
+  }
+  else
+  {
+    myHasThrust = false;
+  }
+
+  aWalk.SetRunning (theRunRatio > 1.0
+                 && myKeys.IsKeyDown (Aspect_VKey_Shift));
+  if (myKeys.HoldDuration (Aspect_VKey_NavJump, aNewEventTime, aDuration))
+  {
+    myKeys.KeyUp (Aspect_VKey_NavJump, aNewEventTime);
+    aWalk.SetJumping (true);
+  }
+  if (!aWalk.IsJumping()
+   && theCrouchRatio < 1.0
+   && myKeys.HoldDuration (Aspect_VKey_NavCrouch, aNewEventTime, aDuration))
+  {
+    aWalk.SetRunning (false);
+    aWalk.SetCrouching (true);
+  }
+
+  const double aMaxDuration = aNewEventTime - aPrevEventTime;
+  const double aRunRatio = aWalk.IsRunning()
+                         ? theRunRatio
+                         : aWalk.IsCrouching()
+                          ? theCrouchRatio
+                          : 1.0;
+  if (myKeys.HoldDuration (Aspect_VKey_NavForward, aNewEventTime, aDuration, aPressure))
+  {
+    double aProgress = Abs (Min (aMaxDuration, aDuration));
+    aProgress *= aRunRatio;
+    aWalk[AIS_WalkTranslation_Forward].Value += aProgress;
+    aWalk[AIS_WalkTranslation_Forward].Pressure = aPressure;
+    aWalk[AIS_WalkTranslation_Forward].Duration = aDuration;
+  }
+  if (myKeys.HoldDuration (Aspect_VKey_NavBackward, aNewEventTime, aDuration, aPressure))
+  {
+    double aProgress = Abs (Min (aMaxDuration, aDuration));
+    aProgress *= aRunRatio;
+    aWalk[AIS_WalkTranslation_Forward].Value += -aProgress;
+    aWalk[AIS_WalkTranslation_Forward].Pressure = aPressure;
+    aWalk[AIS_WalkTranslation_Forward].Duration = aDuration;
+  }
+  if (myKeys.HoldDuration (Aspect_VKey_NavSlideLeft, aNewEventTime, aDuration, aPressure))
+  {
+    double aProgress = Abs (Min (aMaxDuration, aDuration));
+    aProgress *= aRunRatio;
+    aWalk[AIS_WalkTranslation_Side].Value = -aProgress;
+    aWalk[AIS_WalkTranslation_Side].Pressure = aPressure;
+    aWalk[AIS_WalkTranslation_Side].Duration = aDuration;
+  }
+  if (myKeys.HoldDuration (Aspect_VKey_NavSlideRight, aNewEventTime, aDuration, aPressure))
+  {
+    double aProgress = Abs (Min (aMaxDuration, aDuration));
+    aProgress *= aRunRatio;
+    aWalk[AIS_WalkTranslation_Side].Value = aProgress;
+    aWalk[AIS_WalkTranslation_Side].Pressure = aPressure;
+    aWalk[AIS_WalkTranslation_Side].Duration = aDuration;
+  }
+  if (myKeys.HoldDuration (Aspect_VKey_NavLookLeft, aNewEventTime, aDuration, aPressure))
+  {
+    double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
+    aWalk[AIS_WalkRotation_Yaw].Value = aProgress;
+    aWalk[AIS_WalkRotation_Yaw].Pressure = aPressure;
+    aWalk[AIS_WalkRotation_Yaw].Duration = aDuration;
+  }
+  if (myKeys.HoldDuration (Aspect_VKey_NavLookRight, aNewEventTime, aDuration, aPressure))
+  {
+    double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
+    aWalk[AIS_WalkRotation_Yaw].Value = -aProgress;
+    aWalk[AIS_WalkRotation_Yaw].Pressure = aPressure;
+    aWalk[AIS_WalkRotation_Yaw].Duration = aDuration;
+  }
+  if (myKeys.HoldDuration (Aspect_VKey_NavLookUp, aNewEventTime, aDuration, aPressure))
+  {
+    double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
+    aWalk[AIS_WalkRotation_Pitch].Value = !myToInvertPitch ? -aProgress : aProgress;
+    aWalk[AIS_WalkRotation_Pitch].Pressure = aPressure;
+    aWalk[AIS_WalkRotation_Pitch].Duration = aDuration;
+  }
+  if (myKeys.HoldDuration (Aspect_VKey_NavLookDown, aNewEventTime, aDuration, aPressure))
+  {
+    double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
+    aWalk[AIS_WalkRotation_Pitch].Value = !myToInvertPitch ? aProgress : -aProgress;
+    aWalk[AIS_WalkRotation_Pitch].Pressure = aPressure;
+    aWalk[AIS_WalkRotation_Pitch].Duration = aDuration;
+  }
+  if (myKeys.HoldDuration (Aspect_VKey_NavRollCCW, aNewEventTime, aDuration, aPressure))
+  {
+    double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
+    aWalk[AIS_WalkRotation_Roll].Value = -aProgress;
+    aWalk[AIS_WalkRotation_Roll].Pressure = aPressure;
+    aWalk[AIS_WalkRotation_Roll].Duration = aDuration;
+  }
+  if (myKeys.HoldDuration (Aspect_VKey_NavRollCW, aNewEventTime, aDuration, aPressure))
+  {
+    double aProgress = Abs (Min (aMaxDuration, aDuration)) * aPressure;
+    aWalk[AIS_WalkRotation_Roll].Value = aProgress;
+    aWalk[AIS_WalkRotation_Roll].Pressure = aPressure;
+    aWalk[AIS_WalkRotation_Roll].Duration = aDuration;
+  }
+  if (myKeys.HoldDuration (Aspect_VKey_NavSlideUp, aNewEventTime, aDuration, aPressure))
+  {
+    double aProgress = Abs (Min (aMaxDuration, aDuration));
+    aWalk[AIS_WalkTranslation_Up].Value = aProgress;
+    aWalk[AIS_WalkTranslation_Up].Pressure = aPressure;
+    aWalk[AIS_WalkTranslation_Up].Duration = aDuration;
+  }
+  if (myKeys.HoldDuration (Aspect_VKey_NavSlideDown, aNewEventTime, aDuration, aPressure))
+  {
+    double aProgress = Abs (Min (aMaxDuration, aDuration));
+    aWalk[AIS_WalkTranslation_Up].Value = -aProgress;
+    aWalk[AIS_WalkTranslation_Up].Pressure = aPressure;
+    aWalk[AIS_WalkTranslation_Up].Duration = aDuration;
+  }
+  return aWalk;
+}
+
+// =======================================================================
+// function : handlePanning
+// purpose  :
+// =======================================================================
+void AIS_ViewController::handlePanning (const Handle(V3d_View)& theView)
+{
+  if (!myGL.Panning.ToPan
+   || !myToAllowPanning)
+  {
+    return;
+  }
+
+  const Handle(Graphic3d_Camera)& aCam = theView->Camera();
+  if (aCam->IsOrthographic()
+  || !hasPanningAnchorPoint())
+  {
+    theView->Pan (myGL.Panning.Delta.x(), myGL.Panning.Delta.y());
+    theView->Invalidate();
+    return;
+  }
+
+  Graphic3d_Vec2i aWinSize;
+  theView->Window()->Size (aWinSize.x(), aWinSize.y());
+
+  const gp_Dir& aDir = aCam->Direction();
+  const gp_Ax3 aCameraCS (aCam->Center(), aDir.Reversed(), aDir ^ aCam->Up());
+  const gp_XYZ anEyeToPnt = myPanPnt3d.XYZ() - aCam->Eye().XYZ();
+  const gp_Pnt aViewDims = aCam->ViewDimensions (anEyeToPnt.Dot (aCam->Direction().XYZ())); // view dimensions at 3D point
+  const Graphic3d_Vec2d aDxy (-aViewDims.X() * myGL.Panning.Delta.x() / double(aWinSize.x()),
+                              -aViewDims.X() * myGL.Panning.Delta.y() / double(aWinSize.x()));
+
+  //theView->Translate (aCam, aDxy.x(), aDxy.y());
+  gp_Trsf aPanTrsf;
+  const gp_Vec aCameraPan = gp_Vec (aCameraCS.XDirection()) * aDxy.x()
+                          + gp_Vec (aCameraCS.YDirection()) * aDxy.y();
+  aPanTrsf.SetTranslation (aCameraPan);
+  aCam->Transform (aPanTrsf);
+  theView->Invalidate();
+}
+
+// =======================================================================
+// function : handleZRotate
+// purpose  :
+// =======================================================================
+void AIS_ViewController::handleZRotate (const Handle(V3d_View)& theView)
+{
+  if (!myGL.ZRotate.ToRotate
+   || !myToAllowRotation)
+  {
+    return;
+  }
+
+  Graphic3d_Vec2i aViewPort;
+  theView->Window()->Size (aViewPort.x(), aViewPort.y());
+  Graphic3d_Vec2d aRotPnt (0.99 * aViewPort.x(),
+                           0.5  * aViewPort.y());
+  theView->StartRotation (int(aRotPnt.x()), int(aRotPnt.y()), 0.4);
+  aRotPnt.y() += myGL.ZRotate.Angle * aViewPort.y();
+  theView->Rotation (int(aRotPnt.x()), int(aRotPnt.y()));
+  theView->Invalidate();
+}
+
+// =======================================================================
+// function : handleZoom
+// purpose  :
+// =======================================================================
+void AIS_ViewController::handleZoom (const Handle(V3d_View)& theView,
+                                     const Aspect_ScrollDelta& theParams,
+                                     const gp_Pnt* thePnt)
+{
+  if (!myToAllowZooming)
+  {
+    return;
+  }
+
+  const Handle(Graphic3d_Camera)& aCam = theView->Camera();
+  if (thePnt != NULL)
+  {
+    const double aViewDist = Max (myMinCamDistance, (thePnt->XYZ() - aCam->Eye().XYZ()).Modulus());
+    aCam->SetCenter (aCam->Eye().XYZ() + aCam->Direction().XYZ() * aViewDist);
+  }
+
+  if (!theParams.HasPoint())
+  {
+    Standard_Real aCoeff = Abs(theParams.Delta) / 100.0 + 1.0;
+    aCoeff = theParams.Delta > 0.0 ? aCoeff : 1.0 / aCoeff;
+    theView->SetZoom (aCoeff, true);
+    theView->Invalidate();
+    return;
+  }
+
+  // integer delta is too rough for small smooth increments
+  //theView->StartZoomAtPoint (theParams.Point.x(), theParams.Point.y());
+  //theView->ZoomAtPoint (0, 0, (int )theParams.Delta, (int )theParams.Delta);
+
+  double aDZoom = Abs (theParams.Delta) / 100.0 + 1.0;
+  aDZoom = (theParams.Delta > 0.0) ? aDZoom : 1.0 / aDZoom;
+  if (aDZoom <= 0.0)
+  {
+    return;
+  }
+
+  const Graphic3d_Vec2d aViewDims (aCam->ViewDimensions().X(), aCam->ViewDimensions().Y());
+
+  // ensure that zoom will not be too small or too big
+  double aCoef = aDZoom;
+  if (aViewDims.x() < aCoef * Precision::Confusion())
+  {
+    aCoef = aViewDims.x() / Precision::Confusion();
+  }
+  else if (aViewDims.x() > aCoef * 1e12)
+  {
+    aCoef = aViewDims.x() / 1e12;
+  }
+  if (aViewDims.y() < aCoef * Precision::Confusion())
+  {
+    aCoef = aViewDims.y() / Precision::Confusion();
+  }
+  else if (aViewDims.y() > aCoef * 1e12)
+  {
+    aCoef = aViewDims.y() / 1e12;
+  }
+
+  Graphic3d_Vec2d aZoomAtPointXYv (0.0, 0.0);
+  theView->Convert (theParams.Point.x(), theParams.Point.y(),
+                    aZoomAtPointXYv.x(), aZoomAtPointXYv.y());
+  Graphic3d_Vec2d aDxy = aZoomAtPointXYv / aCoef;
+  aCam->SetScale (aCam->Scale() / aCoef);
+
+  const gp_Dir& aDir = aCam->Direction();
+  const gp_Ax3 aCameraCS (aCam->Center(), aDir.Reversed(), aDir ^ aCam->Up());
+
+  // pan back to the point
+  aDxy = aZoomAtPointXYv - aDxy;
+  if (thePnt != NULL)
+  {
+    // zoom at 3D point with perspective projection
+    const gp_XYZ anEyeToPnt = thePnt->XYZ() - aCam->Eye().XYZ();
+    aDxy.SetValues (anEyeToPnt.Dot (aCameraCS.XDirection().XYZ()),
+                    anEyeToPnt.Dot (aCameraCS.YDirection().XYZ()));
+
+    // view dimensions at 3D point
+    const gp_Pnt aViewDims1 = aCam->ViewDimensions (anEyeToPnt.Dot (aCam->Direction().XYZ()));
+
+    Graphic3d_Vec2i aWinSize;
+    theView->Window()->Size (aWinSize.x(), aWinSize.y());
+    const Graphic3d_Vec2d aPanFromCenterPx (double(theParams.Point.x()) - 0.5 * double(aWinSize.x()),
+                                            double(theParams.Point.y()) - 0.5 * double(aWinSize.y()));
+    aDxy.x() += -aViewDims1.X() * aPanFromCenterPx.x() / double(aWinSize.x());
+    aDxy.y() +=  aViewDims1.X() * aPanFromCenterPx.y() / double(aWinSize.x());
+  }
+
+  //theView->Translate (aCam, aDxy.x(), aDxy.y());
+  gp_Trsf aPanTrsf;
+  const gp_Vec aCameraPan = gp_Vec (aCameraCS.XDirection()) * aDxy.x()
+                          + gp_Vec (aCameraCS.YDirection()) * aDxy.y();
+  aPanTrsf.SetTranslation (aCameraPan);
+  aCam->Transform (aPanTrsf);
+  theView->Invalidate();
+}
+
+// =======================================================================
+// function : handleZFocusScroll
+// purpose  :
+// =======================================================================
+void AIS_ViewController::handleZFocusScroll (const Handle(V3d_View)& theView,
+                                             const Aspect_ScrollDelta& theParams)
+{
+  if (!myToAllowZFocus
+   || !theView->Camera()->IsStereo())
+  {
+    return;
+  }
+
+  Standard_Real aFocus = theView->Camera()->ZFocus() + (theParams.Delta > 0.0 ? 0.05 : -0.05);
+  if (aFocus > 0.2
+   && aFocus < 2.0)
+  {
+    theView->Camera()->SetZFocus (theView->Camera()->ZFocusType(), aFocus);
+    theView->Redraw();
+  }
+}
+
+// =======================================================================
+// function : handleOrbitRotation
+// purpose  :
+// =======================================================================
+void AIS_ViewController::handleOrbitRotation (const Handle(V3d_View)& theView,
+                                              const gp_Pnt& thePnt,
+                                              bool theToLockZUp)
+{
+  if (!myToAllowRotation)
+  {
+    return;
+  }
+
+  const Handle(Graphic3d_Camera)& aCam = theView->Camera();
+  if (myGL.OrbitRotation.ToStart)
+  {
+    // default alternatives
+    //if (myRotationMode == AIS_RotationMode_BndBoxActive) theView->StartRotation (myGL.RotateAtPoint.x(), myGL.RotateAtPoint.y());
+    //theView->Rotate (0.0, 0.0, 0.0, thePnt.X(), thePnt.Y(), thePnt.Z(), true);
+
+    myRotatePnt3d      = thePnt;
+    myCamStartOpUp     = aCam->Up();
+    myCamStartOpEye    = aCam->Eye();
+    myCamStartOpCenter = aCam->Center();
+
+    gp_Trsf aTrsf;
+    aTrsf.SetTransformation (gp_Ax3 (myRotatePnt3d, aCam->OrthogonalizedUp(), aCam->Direction()),
+                             gp_Ax3 (myRotatePnt3d, gp::DZ(), gp::DX()));
+    const gp_Quaternion aRot = aTrsf.GetRotation();
+    aRot.GetEulerAngles (gp_YawPitchRoll, myRotateStartYawPitchRoll[0], myRotateStartYawPitchRoll[1], myRotateStartYawPitchRoll[2]);
+
+    aTrsf.Invert();
+    myCamStartOpToEye    = gp_Vec (myRotatePnt3d, aCam->Eye()).Transformed (aTrsf);
+    myCamStartOpToCenter = gp_Vec (myRotatePnt3d, aCam->Center()).Transformed (aTrsf);
+
+    theView->Invalidate();
+  }
+
+  if (!myGL.OrbitRotation.ToRotate)
+  {
+    return;
+  }
+
+  if (theToLockZUp)
+  {
+    // amend camera to exclude roll angle (put camera Up vector to plane containing global Z and view direction)
+    Graphic3d_Vec2i aWinXY;
+    theView->Window()->Size (aWinXY.x(), aWinXY.y());
+    double aYawAngleDelta   =  ((myGL.OrbitRotation.PointStart.x() - myGL.OrbitRotation.PointTo.x()) / double (aWinXY.x())) * (M_PI * 0.5);
+    double aPitchAngleDelta = -((myGL.OrbitRotation.PointStart.y() - myGL.OrbitRotation.PointTo.y()) / double (aWinXY.y())) * (M_PI * 0.5);
+    const double aPitchAngleNew = Max (Min (myRotateStartYawPitchRoll[1] + aPitchAngleDelta, M_PI * 0.5 - M_PI / 180.0), -M_PI * 0.5 + M_PI / 180.0);
+    const double aYawAngleNew   = myRotateStartYawPitchRoll[0] + aYawAngleDelta;
+    const double aRoll = 0.0;
+
+    gp_Quaternion aRot;
+    aRot.SetEulerAngles (gp_YawPitchRoll, aYawAngleNew, aPitchAngleNew, aRoll);
+    gp_Trsf aTrsfRot;
+    aTrsfRot.SetRotation (aRot);
+
+    const gp_Dir aNewUp = gp::DZ().Transformed (aTrsfRot);
+    aCam->SetUp (aNewUp);
+    aCam->SetCenter(myRotatePnt3d.XYZ() + myCamStartOpToCenter.Transformed (aTrsfRot).XYZ());
+    aCam->SetEye   (myRotatePnt3d.XYZ() + myCamStartOpToEye   .Transformed (aTrsfRot).XYZ());
+    aCam->OrthogonalizeUp();
+  }
+  else
+  {
+    // default alternatives
+    //if (myRotationMode == AIS_RotationMode_BndBoxActive) theView->Rotation (myGL.RotateToPoint.x(), myGL.RotateToPoint.y());
+    //theView->Rotate (aDX, aDY, aDZ, myRotatePnt3d.X(), myRotatePnt3d.Y(), myRotatePnt3d.Z(), false);
+
+    // restore previous camera state
+    aCam->SetUp     (myCamStartOpUp);
+    aCam->SetEye    (myCamStartOpEye);
+    aCam->SetCenter (myCamStartOpCenter);
+
+    Graphic3d_Vec2d aWinXY;
+    theView->Size (aWinXY.x(), aWinXY.y());
+    const Standard_Real rx = (Standard_Real )theView->Convert (aWinXY.x());
+    const Standard_Real ry = (Standard_Real )theView->Convert (aWinXY.y());
+
+    const double THE_2PI = M_PI * 2.0;
+    double aDX = (myGL.OrbitRotation.PointTo.x() - myGL.OrbitRotation.PointStart.x()) * M_PI / rx;
+    double aDY = (myGL.OrbitRotation.PointStart.y() - myGL.OrbitRotation.PointTo.y()) * M_PI / ry;
+
+    if     (aDX > 0.0) { while (aDX >  THE_2PI) { aDX -= THE_2PI; } }
+    else if(aDX < 0.0) { while (aDX < -THE_2PI) { aDX += THE_2PI; } }
+    if     (aDY > 0.0) { while (aDY >  THE_2PI) { aDY -= THE_2PI; } }
+    else if(aDY < 0.0) { while (aDY < -THE_2PI) { aDY += THE_2PI; } }
+
+    // rotate camera around 3 initial axes
+    gp_Dir aCamDir (aCam->Direction().Reversed());
+    gp_Dir aCamUp  (aCam->Up());
+    gp_Dir aCamSide(aCamUp.Crossed (aCamDir));
+
+    gp_Trsf aRot[2], aTrsf;
+    aRot[0].SetRotation (gp_Ax1 (myRotatePnt3d, aCamUp),  -aDX);
+    aRot[1].SetRotation (gp_Ax1 (myRotatePnt3d, aCamSide), aDY);
+    aTrsf.Multiply (aRot[0]);
+    aTrsf.Multiply (aRot[1]);
+
+    aCam->Transform (aTrsf);
+  }
+
+  theView->Invalidate();
+}
+
+// =======================================================================
+// function : handleViewRotation
+// purpose  :
+// =======================================================================
+void AIS_ViewController::handleViewRotation (const Handle(V3d_View)& theView,
+                                             double theYawExtra,
+                                             double thePitchExtra,
+                                             double theRoll,
+                                             bool theToRestartOnIncrement)
+{
+  if (!myToAllowRotation)
+  {
+    return;
+  }
+
+  const Handle(Graphic3d_Camera)& aCam = theView->Camera();
+  const bool toRotateAnyway = Abs (theYawExtra)   > gp::Resolution()
+                           || Abs (thePitchExtra) > gp::Resolution()
+                           || Abs (theRoll - myRotateStartYawPitchRoll[2]) > gp::Resolution();
+  if (toRotateAnyway
+   && theToRestartOnIncrement)
+  {
+    myGL.ViewRotation.ToStart = true;
+    myGL.ViewRotation.PointTo = myGL.ViewRotation.PointStart;
+  }
+  if (myGL.ViewRotation.ToStart)
+  {
+    gp_Trsf aTrsf;
+    aTrsf.SetTransformation (gp_Ax3 (gp::Origin(), aCam->OrthogonalizedUp(), aCam->Direction()),
+                             gp_Ax3 (gp::Origin(), gp::DZ(), gp::DX()));
+    const gp_Quaternion aRot = aTrsf.GetRotation();
+    double aRollDummy = 0.0;
+    aRot.GetEulerAngles (gp_YawPitchRoll, myRotateStartYawPitchRoll[0], myRotateStartYawPitchRoll[1], aRollDummy);
+  }
+  if (toRotateAnyway)
+  {
+    myRotateStartYawPitchRoll[0] += theYawExtra;
+    myRotateStartYawPitchRoll[1] += thePitchExtra;
+    myRotateStartYawPitchRoll[2]  = theRoll;
+    myGL.ViewRotation.ToRotate = true;
+  }
+
+  if (!myGL.ViewRotation.ToRotate)
+  {
+    return;
+  }
+
+  Graphic3d_Vec2i aWinXY;
+  theView->Window()->Size (aWinXY.x(), aWinXY.y());
+  double aYawAngleDelta   =  ((myGL.ViewRotation.PointStart.x() - myGL.ViewRotation.PointTo.x()) / double (aWinXY.x())) * (M_PI * 0.5);
+  double aPitchAngleDelta = -((myGL.ViewRotation.PointStart.y() - myGL.ViewRotation.PointTo.y()) / double (aWinXY.y())) * (M_PI * 0.5);
+  const double aPitchAngleNew = Max (Min (myRotateStartYawPitchRoll[1] + aPitchAngleDelta, M_PI * 0.5 - M_PI / 180.0), -M_PI * 0.5 + M_PI / 180.0);
+  const double aYawAngleNew   = myRotateStartYawPitchRoll[0] + aYawAngleDelta;
+  gp_Quaternion aRot;
+  aRot.SetEulerAngles (gp_YawPitchRoll, aYawAngleNew, aPitchAngleNew, theRoll);
+  gp_Trsf aTrsfRot;
+  aTrsfRot.SetRotation (aRot);
+
+  const double aDist   = aCam->Distance();
+  const gp_Dir aNewUp  = gp::DZ().Transformed (aTrsfRot);
+  const gp_Dir aNewDir = gp::DX().Transformed (aTrsfRot);
+  aCam->SetUp     (aNewUp);
+  aCam->SetCenter (aCam->Eye().Translated (gp_Vec (aNewDir) * aDist));
+  aCam->OrthogonalizeUp();
+  theView->Invalidate();
+}
+
+// =======================================================================
+// function : PickPoint
+// purpose  :
+// =======================================================================
+bool AIS_ViewController::PickPoint (gp_Pnt& thePnt,
+                                    const Handle(AIS_InteractiveContext)& theCtx,
+                                    const Handle(V3d_View)& theView,
+                                    const Graphic3d_Vec2i& theCursor,
+                                    bool theToStickToPickRay)
+{
+  ResetPreviousMoveTo();
+
+  const Handle(StdSelect_ViewerSelector3d)& aSelector = theCtx->MainSelector();
+  aSelector->Pick (theCursor.x(), theCursor.y(), theView);
+  if (aSelector->NbPicked() < 1)
+  {
+    return false;
+  }
+
+  const SelectMgr_SortCriterion& aPicked = aSelector->PickedData (1);
+  if (theToStickToPickRay
+  && !Precision::IsInfinite (aPicked.Depth))
+  {
+    thePnt = aSelector->GetManager().DetectedPoint (aPicked.Depth);
+  }
+  else
+  {
+    thePnt = aSelector->PickedPoint (1);
+  }
+  return !Precision::IsInfinite (thePnt.X())
+      && !Precision::IsInfinite (thePnt.Y())
+      && !Precision::IsInfinite (thePnt.Z());
+}
+
+// =======================================================================
+// function : GravityPoint
+// purpose  :
+// =======================================================================
+gp_Pnt AIS_ViewController::GravityPoint (const Handle(AIS_InteractiveContext)& theCtx,
+                                         const Handle(V3d_View)& theView)
+{
+  switch (myRotationMode)
+  {
+    case AIS_RotationMode_PickLast:
+    case AIS_RotationMode_PickCenter:
+    {
+      Graphic3d_Vec2i aCursor ((int )myGL.OrbitRotation.PointStart.x(), (int )myGL.OrbitRotation.PointStart.y());
+      if (myRotationMode == AIS_RotationMode_PickCenter)
+      {
+        Graphic3d_Vec2i aViewPort;
+        theView->Window()->Size (aViewPort.x(), aViewPort.y());
+        aCursor = aViewPort / 2;
+      }
+
+      gp_Pnt aPnt;
+      if (PickPoint (aPnt, theCtx, theView, aCursor, false))
+      {
+        return aPnt;
+      }
+      break;
+    }
+    case AIS_RotationMode_CameraAt:
+    {
+      const Handle(Graphic3d_Camera)& aCam = theView->Camera();
+      return aCam->Center();
+    }
+    case AIS_RotationMode_BndBoxScene:
+    {
+      Bnd_Box aBndBox = theView->View()->MinMaxValues (false);
+      if (!aBndBox.IsVoid())
+      {
+        return (aBndBox.CornerMin().XYZ() + aBndBox.CornerMax().XYZ()) * 0.5;
+      }
+      break;
+    }
+    case AIS_RotationMode_BndBoxActive:
+      break;
+  }
+
+  return theCtx ->GravityPoint (theView);
+}
+
+// =======================================================================
+// function : handleCameraActions
+// purpose  :
+// =======================================================================
+void AIS_ViewController::handleCameraActions (const Handle(AIS_InteractiveContext)& theCtx,
+                                              const Handle(V3d_View)& theView,
+                                              const AIS_WalkDelta& theWalk)
+{
+  // apply view actions
+  if (myGL.Orientation.ToSetViewOrient)
+  {
+    theView->SetProj (myGL.Orientation.ViewOrient);
+    myGL.Orientation.ToFitAll = true;
+  }
+
+  // apply fit all
+  if (myGL.Orientation.ToFitAll)
+  {
+    const double aFitMargin = 0.01;
+    theView->FitAll (aFitMargin, false);
+    theView->Invalidate();
+    myGL.Orientation.ToFitAll = false;
+  }
+
+  if (myGL.IsNewGesture)
+  {
+    if (myAnchorPointPrs1->HasInteractiveContext())
+    {
+      theCtx->Remove (myAnchorPointPrs1, false);
+      if (!theView->Viewer()->ZLayerSettings (myAnchorPointPrs1->ZLayer()).IsImmediate())
+      {
+        theView->Invalidate();
+      }
+      else
+      {
+        theView->InvalidateImmediate();
+      }
+    }
+    if (myAnchorPointPrs2->HasInteractiveContext())
+    {
+      theCtx->Remove (myAnchorPointPrs2, false);
+      if (!theView->Viewer()->ZLayerSettings (myAnchorPointPrs2->ZLayer()).IsImmediate())
+      {
+        theView->Invalidate();
+      }
+      else
+      {
+        theView->InvalidateImmediate();
+      }
+    }
+
+    if (myHasHlrOnBeforeRotation)
+    {
+      myHasHlrOnBeforeRotation = false;
+      theView->SetComputedMode (true);
+      theView->Invalidate();
+    }
+  }
+
+  if (myNavigationMode != AIS_NavigationMode_FirstPersonWalk)
+  {
+    if (myGL.Panning.ToStart
+     && myToAllowPanning)
+    {
+      gp_Pnt aPanPnt (Precision::Infinite(), 0.0, 0.0);
+      if (!theView->Camera()->IsOrthographic())
+      {
+        bool toStickToRay = false;
+        if (myGL.Panning.PointStart.x() >= 0
+         && myGL.Panning.PointStart.y() >= 0)
+        {
+          PickPoint (aPanPnt, theCtx, theView, myGL.Panning.PointStart, toStickToRay);
+        }
+        if (Precision::IsInfinite (aPanPnt.X()))
+        {
+          Graphic3d_Vec2i aWinSize;
+          theView->Window()->Size (aWinSize.x(), aWinSize.y());
+          PickPoint (aPanPnt, theCtx, theView, aWinSize / 2, toStickToRay);
+        }
+        if (!Precision::IsInfinite (aPanPnt.X())
+          && myToShowPanAnchorPoint)
+        {
+          gp_Trsf aPntTrsf;
+          aPntTrsf.SetTranslation (gp_Vec (aPanPnt.XYZ()));
+          theCtx->SetLocation (myAnchorPointPrs2, aPntTrsf);
+        }
+      }
+      setPanningAnchorPoint (aPanPnt);
+    }
+
+    if (myToShowPanAnchorPoint
+    &&  hasPanningAnchorPoint()
+    &&  myGL.Panning.ToPan
+    && !myGL.IsNewGesture
+    && !myAnchorPointPrs2->HasInteractiveContext())
+    {
+      theCtx->Display (myAnchorPointPrs2, 0, -1, false, AIS_DS_Displayed);
+    }
+
+    handlePanning (theView);
+    handleZRotate (theView);
+  }
+
+  if ((myNavigationMode == AIS_NavigationMode_Orbit
+    || myGL.OrbitRotation.ToStart
+    || myGL.OrbitRotation.ToRotate)
+   && myToAllowRotation)
+  {
+    if (myGL.OrbitRotation.ToStart
+    && !myHasHlrOnBeforeRotation)
+    {
+      myHasHlrOnBeforeRotation = theView->ComputedMode();
+      if (myHasHlrOnBeforeRotation)
+      {
+        theView->SetComputedMode (false);
+      }
+    }
+
+    gp_Pnt aGravPnt;
+    if (myGL.OrbitRotation.ToStart)
+    {
+      aGravPnt = GravityPoint (theCtx, theView);
+      if (myToShowRotateCenter)
+      {
+        gp_Trsf aPntTrsf;
+        aPntTrsf.SetTranslation (gp_Vec (aGravPnt.XYZ()));
+        theCtx->SetLocation (myAnchorPointPrs1, aPntTrsf);
+        theCtx->SetLocation (myAnchorPointPrs2, aPntTrsf);
+      }
+    }
+
+    if (myToShowRotateCenter
+    &&  myGL.OrbitRotation.ToRotate
+    && !myGL.IsNewGesture
+    && !myAnchorPointPrs1->HasInteractiveContext())
+    {
+      theCtx->Display (myAnchorPointPrs1, 0, -1, false, AIS_DS_Displayed);
+      theCtx->Display (myAnchorPointPrs2, 0, -1, false, AIS_DS_Displayed);
+    }
+    handleOrbitRotation (theView, aGravPnt,
+                         myToLockOrbitZUp || myNavigationMode != AIS_NavigationMode_Orbit);
+  }
+
+  if ((myNavigationMode != AIS_NavigationMode_Orbit
+    || myGL.ViewRotation.ToStart
+    || myGL.ViewRotation.ToRotate)
+   && myToAllowRotation)
+  {
+    if (myGL.ViewRotation.ToStart
+    && !myHasHlrOnBeforeRotation)
+    {
+      myHasHlrOnBeforeRotation = theView->ComputedMode();
+      if (myHasHlrOnBeforeRotation)
+      {
+        theView->SetComputedMode (false);
+      }
+    }
+
+    double aRoll = 0.0;
+    if (!theWalk[AIS_WalkRotation_Roll].IsEmpty()
+     && !myToLockOrbitZUp)
+    {
+      aRoll = (M_PI / 12.0) * theWalk[AIS_WalkRotation_Roll].Pressure;
+      aRoll *= Min (1000.0  * theWalk[AIS_WalkRotation_Roll].Duration, 100.0) / 100.0;
+      if (theWalk[AIS_WalkRotation_Roll].Value < 0.0)
+      {
+        aRoll = -aRoll;
+      }
+    }
+
+    handleViewRotation (theView, theWalk[AIS_WalkRotation_Yaw].Value, theWalk[AIS_WalkRotation_Pitch].Value, aRoll,
+                        myNavigationMode == AIS_NavigationMode_FirstPersonFlight);
+  }
+
+  if (!myGL.ZoomActions.IsEmpty())
+  {
+    for (NCollection_Sequence<Aspect_ScrollDelta>::Iterator aZoomIter (myGL.ZoomActions); aZoomIter.More(); aZoomIter.Next())
+    {
+      Aspect_ScrollDelta aZoomParams = aZoomIter.Value();
+      if (myToAllowZFocus
+       && (aZoomParams.Flags & Aspect_VKeyFlags_CTRL) != 0
+       && theView->Camera()->IsStereo())
+      {
+        handleZFocusScroll (theView, aZoomParams);
+        continue;
+      }
+
+      if (!myToAllowZooming)
+      {
+        continue;
+      }
+
+      if (!theView->Camera()->IsOrthographic())
+      {
+        // what is more natural to user - point on ray or point exactly on geometry in corner cases?
+        const bool toStickToRay = false; // true;
+
+        gp_Pnt aPnt;
+        if (aZoomParams.HasPoint()
+         && PickPoint (aPnt, theCtx, theView, aZoomParams.Point, toStickToRay))
+        {
+          handleZoom (theView, aZoomParams, &aPnt);
+          continue;
+        }
+
+        Graphic3d_Vec2i aWinSize;
+        theView->Window()->Size (aWinSize.x(), aWinSize.y());
+        if (PickPoint (aPnt, theCtx, theView, aWinSize / 2, toStickToRay))
+        {
+          aZoomParams.ResetPoint(); // do not pretend to zoom at 'nothing'
+          handleZoom (theView, aZoomParams, &aPnt);
+          continue;
+        }
+      }
+      handleZoom (theView, aZoomParams, NULL);
+    }
+    myGL.ZoomActions.Clear();
+  }
+}
+
+// =======================================================================
+// function : OnSelectionChanged
+// purpose  :
+// =======================================================================
+void AIS_ViewController::OnSelectionChanged (const Handle(AIS_InteractiveContext)& ,
+                                             const Handle(V3d_View)& )
+{
+  //
+}
+
+// =======================================================================
+// function : OnObjectDragged
+// purpose  :
+// =======================================================================
+void AIS_ViewController::OnObjectDragged (const Handle(AIS_InteractiveContext)& theCtx,
+                                          const Handle(V3d_View)& theView,
+                                          AIS_DragAction theAction)
+{
+  switch (theAction)
+  {
+    case AIS_DragAction_Start:
+    {
+      myDragObject.Nullify();
+      if (!theCtx->HasDetected())
+      {
+        return;
+      }
+
+      Handle(AIS_InteractiveObject) aPrs = theCtx->DetectedInteractive();
+      if (Handle(AIS_Manipulator) aManip = Handle(AIS_Manipulator)::DownCast (aPrs))
+      {
+        if (aManip->HasActiveMode())
+        {
+          myDragObject = aManip;
+          aManip->StartTransform (myGL.Dragging.PointStart.x(), myGL.Dragging.PointStart.y(), theView);
+        }
+      }
+      return;
+    }
+    case AIS_DragAction_Update:
+    {
+      if (myDragObject.IsNull())
+      {
+        return;
+      }
+
+      if (Handle(SelectMgr_EntityOwner) aGlobOwner = myDragObject->GlobalSelOwner())
+      {
+        theCtx->SetSelectedState (aGlobOwner, true);
+      }
+      if (Handle(AIS_Manipulator) aManip = Handle(AIS_Manipulator)::DownCast (myDragObject))
+      {
+        aManip->Transform (myGL.Dragging.PointTo.x(), myGL.Dragging.PointTo.y(), theView);
+      }
+      theView->Invalidate();
+      return;
+    }
+    case AIS_DragAction_Abort:
+    {
+      if (myDragObject.IsNull())
+      {
+        return;
+      }
+
+      myGL.Dragging.PointTo = myGL.Dragging.PointStart;
+      OnObjectDragged (theCtx, theView, AIS_DragAction_Update);
+
+      if (Handle(AIS_Manipulator) aManip = Handle(AIS_Manipulator)::DownCast (myDragObject))
+      {
+        aManip->StopTransform (false);
+      }
+      Standard_FALLTHROUGH
+    }
+    case AIS_DragAction_Stop:
+    {
+      if (myDragObject.IsNull())
+      {
+        return;
+      }
+
+      if (Handle(SelectMgr_EntityOwner) aGlobOwner = myDragObject->GlobalSelOwner())
+      {
+        theCtx->SetSelectedState (aGlobOwner, false);
+      }
+
+      theView->Invalidate();
+      myDragObject.Nullify();
+      return;
+    }
+  }
+}
+
+// =======================================================================
+// function : contextLazyMoveTo
+// purpose  :
+// =======================================================================
+void AIS_ViewController::contextLazyMoveTo (const Handle(AIS_InteractiveContext)& theCtx,
+                                            const Handle(V3d_View)& theView,
+                                            const Graphic3d_Vec2i& thePnt)
+{
+  if (myPrevMoveTo == thePnt)
+  {
+    return;
+  }
+
+  myPrevMoveTo = thePnt;
+
+  Handle(SelectMgr_EntityOwner) aLastPicked = theCtx->DetectedOwner();
+  theCtx->MoveTo (thePnt.x(), thePnt.y(), theView, false);
+  Handle(SelectMgr_EntityOwner) aNewPicked = theCtx->DetectedOwner();
+
+  if (theView->Viewer()->Grid()->IsActive()
+   && theView->Viewer()->GridEcho())
+  {
+    if (aNewPicked.IsNull())
+    {
+      Graphic3d_Vec3d aPnt3d;
+      theView->ConvertToGrid (thePnt.x(), thePnt.y(), aPnt3d[0], aPnt3d[1], aPnt3d[2]);
+      theView->Viewer()->ShowGridEcho (theView, Graphic3d_Vertex (aPnt3d[0], aPnt3d[1], aPnt3d[2]));
+      theView->InvalidateImmediate();
+    }
+    else
+    {
+      theView->Viewer()->HideGridEcho (theView);
+      theView->InvalidateImmediate();
+    }
+  }
+
+  if (aLastPicked != aNewPicked
+   || (!aNewPicked.IsNull() && aNewPicked->IsForcedHilight()))
+  {
+    // dynamic highlight affects all Views
+    for (V3d_ListOfViewIterator aViewIter (theView->Viewer()->ActiveViewIterator()); aViewIter.More(); aViewIter.Next())
+    {
+      const Handle(V3d_View)& aView = aViewIter.Value();
+      aView->InvalidateImmediate();
+    }
+  }
+}
+
+// =======================================================================
+// function : handleSelectionPick
+// purpose  :
+// =======================================================================
+void AIS_ViewController::handleSelectionPick (const Handle(AIS_InteractiveContext)& theCtx,
+                                              const Handle(V3d_View)& theView)
+{
+  if (myGL.Selection.Tool == AIS_ViewSelectionTool_Picking
+  && !myGL.Selection.Points.IsEmpty())
+  {
+    for (NCollection_Sequence<Graphic3d_Vec2i>::Iterator aPntIter (myGL.Selection.Points); aPntIter.More(); aPntIter.Next())
+    {
+      if (!myGL.Selection.IsXOR)
+      {
+        theCtx->ClearSelected (false);
+      }
+
+      const bool hadPrevMoveTo = HasPreviousMoveTo();
+      contextLazyMoveTo (theCtx, theView, aPntIter.Value());
+      if (!hadPrevMoveTo)
+      {
+        ResetPreviousMoveTo();
+      }
+
+      if (myGL.Selection.IsXOR)
+      {
+        theCtx->ShiftSelect (false);
+      }
+      else
+      {
+        theCtx->Select (false);
+      }
+
+      // selection affects all Views
+      theView->Viewer()->Invalidate();
+
+      OnSelectionChanged (theCtx, theView);
+    }
+
+    myGL.Selection.Points.Clear();
+  }
+}
+
+// =======================================================================
+// function : handleSelectionPoly
+// purpose  :
+// =======================================================================
+void AIS_ViewController::handleSelectionPoly (const Handle(AIS_InteractiveContext)& theCtx,
+                                              const Handle(V3d_View)& theView)
+{
+  // rubber-band & window polygon selection
+  if (myGL.Selection.Tool == AIS_ViewSelectionTool_RubberBand
+   || myGL.Selection.Tool == AIS_ViewSelectionTool_Polygon)
+  {
+    if (!myGL.Selection.Points.IsEmpty())
+    {
+      myRubberBand->ClearPoints();
+      myRubberBand->SetToUpdate();
+
+      const bool anIsRubber = myGL.Selection.Tool == AIS_ViewSelectionTool_RubberBand;
+      if (anIsRubber)
+      {
+        myRubberBand->SetRectangle (myGL.Selection.Points.First().x(), -myGL.Selection.Points.First().y(),
+                                    myGL.Selection.Points.Last().x(),  -myGL.Selection.Points.Last().y());
+      }
+      else
+      {
+        Graphic3d_Vec2i aPrev (IntegerLast(), IntegerLast());
+        for (NCollection_Sequence<Graphic3d_Vec2i>::Iterator aSelIter (myGL.Selection.Points); aSelIter.More(); aSelIter.Next())
+        {
+          Graphic3d_Vec2i aPntNew = Graphic3d_Vec2i (aSelIter.Value().x(), -aSelIter.Value().y());
+          if (aPntNew != aPrev)
+          {
+            aPrev = aPntNew;
+            myRubberBand->AddPoint (Graphic3d_Vec2i (aSelIter.Value().x(), -aSelIter.Value().y()));
+          }
+        }
+      }
+
+      myRubberBand->SetPolygonClosed (anIsRubber);
+      try
+      {
+        theCtx->Display (myRubberBand, 0, -1, false, AIS_DS_Displayed);
+      }
+      catch (const Standard_Failure& theEx)
+      {
+        Message::DefaultMessenger()->Send (TCollection_AsciiString ("Internal error while displaying rubber-band: ")
+                                        + theEx.DynamicType()->Name() + ", " + theEx.GetMessageString(), Message_Warning);
+        myRubberBand->ClearPoints();
+      }
+      if (!theView->Viewer()->ZLayerSettings (myRubberBand->ZLayer()).IsImmediate())
+      {
+        theView->Invalidate();
+      }
+      else
+      {
+        theView->InvalidateImmediate();
+      }
+    }
+    else if (!myRubberBand.IsNull()
+           && myRubberBand->HasInteractiveContext())
+    {
+      theCtx->Remove (myRubberBand, false);
+      myRubberBand->ClearPoints();
+    }
+  }
+
+  if (myGL.Selection.ToApplyTool)
+  {
+    myGL.Selection.ToApplyTool = false;
+    if (theCtx->IsDisplayed (myRubberBand))
+    {
+      theCtx->Remove (myRubberBand, false);
+      {
+        const NCollection_Sequence<Graphic3d_Vec2i>& aPoints = myRubberBand->Points();
+        if (aPoints.Size() == 4
+         && aPoints.Value (1).x() == aPoints.Value (2).x()
+         && aPoints.Value (3).x() == aPoints.Value (4).x()
+         && aPoints.Value (1).y() == aPoints.Value (4).y()
+         && aPoints.Value (2).y() == aPoints.Value (3).y())
+        {
+          const Graphic3d_Vec2i aPnt1 (aPoints.Value (1).x(), -aPoints.Value (1).y());
+          const Graphic3d_Vec2i aPnt2 (aPoints.Value (3).x(), -aPoints.Value (3).y());
+          theCtx->MainSelector()->AllowOverlapDetection (aPnt1.y() != Min (aPnt1.y(), aPnt2.y()));
+          if (myGL.Selection.IsXOR)
+          {
+            theCtx->ShiftSelect (Min (aPnt1.x(), aPnt2.x()), Min (aPnt1.y(), aPnt2.y()),
+                                 Max (aPnt1.x(), aPnt2.x()), Max (aPnt1.y(), aPnt2.y()),
+                                 theView, false);
+          }
+          else
+          {
+            theCtx->Select (Min (aPnt1.x(), aPnt2.x()), Min (aPnt1.y(), aPnt2.y()),
+                            Max (aPnt1.x(), aPnt2.x()), Max (aPnt1.y(), aPnt2.y()),
+                            theView, false);
+          }
+          theCtx->MainSelector()->AllowOverlapDetection (false);
+        }
+        else if (aPoints.Length() >= 3)
+        {
+          TColgp_Array1OfPnt2d aPolyline (1, aPoints.Length());
+          TColgp_Array1OfPnt2d::Iterator aPolyIter (aPolyline);
+          for (NCollection_Sequence<Graphic3d_Vec2i>::Iterator aSelIter (aPoints);
+               aSelIter.More(); aSelIter.Next(), aPolyIter.Next())
+          {
+            const Graphic3d_Vec2i& aNewPnt = aSelIter.Value();
+            aPolyIter.ChangeValue() = gp_Pnt2d (aNewPnt.x(), -aNewPnt.y());
+          }
+
+          theCtx->MainSelector()->AllowOverlapDetection (false);
+          if (myGL.Selection.IsXOR)
+          {
+            theCtx->ShiftSelect (aPolyline, theView, false);
+          }
+          else
+          {
+            theCtx->Select (aPolyline, theView, false);
+          }
+        }
+      }
+
+      // selection affects all Views
+      theView->Viewer()->Invalidate();
+
+      myRubberBand->ClearPoints();
+      OnSelectionChanged (theCtx, theView);
+    }
+  }
+}
+
+// =======================================================================
+// function : handleDynamicHighlight
+// purpose  :
+// =======================================================================
+void AIS_ViewController::handleDynamicHighlight (const Handle(AIS_InteractiveContext)& theCtx,
+                                                 const Handle(V3d_View)& theView)
+{
+  if ((myGL.MoveTo.ToHilight || myGL.Dragging.ToStart)
+   && myNavigationMode != AIS_NavigationMode_FirstPersonWalk)
+  {
+    const Graphic3d_Vec2i& aMoveToPnt = myGL.MoveTo.ToHilight ? myGL.MoveTo.Point : myGL.Dragging.PointStart;
+    if (myGL.Dragging.ToStart && (!myGL.MoveTo.ToHilight || !myToAllowHighlight)
+     && !HasPreviousMoveTo())
+    {
+      contextLazyMoveTo (theCtx, theView, aMoveToPnt);
+      ResetPreviousMoveTo();
+      OnObjectDragged (theCtx, theView, AIS_DragAction_Start);
+      theCtx->ClearDetected();
+    }
+    else if (myToAllowHighlight)
+    {
+      if (myPrevMoveTo != aMoveToPnt
+       || myGL.OrbitRotation.ToRotate
+       || myGL.ViewRotation.ToRotate
+       || theView->IsInvalidated())
+      {
+        ResetPreviousMoveTo();
+        contextLazyMoveTo (theCtx, theView, aMoveToPnt);
+      }
+      if (myGL.Dragging.ToStart)
+      {
+        OnObjectDragged (theCtx, theView, AIS_DragAction_Start);
+      }
+    }
+
+    myGL.MoveTo.ToHilight = false;
+  }
+
+  if (!myDragObject.IsNull())
+  {
+    if (myGL.Dragging.ToAbort)
+    {
+      OnObjectDragged (theCtx, theView, AIS_DragAction_Abort);
+      myGL.OrbitRotation.ToRotate = false;
+      myGL.ViewRotation .ToRotate = false;
+    }
+    else if (myGL.Dragging.ToStop)
+    {
+      OnObjectDragged (theCtx, theView, AIS_DragAction_Stop);
+      myGL.OrbitRotation.ToRotate = false;
+      myGL.ViewRotation .ToRotate = false;
+    }
+    else if (myGL.OrbitRotation.ToRotate
+          || myGL.ViewRotation.ToRotate)
+    {
+      OnObjectDragged (theCtx, theView, AIS_DragAction_Update);
+      myGL.OrbitRotation.ToRotate = false;
+      myGL.ViewRotation .ToRotate = false;
+    }
+  }
+}
+
+// =======================================================================
+// function : handleMoveTo
+// purpose  :
+// =======================================================================
+void AIS_ViewController::handleMoveTo (const Handle(AIS_InteractiveContext)& theCtx,
+                                       const Handle(V3d_View)& theView)
+{
+  handleSelectionPick   (theCtx, theView);
+  handleDynamicHighlight(theCtx, theView);
+  handleSelectionPoly   (theCtx, theView);
+}
+
+// =======================================================================
+// function : handleViewRedraw
+// purpose  :
+// =======================================================================
+void AIS_ViewController::handleViewRedraw (const Handle(AIS_InteractiveContext)& ,
+                                           const Handle(V3d_View)& theView)
+{
+  for (V3d_ListOfViewIterator aViewIter (theView->Viewer()->ActiveViewIterator()); aViewIter.More(); aViewIter.Next())
+  {
+    const Handle(V3d_View)& aView = aViewIter.Value();
+    if (aView->IsInvalidated()
+     || myToAskNextFrame)
+    {
+      if (aView->ComputedMode())
+      {
+        aView->Update();
+      }
+      else
+      {
+        aView->Redraw();
+      }
+    }
+    else if (aView->IsInvalidatedImmediate())
+    {
+      aView->RedrawImmediate();
+    }
+  }
+
+  if (myToAskNextFrame)
+  {
+    // ask more frames
+    theView->Window()->InvalidateContent (Handle(Aspect_DisplayConnection)());
+  }
+}
+
+// =======================================================================
+// function : HandleViewEvents
+// purpose  :
+// =======================================================================
+void AIS_ViewController::HandleViewEvents (const Handle(AIS_InteractiveContext)& theCtx,
+                                           const Handle(V3d_View)& theView)
+{
+  handleMoveTo (theCtx, theView);
+
+  const AIS_WalkDelta aWalk = FetchNavigationKeys (1.0, 1.0);
+  handleCameraActions (theCtx, theView, aWalk);
+  handleViewRedraw (theCtx, theView);
+
+  // make sure to not process the same events twice
+  myGL.Reset();
+  myToAskNextFrame = false;
+}
diff --git a/src/AIS/AIS_ViewController.hxx b/src/AIS/AIS_ViewController.hxx
new file mode 100644 (file)
index 0000000..e82027d
--- /dev/null
@@ -0,0 +1,658 @@
+// Copyright (c) 2016-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _AIS_ViewController_HeaderFile
+#define _AIS_ViewController_HeaderFile
+
+#include <Aspect_VKeySet.hxx>
+#include <Aspect_TouchMap.hxx>
+#include <AIS_DragAction.hxx>
+#include <AIS_MouseGesture.hxx>
+#include <AIS_NavigationMode.hxx>
+#include <AIS_ViewInputBuffer.hxx>
+#include <AIS_RotationMode.hxx>
+#include <AIS_WalkDelta.hxx>
+
+#include <gp_Pnt.hxx>
+#include <Graphic3d_Vec3.hxx>
+#include <NCollection_Array1.hxx>
+#include <OSD_Timer.hxx>
+#include <Precision.hxx>
+#include <Standard_Mutex.hxx>
+
+class AIS_InteractiveObject;
+class AIS_InteractiveContext;
+class AIS_Point;
+class AIS_RubberBand;
+class V3d_View;
+
+//! Auxiliary structure for handling viewer events between GUI and Rendering threads.
+//!
+//! Class implements the following features:
+//! - Buffers storing the state of user input (mouse, touches and keyboard).
+//! - Mapping mouse/multi-touch input to View camera manipulations (panning/rotating/zooming).
+//! - Input events are not applied immediately but queued for separate processing from two working threads
+//!   UI thread receiving user input and Rendering thread for OCCT 3D Viewer drawing.
+class AIS_ViewController
+{
+public:
+
+  //! Empty constructor.
+  Standard_EXPORT AIS_ViewController();
+
+  //! Return input buffer.
+  const AIS_ViewInputBuffer& InputBuffer (AIS_ViewInputBufferType theType) const { return theType == AIS_ViewInputBufferType_UI ? myUI : myGL; }
+
+  //! Return input buffer.
+  AIS_ViewInputBuffer& ChangeInputBuffer (AIS_ViewInputBufferType theType)       { return theType == AIS_ViewInputBufferType_UI ? myUI : myGL; }
+
+public: //! @name global parameters
+
+  //! Return camera rotation mode, AIS_RotationMode_BndBoxActive by default.
+  AIS_RotationMode RotationMode() const { return myRotationMode; }
+
+  //! Set camera rotation mode.
+  void SetRotationMode (AIS_RotationMode theMode) { myRotationMode = theMode; }
+
+  //! Return camera navigation mode; AIS_NavigationMode_Orbit by default.
+  AIS_NavigationMode NavigationMode() const { return myNavigationMode; }
+
+  //! Set camera navigation mode.
+  Standard_EXPORT void SetNavigationMode (AIS_NavigationMode theMode);
+
+  //! Return mouse input acceleration ratio in First Person mode; 1.0 by default.
+  float MouseAcceleration() const { return myMouseAccel; }
+
+  //! Set mouse input acceleration ratio.
+  void SetMouseAcceleration (float theRatio) { myMouseAccel = theRatio; }
+
+  //! Return orbit rotation acceleration ratio; 1.0 by default.
+  float OrbitAcceleration() const { return myOrbitAccel; }
+
+  //! Set orbit rotation acceleration ratio.
+  void SetOrbitAcceleration (float theRatio) { myOrbitAccel = theRatio; }
+
+  //! Return TRUE if panning anchor point within perspective projection should be displayed in 3D Viewer; TRUE by default.
+  bool ToShowPanAnchorPoint() const { return myToShowPanAnchorPoint; }
+
+  //! Set if panning anchor point within perspective projection should be displayed in 3D Viewer.
+  void SetShowPanAnchorPoint (bool theToShow) { myToShowPanAnchorPoint = theToShow; }
+
+  //! Return TRUE if rotation point should be displayed in 3D Viewer; TRUE by default.
+  bool ToShowRotateCenter() const { return myToShowRotateCenter; }
+
+  //! Set if rotation point should be displayed in 3D Viewer.
+  void SetShowRotateCenter (bool theToShow) { myToShowRotateCenter = theToShow; }
+
+  //! Return TRUE if camera up orientation within AIS_NavigationMode_Orbit rotation mode should be forced Z up; FALSE by default.
+  bool ToLockOrbitZUp() const { return myToLockOrbitZUp; }
+
+  //! Set if camera up orientation within AIS_NavigationMode_Orbit rotation mode should be forced Z up.
+  void SetLockOrbitZUp (bool theToForceUp) { myToLockOrbitZUp = theToForceUp; }
+
+  //! Return TRUE if z-rotation via two-touches gesture is enabled; FALSE by default.
+  bool ToAllowTouchZRotation() const { return myToAllowTouchZRotation; }
+
+  //! Set if z-rotation via two-touches gesture is enabled.
+  void SetAllowTouchZRotation (bool theToEnable) { myToAllowTouchZRotation = theToEnable; }
+
+  //! Return TRUE if camera rotation is allowed; TRUE by default.
+  bool ToAllowRotation() const { return myToAllowRotation; }
+
+  //! Set if camera rotation is allowed.
+  void SetAllowRotation (bool theToEnable) { myToAllowRotation = theToEnable; }
+
+  //! Return TRUE if panning is allowed; TRUE by default.
+  bool ToAllowPanning() const { return myToAllowPanning; }
+
+  //! Set if panning is allowed.
+  void SetAllowPanning (bool theToEnable) { myToAllowPanning = theToEnable; }
+
+  //! Return TRUE if zooming is allowed; TRUE by default.
+  bool ToAllowZooming() const { return myToAllowZooming; }
+
+  //! Set if zooming is allowed.
+  void SetAllowZooming (bool theToEnable) { myToAllowZooming = theToEnable; }
+
+  //! Return TRUE if ZFocus change is allowed; TRUE by default.
+  bool ToAllowZFocus() const { return myToAllowZFocus; }
+
+  //! Set if ZFocus change is allowed.
+  void SetAllowZFocus (bool theToEnable) { myToAllowZFocus = theToEnable; }
+
+  //! Return TRUE if dynamic highlight on mouse move is allowed; TRUE by default.
+  bool ToAllowHighlight() const { return myToAllowHighlight; }
+
+  //! Set if dragging object is allowed.
+  void SetAllowHighlight (bool theToEnable) { myToAllowHighlight = theToEnable; }
+
+  //! Return TRUE if dragging object is allowed; TRUE by default.
+  bool ToAllowDragging() const { return myToAllowDragging; }
+
+  //! Set if dynamic highlight on mouse move is allowed.
+  void SetAllowDragging (bool theToEnable) { myToAllowDragging = theToEnable; }
+
+  //! Return TRUE if pitch direction should be inverted while processing Aspect_VKey_NavLookUp/Aspect_VKey_NavLookDown; FALSE by default.
+  bool ToInvertPitch() const { return myToInvertPitch; }
+
+  //! Set flag inverting pitch direction.
+  void SetInvertPitch (bool theToInvert) { myToInvertPitch = theToInvert; }
+
+  //! Return normal walking speed, in m/s; 1.5 by default.
+  float WalkSpeedAbsolute() const { return myWalkSpeedAbsolute; }
+
+  //! Set normal walking speed, in m/s; 1.5 by default.
+  void SetWalkSpeedAbsolute (float theSpeed) { myWalkSpeedAbsolute = theSpeed; }
+
+  //! Return walking speed relative to scene bounding box; 0.1 by default.
+  float WalkSpeedRelative() const { return myWalkSpeedRelative; }
+
+  //! Set walking speed relative to scene bounding box.
+  void SetWalkSpeedRelative (float theFactor) { myWalkSpeedRelative = theFactor; }
+
+  //! Return active thrust value; 0.0f by default.
+  float ThrustSpeed() const { return myThrustSpeed; }
+
+  //! Set active thrust value.
+  void SetThrustSpeed (float theSpeed) { myThrustSpeed = theSpeed; }
+
+  //! Return TRUE if previous position of MoveTo has been defined.
+  bool HasPreviousMoveTo() const { return myPrevMoveTo != Graphic3d_Vec2i (-1); }
+
+  //! Return previous position of MoveTo event in 3D viewer.
+  const Graphic3d_Vec2i& PreviousMoveTo() const { return myPrevMoveTo; }
+
+  //! Reset previous position of MoveTo.
+  void ResetPreviousMoveTo() { myPrevMoveTo = Graphic3d_Vec2i (-1); }
+
+public: //! @name keyboard input
+
+  //! Return keyboard state.
+  const Aspect_VKeySet& Keys() const { return myKeys; }
+
+  //! Return keyboard state.
+  Aspect_VKeySet& ChangeKeys() { return myKeys; }
+
+  //! Press key.
+  //! @param theKey key pressed
+  //! @param theTime event timestamp
+  Standard_EXPORT virtual void KeyDown (Aspect_VKey theKey,
+                                        double theTime,
+                                        double thePressure = 1.0);
+
+  //! Release key.
+  //! @param theKey key pressed
+  //! @param theTime event timestamp
+  Standard_EXPORT virtual void KeyUp (Aspect_VKey theKey,
+                                      double theTime);
+
+  //! Simulate key up/down events from axis value.
+  Standard_EXPORT virtual void KeyFromAxis (Aspect_VKey theNegative,
+                                            Aspect_VKey thePositive,
+                                            double theTime,
+                                            double thePressure);
+
+  //! Fetch active navigation actions.
+  Standard_EXPORT AIS_WalkDelta FetchNavigationKeys (Standard_Real theCrouchRatio,
+                                                     Standard_Real theRunRatio);
+
+public: //! @name mouse input
+
+  //! Return map defining mouse gestures.
+  const AIS_MouseGestureMap& MouseGestureMap() const { return myMouseGestureMap; }
+
+  //! Return map defining mouse gestures.
+  AIS_MouseGestureMap& ChangeMouseGestureMap() { return myMouseGestureMap; }
+
+  //! Return double click interval in seconds; 0.4 by default.
+  double MouseDoubleClickInterval() const { return myMouseDoubleClickInt; }
+
+  //! Set double click interval in seconds.
+  void SetMouseDoubleClickInterval (double theSeconds) { myMouseDoubleClickInt = theSeconds; }
+
+  //! Perform selection in 3D viewer.
+  //! This method is expected to be called from UI thread.
+  //! @param thePnt picking point
+  //! @param theIsXOR XOR selection flag
+  Standard_EXPORT virtual void SelectInViewer (const Graphic3d_Vec2i& thePnt,
+                                               const bool theIsXOR = false);
+
+  //! Perform selection in 3D viewer.
+  //! This method is expected to be called from UI thread.
+  //! @param thePnts picking point
+  //! @param theIsXOR XOR selection flag
+  Standard_EXPORT virtual void SelectInViewer (const NCollection_Sequence<Graphic3d_Vec2i>& thePnts,
+                                               const bool theIsXOR = false);
+
+  //! Update rectangle selection tool.
+  //! This method is expected to be called from UI thread.
+  //! @param thePntFrom rectangle first   corner
+  //! @param thePntTo   rectangle another corner
+  //! @param theIsXOR XOR selection flag
+  Standard_EXPORT virtual void UpdateRubberBand (const Graphic3d_Vec2i& thePntFrom,
+                                                 const Graphic3d_Vec2i& thePntTo,
+                                                 const bool theIsXOR = false);
+
+  //! Update polygonal selection tool.
+  //! This method is expected to be called from UI thread.
+  //! @param thePnt new point to add to polygon
+  //! @param theToAppend append new point or update the last point
+  Standard_EXPORT virtual void UpdatePolySelection (const Graphic3d_Vec2i& thePnt,
+                                                    bool theToAppend);
+
+  //! Update zoom event (e.g. from mouse scroll).
+  //! This method is expected to be called from UI thread.
+  //! @param theDelta mouse cursor position to zoom at and zoom delta
+  //! @return TRUE if new zoom event has been created or FALSE if existing one has been updated
+  Standard_EXPORT virtual bool UpdateZoom (const Aspect_ScrollDelta& theDelta);
+
+  //! Update Z rotation event.
+  //! @param theAngle rotation angle, in radians.
+  //! @return TRUE if new zoom event has been created or FALSE if existing one has been updated
+  Standard_EXPORT virtual bool UpdateZRotation (double theAngle);
+
+  //! Update mouse scroll event; redirects to UpdateZoom by default.
+  //! This method is expected to be called from UI thread.
+  //! @param theDelta mouse cursor position and delta
+  //! @return TRUE if new event has been created or FALSE if existing one has been updated
+  Standard_EXPORT virtual bool UpdateMouseScroll (const Aspect_ScrollDelta& theDelta);
+
+  //! Handle mouse button press/release event.
+  //! This method is expected to be called from UI thread.
+  //! @param thePoint      mouse cursor position
+  //! @param theButtons    pressed buttons
+  //! @param theModifiers  key modifiers
+  //! @param theIsEmulated if TRUE then mouse event comes NOT from real mouse
+  //!                      but emulated from non-precise input like touch on screen
+  //! @return TRUE if View should be redrawn
+  Standard_EXPORT virtual bool UpdateMouseButtons (const Graphic3d_Vec2i& thePoint,
+                                                   Aspect_VKeyMouse theButtons,
+                                                   Aspect_VKeyFlags theModifiers,
+                                                   bool theIsEmulated);
+
+  //! Handle mouse cursor movement event.
+  //! This method is expected to be called from UI thread.
+  //! @param thePoint      mouse cursor position
+  //! @param theButtons    pressed buttons
+  //! @param theModifiers  key modifiers
+  //! @param theIsEmulated if TRUE then mouse event comes NOT from real mouse
+  //!                      but emulated from non-precise input like touch on screen
+  //! @return TRUE if View should be redrawn
+  Standard_EXPORT virtual bool UpdateMousePosition (const Graphic3d_Vec2i& thePoint,
+                                                    Aspect_VKeyMouse theButtons,
+                                                    Aspect_VKeyFlags theModifiers,
+                                                    bool theIsEmulated);
+
+  //! Handle mouse button press event.
+  //! This method is expected to be called from UI thread.
+  //! @param thePoint      mouse cursor position
+  //! @param theButton     pressed button
+  //! @param theModifiers  key modifiers
+  //! @param theIsEmulated if TRUE then mouse event comes NOT from real mouse
+  //!                      but emulated from non-precise input like touch on screen
+  //! @return TRUE if View should be redrawn
+  bool PressMouseButton (const Graphic3d_Vec2i& thePoint,
+                         Aspect_VKeyMouse theButton,
+                         Aspect_VKeyFlags theModifiers,
+                         bool theIsEmulated)
+  {
+    return UpdateMouseButtons (thePoint, myMousePressed | theButton, theModifiers, theIsEmulated);
+  }
+
+  //! Handle mouse button release event.
+  //! This method is expected to be called from UI thread.
+  //! @param thePoint      mouse cursor position
+  //! @param theButton     released button
+  //! @param theModifiers  key modifiers
+  //! @param theIsEmulated if TRUE then mouse event comes NOT from real mouse
+  //!                      but emulated from non-precise input like touch on screen
+  //! @return TRUE if View should be redrawn
+  bool ReleaseMouseButton (const Graphic3d_Vec2i& thePoint,
+                           Aspect_VKeyMouse theButton,
+                           Aspect_VKeyFlags theModifiers,
+                           bool theIsEmulated)
+  {
+    Aspect_VKeyMouse aButtons = myMousePressed & (~theButton);
+    return UpdateMouseButtons (thePoint, aButtons, theModifiers, theIsEmulated);
+  }
+
+  //! Handle mouse button click event (emulated by UpdateMouseButtons() while releasing single button).
+  //! Note that as this method is called by UpdateMouseButtons(), it should be executed from UI thread.
+  //! Default implementation redirects to SelectInViewer().
+  //! This method is expected to be called from UI thread.
+  //! @param thePoint      mouse cursor position
+  //! @param theButton     clicked button
+  //! @param theModifiers  key modifiers
+  //! @param theIsDoubleClick flag indicating double mouse click
+  //! @return TRUE if View should be redrawn
+  Standard_EXPORT virtual bool UpdateMouseClick (const Graphic3d_Vec2i& thePoint,
+                                                 Aspect_VKeyMouse theButton,
+                                                 Aspect_VKeyFlags theModifiers,
+                                                 bool theIsDoubleClick);
+
+  //! Return currently pressed mouse buttons.
+  Aspect_VKeyMouse PressedMouseButtons() const { return myMousePressed; }
+
+  //! Return active key modifiers passed with last mouse event.
+  Aspect_VKeyFlags LastMouseFlags() const { return myMouseModifiers; }
+
+  //! Return last mouse position.
+  const Graphic3d_Vec2i& LastMousePosition() const { return myMousePositionLast; }
+
+public: //! @name multi-touch input
+
+  //! Return scale factor for adjusting tolerances for starting multi-touch gestures; 1.0 by default
+  //! This scale factor is expected to be computed from touch screen resolution.
+  float TouchToleranceScale() const { return myTouchToleranceScale; }
+
+  //! Set scale factor for adjusting tolerances for starting multi-touch gestures.
+  void SetTouchToleranceScale (float theTolerance) { myTouchToleranceScale = theTolerance; }
+
+  //! Return TRUE if touches map is not empty.
+  bool HasTouchPoints() const { return !myTouchPoints.IsEmpty(); }
+
+  //! Add touch point with the given ID.
+  //! This method is expected to be called from UI thread.
+  //! @param theId touch unique identifier
+  //! @param thePnt touch coordinates
+  //! @param theClearBefore if TRUE previously registered touches will be removed
+  Standard_EXPORT virtual void AddTouchPoint (Standard_Size theId,
+                                              const Graphic3d_Vec2d& thePnt,
+                                              Standard_Boolean theClearBefore = false);
+
+  //! Remove touch point with the given ID.
+  //! This method is expected to be called from UI thread.
+  //! @param theId touch unique identifier
+  //! @param theClearSelectPnts if TRUE will initiate clearing of selection points
+  //! @return TRUE if point has been removed
+  Standard_EXPORT virtual bool RemoveTouchPoint (Standard_Size theId,
+                                                 Standard_Boolean theClearSelectPnts = false);
+
+  //! Update touch point with the given ID.
+  //! If point with specified ID was not registered before, it will be added.
+  //! This method is expected to be called from UI thread.
+  //! @param theId touch unique identifier
+  //! @param thePnt touch coordinates
+  Standard_EXPORT virtual void UpdateTouchPoint (Standard_Size theId,
+                                                 const Graphic3d_Vec2d& thePnt);
+
+public:
+
+  //! Return event time (e.g. current time).
+  double EventTime() const { return myEventTimer.ElapsedTime(); }
+
+  //! Reset input state (pressed keys, mouse buttons, etc.) e.g. on window focus loss.
+  //! This method is expected to be called from UI thread.
+  Standard_EXPORT virtual void ResetViewInput();
+
+  //! Reset view orientation.
+  //! This method is expected to be called from UI thread.
+  Standard_EXPORT virtual void UpdateViewOrientation (V3d_TypeOfOrientation theOrientation,
+                                                      bool theToFitAll);
+
+  //! Update buffer for rendering thread.
+  //! This method is expected to be called within synchronization barrier between GUI
+  //! and Rendering threads (e.g. GUI thread should be locked beforehand to avoid data races).
+  //! @param theCtx interactive context
+  //! @param theView active view
+  //! @param theToHandle if TRUE, the HandleViewEvents() will be called
+  Standard_EXPORT virtual void FlushViewEvents (const Handle(AIS_InteractiveContext)& theCtx,
+                                                const Handle(V3d_View)& theView,
+                                                Standard_Boolean theToHandle = Standard_False);
+
+  //! Process events within rendering thread.
+  Standard_EXPORT virtual void HandleViewEvents (const Handle(AIS_InteractiveContext)& theCtx,
+                                                 const Handle(V3d_View)& theView);
+
+public:
+
+  //! Callback called by handleMoveTo() on Selection in 3D Viewer.
+  //! This method is expected to be called from rendering thread.
+  Standard_EXPORT virtual void OnSelectionChanged (const Handle(AIS_InteractiveContext)& theCtx,
+                                                   const Handle(V3d_View)& theView);
+
+  //! Callback called by handleMoveTo() on dragging object in 3D Viewer.
+  //! This method is expected to be called from rendering thread.
+  Standard_EXPORT virtual void OnObjectDragged (const Handle(AIS_InteractiveContext)& theCtx,
+                                                const Handle(V3d_View)& theView,
+                                                AIS_DragAction theAction);
+
+  //! Pick closest point under mouse cursor.
+  //! This method is expected to be called from rendering thread.
+  //! @param thePnt   [out] result point
+  //! @param theCtx    [in] interactive context
+  //! @param theView   [in] active view
+  //! @param theCursor [in] mouse cursor
+  //! @param theToStickToPickRay [in] when TRUE, the result point will lie on picking ray
+  //! @return TRUE if result has been found
+  Standard_EXPORT virtual bool PickPoint (gp_Pnt& thePnt,
+                                          const Handle(AIS_InteractiveContext)& theCtx,
+                                          const Handle(V3d_View)& theView,
+                                          const Graphic3d_Vec2i& theCursor,
+                                          bool theToStickToPickRay);
+
+  //! Compute rotation gravity center point depending on rotation mode.
+  //! This method is expected to be called from rendering thread.
+  Standard_EXPORT virtual gp_Pnt GravityPoint (const Handle(AIS_InteractiveContext)& theCtx,
+                                               const Handle(V3d_View)& theView);
+
+public:
+
+  //! Perform camera actions.
+  //! This method is expected to be called from rendering thread.
+  Standard_EXPORT virtual void handleCameraActions (const Handle(AIS_InteractiveContext)& theCtx,
+                                                    const Handle(V3d_View)& theView,
+                                                    const AIS_WalkDelta& theWalk);
+
+  //! Perform moveto/selection/dragging.
+  //! This method is expected to be called from rendering thread.
+  Standard_EXPORT virtual void handleMoveTo (const Handle(AIS_InteractiveContext)& theCtx,
+                                             const Handle(V3d_View)& theView);
+
+  //! Return TRUE if another frame should be drawn right after this one.
+  bool toAskNextFrame() const { return myToAskNextFrame; }
+
+  //! Set if another frame should be drawn right after this one.
+  void setAskNextFrame (bool theToDraw = true) { myToAskNextFrame = theToDraw; }
+
+  //! Return if panning anchor point has been defined.
+  bool hasPanningAnchorPoint() const { return !Precision::IsInfinite (myPanPnt3d.X()); }
+
+  //! Return active panning anchor point.
+  const gp_Pnt& panningAnchorPoint() const { return myPanPnt3d; }
+
+  //! Set active panning anchor point.
+  void setPanningAnchorPoint (const gp_Pnt& thePnt) { myPanPnt3d = thePnt; }
+
+  //! Handle panning event myGL.Panning.
+  Standard_EXPORT virtual void handlePanning (const Handle(V3d_View)& theView);
+
+  //! Handle Z rotation event myGL.ZRotate.
+  Standard_EXPORT virtual void handleZRotate (const Handle(V3d_View)& theView);
+
+  //! Return minimal camera distance for zoom operation.
+  double MinZoomDistance() const { return myMinCamDistance; }
+
+  //! Set minimal camera distance for zoom operation.
+  void SetMinZoomDistance (double theDist) { myMinCamDistance = theDist; }
+
+  //! Handle zoom event myGL.ZoomActions.
+  //! This method is expected to be called from rendering thread.
+  Standard_EXPORT virtual void handleZoom (const Handle(V3d_View)& theView,
+                                           const Aspect_ScrollDelta& theParams,
+                                           const gp_Pnt* thePnt);
+
+  //! Handle ZScroll event myGL.ZoomActions.
+  //! This method is expected to be called from rendering thread.
+  Standard_EXPORT virtual void handleZFocusScroll (const Handle(V3d_View)& theView,
+                                                   const Aspect_ScrollDelta& theParams);
+
+  //! Handle orbital rotation events myGL.OrbitRotation.
+  //! @param theView view to modify
+  //! @param thePnt 3D point to rotate around
+  //! @param theToLockZUp amend camera to exclude roll angle (put camera Up vector to plane containing global Z and view direction)
+  Standard_EXPORT virtual void handleOrbitRotation (const Handle(V3d_View)& theView,
+                                                    const gp_Pnt& thePnt,
+                                                    bool theToLockZUp);
+
+  //! Handle view direction rotation events myGL.ViewRotation.
+  //! This method is expected to be called from rendering thread.
+  //! @param theView       camera to modify
+  //! @param theYawExtra   extra yaw increment
+  //! @param thePitchExtra extra pitch increment
+  //! @param theRoll       roll value
+  //! @param theToRestartOnIncrement flag indicating flight mode
+  Standard_EXPORT virtual void handleViewRotation (const Handle(V3d_View)& theView,
+                                                   double theYawExtra,
+                                                   double thePitchExtra,
+                                                   double theRoll,
+                                                   bool theToRestartOnIncrement);
+
+  //! Handle view redraw.
+  //! This method is expected to be called from rendering thread.
+  Standard_EXPORT virtual void handleViewRedraw (const Handle(AIS_InteractiveContext)& theCtx,
+                                                 const Handle(V3d_View)& theView);
+
+protected:
+
+  //! Flush buffers.
+  Standard_EXPORT virtual void flushBuffers (const Handle(AIS_InteractiveContext)& theCtx,
+                                             const Handle(V3d_View)& theView);
+
+  //! Flush touch gestures.
+  Standard_EXPORT virtual void flushGestures (const Handle(AIS_InteractiveContext)& theCtx,
+                                              const Handle(V3d_View)& theView);
+
+  //! Return current and previously fetched event times.
+  //! This callback is intended to compute delta between sequentially processed events.
+  //! @param thePrevTime [out] events time fetched previous time by this method
+  //! @param theCurrTime [out] actual events time
+  void updateEventsTime (double& thePrevTime,
+                         double& theCurrTime)
+  {
+    thePrevTime = myLastEventsTime;
+    myLastEventsTime = EventTime();
+    theCurrTime = myLastEventsTime;
+  }
+
+  //! Perform selection via mouse click.
+  //! This method is expected to be called from rendering thread.
+  Standard_EXPORT virtual void handleSelectionPick (const Handle(AIS_InteractiveContext)& theCtx,
+                                                    const Handle(V3d_View)& theView);
+
+  //! Perform dynamic highlight on mouse move.
+  //! This method is expected to be called from rendering thread.
+  Standard_EXPORT virtual void handleDynamicHighlight (const Handle(AIS_InteractiveContext)& theCtx,
+                                                       const Handle(V3d_View)& theView);
+
+  //! Perform rubber-band selection.
+  //! This method is expected to be called from rendering thread.
+  Standard_EXPORT virtual void handleSelectionPoly (const Handle(AIS_InteractiveContext)& theCtx,
+                                                    const Handle(V3d_View)& theView);
+
+  //! Lazy AIS_InteractiveContext::MoveTo() with myPrevMoveTo check.
+  Standard_EXPORT virtual void contextLazyMoveTo (const Handle(AIS_InteractiveContext)& theCtx,
+                                                  const Handle(V3d_View)& theView,
+                                                  const Graphic3d_Vec2i& thePnt);
+
+protected:
+
+  AIS_ViewInputBuffer myUI;                       //!< buffer for UI thread
+  AIS_ViewInputBuffer myGL;                       //!< buffer for rendering thread
+
+  OSD_Timer           myEventTimer;               //!< timer for timestamping events
+  Standard_Real       myLastEventsTime;           //!< last fetched events timer value for computing delta/progress
+  Standard_Boolean    myToAskNextFrame;           //!< flag indicating that another frame should be drawn right after this one
+
+  Standard_Real       myMinCamDistance;           //!< minimal camera distance for zoom operation
+  AIS_RotationMode    myRotationMode;             //!< rotation mode
+  AIS_NavigationMode  myNavigationMode;           //!< navigation mode (orbit rotation / first person)
+  Standard_ShortReal  myMouseAccel;               //!< mouse input acceleration ratio in First Person mode
+  Standard_ShortReal  myOrbitAccel;               //!< Orbit rotation acceleration ratio
+  Standard_Boolean    myToShowPanAnchorPoint;     //!< option displaying panning  anchor point
+  Standard_Boolean    myToShowRotateCenter;       //!< option displaying rotation center point
+  Standard_Boolean    myToLockOrbitZUp;           //!< force camera up orientation within AIS_NavigationMode_Orbit rotation mode
+  Standard_Boolean    myToInvertPitch;            //!< flag inverting pitch direction while processing Aspect_VKey_NavLookUp/Aspect_VKey_NavLookDown
+  Standard_Boolean    myToAllowTouchZRotation;    //!< enable z-rotation two-touches gesture; FALSE by default
+  Standard_Boolean    myToAllowRotation;          //!< enable rotation; TRUE by default
+  Standard_Boolean    myToAllowPanning;           //!< enable panning; TRUE by default
+  Standard_Boolean    myToAllowZooming;           //!< enable zooming; TRUE by default
+  Standard_Boolean    myToAllowZFocus;            //!< enable ZFocus change; TRUE by default
+  Standard_Boolean    myToAllowHighlight;         //!< enable dynamic highlight on mouse move; TRUE by default
+  Standard_Boolean    myToAllowDragging;          //!< enable dragging object; TRUE by default
+
+  Standard_ShortReal  myWalkSpeedAbsolute;        //!< normal walking speed, in m/s; 1.5 by default
+  Standard_ShortReal  myWalkSpeedRelative;        //!< walking speed relative to scene bounding box; 0.1 by default
+  Standard_ShortReal  myThrustSpeed;              //!< active thrust value
+  Standard_Boolean    myHasThrust;                //!< flag indicating active thrust
+
+  Handle(AIS_RubberBand) myRubberBand;            //!< Rubber-band presentation
+  Handle(AIS_InteractiveObject) myDragObject;     //!< currently dragged object
+  Graphic3d_Vec2i     myPrevMoveTo;               //!< previous position of MoveTo event in 3D viewer
+  Standard_Boolean    myHasHlrOnBeforeRotation;   //!< flag for restoring Computed mode after rotation
+
+protected: //! @name keyboard input variables
+
+  Aspect_VKeySet      myKeys;                     //!< keyboard state
+
+protected: //! @name mouse input variables
+
+  Standard_Real       myMouseClickThreshold;      //!< mouse click threshold in pixels; 3 by default
+  Standard_Real       myMouseDoubleClickInt;      //!< double click interval in seconds; 0.4 by default
+  Standard_ShortReal  myScrollZoomRatio;          //!< distance ratio for mapping mouse scroll event to zoom; 15.0 by default
+
+  AIS_MouseGestureMap myMouseGestureMap;          //!< map defining mouse gestures
+  AIS_MouseGesture    myMouseActiveGesture;       //!< initiated mouse gesture (by pressing mouse button)
+  Standard_Boolean    myMouseActiveIdleRotation;  //!< flag indicating view idle rotation state
+  Graphic3d_Vec2i     myMousePositionLast;        //!< last mouse position
+  Graphic3d_Vec2i     myMousePressPoint;          //!< mouse position where active gesture was been initiated
+  Graphic3d_Vec2i     myMouseProgressPoint;       //!< gesture progress
+  OSD_Timer           myMouseClickTimer;          //!< timer for handling double-click event
+  Standard_Integer    myMouseClickCounter;        //!< counter for handling double-click event
+  Aspect_VKeyMouse    myMousePressed;             //!< active mouse buttons
+  Aspect_VKeyFlags    myMouseModifiers;           //!< active key modifiers passed with last mouse event
+  Standard_Integer    myMouseSingleButton;        //!< index of mouse button pressed alone (>0)
+
+protected: //! @name multi-touch input variables
+
+  Standard_ShortReal  myTouchToleranceScale;      //!< tolerance scale factor; 1.0 by default
+  Standard_ShortReal  myTouchRotationThresholdPx; //!< threshold for starting one-touch rotation     gesture in pixels;  6 by default
+  Standard_ShortReal  myTouchZRotationThreshold;  //!< threshold for starting two-touch Z-rotation   gesture in radians; 2 degrees by default
+  Standard_ShortReal  myTouchPanThresholdPx;      //!< threshold for starting two-touch panning      gesture in pixels;  4 by default
+  Standard_ShortReal  myTouchZoomThresholdPx;     //!< threshold for starting two-touch zoom (pitch) gesture in pixels;  6 by default
+  Standard_ShortReal  myTouchZoomRatio;           //!< distance ratio for mapping two-touch zoom (pitch) gesture from pixels to zoom; 0.13 by default
+
+  Aspect_TouchMap     myTouchPoints;              //!< map of active touches
+  Graphic3d_Vec2d     myStartPanCoord;            //!< touch coordinates at the moment of starting panning  gesture
+  Graphic3d_Vec2d     myStartRotCoord;            //!< touch coordinates at the moment of starting rotating gesture
+  Standard_Integer    myNbTouchesLast;            //!< number of touches within previous gesture flush to track gesture changes
+  Standard_Boolean    myUpdateStartPointPan;      //!< flag indicating that new anchor  point should be picked for starting panning    gesture
+  Standard_Boolean    myUpdateStartPointRot;      //!< flag indicating that new gravity point should be picked for starting rotation   gesture
+  Standard_Boolean    myUpdateStartPointZRot;     //!< flag indicating that new gravity point should be picked for starting Z-rotation gesture
+
+protected: //! @name rotation/panning transient state variables
+
+  Handle(AIS_Point)   myAnchorPointPrs1;          //!< anchor point presentation (Graphic3d_ZLayerId_Top)
+  Handle(AIS_Point)   myAnchorPointPrs2;          //!< anchor point presentation (Graphic3d_ZLayerId_Topmost)
+  gp_Pnt              myPanPnt3d;                 //!< active panning anchor point
+  gp_Pnt              myRotatePnt3d;              //!< active rotation center of gravity
+  gp_Dir              myCamStartOpUp;             //!< camera Up    direction at the beginning of rotation
+  gp_Pnt              myCamStartOpEye;            //!< camera Eye    position at the beginning of rotation
+  gp_Pnt              myCamStartOpCenter;         //!< camera Center position at the beginning of rotation
+  gp_Vec              myCamStartOpToCenter;       //!< vector from rotation gravity point to camera Center at the beginning of rotation
+  gp_Vec              myCamStartOpToEye;          //!< vector from rotation gravity point to camera Eye    at the beginning of rotation
+  Graphic3d_Vec3d     myRotateStartYawPitchRoll;  //!< camera yaw pitch roll at the beginning of rotation
+
+};
+
+#endif // _AIS_ViewController_HeaderFile
diff --git a/src/AIS/AIS_ViewInputBuffer.hxx b/src/AIS/AIS_ViewInputBuffer.hxx
new file mode 100644 (file)
index 0000000..48d2705
--- /dev/null
@@ -0,0 +1,153 @@
+// Copyright (c) 2016-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _AIS_ViewInputBuffer_HeaderFile
+#define _AIS_ViewInputBuffer_HeaderFile
+
+#include <Aspect_ScrollDelta.hxx>
+
+#include <Graphic3d_Vec2.hxx>
+#include <NCollection_Sequence.hxx>
+#include <V3d_TypeOfOrientation.hxx>
+
+//! Selection mode
+enum AIS_ViewSelectionTool
+{
+  AIS_ViewSelectionTool_Picking,    //!< pick to select
+  AIS_ViewSelectionTool_RubberBand, //!< rubber-band to select
+  AIS_ViewSelectionTool_Polygon     //!< polyline to select
+};
+
+//! Input buffer type.
+enum AIS_ViewInputBufferType
+{
+  AIS_ViewInputBufferType_UI, //!< input buffer for filling from UI thread
+  AIS_ViewInputBufferType_GL, //!< input buffer accessible  from GL thread
+};
+
+//! Auxiliary structure defining viewer events
+class AIS_ViewInputBuffer
+{
+public:
+
+  bool IsNewGesture;     //!< transition from one action to another
+
+  NCollection_Sequence<Aspect_ScrollDelta> ZoomActions; //!< the queue with zoom actions
+
+  struct _orientation
+  {
+    bool                  ToFitAll;         //!< perform FitAll operation
+    bool                  ToSetViewOrient;  //!< set new view orientation
+    V3d_TypeOfOrientation ViewOrient;       //!< new view orientation
+
+    _orientation() : ToFitAll (false), ToSetViewOrient (false), ViewOrient (V3d_Xpos) {}
+  } Orientation;
+
+  struct _highlighting
+  {
+    bool            ToHilight; //!< perform dynamic highlighting at specified point
+    Graphic3d_Vec2i Point;     //!< the new point for dynamic highlighting
+
+    _highlighting() : ToHilight (false) {}
+  } MoveTo;
+
+  struct _selection
+  {
+    AIS_ViewSelectionTool Tool;          //!< perform selection
+    bool                  IsXOR;         //!< perform shift selection
+    NCollection_Sequence<Graphic3d_Vec2i>
+                          Points;        //!< the points for selection
+    bool                  ToApplyTool;   //!< apply rubber-band selection tool
+
+    _selection() : Tool (AIS_ViewSelectionTool_Picking), IsXOR (false), ToApplyTool (false) {}
+  } Selection;
+
+  struct _panningParams
+  {
+    bool            ToStart;    //!< start panning
+    Graphic3d_Vec2i PointStart; //!< panning start point
+    bool            ToPan;      //!< perform panning
+    Graphic3d_Vec2i Delta;      //!< panning delta
+
+    _panningParams() : ToStart (false), ToPan (false) {}
+  } Panning;
+
+  struct _draggingParams
+  {
+    bool            ToStart;    //!< start dragging
+    bool            ToStop;     //!< stop  dragging
+    bool            ToAbort;    //!< abort dragging (restore previous position)
+    Graphic3d_Vec2i PointStart; //!< drag start point
+    Graphic3d_Vec2i PointTo;    //!< drag end point
+
+    _draggingParams() : ToStart (false), ToStop (false), ToAbort (false) {}
+  } Dragging;
+
+  struct _orbitRotation
+  {
+    bool            ToStart;    //!< start orbit rotation
+    Graphic3d_Vec2d PointStart; //!< orbit rotation start point
+    bool            ToRotate;   //!< perform orbit rotation
+    Graphic3d_Vec2d PointTo;    //!< orbit rotation end point
+
+    _orbitRotation() : ToStart (false), ToRotate (false) {}
+  } OrbitRotation;
+
+  struct _viewRotation
+  {
+    bool            ToStart;    //!< start view rotation
+    Graphic3d_Vec2d PointStart; //!< view rotation start point
+    bool            ToRotate;   //!< perform view rotation
+    Graphic3d_Vec2d PointTo;    //!< view rotation end point
+
+    _viewRotation() : ToStart (false), ToRotate (false) {}
+  } ViewRotation;
+
+  struct _zrotateParams
+  {
+    Graphic3d_Vec2i Point;    //!< Z rotation start point
+    double          Angle;    //!< Z rotation angle
+    bool            ToRotate; //!< start Z rotation
+
+    _zrotateParams() : Angle (0.0), ToRotate (false) {}
+  } ZRotate;
+
+public:
+
+  AIS_ViewInputBuffer()
+  : IsNewGesture (false) {}
+
+  //! Reset events buffer.
+  void Reset()
+  {
+    Orientation.ToFitAll = false;
+    Orientation.ToSetViewOrient = false;
+    MoveTo.ToHilight = false;
+    Selection.ToApplyTool = false;
+    IsNewGesture     = false;
+    ZoomActions.Clear();
+    Panning.ToStart  = false;
+    Panning.ToPan    = false;
+    Dragging.ToStart = false;
+    Dragging.ToStop  = false;
+    Dragging.ToAbort = false;
+    OrbitRotation.ToStart  = false;
+    OrbitRotation.ToRotate = false;
+    ViewRotation.ToStart   = false;
+    ViewRotation.ToRotate  = false;
+    ZRotate.ToRotate = false;
+  }
+
+};
+
+#endif // _AIS_ViewInputBuffer_HeaderFile
diff --git a/src/AIS/AIS_WalkDelta.hxx b/src/AIS/AIS_WalkDelta.hxx
new file mode 100644 (file)
index 0000000..40b0eae
--- /dev/null
@@ -0,0 +1,115 @@
+// Copyright (c) 2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _AIS_WalkDelta_HeaderFile
+#define _AIS_WalkDelta_HeaderFile
+
+#include <Standard_Real.hxx>
+
+//! Walking translation components.
+enum AIS_WalkTranslation
+{
+  AIS_WalkTranslation_Forward = 0, //!< translation delta, Forward walk
+  AIS_WalkTranslation_Side,        //!< translation delta, Side walk
+  AIS_WalkTranslation_Up,          //!< translation delta, Up walk
+};
+
+//! Walking rotation components.
+enum AIS_WalkRotation
+{
+  AIS_WalkRotation_Yaw = 0,  //!< yaw   rotation angle
+  AIS_WalkRotation_Pitch,    //!< pitch rotation angle
+  AIS_WalkRotation_Roll,     //!< roll  rotation angle
+};
+
+//! Walking value.
+struct AIS_WalkPart
+{
+  Standard_Real Value;    //!< value
+  Standard_Real Pressure; //!< key pressure
+  Standard_Real Duration; //!< duration
+
+  //! Return TRUE if delta is empty.
+  bool IsEmpty() const { return Abs (Value) <= RealSmall(); }
+
+  //! Empty constructor.
+  AIS_WalkPart() : Value (0.0), Pressure (1.0), Duration (0.0) {}
+};
+
+//! Walking values.
+struct AIS_WalkDelta
+{
+  //! Empty constructor.
+  AIS_WalkDelta()
+  : myIsJumping (false), myIsCrouching (false), myIsRunning (false) {}
+
+  //! Return translation component.
+  const AIS_WalkPart& operator[] (AIS_WalkTranslation thePart) const { return myTranslation[thePart]; }
+
+  //! Return translation component.
+  AIS_WalkPart&       operator[] (AIS_WalkTranslation thePart)       { return myTranslation[thePart]; }
+
+  //! Return rotation component.
+  const AIS_WalkPart& operator[] (AIS_WalkRotation thePart) const { return myRotation[thePart]; }
+
+  //! Return rotation component.
+  AIS_WalkPart&       operator[] (AIS_WalkRotation thePart)       { return myRotation[thePart]; }
+
+  //! Return jumping state.
+  bool IsJumping() const { return myIsJumping; }
+
+  //! Set jumping state.
+  void SetJumping (bool theIsJumping) { myIsJumping = theIsJumping; }
+
+  //! Return crouching state.
+  bool IsCrouching() const { return myIsCrouching; }
+
+  //! Set crouching state.
+  void SetCrouching (bool theIsCrouching) { myIsCrouching = theIsCrouching; }
+
+  //! Return running state.
+  bool IsRunning() const { return myIsRunning; }
+
+  //! Set running state.
+  void SetRunning (bool theIsRunning) { myIsRunning = theIsRunning; }
+
+  //! Return TRUE when both Rotation and Translation deltas are empty.
+  bool IsEmpty() const { return !ToMove() && !ToRotate(); }
+
+  //! Return TRUE if translation delta is defined.
+  bool ToMove() const
+  {
+    return !myTranslation[AIS_WalkTranslation_Forward].IsEmpty()
+        || !myTranslation[AIS_WalkTranslation_Side].IsEmpty()
+        || !myTranslation[AIS_WalkTranslation_Up].IsEmpty();
+  }
+
+  //! Return TRUE if rotation delta is defined.
+  bool ToRotate() const
+  {
+    return !myRotation[AIS_WalkRotation_Yaw].IsEmpty()
+        || !myRotation[AIS_WalkRotation_Pitch].IsEmpty()
+        || !myRotation[AIS_WalkRotation_Roll].IsEmpty();
+  }
+
+private:
+
+  AIS_WalkPart myTranslation[3];
+  AIS_WalkPart myRotation[3];
+  bool myIsJumping;
+  bool myIsCrouching;
+  bool myIsRunning;
+
+};
+
+#endif // _AIS_WalkDelta_HeaderFile
index 9a17309..7d225a6 100644 (file)
@@ -56,6 +56,7 @@ AIS_DimensionSelectionMode.hxx
 AIS_DisplayMode.hxx
 AIS_DisplaySpecialSymbol.hxx
 AIS_DisplayStatus.hxx
+AIS_DragAction.hxx
 AIS_EllipseRadiusDimension.cxx
 AIS_EllipseRadiusDimension.hxx
 AIS_EqualDistanceRelation.cxx
@@ -106,6 +107,7 @@ AIS_MaxRadiusDimension.cxx
 AIS_MaxRadiusDimension.hxx
 AIS_MediaPlayer.cxx
 AIS_MediaPlayer.hxx
+AIS_MouseGesture.hxx
 AIS_MidPointRelation.cxx
 AIS_MidPointRelation.hxx
 AIS_MidPointRelation.lxx
@@ -114,6 +116,7 @@ AIS_MinRadiusDimension.hxx
 AIS_MultipleConnectedInteractive.cxx
 AIS_MultipleConnectedInteractive.hxx
 AIS_MultipleConnectedInteractive.lxx
+AIS_NavigationMode.hxx
 AIS_NListOfEntityOwner.hxx
 AIS_OffsetDimension.cxx
 AIS_OffsetDimension.hxx
@@ -135,6 +138,7 @@ AIS_RadiusDimension.cxx
 AIS_RadiusDimension.hxx
 AIS_Relation.cxx
 AIS_Relation.hxx
+AIS_RotationMode.hxx
 AIS_RubberBand.hxx
 AIS_RubberBand.cxx
 AIS_Selection.cxx
@@ -175,3 +179,7 @@ AIS_TypeOfAxis.hxx
 AIS_TypeOfDist.hxx
 AIS_TypeOfIso.hxx
 AIS_TypeOfPlane.hxx
+AIS_ViewController.cxx
+AIS_ViewController.hxx
+AIS_ViewInputBuffer.hxx
+AIS_WalkDelta.hxx
diff --git a/src/Aspect/Aspect_ScrollDelta.hxx b/src/Aspect/Aspect_ScrollDelta.hxx
new file mode 100644 (file)
index 0000000..b764522
--- /dev/null
@@ -0,0 +1,59 @@
+// Copyright (c) 2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_ScrollDelta_HeaderFile
+#define _Aspect_ScrollDelta_HeaderFile
+
+#include <Aspect_VKeyFlags.hxx>
+#include <NCollection_Vec2.hxx>
+#include <Standard_Real.hxx>
+
+//! Parameters for mouse scroll action.
+struct Aspect_ScrollDelta
+{
+
+  NCollection_Vec2<int> Point; //!< scale position
+  Standard_Real         Delta; //!< delta in pixels
+  Aspect_VKeyFlags      Flags; //!< key flags
+
+  //! Return true if action has point defined.
+  bool HasPoint() const
+  {
+    return Point.x() >= 0
+        && Point.y() >= 0;
+  }
+
+  //! Reset at point.
+  void ResetPoint()
+  {
+    Point.SetValues (-1, -1);
+  }
+
+  //! Empty constructor.
+  Aspect_ScrollDelta()
+  : Point (-1, -1), Delta (0.0), Flags (Aspect_VKeyFlags_NONE) {}
+
+  //! Constructor.
+  Aspect_ScrollDelta (const NCollection_Vec2<int>& thePnt,
+                      Standard_Real theValue,
+                      Aspect_VKeyFlags theFlags = Aspect_VKeyFlags_NONE)
+  : Point (thePnt), Delta (theValue), Flags (theFlags) {}
+
+  //! Constructor with undefined point.
+  Aspect_ScrollDelta (Standard_Real theValue,
+                      Aspect_VKeyFlags theFlags = Aspect_VKeyFlags_NONE)
+  : Point (-1, -1), Delta (theValue), Flags (theFlags) {}
+
+};
+
+#endif // _Aspect_ScrollDelta_HeaderFile
diff --git a/src/Aspect/Aspect_Touch.hxx b/src/Aspect/Aspect_Touch.hxx
new file mode 100644 (file)
index 0000000..6ccf11d
--- /dev/null
@@ -0,0 +1,49 @@
+// Copyright (c) 2016-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_Touch_HeaderFile
+#define _Aspect_Touch_HeaderFile
+
+#include <NCollection_Vec2.hxx>
+#include <Standard_Boolean.hxx>
+#include <Standard_Real.hxx>
+
+//! Structure holding touch position - original and current location.
+class Aspect_Touch
+{
+public:
+
+  NCollection_Vec2<Standard_Real> From;            //!< original touch position
+  NCollection_Vec2<Standard_Real> To;              //!< current  touch position
+  Standard_Boolean                IsPreciseDevice; //!< precise device input (e.g. mouse cursor, NOT emulated from touch screen)
+
+  //! Return values delta.
+  NCollection_Vec2<Standard_Real> Delta() const { return To - From; }
+
+  //! Empty constructor
+  Aspect_Touch()
+  : From (0.0, 0.0), To (0.0, 0.0), IsPreciseDevice (false) {}
+
+  //! Constructor with initialization.
+  Aspect_Touch (const NCollection_Vec2<Standard_Real>& thePnt,
+                Standard_Boolean theIsPreciseDevice)
+  : From (thePnt), To (thePnt), IsPreciseDevice (theIsPreciseDevice) {}
+
+  //! Constructor with initialization.
+  Aspect_Touch (Standard_Real theX, Standard_Real theY,
+                Standard_Boolean theIsPreciseDevice)
+  : From (theX, theY), To (theX, theY), IsPreciseDevice (theIsPreciseDevice) {}
+
+};
+
+#endif // _Aspect_Touch_HeaderFile
similarity index 65%
rename from src/ViewerTest/ViewerTest_EventManager.lxx
rename to src/Aspect/Aspect_TouchMap.hxx
index 88a8ae0..0f4b1d3 100644 (file)
@@ -1,7 +1,4 @@
-// Created on: 1998-08-27
-// Created by: Robert COUBLANC
-// Copyright (c) 1998-1999 Matra Datavision
-// Copyright (c) 1999-2014 OPEN CASCADE SAS
+// Copyright (c) 2016-2019 OPEN CASCADE SAS
 //
 // This file is part of Open CASCADE Technology software library.
 //
 // Alternatively, this file may be used under the terms of Open CASCADE
 // commercial license or contractual agreement.
 
-inline const Handle(AIS_InteractiveContext)& ViewerTest_EventManager::Context() const
-{return myCtx;}
+#ifndef _Aspect_TouchMap_HeaderFile
+#define _Aspect_TouchMap_HeaderFile
+
+#include <Aspect_Touch.hxx>
+
+#include <NCollection_IndexedDataMap.hxx>
+
+typedef NCollection_IndexedDataMap<Standard_Size, Aspect_Touch> Aspect_TouchMap;
+
+#endif // _Aspect_TouchMap_HeaderFile
diff --git a/src/Aspect/Aspect_VKey.hxx b/src/Aspect/Aspect_VKey.hxx
new file mode 100644 (file)
index 0000000..ff0def5
--- /dev/null
@@ -0,0 +1,199 @@
+// Copyright (c) 2016-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_VKey_HeaderFile
+#define _Aspect_VKey_HeaderFile
+
+#include <Aspect_VKeyFlags.hxx>
+
+//! Define virtual key as integer number to allow extensions.
+typedef unsigned int Aspect_VKey;
+
+//! Enumeration defining virtual keys irrelevant to current keyboard layout for simplified hot-keys management logic.
+enum Aspect_VKeyBasic
+{
+  Aspect_VKey_UNKNOWN = 0,
+
+  // main latin alphabet keys
+  Aspect_VKey_A = 1,
+  Aspect_VKey_B,
+  Aspect_VKey_C,
+  Aspect_VKey_D,
+  Aspect_VKey_E,
+  Aspect_VKey_F,
+  Aspect_VKey_G,
+  Aspect_VKey_H,
+  Aspect_VKey_I,
+  Aspect_VKey_J,
+  Aspect_VKey_K,
+  Aspect_VKey_L,
+  Aspect_VKey_M,
+  Aspect_VKey_N,
+  Aspect_VKey_O,
+  Aspect_VKey_P,
+  Aspect_VKey_Q,
+  Aspect_VKey_R,
+  Aspect_VKey_S,
+  Aspect_VKey_T,
+  Aspect_VKey_U,
+  Aspect_VKey_V,
+  Aspect_VKey_W,
+  Aspect_VKey_X,
+  Aspect_VKey_Y,
+  Aspect_VKey_Z,
+
+  Aspect_VKey_0,
+  Aspect_VKey_1,
+  Aspect_VKey_2,
+  Aspect_VKey_3,
+  Aspect_VKey_4,
+  Aspect_VKey_5,
+  Aspect_VKey_6,
+  Aspect_VKey_7,
+  Aspect_VKey_8,
+  Aspect_VKey_9,
+
+  Aspect_VKey_F1,
+  Aspect_VKey_F2,
+  Aspect_VKey_F3,
+  Aspect_VKey_F4,
+  Aspect_VKey_F5,
+  Aspect_VKey_F6,
+  Aspect_VKey_F7,
+  Aspect_VKey_F8,
+  Aspect_VKey_F9,
+  Aspect_VKey_F10,
+  Aspect_VKey_F11,
+  Aspect_VKey_F12,
+
+  // standard keys
+  Aspect_VKey_Up,
+  Aspect_VKey_Down,
+  Aspect_VKey_Left,
+  Aspect_VKey_Right,
+  Aspect_VKey_Plus,         //!< '+'
+  Aspect_VKey_Minus,        //!< '-'
+  Aspect_VKey_Equal,        //!< '=+'
+  Aspect_VKey_PageUp,
+  Aspect_VKey_PageDown,
+  Aspect_VKey_Home,
+  Aspect_VKey_End,
+  Aspect_VKey_Escape,
+  Aspect_VKey_Back,
+  Aspect_VKey_Enter,
+  Aspect_VKey_Backspace,
+  Aspect_VKey_Space,
+  Aspect_VKey_Delete,
+  Aspect_VKey_Tilde,
+  Aspect_VKey_Tab,
+  Aspect_VKey_Comma,        //!< ','
+  Aspect_VKey_Period,       //!< '.'
+  Aspect_VKey_Semicolon,    //!< ';:'
+  Aspect_VKey_Slash,        //!< '/?'
+  Aspect_VKey_BracketLeft,  //!< '[{'
+  Aspect_VKey_Backslash,    //!< '\|'
+  Aspect_VKey_BracketRight, //!< ']}'
+  Aspect_VKey_Apostrophe,   //!< ''"'
+  Aspect_VKey_Numlock,      //!< Num Lock key
+  Aspect_VKey_Scroll,       //!< Scroll Lock key
+
+  // numpad keys
+  Aspect_VKey_Numpad0,
+  Aspect_VKey_Numpad1,
+  Aspect_VKey_Numpad2,
+  Aspect_VKey_Numpad3,
+  Aspect_VKey_Numpad4,
+  Aspect_VKey_Numpad5,
+  Aspect_VKey_Numpad6,
+  Aspect_VKey_Numpad7,
+  Aspect_VKey_Numpad8,
+  Aspect_VKey_Numpad9,
+  Aspect_VKey_NumpadMultiply, //!< numpad '*'
+  Aspect_VKey_NumpadAdd,      //!< numpad '+'
+  Aspect_VKey_NumpadSubtract, //!< numpad '-'
+  Aspect_VKey_NumpadDivide,   //!< numpad '/'
+
+  // Multimedia keys
+  Aspect_VKey_MediaNextTrack,
+  Aspect_VKey_MediaPreviousTrack,
+  Aspect_VKey_MediaStop,
+  Aspect_VKey_MediaPlayPause,
+  Aspect_VKey_VolumeMute,
+  Aspect_VKey_VolumeDown,
+  Aspect_VKey_VolumeUp,
+  Aspect_VKey_BrowserBack,
+  Aspect_VKey_BrowserForward,
+  Aspect_VKey_BrowserRefresh,
+  Aspect_VKey_BrowserStop,
+  Aspect_VKey_BrowserSearch,
+  Aspect_VKey_BrowserFavorites,
+  Aspect_VKey_BrowserHome,
+
+  // modifier keys, @sa Aspect_VKey_ModifiersLower and Aspect_VKey_ModifiersUpper below
+  Aspect_VKey_Shift,
+  Aspect_VKey_Control,
+  Aspect_VKey_Alt,
+  Aspect_VKey_Menu,
+  Aspect_VKey_Meta,
+
+  // virtual navigation keys, @sa Aspect_VKey_NavigationKeysLower and Aspect_VKey_NavigationKeysUpper below
+  Aspect_VKey_NavInteract,         //!< interact
+  Aspect_VKey_NavForward,          //!< go forward
+  Aspect_VKey_NavBackward,         //!< go backward
+  Aspect_VKey_NavSlideLeft,        //!< sidewalk, left
+  Aspect_VKey_NavSlideRight,       //!< sidewalk, right
+  Aspect_VKey_NavSlideUp,          //!< lift up
+  Aspect_VKey_NavSlideDown,        //!< fall down
+  Aspect_VKey_NavRollCCW,          //!< bank left  (roll counter-clockwise)
+  Aspect_VKey_NavRollCW,           //!< bank right (roll clockwise)
+  Aspect_VKey_NavLookLeft,         //!< look left  (yaw counter-clockwise)
+  Aspect_VKey_NavLookRight,        //!< look right (yaw clockwise)
+  Aspect_VKey_NavLookUp,           //!< look up    (pitch clockwise)
+  Aspect_VKey_NavLookDown,         //!< look down  (pitch counter-clockwise)
+  Aspect_VKey_NavCrouch,           //!< crouch walking
+  Aspect_VKey_NavJump,             //!< jump
+  Aspect_VKey_NavThrustForward,    //!< increase continuous velocity in forward  direction
+  Aspect_VKey_NavThrustBackward,   //!< increase continuous velocity in reversed direction
+  Aspect_VKey_NavThrustStop,       //!< reset continuous velocity
+  Aspect_VKey_NavSpeedIncrease,    //!< increase navigation speed
+  Aspect_VKey_NavSpeedDecrease,    //!< decrease navigation speed
+};
+
+//! Auxiliary ranges.
+enum
+{
+  Aspect_VKey_Lower = 0,
+  Aspect_VKey_ModifiersLower      = Aspect_VKey_Shift,
+  Aspect_VKey_ModifiersUpper      = Aspect_VKey_Meta,
+  Aspect_VKey_NavigationKeysLower = Aspect_VKey_NavInteract,
+  Aspect_VKey_NavigationKeysUpper = Aspect_VKey_NavSpeedDecrease,
+  Aspect_VKey_Upper = Aspect_VKey_NavSpeedDecrease,
+  Aspect_VKey_NB  = Aspect_VKey_Upper - Aspect_VKey_Lower + 1,
+  Aspect_VKey_MAX = 255
+};
+
+//! Return modifier flags for specified modifier key.
+inline Aspect_VKeyFlags Aspect_VKey2Modifier (Aspect_VKey theKey)
+{
+  switch (theKey)
+  {
+    case Aspect_VKey_Shift:   return Aspect_VKeyFlags_SHIFT;
+    case Aspect_VKey_Control: return Aspect_VKeyFlags_CTRL;
+    case Aspect_VKey_Alt:     return Aspect_VKeyFlags_ALT;
+    case Aspect_VKey_Menu:    return Aspect_VKeyFlags_MENU;
+    case Aspect_VKey_Meta:    return Aspect_VKeyFlags_META;
+    default:                  return 0;
+  }
+}
+
+#endif // _Aspect_VKey_HeaderFile
diff --git a/src/Aspect/Aspect_VKeyFlags.hxx b/src/Aspect/Aspect_VKeyFlags.hxx
new file mode 100644 (file)
index 0000000..865fb71
--- /dev/null
@@ -0,0 +1,49 @@
+// Copyright (c) 2016-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_VKeyFlags_HeaderFile
+#define _Aspect_VKeyFlags_HeaderFile
+
+//! Key modifier, for combining with general key from Aspect_VKey.
+typedef unsigned int Aspect_VKeyFlags;
+
+//! Key modifier, for combining with general key from Aspect_VKey.
+enum
+{
+  Aspect_VKeyFlags_NONE  = 0,
+  // reserve first 8 bits to combine value with Aspect_VKey
+  Aspect_VKeyFlags_SHIFT = 1 <<  8, //!< Aspect_VKey_Shift
+  Aspect_VKeyFlags_CTRL  = 1 <<  9, //!< Aspect_VKey_Control
+  Aspect_VKeyFlags_ALT   = 1 << 10, //!< Aspect_VKey_Alt
+  Aspect_VKeyFlags_MENU  = 1 << 11, //!< Aspect_VKey_Menu
+  Aspect_VKeyFlags_META  = 1 << 12, //!< Aspect_VKey_Meta
+
+  Aspect_VKeyFlags_ALL = Aspect_VKeyFlags_SHIFT | Aspect_VKeyFlags_CTRL | Aspect_VKeyFlags_ALT | Aspect_VKeyFlags_MENU | Aspect_VKeyFlags_META
+};
+
+//! Mouse buttons, for combining with Aspect_VKey and Aspect_VKeyFlags.
+typedef unsigned int Aspect_VKeyMouse;
+
+//! Mouse button bitmask
+enum
+{
+  Aspect_VKeyMouse_NONE         = 0,       //!< no buttons
+
+  Aspect_VKeyMouse_LeftButton   = 1 << 13, //!< mouse left   button
+  Aspect_VKeyMouse_MiddleButton = 1 << 14, //!< mouse middle button (scroll)
+  Aspect_VKeyMouse_RightButton  = 1 << 15, //!< mouse right  button
+
+  Aspect_VKeyMouse_MainButtons = Aspect_VKeyMouse_LeftButton | Aspect_VKeyMouse_MiddleButton | Aspect_VKeyMouse_RightButton
+};
+
+#endif // _Aspect_VKeyFlags_HeaderFile
diff --git a/src/Aspect/Aspect_VKeySet.cxx b/src/Aspect/Aspect_VKeySet.cxx
new file mode 100644 (file)
index 0000000..e5c8041
--- /dev/null
@@ -0,0 +1,150 @@
+// Copyright (c) 2016-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include "Aspect_VKeySet.hxx"
+
+IMPLEMENT_STANDARD_RTTIEXT(Aspect_VKeySet, Standard_Transient)
+
+// ================================================================
+// Function : As1pect_VKeySet
+// Purpose  :
+// ================================================================
+Aspect_VKeySet::Aspect_VKeySet()
+: myKeys (0, Aspect_VKey_MAX),
+  myModifiers (Aspect_VKeyFlags_NONE)
+{
+  //
+}
+
+// ================================================================
+// Function : Reset
+// Purpose  :
+// ================================================================
+void Aspect_VKeySet::Reset()
+{
+  Standard_Mutex::Sentry aLock (myLock);
+  myModifiers = 0;
+  for (NCollection_Array1<KeyState>::Iterator aKeyIter (myKeys); aKeyIter.More(); aKeyIter.Next())
+  {
+    aKeyIter.ChangeValue().Reset();
+  }
+}
+
+// ================================================================
+// Function : KeyDown
+// Purpose  :
+// ================================================================
+void Aspect_VKeySet::KeyDown (Aspect_VKey theKey,
+                              double theTime,
+                              double thePressure)
+{
+  Standard_Mutex::Sentry aLock (myLock);
+  if (myKeys[theKey].Status != KeyStatus_Pressed)
+  {
+    myKeys[theKey].Status   = KeyStatus_Pressed;
+    myKeys[theKey].TimeDown = theTime;
+  }
+  myKeys[theKey].Pressure = thePressure;
+
+  const unsigned int aModif = Aspect_VKey2Modifier (theKey);
+  myModifiers = myModifiers | aModif;
+}
+
+// ================================================================
+// Function : KeyUp
+// Purpose  :
+// ================================================================
+void Aspect_VKeySet::KeyUp (Aspect_VKey theKey,
+                            double theTime)
+{
+  Standard_Mutex::Sentry aLock (myLock);
+  if (myKeys[theKey].Status == KeyStatus_Pressed)
+  {
+    myKeys[theKey].Status = KeyStatus_Released;
+    myKeys[theKey].TimeUp = theTime;
+  }
+
+  const unsigned int aModif = Aspect_VKey2Modifier (theKey);
+  if (aModif != 0)
+  {
+    myModifiers = myModifiers & ~aModif;
+  }
+}
+
+// ================================================================
+// Function : KeyFromAxis
+// Purpose  :
+// ================================================================
+void Aspect_VKeySet::KeyFromAxis (Aspect_VKey theNegative,
+                                  Aspect_VKey thePositive,
+                                  double theTime,
+                                  double thePressure)
+{
+  Standard_Mutex::Sentry aLock (myLock);
+  if (thePressure != 0)
+  {
+    const Aspect_VKey aKeyDown = thePressure > 0 ? thePositive : theNegative;
+    const Aspect_VKey aKeyUp   = thePressure < 0 ? thePositive : theNegative;
+
+    KeyDown (aKeyDown, theTime, Abs (thePressure));
+    if (myKeys[aKeyUp].Status == KeyStatus_Pressed)
+    {
+      KeyUp (aKeyUp, theTime);
+    }
+  }
+  else
+  {
+    if (myKeys[theNegative].Status == KeyStatus_Pressed)
+    {
+      KeyUp (theNegative, theTime);
+    }
+    if (myKeys[thePositive].Status == KeyStatus_Pressed)
+    {
+      KeyUp (thePositive, theTime);
+    }
+  }
+}
+
+// ================================================================
+// Function : HoldDuration
+// Purpose  :
+// ================================================================
+bool Aspect_VKeySet::HoldDuration (Aspect_VKey theKey,
+                                   double theTime,
+                                   double& theDuration,
+                                   double& thePressure)
+{
+  Standard_Mutex::Sentry aLock (myLock);
+  switch (myKeys[theKey].Status)
+  {
+    case KeyStatus_Free:
+    {
+      theDuration = 0.0;
+      return false;
+    }
+    case KeyStatus_Released:
+    {
+      myKeys[theKey].Status = KeyStatus_Free;
+      theDuration = myKeys[theKey].TimeUp - myKeys[theKey].TimeDown;
+      thePressure = myKeys[theKey].Pressure;
+      return true;
+    }
+    case KeyStatus_Pressed:
+    {
+      theDuration = theTime - myKeys[theKey].TimeDown;
+      thePressure = myKeys[theKey].Pressure;
+      return true;
+    }
+  }
+  return false;
+}
diff --git a/src/Aspect/Aspect_VKeySet.hxx b/src/Aspect/Aspect_VKeySet.hxx
new file mode 100644 (file)
index 0000000..bc7b312
--- /dev/null
@@ -0,0 +1,152 @@
+// Copyright (c) 2016-2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Aspect_VKeySet_HeaderFile
+#define _Aspect_VKeySet_HeaderFile
+
+#include <Aspect_VKey.hxx>
+
+#include <NCollection_Array1.hxx>
+#include <OSD_Timer.hxx>
+#include <Standard_Mutex.hxx>
+#include <Standard_Transient.hxx>
+
+//! Structure defining key state.
+class Aspect_VKeySet : public Standard_Transient
+{
+  DEFINE_STANDARD_RTTIEXT(Aspect_VKeySet, Standard_Transient)
+public:
+
+  //! Main constructor.
+  Standard_EXPORT Aspect_VKeySet();
+
+  //! Return active modifiers.
+  Aspect_VKeyFlags Modifiers() const
+  {
+    Standard_Mutex::Sentry aLock (myLock);
+    return myModifiers;
+  }
+
+  //! Return timestamp of press event.
+  double DownTime (Aspect_VKey theKey) const
+  {
+    Standard_Mutex::Sentry aLock (myLock);
+    return myKeys[theKey].TimeDown;
+  }
+
+  //! Return timestamp of release event.
+  double TimeUp (Aspect_VKey theKey) const
+  {
+    Standard_Mutex::Sentry aLock (myLock);
+    return myKeys[theKey].TimeUp;
+  }
+
+  //! Return TRUE if key is in Free state.
+  bool IsFreeKey (Aspect_VKey theKey) const
+  {
+    Standard_Mutex::Sentry aLock (myLock);
+    return myKeys[theKey].Status == KeyStatus_Free;
+  }
+
+  //! Return TRUE if key is in Pressed state.
+  bool IsKeyDown (Aspect_VKey theKey) const
+  {
+    Standard_Mutex::Sentry aLock (myLock);
+    return myKeys[theKey].Status == KeyStatus_Pressed;
+  }
+
+public:
+
+  //! Reset the key state into unpressed state.
+  Standard_EXPORT void Reset();
+
+  //! Press key.
+  //! @param theKey key pressed
+  //! @param theTime event timestamp
+  Standard_EXPORT void KeyDown (Aspect_VKey theKey,
+                                double theTime,
+                                double thePressure = 1.0);
+
+  //! Release key.
+  //! @param theKey key pressed
+  //! @param theTime event timestamp
+  Standard_EXPORT void KeyUp (Aspect_VKey theKey,
+                              double theTime);
+
+  //! Simulate key up/down events from axis value.
+  Standard_EXPORT void KeyFromAxis (Aspect_VKey theNegative,
+                                    Aspect_VKey thePositive,
+                                    double theTime,
+                                    double thePressure);
+
+  //! Return duration of the button in pressed state.
+  //! @param theKey      key to check
+  //! @param theTime     current time (for computing duration from key down time)
+  //! @param theDuration key press duration
+  //! @return TRUE if key was in pressed state
+  bool HoldDuration (Aspect_VKey theKey,
+                     double theTime,
+                     double& theDuration)
+  {
+    double aPressure = -1.0;
+    return HoldDuration (theKey, theTime, theDuration, aPressure);
+  }
+
+  //! Return duration of the button in pressed state.
+  //! @param theKey      key to check
+  //! @param theTime     current time (for computing duration from key down time)
+  //! @param theDuration key press duration
+  //! @param thePressure key pressure
+  //! @return TRUE if key was in pressed state
+  Standard_EXPORT bool HoldDuration (Aspect_VKey theKey,
+                                     double theTime,
+                                     double& theDuration,
+                                     double& thePressure);
+
+private:
+
+  //! Key state.
+  enum KeyStatus
+  {
+    KeyStatus_Free,     //!< free status
+    KeyStatus_Pressed,  //!< key is in pressed state
+    KeyStatus_Released, //!< key has been just released (transient state before KeyStatus_Free)
+  };
+
+  //! Structure defining key state.
+  struct KeyState
+  {
+    KeyState() : TimeDown (0.0), TimeUp (0.0), Pressure (1.0), Status (KeyStatus_Free) {}
+    void Reset()
+    {
+      Status = KeyStatus_Free;
+      TimeDown = 0.0;
+      TimeUp   = 0.0;
+      Pressure = 1.0;
+    }
+
+    double    TimeDown; //!< time of key press   event
+    double    TimeUp;   //!< time of key release event
+    double    Pressure; //!< key pressure
+    KeyStatus Status;   //!< key status
+  };
+
+private:
+
+  NCollection_Array1<KeyState> myKeys;      //!< keys state
+  mutable Standard_Mutex       myLock;      //!< mutex for thread-safe updates
+  Aspect_VKeyFlags             myModifiers; //!< active modifiers
+
+};
+
+#endif // _Aspect_VKeySet_HeaderFile
index 2783f74..5e5289b 100644 (file)
 #ifndef _Aspect_Window_HeaderFile
 #define _Aspect_Window_HeaderFile
 
-#include <Standard.hxx>
-#include <Standard_Type.hxx>
-
 #include <Aspect_Background.hxx>
 #include <Aspect_GradientBackground.hxx>
 #include <Aspect_FBConfig.hxx>
 #include <Aspect_FillMethod.hxx>
-#include <Standard_Boolean.hxx>
-#include <Standard_Transient.hxx>
 #include <Quantity_Color.hxx>
 #include <Aspect_GradientFillMethod.hxx>
 #include <Aspect_TypeOfResize.hxx>
-#include <Standard_Integer.hxx>
 #include <Aspect_Drawable.hxx>
+#include <Standard.hxx>
+#include <Standard_Transient.hxx>
+#include <Standard_Type.hxx>
+#include <TCollection_AsciiString.hxx>
 
 class Aspect_DisplayConnection;
-class Aspect_WindowDefinitionError;
-class Aspect_WindowError;
-class Aspect_Background;
-class Aspect_GradientBackground;
 
-class Aspect_Window;
 DEFINE_STANDARD_HANDLE(Aspect_Window, Standard_Transient)
 
 //! Defines a window.
@@ -109,6 +102,9 @@ public:
   //! Returns native Window FB config (GLXFBConfig on Xlib)
   Standard_EXPORT virtual Aspect_FBConfig NativeFBConfig() const = 0;
 
+  //! Sets window title.
+  virtual void SetTitle (const TCollection_AsciiString& theTitle) { (void )theTitle; }
+
   //! Invalidate entire window content.
   //!
   //! Implementation is expected to allow calling this method from non-GUI thread,
index c63cdf0..933f271 100755 (executable)
@@ -34,6 +34,9 @@ Aspect_RectangularGrid.cxx
 Aspect_RectangularGrid.hxx
 Aspect_RenderingContext.hxx
 Aspect_SequenceOfColor.hxx
+Aspect_ScrollDelta.hxx
+Aspect_Touch.hxx
+Aspect_TouchMap.hxx
 Aspect_TypeOfColorScaleData.hxx
 Aspect_TypeOfColorScaleOrientation.hxx
 Aspect_TypeOfColorScalePosition.hxx
@@ -47,6 +50,10 @@ Aspect_TypeOfResize.hxx
 Aspect_TypeOfStyleText.hxx
 Aspect_TypeOfTriedronPosition.hxx
 Aspect_Units.hxx
+Aspect_VKey.hxx
+Aspect_VKeyFlags.hxx
+Aspect_VKeySet.cxx
+Aspect_VKeySet.hxx
 Aspect_WidthOfLine.hxx
 Aspect_Window.cxx
 Aspect_Window.hxx
index dcfe0d3..694d84d 100644 (file)
@@ -47,6 +47,7 @@
 #include <Aspect_GradientFillMethod.hxx>
 #include <Aspect_Handle.hxx>
 #include <Aspect_TypeOfResize.hxx>
+#include <Aspect_VKey.hxx>
 #include <Quantity_NameOfColor.hxx>
 
 class Aspect_WindowDefinitionError;
@@ -58,6 +59,10 @@ class Aspect_GradientBackground;
 //! This class defines Cocoa window
 class Cocoa_Window : public Aspect_Window
 {
+public:
+
+  //! Convert Carbon virtual key into Aspect_VKey.
+  Standard_EXPORT static Aspect_VKey VirtualKeyFromNative (Standard_Integer theKey);
 
 public:
 
@@ -136,6 +141,9 @@ public:
   //! Returns nothing on OS X
   virtual Aspect_FBConfig NativeFBConfig() const Standard_OVERRIDE { return NULL; }
 
+  //! Sets window title.
+  Standard_EXPORT virtual void SetTitle (const TCollection_AsciiString& theTitle) Standard_OVERRIDE;
+
   //! Invalidate entire window content by setting NSView::setNeedsDisplay property.
   //! Call will be implicitly redirected to the main thread when called from non-GUI thread.
   Standard_EXPORT virtual void InvalidateContent (const Handle(Aspect_DisplayConnection)& theDisp = NULL) Standard_OVERRIDE;
index 2613a57..5d6dc98 100644 (file)
@@ -404,6 +404,27 @@ void Cocoa_Window::Size (Standard_Integer& theWidth,
 }
 
 // =======================================================================
+// function : SetTitle
+// purpose  :
+// =======================================================================
+void Cocoa_Window::SetTitle (const TCollection_AsciiString& theTitle)
+{
+  if (myHView == NULL)
+  {
+    return;
+  }
+
+#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+  (void )theTitle;
+#else
+  NSWindow* aWindow  = [myHView window];
+  NSString* aTitleNS = [[NSString alloc] initWithUTF8String: theTitle.ToCString()];
+  [aWindow setTitle: aTitleNS];
+  [aTitleNS release];
+#endif
+}
+
+// =======================================================================
 // function : InvalidateContent
 // purpose  :
 // =======================================================================
@@ -429,3 +450,143 @@ void Cocoa_Window::InvalidateContent (const Handle(Aspect_DisplayConnection)& )
                            waitUntilDone: NO];
   }
 }
+
+// =======================================================================
+// function : VirtualKeyFromNative
+// purpose  :
+// =======================================================================
+Aspect_VKey Cocoa_Window::VirtualKeyFromNative (Standard_Integer theKey)
+{
+  switch (theKey)
+  {
+    case 0x00: return Aspect_VKey_A;
+    case 0x01: return Aspect_VKey_S;
+    case 0x02: return Aspect_VKey_D;
+    case 0x03: return Aspect_VKey_F;
+    case 0x04: return Aspect_VKey_H;
+    case 0x05: return Aspect_VKey_G;
+    case 0x06: return Aspect_VKey_Z;
+    case 0x07: return Aspect_VKey_X;
+    case 0x08: return Aspect_VKey_C;
+    case 0x09: return Aspect_VKey_V;
+    case 0x0A: return Aspect_VKey_UNKNOWN;
+    case 0x0B: return Aspect_VKey_B;
+    case 0x0C: return Aspect_VKey_Q;
+    case 0x0D: return Aspect_VKey_W;
+    case 0x0E: return Aspect_VKey_E;
+    case 0x0F: return Aspect_VKey_R;
+    case 0x10: return Aspect_VKey_Y;
+    case 0x11: return Aspect_VKey_T;
+    case 0x12: return Aspect_VKey_1;
+    case 0x13: return Aspect_VKey_2;
+    case 0x14: return Aspect_VKey_3;
+    case 0x15: return Aspect_VKey_4;
+    case 0x16: return Aspect_VKey_6;
+    case 0x17: return Aspect_VKey_5;
+    case 0x18: return Aspect_VKey_Plus;
+    case 0x19: return Aspect_VKey_9;
+    case 0x1A: return Aspect_VKey_7;
+    case 0x1B: return Aspect_VKey_Minus;
+    case 0x1C: return Aspect_VKey_8;
+    case 0x1D: return Aspect_VKey_0;
+    case 0x1E: return Aspect_VKey_BracketRight;
+    case 0x1F: return Aspect_VKey_O;
+    case 0x20: return Aspect_VKey_U;
+    case 0x21: return Aspect_VKey_BracketLeft;
+    case 0x22: return Aspect_VKey_I;
+    case 0x23: return Aspect_VKey_P;
+    case 0x24: return Aspect_VKey_Enter;
+    case 0x25: return Aspect_VKey_L;
+    case 0x26: return Aspect_VKey_J;
+    case 0x27: return Aspect_VKey_Apostrophe;
+    case 0x28: return Aspect_VKey_K;
+    case 0x29: return Aspect_VKey_Semicolon;
+    case 0x2A: return Aspect_VKey_Backslash;
+    case 0x2B: return Aspect_VKey_Comma; // 43, ',<'
+    case 0x2C: return Aspect_VKey_Slash; //ST_VK_OEM_2, // 44, '?/'
+    case 0x2D: return Aspect_VKey_N;
+    case 0x2E: return Aspect_VKey_M;
+    case 0x2F: return Aspect_VKey_Period; // 47, '.>'
+    case 0x30: return Aspect_VKey_Tab;
+    case 0x31: return Aspect_VKey_Space;
+    case 0x32: return Aspect_VKey_Tilde;  // '~`'
+    case 0x33: return Aspect_VKey_Backspace;
+    case 0x34: return Aspect_VKey_UNKNOWN;
+    case 0x35: return Aspect_VKey_Escape;
+    case 0x36: return Aspect_VKey_UNKNOWN; // Aspect_VKey_Cmd, right Command
+    case 0x37: return Aspect_VKey_UNKNOWN; // Aspect_VKey_Cmd, left  Command
+    case 0x38: return Aspect_VKey_Shift;   // left shift
+    case 0x39: return Aspect_VKey_UNKNOWN;
+    case 0x3A: return Aspect_VKey_Alt;     // left alt/option
+    case 0x3B: return Aspect_VKey_Control;
+    case 0x3C: return Aspect_VKey_Shift;   // right shift
+    case 0x3D: return Aspect_VKey_Alt;     // right alt/option
+    case 0x3E: return Aspect_VKey_UNKNOWN;
+    case 0x3F: return Aspect_VKey_UNKNOWN; // Aspect_VKey_Func, fn
+    case 0x40:
+    case 0x41:
+    case 0x42:
+    case 0x43:
+    case 0x44:
+    case 0x45:
+    case 0x46:
+    case 0x47:
+    case 0x48:
+    case 0x49:
+    case 0x4A:
+    case 0x4B: return Aspect_VKey_UNKNOWN;
+    case 0x4C: return Aspect_VKey_Enter;   // fn + return
+    case 0x4D:
+    case 0x4E:
+    case 0x4F:
+    case 0x50:
+    case 0x51:
+    case 0x52:
+    case 0x53:
+    case 0x54:
+    case 0x55:
+    case 0x56:
+    case 0x57:
+    case 0x58:
+    case 0x59:
+    case 0x5A:
+    case 0x5B:
+    case 0x5C:
+    case 0x5D:
+    case 0x5E:
+    case 0x5F: return Aspect_VKey_UNKNOWN;
+    case 0x60: return Aspect_VKey_F5;
+    case 0x61: return Aspect_VKey_F6;
+    case 0x62: return Aspect_VKey_F7;
+    case 0x63: return Aspect_VKey_F3;
+    case 0x64: return Aspect_VKey_F8;
+    case 0x65: return Aspect_VKey_F9;
+    //case 0x66: return Aspect_VKey_UNKNOWN;
+    case 0x67: return Aspect_VKey_F11;
+    //case 0x68: return Aspect_VKey_UNKNOWN;
+    //case 0x69: return Aspect_VKey_UNKNOWN;
+    //case 0x6A: return Aspect_VKey_UNKNOWN;
+    //case 0x6B: return Aspect_VKey_UNKNOWN;
+    //case 0x6C: return Aspect_VKey_UNKNOWN;
+    case 0x6D: return Aspect_VKey_F10;
+    //case 0x6E: return Aspect_VKey_UNKNOWN;
+    case 0x6F: return Aspect_VKey_F12;
+    //case 0x70: return Aspect_VKey_UNKNOWN;
+    //case 0x71: return Aspect_VKey_UNKNOWN;
+    //case 0x72: return Aspect_VKey_UNKNOWN;
+    case 0x73: return Aspect_VKey_Home;
+    case 0x74: return Aspect_VKey_PageUp;
+    case 0x75: return Aspect_VKey_Delete;
+    case 0x76: return Aspect_VKey_F4;
+    case 0x77: return Aspect_VKey_End;
+    case 0x78: return Aspect_VKey_F2;
+    case 0x79: return Aspect_VKey_PageDown;
+    case 0x7A: return Aspect_VKey_F1;
+    case 0x7B: return Aspect_VKey_Left;
+    case 0x7C: return Aspect_VKey_Right;
+    case 0x7D: return Aspect_VKey_Down;
+    case 0x7E: return Aspect_VKey_Up;
+    case 0x7F: return Aspect_VKey_UNKNOWN;
+  }
+  return Aspect_VKey_UNKNOWN;
+}
index 3f8b3b0..e4c097e 100644 (file)
@@ -57,8 +57,6 @@ extern ViewerTest_DoubleMapOfInteractiveAndName& GetMapOfAIS();
 Standard_EXPORT ViewerTest_DoubleMapOfInteractiveAndName& GetMapOfAIS();
 #endif
 
-static TColStd_MapOfInteger theactivatedmodes(8);
-
 #include <AIS_PlaneTrihedron.hxx>
 #include <TopoDS_Face.hxx>
 #include <TopExp_Explorer.hxx>
index b67123a..61ae63b 100644 (file)
@@ -64,7 +64,8 @@ namespace
 //purpose  :
 //=============================================================================
 V3d_View::V3d_View (const Handle(V3d_Viewer)& theViewer, const V3d_TypeOfView theType)
-: MyViewer (theViewer.operator->()),
+: myIsInvalidatedImmediate (Standard_True),
+  MyViewer (theViewer.operator->()),
   SwitchSetFront (Standard_False),
   myZRotation (Standard_False),
   myTrihedron (new V3d_Trihedron()),
@@ -114,7 +115,8 @@ V3d_View::V3d_View (const Handle(V3d_Viewer)& theViewer, const V3d_TypeOfView th
 //purpose  :
 //=============================================================================
 V3d_View::V3d_View (const Handle(V3d_Viewer)& theViewer, const Handle(V3d_View)& theView)
-: MyViewer (theViewer.operator->()),
+: myIsInvalidatedImmediate (Standard_True),
+  MyViewer (theViewer.operator->()),
   SwitchSetFront(Standard_False),
   myZRotation (Standard_False),
   MyTrsf (1, 4, 1, 4)
@@ -228,6 +230,7 @@ void V3d_View::Update() const
     return;
   }
 
+  myIsInvalidatedImmediate = Standard_False;
   myView->Update();
   myView->Compute();
   myView->Redraw();
@@ -245,6 +248,7 @@ void V3d_View::Redraw() const
     return;
   }
 
+  myIsInvalidatedImmediate = Standard_False;
   Handle(Graphic3d_StructureManager) aStructureMgr  = MyViewer->StructureManager();
   for (Standard_Integer aRetryIter = 0; aRetryIter < 2; ++aRetryIter)
   {
@@ -276,6 +280,7 @@ void V3d_View::RedrawImmediate() const
     return;
   }
 
+  myIsInvalidatedImmediate = Standard_False;
   myView->RedrawImmediate();
 }
 
@@ -1950,12 +1955,10 @@ Standard_Integer V3d_View::MinMax(Standard_Real& Xmin,
 }
 
 //=======================================================================
-//function : Gravity
+//function : GravityPoint
 //purpose  :
 //=======================================================================
-void V3d_View::Gravity (Standard_Real& theX,
-                        Standard_Real& theY,
-                        Standard_Real& theZ) const
+gp_Pnt V3d_View::GravityPoint() const
 {
   Graphic3d_MapOfStructure aSetOfStructures;
   myView->DisplayedStructures (aSetOfStructures);
@@ -2055,9 +2058,8 @@ void V3d_View::Gravity (Standard_Real& theX,
   {
     aResult /= aNbPoints;
   }
-  theX = aResult.X();
-  theY = aResult.Y();
-  theZ = aResult.Z();
+
+  return aResult;
 }
 
 //=======================================================================
@@ -2513,8 +2515,10 @@ void V3d_View::StartRotation(const Standard_Integer X,
   Size(x,y);
   rx = Standard_Real(Convert(x));
   ry = Standard_Real(Convert(y));
-  Gravity(gx,gy,gz);
-  Rotate(0.,0.,0.,gx,gy,gz,Standard_True);
+  myRotateGravity = GravityPoint();
+  Rotate (0.0, 0.0, 0.0,
+          myRotateGravity.X(), myRotateGravity.Y(), myRotateGravity.Z(),
+          Standard_True);
   myZRotation = Standard_False;
   if( zRotationThreshold > 0. ) {
     Standard_Real dx = Abs(sx - rx/2.);
@@ -2546,7 +2550,9 @@ void V3d_View::Rotation(const Standard_Integer X,
     dy = (sy - Standard_Real(Y)) * M_PI / ry;
   }
 
-  Rotate(dx, dy, dz, gx, gy, gz, Standard_False);
+  Rotate (dx, dy, dz,
+          myRotateGravity.X(), myRotateGravity.Y(), myRotateGravity.Z(),
+          Standard_False);
 }
 
 //=============================================================================
index d1e6129..c27d6f8 100644 (file)
@@ -148,6 +148,12 @@ public:
   //! Returns true if cached view content has been invalidated.
   Standard_EXPORT Standard_Boolean IsInvalidated() const;
 
+  //! Returns true if immediate layer content has been invalidated.
+  Standard_Boolean IsInvalidatedImmediate() const { return myIsInvalidatedImmediate; }
+
+  //! Invalidates view content within immediate layer but does not redraw it.
+  void InvalidateImmediate() { myIsInvalidatedImmediate = Standard_True; }
+
   //! Must be called when the window supporting the
   //! view changes size.
   //! if the view is not mapped on a window.
@@ -939,6 +945,9 @@ public:
   //! Fills in the dictionary with statistic performance info.
   Standard_EXPORT void StatisticInformation (TColStd_IndexedDataMapOfStringString& theDict) const;
 
+  //! Returns the Objects number and the gravity center of ALL viewable points in the view
+  Standard_EXPORT gp_Pnt GravityPoint() const;
+
   DEFINE_STANDARD_RTTIEXT(V3d_View,Standard_Transient)
 
 protected:
@@ -976,10 +985,6 @@ private:
   //! the objects contained in the view
   Standard_EXPORT Standard_Integer MinMax (Standard_Real& Xmin, Standard_Real& Ymin, Standard_Real& Zmin, Standard_Real& Xmax, Standard_Real& Ymax, Standard_Real& Zmax) const;
   
-  //! Returns the Objects number and the gravity center
-  //! of ALL viewable points in the view
-  Standard_EXPORT void Gravity (Standard_Real& X, Standard_Real& Y, Standard_Real& Z) const;
-  
   Standard_EXPORT void Init();
   
   //! Returns a new vertex when the grid is activated.
@@ -996,6 +1001,7 @@ protected:
   Handle(Graphic3d_Camera) myDefaultCamera;
   Handle(Graphic3d_CView) myView;
   Standard_Boolean myImmediateUpdate;
+  mutable Standard_Boolean myIsInvalidatedImmediate;
 
 private:
 
@@ -1009,9 +1015,7 @@ private:
   Standard_Integer sy;
   Standard_Real rx;
   Standard_Real ry;
-  Standard_Real gx;
-  Standard_Real gy;
-  Standard_Real gz;
+  gp_Pnt myRotateGravity;
   Standard_Boolean myComputedMode;
   Standard_Boolean SwitchSetFront;
   Standard_Boolean myZRotation;
index 148e5e3..2f929a1 100755 (executable)
@@ -8,10 +8,11 @@ ViewerTest_DoubleMapIteratorOfDoubleMapOfInteractiveAndName.hxx
 ViewerTest_DoubleMapOfInteractiveAndName.hxx
 ViewerTest_EventManager.cxx
 ViewerTest_EventManager.hxx
-ViewerTest_EventManager.lxx
 ViewerTest_FilletCommands.cxx
 ViewerTest_ObjectCommands.cxx
 ViewerTest_OpenGlCommands.cxx
 ViewerTest_RelationCommands.cxx
 ViewerTest_ViewerCommands.cxx
 ViewerTest_ViewerCommands_1.mm
+ViewerTest_V3dView.cxx
+ViewerTest_V3dView.hxx
index 6624596..1ef047c 100644 (file)
@@ -766,15 +766,7 @@ Standard_EXPORT Standard_Boolean VDisplayAISObject (const TCollection_AsciiStrin
   return ViewerTest::Display (theName, theObject, Standard_True, theReplaceIfExists);
 }
 
-static TColStd_MapOfInteger theactivatedmodes(8);
-static TColStd_ListOfTransient theEventMgrs;
-
-static void VwrTst_InitEventMgr(const Handle(V3d_View)& aView,
-                                const Handle(AIS_InteractiveContext)& Ctx)
-{
-  theEventMgrs.Clear();
-  theEventMgrs.Prepend(new ViewerTest_EventManager(aView, Ctx));
-}
+static NCollection_List<Handle(ViewerTest_EventManager)> theEventMgrs;
 
 static Handle(V3d_View)&  a3DView()
 {
@@ -831,17 +823,15 @@ void ViewerTest::UnsetEventManager()
 
 void ViewerTest::ResetEventManager()
 {
-  const Handle(V3d_View) aView = ViewerTest::CurrentView();
-  VwrTst_InitEventMgr(aView, ViewerTest::GetAISContext());
+  theEventMgrs.Clear();
+  theEventMgrs.Prepend (new ViewerTest_EventManager (ViewerTest::CurrentView(), ViewerTest::GetAISContext()));
 }
 
 Handle(ViewerTest_EventManager) ViewerTest::CurrentEventManager()
 {
-  Handle(ViewerTest_EventManager) EM;
-  if(theEventMgrs.IsEmpty()) return EM;
-  Handle(Standard_Transient) Tr =  theEventMgrs.First();
-  EM = Handle(ViewerTest_EventManager)::DownCast (Tr);
-  return EM;
+  return !theEventMgrs.IsEmpty()
+        ? theEventMgrs.First()
+        : Handle(ViewerTest_EventManager)();
 }
 
 //=======================================================================
index 661a078..d2e8105 100644 (file)
 #include <ViewerTest_EventManager.hxx>
 
 #include <AIS_InteractiveContext.hxx>
+#include <AIS_Shape.hxx>
 #include <Aspect_Grid.hxx>
-#include <Standard_Type.hxx>
-#include <V3d_View.hxx>
+#include <Draw.hxx>
+#include <ViewerTest_V3dView.hxx>
+
+Standard_IMPORT Standard_Boolean Draw_Interprete (const char* theCommand);
 
 IMPLEMENT_STANDARD_RTTIEXT(ViewerTest_EventManager,Standard_Transient)
 
@@ -31,204 +34,309 @@ ViewerTest_EventManager::ViewerTest_EventManager (const Handle(V3d_View)&
                                                   const Handle(AIS_InteractiveContext)& theCtx)
 : myCtx  (theCtx),
   myView (theView),
-  myX    (-1),
-  myY    (-1)
+  myToPickPnt (Standard_False)
 {}
 
 //=======================================================================
-//function : MoveTo
+//function : UpdateMouseButtons
 //purpose  :
 //=======================================================================
-
-void ViewerTest_EventManager::MoveTo (const Standard_Integer theXPix,
-                                      const Standard_Integer theYPix)
+bool ViewerTest_EventManager::UpdateMouseButtons (const Graphic3d_Vec2i& thePoint,
+                                                  Aspect_VKeyMouse theButtons,
+                                                  Aspect_VKeyFlags theModifiers,
+                                                  bool theIsEmulated)
 {
-  Standard_Real aPnt3d[3] = {0.0, 0.0, 0.0};
-  if (!myCtx.IsNull()
-   && !myView.IsNull())
+  SetAllowRotation (!ViewerTest_V3dView::IsCurrentViewIn2DMode());
+
+  if (theButtons == Aspect_VKeyMouse_LeftButton)
   {
-    const Standard_Boolean toEchoGrid = myView->Viewer()->Grid()->IsActive()
-                                     && myView->Viewer()->GridEcho();
-    switch (myCtx->MoveTo (theXPix, theYPix, myView, !toEchoGrid))
+    if (myToPickPnt && (theModifiers & Aspect_VKeyFlags_CTRL) != 0)
     {
-      case AIS_SOD_Nothing:
-      {
-        if (toEchoGrid)
-        {
-          myView->ConvertToGrid (theXPix, theYPix, aPnt3d[0], aPnt3d[1], aPnt3d[2]);
-          myView->Viewer()->ShowGridEcho (myView, Graphic3d_Vertex (aPnt3d[0], aPnt3d[1], aPnt3d[2]));
-          myView->RedrawImmediate();
-        }
-        break;
-      }
-      default:
-      {
-        if (toEchoGrid)
-        {
-          myView->Viewer()->HideGridEcho (myView);
-          myView->RedrawImmediate();
-        }
-        break;
-      }
+      Graphic3d_Vec3d anXYZ;
+      myView->Convert (thePoint.x(), thePoint.y(), anXYZ.x(), anXYZ.y(), anXYZ.z());
+      Draw::Set (myPickPntArgVec[0].ToCString(), anXYZ.x());
+      Draw::Set (myPickPntArgVec[1].ToCString(), anXYZ.y());
+      Draw::Set (myPickPntArgVec[2].ToCString(), anXYZ.z());
+      myToPickPnt = false;
     }
   }
 
-  myX = theXPix;
-  myY = theYPix;
+  return AIS_ViewController::UpdateMouseButtons (thePoint, theButtons, theModifiers, theIsEmulated);
 }
 
-//=======================================================================
-//function : Select
+//==============================================================================
+//function : ProcessExpose
 //purpose  :
-//=======================================================================
-
-void ViewerTest_EventManager::Select (const Standard_Integer theXPressed,
-                                      const Standard_Integer theYPressed,
-                                      const Standard_Integer theXMotion,
-                                      const Standard_Integer theYMotion,
-                                      const Standard_Boolean theIsAutoAllowOverlap)
+//==============================================================================
+void ViewerTest_EventManager::ProcessExpose()
 {
-  if (myView.IsNull()
-   || myCtx.IsNull()
-   || Abs (theXPressed - theXMotion) < 2
-   || Abs (theYPressed - theYMotion) < 2)
-  {
-    return;
-  }
-
-  if (theIsAutoAllowOverlap)
-  {
-    const Standard_Boolean toAllowOverlap = theYPressed != Min (theYPressed, theYMotion);
-    myCtx->MainSelector()->AllowOverlapDetection (toAllowOverlap);
-  }
-  myCtx->Select (Min (theXPressed, theXMotion),
-                 Min (theYPressed, theYMotion),
-                 Max (theXPressed, theXMotion),
-                 Max (theYPressed, theYMotion),
-                 myView,
-                 Standard_False);
-
-  // to restore default state of viewer selector
-  if (theIsAutoAllowOverlap)
+  if (!myView.IsNull())
   {
-    myCtx->MainSelector()->AllowOverlapDetection (Standard_False);
+    myView->Invalidate();
+    FlushViewEvents (myCtx, myView, true);
   }
-  myView->Redraw();
 }
 
-//=======================================================================
-//function : ShiftSelect
+//==============================================================================
+//function : ProcessConfigure
 //purpose  :
-//=======================================================================
-
-void ViewerTest_EventManager::ShiftSelect (const Standard_Integer theXPressed,
-                                           const Standard_Integer theYPressed,
-                                           const Standard_Integer theXMotion,
-                                           const Standard_Integer theYMotion,
-                                           const Standard_Boolean theIsAutoAllowOverlap)
+//==============================================================================
+void ViewerTest_EventManager::ProcessConfigure()
 {
-  if (myView.IsNull()
-   || myCtx.IsNull()
-   || Abs (theXPressed - theXMotion) < 2
-   || Abs (theYPressed - theYMotion) < 2)
+  if (!myView.IsNull())
   {
-    return;
+    myView->MustBeResized();
+    FlushViewEvents (myCtx, myView, true);
   }
-
-  if (theIsAutoAllowOverlap)
-  {
-    const Standard_Boolean toAllowOverlap = theYPressed != Min (theYPressed, theYMotion);
-    myCtx->MainSelector()->AllowOverlapDetection (toAllowOverlap);
-  }
-  myCtx->ShiftSelect (Min (theXPressed, theXMotion),
-                      Min (theYPressed, theYMotion),
-                      Max (theXPressed, theXMotion),
-                      Max (theYPressed, theYMotion),
-                      myView,
-                      Standard_False);
-
-  // to restore default state of viewer selector
-  if (theIsAutoAllowOverlap)
-  {
-    myCtx->MainSelector()->AllowOverlapDetection (Standard_False);
-  }
-  myView->Redraw();
 }
 
 //=======================================================================
-//function : Select
+//function : KeyUp
 //purpose  :
 //=======================================================================
-
-void ViewerTest_EventManager::Select()
+void ViewerTest_EventManager::KeyUp (Aspect_VKey theKey,
+                                     double theTime)
 {
-  if (myView.IsNull()
-   || myCtx.IsNull())
-  {
-    return;
-  }
-
-  myCtx->Select (Standard_False);
-  myView->Redraw();
+  AIS_ViewController::KeyUp (theKey, theTime);
+  ProcessKeyPress (theKey);
 }
 
-//=======================================================================
-//function : ShiftSelect
+//==============================================================================
+//function : ProcessKeyPress
 //purpose  :
-//=======================================================================
-
-void ViewerTest_EventManager::ShiftSelect()
+//==============================================================================
+void ViewerTest_EventManager::ProcessKeyPress (Aspect_VKey theKey)
 {
-  if (myView.IsNull()
-   || myCtx.IsNull())
+  if (myCtx.IsNull()
+   || myView.IsNull())
   {
     return;
   }
 
-  myCtx->ShiftSelect (Standard_False);
-  myView->Redraw();
-}
-
-//=======================================================================
-//function : Select
-//purpose  : Selection with polyline
-//=======================================================================
-
-void ViewerTest_EventManager::Select (const TColgp_Array1OfPnt2d& thePolyline)
-{
-  if (myView.IsNull()
-   || myCtx.IsNull())
+  switch (theKey)
   {
-    return;
-  }
-
-  myCtx->Select (thePolyline, myView, Standard_False);
-  myView->Redraw();
-}
+    case Aspect_VKey_A: // AXO
+    {
+      if (!ViewerTest_V3dView::IsCurrentViewIn2DMode())
+      {
+        myView->SetProj(V3d_XposYnegZpos);
+      }
+      break;
+    }
+    case Aspect_VKey_D: // Reset
+    {
+      if (!ViewerTest_V3dView::IsCurrentViewIn2DMode())
+      {
+        myView->Reset();
+      }
+      break;
+    }
+    case Aspect_VKey_F:
+    {
+      if (myCtx->NbSelected() > 0)
+      {
+        myCtx->FitSelected (myView);
+      }
+      else
+      {
+        myView->FitAll();
+      }
+      break;
+    }
+    case Aspect_VKey_H: // HLR
+    {
+      std::cout << "HLR\n";
+      myView->SetComputedMode (!myView->ComputedMode());
+      myView->Redraw();
+      break;
+    }
+    case Aspect_VKey_P: // Type of HLR
+    {
+      myCtx->DefaultDrawer()->SetTypeOfHLR (myCtx->DefaultDrawer()->TypeOfHLR() == Prs3d_TOH_Algo
+                                          ? Prs3d_TOH_PolyAlgo
+                                          : Prs3d_TOH_Algo);
+      if (myCtx->NbSelected() == 0)
+      {
+        AIS_ListOfInteractive aListOfShapes;
+        myCtx->DisplayedObjects (aListOfShapes);
+        for (AIS_ListIteratorOfListOfInteractive anIter (aListOfShapes); anIter.More(); anIter.Next())
+        {
+          if (Handle(AIS_Shape) aShape = Handle(AIS_Shape)::DownCast (anIter.Value()))
+          {
+            aShape->SetTypeOfHLR (aShape->TypeOfHLR() == Prs3d_TOH_PolyAlgo
+                                ? Prs3d_TOH_Algo
+                                : Prs3d_TOH_PolyAlgo);
+            myCtx->Redisplay (aShape, Standard_False);
+          }
+        }
+      }
+      else
+      {
+        for (myCtx->InitSelected(); myCtx->MoreSelected(); myCtx->NextSelected())
+        {
+          if (Handle(AIS_Shape) aShape = Handle(AIS_Shape)::DownCast (myCtx->SelectedInteractive()))
+          {
+            aShape->SetTypeOfHLR (aShape->TypeOfHLR() == Prs3d_TOH_PolyAlgo
+                                ? Prs3d_TOH_Algo
+                                : Prs3d_TOH_PolyAlgo);
+            myCtx->Redisplay (aShape, Standard_False);
+          }
+        }
+      }
+      myCtx->UpdateCurrentViewer();
+      break;
+    }
+    case Aspect_VKey_S:
+    case Aspect_VKey_W:
+    {
+      Standard_Integer aDispMode = AIS_Shaded;
+      if (theKey == Aspect_VKey_S)
+      {
+        aDispMode = AIS_Shaded;
+        std::cout << "setup Shaded display mode\n";
+      }
+      else
+      {
+        aDispMode = AIS_WireFrame;
+        std::cout << "setup WireFrame display mode\n";
+      }
 
-//=======================================================================
-//function : ShiftSelect
-//purpose  : Selection with polyline without erasing of current selection
-//=======================================================================
+      if (myCtx->NbSelected() == 0)
+      {
+        myCtx->SetDisplayMode (aDispMode, true);
+      }
+      else
+      {
+        for (myCtx->InitSelected(); myCtx->MoreSelected(); myCtx->NextSelected())
+        {
+          myCtx->SetDisplayMode (myCtx->SelectedInteractive(), aDispMode, false);
+        }
+        myCtx->UpdateCurrentViewer();
+      }
+      break;
+    }
+    case Aspect_VKey_U: // Unset display mode
+    {
+      std::cout << "reset display mode to defaults\n";
+      if (myCtx->NbSelected() == 0)
+      {
+        myCtx->SetDisplayMode (AIS_WireFrame, true);
+      }
+      else
+      {
+        for (myCtx->InitSelected(); myCtx->MoreSelected(); myCtx->NextSelected())
+        {
+          myCtx->UnsetDisplayMode (myCtx->SelectedInteractive(), false);
+        }
+        myCtx->UpdateCurrentViewer();
+      }
+      break;
+    }
+    case Aspect_VKey_T:
+    {
+      if (!ViewerTest_V3dView::IsCurrentViewIn2DMode())
+      {
+        myView->SetProj (V3d_TypeOfOrientation_Zup_Top);
+      }
+      break;
+    }
+    case Aspect_VKey_B:
+    {
+      if (!ViewerTest_V3dView::IsCurrentViewIn2DMode())
+      {
+        myView->SetProj (V3d_TypeOfOrientation_Zup_Bottom);
+      }
+      break;
+    }
+    case Aspect_VKey_L:
+    {
+      if (!ViewerTest_V3dView::IsCurrentViewIn2DMode())
+      {
+        myView->SetProj (V3d_TypeOfOrientation_Zup_Left);
+      }
+      break;
+    }
+    case Aspect_VKey_R:
+    {
+      if (!ViewerTest_V3dView::IsCurrentViewIn2DMode())
+      {
+        myView->SetProj (V3d_TypeOfOrientation_Zup_Right);
+      }
+      break;
+    }
+    case Aspect_VKey_Comma:
+    {
+      myCtx->HilightNextDetected (myView);
+      break;
+    }
+    case Aspect_VKey_Period:
+    {
+      myCtx->HilightPreviousDetected (myView);
+      break;
+    }
+    case Aspect_VKey_Slash:
+    case Aspect_VKey_NumpadDivide:
+    {
+      Handle(Graphic3d_Camera) aCamera = myView->Camera();
+      if (aCamera->IsStereo())
+      {
+        aCamera->SetIOD (aCamera->GetIODType(), aCamera->IOD() - 0.01);
+        myView->Redraw();
+      }
+      break;
+    }
+    case Aspect_VKey_NumpadMultiply:
+    {
+      Handle(Graphic3d_Camera) aCamera = myView->Camera();
+      if (aCamera->IsStereo())
+      {
+        aCamera->SetIOD (aCamera->GetIODType(), aCamera->IOD() + 0.01);
+        myView->Redraw();
+      }
+      break;
+    }
+    case Aspect_VKey_Delete:
+    {
+      if (!myCtx.IsNull()
+        && myCtx->NbSelected() > 0)
+      {
+        Draw_Interprete ("verase");
+      }
+      break;
+    }
+    case Aspect_VKey_Escape:
+    {
+      if (!myCtx.IsNull()
+        && ViewerTest_EventManager::ToCloseViewOnEscape())
+      {
+        Draw_Interprete (ViewerTest_EventManager::ToExitOnCloseView() ? "exit" : "vclose");
+      }
+    }
+  }
 
-void ViewerTest_EventManager::ShiftSelect (const TColgp_Array1OfPnt2d& thePolyline)
-{
-  if (myView.IsNull()
-   || myCtx.IsNull())
+  if (theKey >= Aspect_VKey_0
+   && theKey <= Aspect_VKey_7)
   {
-    return;
+    const Standard_Integer aSelMode = theKey - Aspect_VKey_0;
+    bool toEnable = true;
+    if (!myCtx.IsNull())
+    {
+      AIS_ListOfInteractive aPrsList;
+      myCtx->DisplayedObjects (aPrsList);
+      for (AIS_ListOfInteractive::Iterator aPrsIter (aPrsList); aPrsIter.More() && toEnable; aPrsIter.Next())
+      {
+        TColStd_ListOfInteger aModes;
+        myCtx->ActivatedModes (aPrsIter.Value(), aModes);
+        for (TColStd_ListOfInteger::Iterator aModeIter (aModes); aModeIter.More() && toEnable; aModeIter.Next())
+        {
+          if (aModeIter.Value() == aSelMode)
+          {
+            toEnable = false;
+          }
+        }
+      }
+    }
+    TCollection_AsciiString aCmd = TCollection_AsciiString ("vselmode ") + aSelMode + (toEnable ? " 1" : " 0");
+    Draw_Interprete (aCmd.ToCString());
   }
-
-  myCtx->ShiftSelect (thePolyline, myView, Standard_False);
-  myView->Redraw();
-}
-
-//=======================================================================
-//function : GetCurrentPosition
-//purpose  :
-//=======================================================================
-void ViewerTest_EventManager::GetCurrentPosition (Standard_Integer& theXPix, Standard_Integer& theYPix) const
-{
-  theXPix = myX;
-  theYPix = myY;
 }
index 05352ce..25f23db 100644 (file)
 #ifndef _ViewerTest_EventManager_HeaderFile
 #define _ViewerTest_EventManager_HeaderFile
 
-#include <Standard.hxx>
-#include <Standard_Type.hxx>
-
-#include <Standard_Integer.hxx>
-#include <Standard_Transient.hxx>
-#include <Standard_Boolean.hxx>
+#include <AIS_ViewController.hxx>
 #include <TColgp_Array1OfPnt2d.hxx>
+#include <TCollection_AsciiString.hxx>
+
 class AIS_InteractiveContext;
 class V3d_View;
 
-
-class ViewerTest_EventManager;
 DEFINE_STANDARD_HANDLE(ViewerTest_EventManager, Standard_Transient)
 
 //! used to manage mouse event (move,select,shiftselect)
 //! By default the events are transmitted to interactive context.
-class ViewerTest_EventManager : public Standard_Transient
+class ViewerTest_EventManager : public Standard_Transient, public AIS_ViewController
 {
-
+  DEFINE_STANDARD_RTTIEXT(ViewerTest_EventManager, Standard_Transient)
 public:
 
-  
-  Standard_EXPORT ViewerTest_EventManager(const Handle(V3d_View)& aView, const Handle(AIS_InteractiveContext)& aCtx);
-  
-  Standard_EXPORT virtual void MoveTo (const Standard_Integer xpix, const Standard_Integer ypix);
-  
-  Standard_EXPORT virtual void Select();
-  
-  Standard_EXPORT virtual void ShiftSelect();
-  
-  Standard_EXPORT virtual void Select (const Standard_Integer theXPressed, const Standard_Integer theYPressed, const Standard_Integer theXMotion, const Standard_Integer theYMotion, const Standard_Boolean theIsAutoAllowOverlap = Standard_True);
-  
-  Standard_EXPORT virtual void ShiftSelect (const Standard_Integer theXPressed, const Standard_Integer theYPressed, const Standard_Integer theXMotion, const Standard_Integer theYMotion, const Standard_Boolean theIsAutoAllowOverlap = Standard_True);
-  
-  Standard_EXPORT virtual void Select (const TColgp_Array1OfPnt2d& thePolyline);
-  
-  Standard_EXPORT virtual void ShiftSelect (const TColgp_Array1OfPnt2d& thePolyline);
-  
-    const Handle(AIS_InteractiveContext)& Context() const;
-  
-  //! Gets current mouse position. It tracks change of mouse position
-  //! with mouse drugging or with DRAW command call (vmoveto).
-  Standard_EXPORT void GetCurrentPosition (Standard_Integer& theXPix, Standard_Integer& theYPix) const;
-
-
-
-
-  DEFINE_STANDARD_RTTIEXT(ViewerTest_EventManager,Standard_Transient)
-
-protected:
+  //! Return TRUE if View should be closed on escape.
+  static Standard_Boolean& ToCloseViewOnEscape()
+  {
+    static Standard_Boolean Draw_ToCloseViewOnEsc = Standard_False;
+    return Draw_ToCloseViewOnEsc;
+  }
 
+  //! Return TRUE if Draw Harness should exit on closing View.
+  static Standard_Boolean& ToExitOnCloseView()
+  {
+    static Standard_Boolean Draw_ToExitOnCloseView = Standard_False;
+    return Draw_ToExitOnCloseView;
+  }
 
+public:
 
+  //! Main constructor.
+  Standard_EXPORT ViewerTest_EventManager(const Handle(V3d_View)& aView, const Handle(AIS_InteractiveContext)& aCtx);
+  
+  //! Return interactive context.
+  const Handle(AIS_InteractiveContext)& Context() const { return myCtx; }
+
+  //! Returns TRUE if picking point mode has been enabled (for VPick command).
+  Standard_Boolean ToPickPoint() const { return myToPickPnt; }
+
+  //! Start picking point for VPick command.
+  void StartPickPoint (const char* theArgX,
+                       const char* theArgY,
+                       const char* theArgZ)
+  {
+    myToPickPnt = Standard_True;
+    myPickPntArgVec[0] = theArgX;
+    myPickPntArgVec[1] = theArgY;
+    myPickPntArgVec[2] = theArgZ;
+  }
+
+  //! Handle mouse button press/release event.
+  Standard_EXPORT virtual bool UpdateMouseButtons (const Graphic3d_Vec2i& thePoint,
+                                                   Aspect_VKeyMouse theButtons,
+                                                   Aspect_VKeyFlags theModifiers,
+                                                   bool theIsEmulated) Standard_OVERRIDE;
+
+  //! Release key.
+  Standard_EXPORT virtual void KeyUp (Aspect_VKey theKey,
+                                      double theTime) Standard_OVERRIDE;
+
+  //! Redraw the View on an Expose Event
+  Standard_EXPORT virtual void ProcessExpose();
+
+  //! Resize View.
+  Standard_EXPORT virtual void ProcessConfigure();
+
+  //! Handle KeyPress event.
+  Standard_EXPORT void ProcessKeyPress (Aspect_VKey theKey);
 
 private:
 
-
   Handle(AIS_InteractiveContext) myCtx;
   Handle(V3d_View) myView;
-  Standard_Integer myX;
-  Standard_Integer myY;
 
+  TCollection_AsciiString myPickPntArgVec[3];
+  Standard_Boolean myToPickPnt;
 
 };
 
-
-#include <ViewerTest_EventManager.lxx>
-
-
-
-
-
 #endif // _ViewerTest_EventManager_HeaderFile
index 954861d..5b9560c 100644 (file)
@@ -159,7 +159,6 @@ extern ViewerTest_DoubleMapOfInteractiveAndName& GetMapOfAIS();
 extern Standard_Boolean VDisplayAISObject (const TCollection_AsciiString& theName,
                                            const Handle(AIS_InteractiveObject)& theAISObj,
                                            Standard_Boolean theReplaceIfExists = Standard_True);
-extern int ViewerMainLoop(Standard_Integer argc, const char** argv);
 extern Handle(AIS_InteractiveContext)& TheAISContext();
 
 namespace
diff --git a/src/ViewerTest/ViewerTest_V3dView.cxx b/src/ViewerTest/ViewerTest_V3dView.cxx
new file mode 100644 (file)
index 0000000..cee18f9
--- /dev/null
@@ -0,0 +1,71 @@
+// Copyright (c) 2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <ViewerTest_V3dView.hxx>
+
+#include <ViewerTest.hxx>
+
+IMPLEMENT_STANDARD_RTTIEXT(ViewerTest_V3dView, V3d_View)
+
+// =======================================================================
+// function : ViewerTest_V3dView
+// purpose  :
+// =======================================================================
+ViewerTest_V3dView::ViewerTest_V3dView (const Handle(V3d_Viewer)& theViewer,
+                                        const V3d_TypeOfView theType,
+                                        bool theIs2dMode)
+: V3d_View (theViewer, theType),
+  myIs2dMode (theIs2dMode)
+{
+  //
+}
+
+// =======================================================================
+// function : ViewerTest_V3dView
+// purpose  :
+// =======================================================================
+ViewerTest_V3dView::ViewerTest_V3dView (const Handle(V3d_Viewer)& theViewer,
+                                        const Handle(V3d_View)& theView)
+: V3d_View (theViewer, theView),
+  myIs2dMode (false)
+{
+  if (Handle(ViewerTest_V3dView) aV3dView = Handle(ViewerTest_V3dView)::DownCast (theView))
+  {
+    myIs2dMode = aV3dView->IsViewIn2DMode();
+  }
+}
+
+// =======================================================================
+// function : IsCurrentViewIn2DMode
+// purpose  :
+// =======================================================================
+bool ViewerTest_V3dView::IsCurrentViewIn2DMode()
+{
+  if (Handle(ViewerTest_V3dView) aV3dView = Handle(ViewerTest_V3dView)::DownCast (ViewerTest::CurrentView()))
+  {
+    return aV3dView->IsViewIn2DMode();
+  }
+  return false;
+}
+
+// =======================================================================
+// function : SetCurrentView2DMode
+// purpose  :
+// =======================================================================
+void ViewerTest_V3dView::SetCurrentView2DMode (bool theIs2d)
+{
+  if (Handle(ViewerTest_V3dView) aV3dView = Handle(ViewerTest_V3dView)::DownCast (ViewerTest::CurrentView()))
+  {
+    aV3dView->SetView2DMode (theIs2d);
+  }
+}
diff --git a/src/ViewerTest/ViewerTest_V3dView.hxx b/src/ViewerTest/ViewerTest_V3dView.hxx
new file mode 100644 (file)
index 0000000..6eda37a
--- /dev/null
@@ -0,0 +1,53 @@
+// Copyright (c) 2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _ViewerTest_V3dView_HeaderFile
+#define _ViewerTest_V3dView_HeaderFile
+
+#include <V3d_View.hxx>
+
+//! Setting additional flag to store 2D mode of the View to avoid scene rotation by mouse/key events
+class ViewerTest_V3dView : public V3d_View
+{
+  DEFINE_STANDARD_RTTIEXT(ViewerTest_V3dView, V3d_View)
+public:
+  //! Initializes the view.
+  Standard_EXPORT ViewerTest_V3dView (const Handle(V3d_Viewer)& theViewer,
+                                      const V3d_TypeOfView theType = V3d_ORTHOGRAPHIC,
+                                      bool theIs2dMode = false);
+
+  //! Initializes the view by copying.
+  Standard_EXPORT ViewerTest_V3dView (const Handle(V3d_Viewer)& theViewer,
+                                      const Handle(V3d_View)& theView);
+
+  //! Returns true if 2D mode is set for the view
+  bool IsViewIn2DMode() const { return myIs2dMode; }
+
+  //! Sets 2D mode for the view
+  void SetView2DMode (bool the2dMode) { myIs2dMode = the2dMode; }
+
+public:
+
+  //! Returns true if active view in 2D mode.
+  Standard_EXPORT  static bool IsCurrentViewIn2DMode();
+
+  //! Set if active view in 2D mode.
+  Standard_EXPORT static void SetCurrentView2DMode (bool theIs2d);
+
+private:
+
+  Standard_Boolean myIs2dMode; //!< 2D mode flag
+
+};
+
+#endif // _ViewerTest_V3dView_HeaderFile
index 523c03b..f4801fe 100644 (file)
 // commercial license or contractual agreement.
 
 #include <OpenGl_GlCore20.hxx>
+#include <ViewerTest.hxx>
 
-#include <AIS_Animation.hxx>
 #include <AIS_AnimationCamera.hxx>
 #include <AIS_AnimationObject.hxx>
 #include <AIS_CameraFrustum.hxx>
 #include <AIS_ColorScale.hxx>
-#include <AIS_Manipulator.hxx>
-#include <AIS_RubberBand.hxx>
-#include <AIS_Shape.hxx>
-#include <AIS_InteractiveObject.hxx>
+#include <AIS_InteractiveContext.hxx>
 #include <AIS_ListOfInteractive.hxx>
 #include <AIS_ListIteratorOfListOfInteractive.hxx>
+#include <AIS_Manipulator.hxx>
+#include <AIS_Shape.hxx>
+#include <Aspect_DisplayConnection.hxx>
 #include <Aspect_Grid.hxx>
-#include <DBRep.hxx>
+#include <Aspect_TypeOfLine.hxx>
+#include <Draw.hxx>
+#include <Draw_Appli.hxx>
+#include <Draw_Interpretor.hxx>
 #include <Draw_ProgressIndicator.hxx>
+#include <gp_Dir.hxx>
+#include <gp_Pln.hxx>
+#include <gp_Pnt.hxx>
 #include <Graphic3d_ArrayOfPolylines.hxx>
+#include <Graphic3d_AspectFillArea3d.hxx>
 #include <Graphic3d_AspectMarker3d.hxx>
-#include <Graphic3d_NameOfTextureEnv.hxx>
+#include <Graphic3d_ClipPlane.hxx>
 #include <Graphic3d_GraduatedTrihedron.hxx>
+#include <Graphic3d_NameOfTextureEnv.hxx>
+#include <Graphic3d_Texture2Dmanual.hxx>
 #include <Graphic3d_TextureEnv.hxx>
 #include <Graphic3d_TextureParams.hxx>
 #include <Graphic3d_TypeOfTextureFilter.hxx>
-#include <Graphic3d_AspectFillArea3d.hxx>
-#include <ViewerTest.hxx>
-#include <ViewerTest_AutoUpdater.hxx>
-#include <ViewerTest_EventManager.hxx>
-#include <ViewerTest_DoubleMapOfInteractiveAndName.hxx>
-#include <ViewerTest_DoubleMapIteratorOfDoubleMapOfInteractiveAndName.hxx>
-#include <ViewerTest_CmdParser.hxx>
-#include <V3d_AmbientLight.hxx>
-#include <V3d_DirectionalLight.hxx>
-#include <V3d_PositionalLight.hxx>
-#include <V3d_SpotLight.hxx>
+#include <Image_AlienPixMap.hxx>
+#include <Image_Diff.hxx>
+#include <Image_VideoRecorder.hxx>
 #include <Message_ProgressSentry.hxx>
-#include <NCollection_DoubleMap.hxx>
+#include <NCollection_DataMap.hxx>
 #include <NCollection_List.hxx>
 #include <NCollection_Vector.hxx>
-#include <AIS_InteractiveContext.hxx>
-#include <Draw_Interpretor.hxx>
-#include <Draw.hxx>
-#include <Draw_Appli.hxx>
-#include <Image_AlienPixMap.hxx>
-#include <Image_VideoRecorder.hxx>
-#include <OpenGl_GraphicDriver.hxx>
 #include <OSD.hxx>
 #include <OSD_Timer.hxx>
-#include <TColStd_HSequenceOfAsciiString.hxx>
-#include <TColStd_SequenceOfInteger.hxx>
-#include <TColStd_HSequenceOfReal.hxx>
-#include <TColgp_Array1OfPnt2d.hxx>
-#include <TColStd_MapOfAsciiString.hxx>
-#include <Aspect_TypeOfLine.hxx>
-#include <Image_Diff.hxx>
-#include <Aspect_DisplayConnection.hxx>
-#include <gp_Pnt.hxx>
-#include <gp_Dir.hxx>
-#include <gp_Pln.hxx>
-#include <PrsMgr_PresentableObject.hxx>
-#include <Graphic3d_ClipPlane.hxx>
-#include <NCollection_DataMap.hxx>
-#include <Graphic3d_Texture2Dmanual.hxx>
+#include <OpenGl_GraphicDriver.hxx>
 #include <Prs3d_ShadingAspect.hxx>
 #include <Prs3d_Drawer.hxx>
 #include <Prs3d_LineAspect.hxx>
 #include <Prs3d_Root.hxx>
 #include <Prs3d_Text.hxx>
 #include <Select3D_SensitivePrimitiveArray.hxx>
+#include <TColStd_HSequenceOfAsciiString.hxx>
+#include <TColStd_SequenceOfInteger.hxx>
+#include <TColStd_HSequenceOfReal.hxx>
+#include <TColgp_Array1OfPnt2d.hxx>
+#include <TColStd_MapOfAsciiString.hxx>
+#include <ViewerTest_AutoUpdater.hxx>
+#include <ViewerTest_EventManager.hxx>
+#include <ViewerTest_DoubleMapOfInteractiveAndName.hxx>
+#include <ViewerTest_DoubleMapIteratorOfDoubleMapOfInteractiveAndName.hxx>
+#include <ViewerTest_CmdParser.hxx>
+#include <ViewerTest_V3dView.hxx>
+#include <V3d_AmbientLight.hxx>
+#include <V3d_DirectionalLight.hxx>
+#include <V3d_PositionalLight.hxx>
+#include <V3d_SpotLight.hxx>
 
 #include <tcl.h>
 
-#ifdef _WIN32
-#undef DrawText
-#endif
-
 #include <cstdlib>
 
 #if defined(_WIN32)
   #include <tk.h>
 #endif
 
-// Auxiliary definitions
-static const char THE_KEY_DELETE = 127;
-static const char THE_KEY_ESCAPE = 27;
-
 //==============================================================================
 //  VIEWER GLOBAL VARIABLES
 //==============================================================================
@@ -117,10 +104,6 @@ Standard_IMPORT Standard_Boolean Draw_Interprete (const char* theCommand);
 Standard_EXPORT int ViewerMainLoop(Standard_Integer , const char** argv);
 extern ViewerTest_DoubleMapOfInteractiveAndName& GetMapOfAIS();
 
-extern int VErase (Draw_Interpretor& theDI,
-                   Standard_Integer  theArgNb,
-                   const char**      theArgVec);
-
 #if defined(_WIN32)
 static Handle(WNT_Window)& VT_GetWindow() {
   static Handle(WNT_Window) WNTWin;
@@ -133,7 +116,6 @@ static Handle(Cocoa_Window)& VT_GetWindow()
   return aWindow;
 }
 extern void ViewerTest_SetCocoaEventManagerView (const Handle(Cocoa_Window)& theWindow);
-extern void SetCocoaWindowTitle (const Handle(Cocoa_Window)& theWindow, Standard_CString theTitle);
 extern void GetCocoaScreenResolution (Standard_Integer& theWidth, Standard_Integer& theHeight);
 
 #else
@@ -156,76 +138,6 @@ static void SetDisplayConnection (const Handle(Aspect_DisplayConnection)& theDis
   GetDisplayConnection() = theDisplayConnection;
 }
 
-#if defined(_WIN32) || (!defined(__APPLE__) || defined(MACOSX_USE_GLX))
-Aspect_Handle GetWindowHandle(const Handle(Aspect_Window)& theWindow)
-{
-  Aspect_Handle aWindowHandle = (Aspect_Handle)NULL;
-#if defined(_WIN32)
-  const Handle (WNT_Window) aWindow = Handle(WNT_Window)::DownCast (theWindow);
-  if (!aWindow.IsNull())
-    return aWindow->HWindow();
-#elif (!defined(__APPLE__) || defined(MACOSX_USE_GLX))
-  const Handle (Xw_Window) aWindow = Handle(Xw_Window)::DownCast (theWindow);
-  if (!aWindow.IsNull())
-  return aWindow->XWindow();
-#endif
-  return aWindowHandle;
-}
-#endif
-
-//! Setting additional flag to store 2D mode of the View to avoid scene rotation by mouse/key events
-class ViewerTest_V3dView : public V3d_View
-{
-  DEFINE_STANDARD_RTTI_INLINE(ViewerTest_V3dView, V3d_View)
-public:
-  //! Initializes the view.
-  ViewerTest_V3dView (const Handle(V3d_Viewer)& theViewer, const V3d_TypeOfView theType = V3d_ORTHOGRAPHIC,
-                      bool theIs2dMode = false)
-  : V3d_View (theViewer, theType), myIs2dMode (theIs2dMode) {}
-
-  //! Initializes the view by copying.
-  ViewerTest_V3dView (const Handle(V3d_Viewer)& theViewer, const Handle(V3d_View)& theView)
-  : V3d_View (theViewer, theView), myIs2dMode (false)
-  {
-    if (Handle(ViewerTest_V3dView) aV3dView = Handle(ViewerTest_V3dView)::DownCast (theView))
-    {
-      myIs2dMode = aV3dView->IsViewIn2DMode();
-    }
-  }
-
-  //! Returns true if 2D mode is set for the view
-  bool IsViewIn2DMode() const { return myIs2dMode; }
-
-  //! Sets 2D mode for the view
-  void SetView2DMode (bool the2dMode) { myIs2dMode = the2dMode; }
-
-public:
-
-  //! Returns true if active view in 2D mode.
-  static bool IsCurrentViewIn2DMode()
-  {
-    if (Handle(ViewerTest_V3dView) aV3dView = Handle(ViewerTest_V3dView)::DownCast (ViewerTest::CurrentView()))
-    {
-      return aV3dView->IsViewIn2DMode();
-    }
-    return false;
-  }
-
-  //! Set if active view in 2D mode.
-  static void SetCurrentView2DMode (bool theIs2d)
-  {
-    if (Handle(ViewerTest_V3dView) aV3dView = Handle(ViewerTest_V3dView)::DownCast (ViewerTest::CurrentView()))
-    {
-      aV3dView->SetView2DMode (theIs2d);
-    }
-  }
-
-private:
-
-  Standard_Boolean myIs2dMode; //!< 2D mode flag
-
-};
-
 NCollection_DoubleMap <TCollection_AsciiString, Handle(V3d_View)> ViewerTest_myViews;
 static NCollection_DoubleMap <TCollection_AsciiString, Handle(AIS_InteractiveContext)>  ViewerTest_myContexts;
 static NCollection_DoubleMap <TCollection_AsciiString, Handle(Graphic3d_GraphicDriver)> ViewerTest_myDrivers;
@@ -245,17 +157,7 @@ static struct
 //  EVENT GLOBAL VARIABLES
 //==============================================================================
 
-static int Start_Rot = 0;
-Standard_Boolean HasHlrOnBeforeRotation = Standard_False;
-int X_Motion = 0; // Current cursor position
-int Y_Motion = 0;
-int X_ButtonPress = 0; // Last ButtonPress position
-int Y_ButtonPress = 0;
-Standard_Boolean IsDragged = Standard_False;
-Standard_Boolean DragFirst = Standard_False;
 Standard_Boolean TheIsAnimating = Standard_False;
-Standard_Boolean Draw_ToExitOnCloseView = Standard_False;
-Standard_Boolean Draw_ToCloseViewOnEsc  = Standard_False;
 
 namespace
 {
@@ -1281,17 +1183,6 @@ namespace
 
 } // namespace
 
-Standard_EXPORT const Handle(AIS_RubberBand)& GetRubberBand()
-{
-  static Handle(AIS_RubberBand) aBand;
-  if (aBand.IsNull())
-  {
-    aBand = new AIS_RubberBand (Quantity_NOC_LIGHTBLUE, Aspect_TOL_SOLID, Quantity_NOC_LIGHTBLUE, 0.4, 1.0);
-    aBand->SetDisplayMode (0);
-  }
-  return aBand;
-}
-
 typedef NCollection_Map<AIS_Manipulator*> ViewerTest_MapOfAISManipulators;
 
 Standard_EXPORT ViewerTest_MapOfAISManipulators& GetMapOfAISManipulators()
@@ -1529,30 +1420,6 @@ Handle(AIS_InteractiveContext) FindContextByView (const Handle(V3d_View)& theVie
   return anAISContext;
 }
 
-
-//==============================================================================
-//function : SetWindowTitle
-//purpose  : Set window title
-//==============================================================================
-
-void SetWindowTitle (const Handle(Aspect_Window)& theWindow,
-                     Standard_CString theTitle)
-{
-#if defined(_WIN32)
-  const TCollection_ExtendedString theTitleW (theTitle);
-  SetWindowTextW ((HWND )Handle(WNT_Window)::DownCast(theWindow)->HWindow(), theTitleW.ToWideString());
-#elif defined(__APPLE__) && !defined(MACOSX_USE_GLX)
-  SetCocoaWindowTitle (Handle(Cocoa_Window)::DownCast(theWindow), theTitle);
-#else
-  if(GetDisplayConnection()->GetDisplay())
-  {
-    Window aWindow =
-      Handle(Xw_Window)::DownCast(theWindow)->XWindow();
-    XStoreName (GetDisplayConnection()->GetDisplay(), aWindow , theTitle);
-  }
-#endif
-}
-
 //==============================================================================
 //function : IsWindowOverlapped
 //purpose  : Check if theWindow overlapp another view
@@ -1853,12 +1720,9 @@ TCollection_AsciiString ViewerTest::ViewerInit (const Standard_Integer thePxLeft
   aTitle = aTitle + aViewNames.GetViewName() + "(*)";
 
   // Change name of current active window
-  if (!ViewerTest::CurrentView().IsNull())
+  if (const Handle(V3d_View)& aCurrentView = ViewerTest::CurrentView())
   {
-    TCollection_AsciiString anActiveWindowTitle("3D View - ");
-    anActiveWindowTitle = anActiveWindowTitle
-      + ViewerTest_myViews.Find2 (ViewerTest::CurrentView());
-    SetWindowTitle (ViewerTest::CurrentView()->Window(), anActiveWindowTitle.ToCString());
+    aCurrentView->Window()->SetTitle (TCollection_AsciiString ("3D View - ") + ViewerTest_myViews.Find2 (aCurrentView));
   }
 
   // Create viewer
@@ -2036,9 +1900,9 @@ static int VInit (Draw_Interpretor& theDi, Standard_Integer theArgsNb, const cha
     }
     else if (anArgCase == "-exitonclose")
     {
-      Draw_ToExitOnCloseView = true;
+      ViewerTest_EventManager::ToExitOnCloseView() = true;
       if (anArgIt + 1 < theArgsNb
-       && ViewerTest::ParseOnOff (theArgVec[anArgIt + 1], Draw_ToExitOnCloseView))
+       && ViewerTest::ParseOnOff (theArgVec[anArgIt + 1], ViewerTest_EventManager::ToExitOnCloseView()))
       {
         ++anArgIt;
       }
@@ -2046,9 +1910,9 @@ static int VInit (Draw_Interpretor& theDi, Standard_Integer theArgsNb, const cha
     else if (anArgCase == "-closeonescape"
           || anArgCase == "-closeonesc")
     {
-      Draw_ToCloseViewOnEsc = true;
+      ViewerTest_EventManager::ToCloseViewOnEscape() = true;
       if (anArgIt + 1 < theArgsNb
-       && ViewerTest::ParseOnOff (theArgVec[anArgIt + 1], Draw_ToCloseViewOnEsc))
+       && ViewerTest::ParseOnOff (theArgVec[anArgIt + 1], ViewerTest_EventManager::ToCloseViewOnEscape()))
       {
         ++anArgIt;
       }
@@ -2400,13 +2264,13 @@ static int VHLRType (Draw_Interpretor& , Standard_Integer argc, const char** arg
 //function : FindViewIdByWindowHandle
 //purpose  : Find theView Id in the map of views by window handle
 //==============================================================================
-#if defined(_WIN32) || defined(__WIN32__) || (!defined(__APPLE__) || defined(MACOSX_USE_GLX))
-TCollection_AsciiString FindViewIdByWindowHandle(const Aspect_Handle theWindowHandle)
+#if defined(_WIN32) || (!defined(__APPLE__) || defined(MACOSX_USE_GLX))
+TCollection_AsciiString FindViewIdByWindowHandle (Aspect_Drawable theWindowHandle)
 {
   for (NCollection_DoubleMap<TCollection_AsciiString, Handle(V3d_View)>::Iterator
        anIter(ViewerTest_myViews); anIter.More(); anIter.Next())
   {
-    Aspect_Handle aWindowHandle = GetWindowHandle(anIter.Value()->Window());
+    Aspect_Drawable aWindowHandle = anIter.Value()->Window()->NativeHandle();
     if (aWindowHandle == theWindowHandle)
       return anIter.Key1();
   }
@@ -2427,17 +2291,14 @@ void ActivateView (const TCollection_AsciiString& theViewName,
   Handle(AIS_InteractiveContext) anAISContext = FindContextByView(aView);
   if (!anAISContext.IsNull())
   {
-    if (!ViewerTest::CurrentView().IsNull())
+    if (const Handle(V3d_View)& aCurrentView = ViewerTest::CurrentView())
     {
-      TCollection_AsciiString aTitle("3D View - ");
-      aTitle = aTitle + ViewerTest_myViews.Find2 (ViewerTest::CurrentView());
-      SetWindowTitle (ViewerTest::CurrentView()->Window(), aTitle.ToCString());
+      aCurrentView->Window()->SetTitle (TCollection_AsciiString ("3D View - ") + ViewerTest_myViews.Find2 (aCurrentView));
     }
 
     ViewerTest::CurrentView (aView);
     ViewerTest::SetAISContext (anAISContext);
-    TCollection_AsciiString aTitle = TCollection_AsciiString("3D View - ") + theViewName + "(*)";
-    SetWindowTitle (ViewerTest::CurrentView()->Window(), aTitle.ToCString());
+    aView->Window()->SetTitle (TCollection_AsciiString("3D View - ") + theViewName + "(*)");
 #if defined(_WIN32)
     VT_GetWindow() = Handle(WNT_Window)::DownCast(ViewerTest::CurrentView()->Window());
 #elif defined(__APPLE__) && !defined(MACOSX_USE_GLX)
@@ -2559,7 +2420,7 @@ void ViewerTest::RemoveView (const TCollection_AsciiString& theViewName, const S
     }
   }
   cout << "3D View - " << theViewName << " was deleted.\n";
-  if (Draw_ToExitOnCloseView)
+  if (ViewerTest_EventManager::ToExitOnCloseView())
   {
     Draw_Interprete ("exit");
   }
@@ -2654,9 +2515,7 @@ static int VActivate (Draw_Interpretor& theDi, Standard_Integer theArgsNb, const
           && aNameString.IsEmpty()
           && anArg == "none")
     {
-      TCollection_AsciiString aTitle("3D View - ");
-      aTitle = aTitle + ViewerTest_myViews.Find2(ViewerTest::CurrentView());
-      SetWindowTitle (ViewerTest::CurrentView()->Window(), aTitle.ToCString());
+      ViewerTest::CurrentView()->Window()->SetTitle (TCollection_AsciiString ("3D View - ") + ViewerTest_myViews.Find2 (ViewerTest::CurrentView()));
       VT_GetWindow().Nullify();
       ViewerTest::CurrentView (Handle(V3d_View)());
       ViewerTest::ResetEventManager();
@@ -2772,414 +2631,20 @@ static int VViewList (Draw_Interpretor& theDi, Standard_Integer theArgsNb, const
 }
 
 //==============================================================================
-//function : VT_ProcessKeyPress
-//purpose  : Handle KeyPress event from a CString
-//==============================================================================
-void VT_ProcessKeyPress (const char* buf_ret)
-{
-  //cout << "KeyPress" << endl;
-  const Handle(V3d_View) aView = ViewerTest::CurrentView();
-  // Letter in alphabetic order
-
-  if (!strcasecmp (buf_ret, "A")
-   && !ViewerTest_V3dView::IsCurrentViewIn2DMode())
-  {
-    // AXO
-    aView->SetProj(V3d_XposYnegZpos);
-  }
-  else if (!strcasecmp (buf_ret, "D")
-        && !ViewerTest_V3dView::IsCurrentViewIn2DMode())
-  {
-    // Reset
-    aView->Reset();
-  }
-  else if (!strcasecmp (buf_ret, "F"))
-  {
-    if (ViewerTest::GetAISContext()->NbSelected() > 0)
-    {
-      ViewerTest::GetAISContext()->FitSelected (aView);
-    }
-    else
-    {
-      // FitAll
-      aView->FitAll();
-    }
-  }
-  else if (!strcasecmp (buf_ret, "H"))
-  {
-    // HLR
-    std::cout << "HLR" << std::endl;
-    aView->SetComputedMode (!aView->ComputedMode());
-    aView->Redraw();
-  }
-  else if (!strcasecmp (buf_ret, "P"))
-  {
-    // Type of HLR
-    Handle(AIS_InteractiveContext) aContext = ViewerTest::GetAISContext();
-    if (aContext->DefaultDrawer()->TypeOfHLR() == Prs3d_TOH_Algo)
-      aContext->DefaultDrawer()->SetTypeOfHLR(Prs3d_TOH_PolyAlgo);
-    else
-      aContext->DefaultDrawer()->SetTypeOfHLR(Prs3d_TOH_Algo);
-    if (aContext->NbSelected()==0)
-    {
-      AIS_ListOfInteractive aListOfShapes;
-      aContext->DisplayedObjects(aListOfShapes);
-      for (AIS_ListIteratorOfListOfInteractive anIter(aListOfShapes);
-        anIter.More(); anIter.Next())
-      {
-        Handle(AIS_Shape) aShape = Handle(AIS_Shape)::DownCast(anIter.Value());
-        if (aShape.IsNull())
-          continue;
-        if (aShape->TypeOfHLR() == Prs3d_TOH_PolyAlgo)
-          aShape->SetTypeOfHLR (Prs3d_TOH_Algo);
-        else
-          aShape->SetTypeOfHLR (Prs3d_TOH_PolyAlgo);
-        aContext->Redisplay (aShape, Standard_False);
-      }
-    }
-    else
-    {
-      for (aContext->InitSelected();aContext->MoreSelected();aContext->NextSelected())
-      {
-        Handle(AIS_Shape) aShape = Handle(AIS_Shape)::DownCast(aContext->SelectedInteractive());
-        if (aShape.IsNull())
-          continue;
-        if(aShape->TypeOfHLR() == Prs3d_TOH_PolyAlgo)
-          aShape->SetTypeOfHLR (Prs3d_TOH_Algo);
-        else
-          aShape->SetTypeOfHLR (Prs3d_TOH_PolyAlgo);
-        aContext->Redisplay (aShape, Standard_False);
-      }
-    }
-
-    aContext->UpdateCurrentViewer();
-
-  }
-  else if (!strcasecmp (buf_ret, "S"))
-  {
-    std::cout << "setup Shaded display mode" << std::endl;
-
-    Handle(AIS_InteractiveContext) Ctx = ViewerTest::GetAISContext();
-    if(Ctx->NbSelected()==0)
-      Ctx->SetDisplayMode (AIS_Shaded, Standard_True);
-    else{
-      for(Ctx->InitSelected();Ctx->MoreSelected();Ctx->NextSelected())
-        Ctx->SetDisplayMode(Ctx->SelectedInteractive(),1,Standard_False);
-      Ctx->UpdateCurrentViewer();
-    }
-  }
-  else if (!strcasecmp (buf_ret, "U"))
-  {
-    // Unset display mode
-    std::cout << "reset display mode to defaults" << std::endl;
-
-    Handle(AIS_InteractiveContext) Ctx = ViewerTest::GetAISContext();
-    if(Ctx->NbSelected()==0)
-      Ctx->SetDisplayMode (AIS_WireFrame, Standard_True);
-    else{
-      for(Ctx->InitSelected();Ctx->MoreSelected();Ctx->NextSelected())
-        Ctx->UnsetDisplayMode(Ctx->SelectedInteractive(),Standard_False);
-      Ctx->UpdateCurrentViewer();
-    }
-
-  }
-  else if (!strcasecmp (buf_ret, "T")
-        && !ViewerTest_V3dView::IsCurrentViewIn2DMode())
-  {
-    // Top
-    aView->SetProj(V3d_Zpos);
-  }
-  else if (!strcasecmp (buf_ret, "B")
-        && !ViewerTest_V3dView::IsCurrentViewIn2DMode())
-  {
-    // Bottom
-    aView->SetProj(V3d_Zneg);
-  }
-  else if (!strcasecmp (buf_ret, "L")
-        && !ViewerTest_V3dView::IsCurrentViewIn2DMode())
-  {
-    // Left
-    aView->SetProj(V3d_Xneg);
-  }
-  else if (!strcasecmp (buf_ret, "R")
-        && !ViewerTest_V3dView::IsCurrentViewIn2DMode())
-  {
-    // Right
-    aView->SetProj(V3d_Xpos);
-  }
-  else if (!strcasecmp (buf_ret, "W"))
-  {
-    std::cout << "setup WireFrame display mode" << std::endl;
-    Handle(AIS_InteractiveContext) Ctx = ViewerTest::GetAISContext();
-    if(Ctx->NbSelected()==0)
-      Ctx->SetDisplayMode (AIS_WireFrame, Standard_True);
-    else{
-      for(Ctx->InitSelected();Ctx->MoreSelected();Ctx->NextSelected())
-        Ctx->SetDisplayMode(Ctx->SelectedInteractive(),0,Standard_False);
-      Ctx->UpdateCurrentViewer();
-    }
-  }
-  else if (!strcasecmp (buf_ret, ","))
-  {
-    ViewerTest::GetAISContext()->HilightNextDetected(ViewerTest::CurrentView());
-  }
-  else if (!strcasecmp (buf_ret, "."))
-  {
-    ViewerTest::GetAISContext()->HilightPreviousDetected(ViewerTest::CurrentView());
-  }
-  else if (!strcasecmp (buf_ret, "/"))
-  {
-    Handle(Graphic3d_Camera) aCamera = aView->Camera();
-    if (aCamera->IsStereo())
-    {
-      aCamera->SetIOD (aCamera->GetIODType(), aCamera->IOD() - 0.01);
-      aView->Redraw();
-    }
-  }
-  else if (!strcasecmp (buf_ret, "*"))
-  {
-    Handle(Graphic3d_Camera) aCamera = aView->Camera();
-    if (aCamera->IsStereo())
-    {
-      aCamera->SetIOD (aCamera->GetIODType(), aCamera->IOD() + 0.01);
-      aView->Redraw();
-    }
-  }
-  else if (*buf_ret == THE_KEY_DELETE)
-  {
-    Handle(AIS_InteractiveContext) aCtx = ViewerTest::GetAISContext();
-    if (!aCtx.IsNull()
-     && aCtx->NbSelected() > 0)
-    {
-      Draw_Interprete ("verase");
-    }
-  }
-  else if (*buf_ret == THE_KEY_ESCAPE)
-  {
-    Handle(AIS_InteractiveContext) aCtx = ViewerTest::GetAISContext();
-    if (!aCtx.IsNull()
-     && Draw_ToCloseViewOnEsc)
-    {
-      Draw_Interprete (Draw_ToExitOnCloseView ? "exit" : "vclose");
-    }
-  }
-  else if (*buf_ret >= '0' && *buf_ret <= '7') // Number
-  {
-    const Standard_Integer aSelMode = Draw::Atoi (buf_ret);
-    bool toEnable = true;
-    if (const Handle(AIS_InteractiveContext)& aCtx = ViewerTest::GetAISContext())
-    {
-      AIS_ListOfInteractive aPrsList;
-      aCtx->DisplayedObjects (aPrsList);
-      for (AIS_ListOfInteractive::Iterator aPrsIter (aPrsList); aPrsIter.More() && toEnable; aPrsIter.Next())
-      {
-        TColStd_ListOfInteger aModes;
-        aCtx->ActivatedModes (aPrsIter.Value(), aModes);
-        for (TColStd_ListOfInteger::Iterator aModeIter (aModes); aModeIter.More() && toEnable; aModeIter.Next())
-        {
-          if (aModeIter.Value() == aSelMode)
-          {
-            toEnable = false;
-          }
-        }
-      }
-    }
-    TCollection_AsciiString aCmd = TCollection_AsciiString ("vselmode ") + aSelMode + (toEnable ? " 1" : " 0");
-    Draw_Interprete (aCmd.ToCString());
-  }
-}
-
-//==============================================================================
-//function : VT_ProcessExpose
-//purpose  : Redraw the View on an Expose Event
-//==============================================================================
-void VT_ProcessExpose()
-{
-  Handle(V3d_View) aView3d = ViewerTest::CurrentView();
-  if (!aView3d.IsNull())
-  {
-    aView3d->Redraw();
-  }
-}
-
-//==============================================================================
-//function : VT_ProcessConfigure
-//purpose  : Resize the View on an Configure Event
-//==============================================================================
-void VT_ProcessConfigure()
-{
-  Handle(V3d_View) aView3d = ViewerTest::CurrentView();
-  if (aView3d.IsNull())
-  {
-    return;
-  }
-
-  aView3d->MustBeResized();
-  aView3d->Update();
-  aView3d->Redraw();
-}
-
-//==============================================================================
-//function : VT_ProcessButton1Press
-//purpose  : Picking
-//==============================================================================
-Standard_Boolean VT_ProcessButton1Press (Standard_Integer ,
-                                         const char**     theArgVec,
-                                         Standard_Boolean theToPick,
-                                         Standard_Boolean theIsShift)
-{
-  if (TheIsAnimating)
-  {
-    TheIsAnimating = Standard_False;
-    return Standard_False;
-  }
-
-  if (theToPick)
-  {
-    Standard_Real X, Y, Z;
-    ViewerTest::CurrentView()->Convert (X_Motion, Y_Motion, X, Y, Z);
-
-    Draw::Set (theArgVec[1], X);
-    Draw::Set (theArgVec[2], Y);
-    Draw::Set (theArgVec[3], Z);
-  }
-
-  if (theIsShift)
-  {
-    ViewerTest::CurrentEventManager()->ShiftSelect();
-  }
-  else
-  {
-    ViewerTest::CurrentEventManager()->Select();
-  }
-
-  return Standard_False;
-}
-
-//==============================================================================
-//function : VT_ProcessButton1Release
-//purpose  : End selecting
-//==============================================================================
-void VT_ProcessButton1Release (Standard_Boolean theIsShift)
-{
-  if (IsDragged)
-  {
-    IsDragged = Standard_False;
-    Handle(ViewerTest_EventManager) EM = ViewerTest::CurrentEventManager();
-    if (theIsShift)
-    {
-      EM->ShiftSelect (X_ButtonPress, Y_ButtonPress,
-                       X_Motion, Y_Motion);
-    }
-    else
-    {
-      EM->Select (X_ButtonPress, Y_ButtonPress,
-                  X_Motion, Y_Motion);
-    }
-  }
-}
-
-//==============================================================================
-//function : VT_ProcessButton3Press
-//purpose  : Start Rotation
-//==============================================================================
-void VT_ProcessButton3Press()
-{
-  if (ViewerTest_V3dView::IsCurrentViewIn2DMode())
-  {
-    return;
-  }
-
-  Start_Rot = 1;
-  HasHlrOnBeforeRotation = ViewerTest::CurrentView()->ComputedMode();
-  if (HasHlrOnBeforeRotation)
-  {
-    ViewerTest::CurrentView()->SetComputedMode (Standard_False);
-  }
-  ViewerTest::CurrentView()->StartRotation( X_ButtonPress, Y_ButtonPress );
-}
-
-//==============================================================================
-//function : VT_ProcessButton3Release
-//purpose  : End rotation
-//==============================================================================
-void VT_ProcessButton3Release()
-{
-  if (Start_Rot)
-  {
-    Start_Rot = 0;
-    if (HasHlrOnBeforeRotation)
-    {
-      HasHlrOnBeforeRotation = Standard_False;
-      ViewerTest::CurrentView()->SetComputedMode (Standard_True);
-      ViewerTest::CurrentView()->Redraw();
-    }
-  }
-}
-
-//==============================================================================
-//function : ProcessControlButton1Motion
-//purpose  : Zoom
-//==============================================================================
-
-#if defined(_WIN32) || ! defined(__APPLE__) || defined(MACOSX_USE_GLX)
-static void ProcessControlButton1Motion()
-{
-  ViewerTest::CurrentView()->Zoom( X_ButtonPress, Y_ButtonPress, X_Motion, Y_Motion);
-
-  X_ButtonPress = X_Motion;
-  Y_ButtonPress = Y_Motion;
-}
-#endif
-
-//==============================================================================
-//function : VT_ProcessControlButton2Motion
-//purpose  : Panning
-//==============================================================================
-void VT_ProcessControlButton2Motion()
-{
-  Standard_Integer aDx = X_Motion - X_ButtonPress;
-  Standard_Integer aDy = Y_Motion - Y_ButtonPress;
-
-  aDy = -aDy; // Xwindow Y axis is from top to Bottom
-
-  ViewerTest::CurrentView()->Pan (aDx, aDy);
-
-  X_ButtonPress = X_Motion;
-  Y_ButtonPress = Y_Motion;
-}
-
-//==============================================================================
-//function : VT_ProcessControlButton3Motion
-//purpose  : Rotation
+//function : GetMousePosition
+//purpose  :
 //==============================================================================
-void VT_ProcessControlButton3Motion()
+void ViewerTest::GetMousePosition (Standard_Integer& theX,
+                                   Standard_Integer& theY)
 {
-  if (Start_Rot)
+  if (Handle(ViewerTest_EventManager) aViewCtrl = ViewerTest::CurrentEventManager())
   {
-    ViewerTest::CurrentView()->Rotation (X_Motion, Y_Motion);
+    theX = aViewCtrl->LastMousePosition().x();
+    theY = aViewCtrl->LastMousePosition().y();
   }
 }
 
 //==============================================================================
-//function : VT_ProcessMotion
-//purpose  :
-//==============================================================================
-void VT_ProcessMotion()
-{
-  //pre-hilights detected objects at mouse position
-
-  Handle(ViewerTest_EventManager) EM = ViewerTest::CurrentEventManager();
-  EM->MoveTo(X_Motion, Y_Motion);
-}
-
-
-void ViewerTest::GetMousePosition(Standard_Integer& Xpix,Standard_Integer& Ypix)
-{
-  Xpix = X_Motion;Ypix=Y_Motion;
-}
-
-//==============================================================================
 //function : VViewProj
 //purpose  : Switch view projection
 //==============================================================================
@@ -3491,9 +2956,6 @@ static int VViewProj (Draw_Interpretor& ,
 
 static int VHelp(Draw_Interpretor& di, Standard_Integer , const char** )
 {
-
-  di << "Q : Quit the application\n";
-
   di << "=========================\n";
   di << "F : FitAll\n";
   di << "T : TopView\n";
@@ -3506,7 +2968,7 @@ static int VHelp(Draw_Interpretor& di, Standard_Integer , const char** )
   di << "=========================\n";
   di << "S : Shading\n";
   di << "W : Wireframe\n";
-  di << "H : HidelLineRemoval\n";
+  di << "H : HiddenLineRemoval\n";
   di << "U : Unset display mode\n";
   di << "Delete : Remove selection from viewer\n";
 
@@ -3522,338 +2984,220 @@ static int VHelp(Draw_Interpretor& di, Standard_Integer , const char** )
   di << "7 : Compound\n";
 
   di << "=========================\n";
-  di << "Z : Switch Z clipping On/Off\n";
-  di << ", : Hilight next detected\n";
-  di << ". : Hilight previous detected\n";
+  di << "< : Hilight next detected\n";
+  di << "> : Hilight previous detected\n";
 
   return 0;
 }
 
 #ifdef _WIN32
 
-static Standard_Boolean Ppick = 0;
-static Standard_Integer Pargc = 0;
-static const char**           Pargv = NULL;
-
-
-static LRESULT WINAPI AdvViewerWindowProc( HWND hwnd,
-                                          UINT Msg,
-                                          WPARAM wParam,
-                                          LPARAM lParam )
+static LRESULT WINAPI AdvViewerWindowProc (HWND theWinHandle,
+                                           UINT theMsg,
+                                           WPARAM wParam,
+                                           LPARAM lParam )
 {
-  if (!ViewerTest_myViews.IsEmpty()) {
-
-    WPARAM fwKeys = wParam;
+  if (ViewerTest_myViews.IsEmpty())
+  {
+    return ViewerWindowProc (theWinHandle, theMsg, wParam, lParam);
+  }
 
-    switch( Msg ) {
+  switch (theMsg)
+  {
     case WM_CLOSE:
-       {
-         // Delete view from map of views
-         ViewerTest::RemoveView(FindViewIdByWindowHandle(hwnd));
-         return 0;
-       }
-       break;
+    {
+      // Delete view from map of views
+      ViewerTest::RemoveView (FindViewIdByWindowHandle (theWinHandle));
+      return 0;
+    }
     case WM_ACTIVATE:
-      if(LOWORD(wParam) == WA_CLICKACTIVE || LOWORD(wParam) == WA_ACTIVE
-        || ViewerTest::CurrentView().IsNull())
+    {
+      if (LOWORD(wParam) == WA_CLICKACTIVE
+       || LOWORD(wParam) == WA_ACTIVE
+       || ViewerTest::CurrentView().IsNull())
       {
         // Activate inactive window
-        if(GetWindowHandle(VT_GetWindow()) != hwnd)
-        {
-          ActivateView (FindViewIdByWindowHandle(hwnd));
-        }
-      }
-      break;
-
-    case WM_LBUTTONUP:
-      if (IsDragged && !DragFirst)
-      {
-        if (!GetActiveAISManipulator().IsNull())
-        {
-          GetActiveAISManipulator()->StopTransform();
-          ViewerTest::GetAISContext()->ClearSelected (Standard_True);
-        }
-
-        if (ViewerTest::GetAISContext()->IsDisplayed (GetRubberBand()))
-        {
-          ViewerTest::GetAISContext()->Remove (GetRubberBand(), Standard_False);
-          ViewerTest::GetAISContext()->CurrentViewer()->RedrawImmediate();
-        }
-
-        VT_ProcessButton1Release ((fwKeys & MK_SHIFT) != 0);
-      }
-      IsDragged = Standard_False;
-      return ViewerWindowProc( hwnd, Msg, wParam, lParam );
-
-    case WM_RBUTTONUP:
-      if (IsDragged && !DragFirst)
-      {
-        if (!GetActiveAISManipulator().IsNull())
+        if (VT_GetWindow().IsNull()
+         || (HWND )VT_GetWindow()->HWindow() != theWinHandle)
         {
-          GetActiveAISManipulator()->StopTransform (Standard_False);
-          ViewerTest::GetAISContext()->ClearSelected (Standard_True);
+          ActivateView (FindViewIdByWindowHandle (theWinHandle));
         }
-        IsDragged = Standard_False;
-      }
-      return ViewerWindowProc (hwnd, Msg, wParam, lParam);
-
-    case WM_LBUTTONDOWN:
-      if (!GetActiveAISManipulator().IsNull())
-      {
-        IsDragged = ( fwKeys == MK_LBUTTON );
-      }
-      else
-      {
-        IsDragged = ( fwKeys == MK_LBUTTON || fwKeys == ( MK_LBUTTON | MK_SHIFT ) );
-      }
-
-      if (IsDragged)
-      {
-        DragFirst = Standard_True;
-        X_ButtonPress = LOWORD(lParam);
-        Y_ButtonPress = HIWORD(lParam);
-      }
-      return ViewerWindowProc( hwnd, Msg, wParam, lParam );
-
-    case WM_MOUSEMOVE:
-      if (IsDragged)
-      {
-        X_Motion = LOWORD (lParam);
-        Y_Motion = HIWORD (lParam);
-        if (!GetActiveAISManipulator().IsNull())
-        {
-          if (DragFirst)
-          {
-            GetActiveAISManipulator()->StartTransform (X_ButtonPress, Y_ButtonPress, ViewerTest::CurrentView());
-          }
-          else
-          {
-            GetActiveAISManipulator()->Transform (X_Motion, Y_Motion, ViewerTest::CurrentView());
-            ViewerTest::GetAISContext()->CurrentViewer()->Redraw();
-          }
-        }
-        else
-        {
-          bool toRedraw = false;
-          if (!DragFirst && ViewerTest::GetAISContext()->IsDisplayed (GetRubberBand()))
-          {
-            ViewerTest::GetAISContext()->Remove (GetRubberBand(), Standard_False);
-            toRedraw = true;
-          }
-
-          RECT aRect;
-          if (GetClientRect (hwnd, &aRect))
-          {
-            int aHeight = aRect.bottom - aRect.top;
-            GetRubberBand()->SetRectangle (X_ButtonPress, aHeight - Y_ButtonPress, X_Motion, aHeight - Y_Motion);
-            ViewerTest::GetAISContext()->Display (GetRubberBand(), 0, -1, Standard_False, AIS_DS_Displayed);
-            toRedraw = true;
-          }
-          if (toRedraw)
-          {
-            ViewerTest::GetAISContext()->CurrentViewer()->RedrawImmediate();
-          }
-        }
-
-        DragFirst = Standard_False;
       }
-      else
-        return ViewerWindowProc( hwnd, Msg, wParam, lParam );
       break;
-
+    }
     default:
-      return ViewerWindowProc( hwnd, Msg, wParam, lParam );
+    {
+      return ViewerWindowProc (theWinHandle, theMsg, wParam, lParam);
     }
-    return 0;
   }
-  return ViewerWindowProc( hwnd, Msg, wParam, lParam );
+  return 0;
 }
 
-
-static LRESULT WINAPI ViewerWindowProc( HWND hwnd,
-                                       UINT Msg,
-                                       WPARAM wParam,
-                                       LPARAM lParam )
+static LRESULT WINAPI ViewerWindowProc (HWND theWinHandle,
+                                        UINT theMsg,
+                                        WPARAM wParam,
+                                        LPARAM lParam)
 {
-  static int Up = 1;
   const Handle(V3d_View)& aView = ViewerTest::CurrentView();
   if (aView.IsNull())
   {
-    return DefWindowProcW (hwnd, Msg, wParam, lParam);
+    return DefWindowProcW (theWinHandle, theMsg, wParam, lParam);
   }
 
-    PAINTSTRUCT    ps;
-
-    switch( Msg ) {
+  switch (theMsg)
+  {
     case WM_PAINT:
-      BeginPaint(hwnd, &ps);
-      EndPaint(hwnd, &ps);
-      VT_ProcessExpose();
+    {
+      PAINTSTRUCT aPaint;
+      BeginPaint(theWinHandle, &aPaint);
+      EndPaint  (theWinHandle, &aPaint);
+      ViewerTest::CurrentEventManager()->ProcessExpose();
       break;
-
+    }
     case WM_SIZE:
-      VT_ProcessConfigure();
+    {
+      ViewerTest::CurrentEventManager()->ProcessConfigure();
       break;
+    }
     case WM_MOVE:
     case WM_MOVING:
     case WM_SIZING:
+    {
       switch (aView->RenderingParams().StereoMode)
       {
         case Graphic3d_StereoMode_RowInterlaced:
         case Graphic3d_StereoMode_ColumnInterlaced:
         case Graphic3d_StereoMode_ChessBoard:
-          VT_ProcessConfigure(); // track window moves to reverse stereo pair
+        {
+          // track window moves to reverse stereo pair
+          aView->MustBeResized();
+          aView->Update();
           break;
+        }
         default:
           break;
       }
       break;
-
+    }
+    case WM_KEYUP:
     case WM_KEYDOWN:
-      if ((wParam != VK_SHIFT) && (wParam != VK_CONTROL))
+    {
+      const Aspect_VKey aVKey = WNT_Window::VirtualKeyFromNative ((Standard_Integer )wParam);
+      if (aVKey != Aspect_VKey_UNKNOWN)
       {
-        char c[2];
-        c[0] = (char) wParam;
-        c[1] = '\0';
-        if (wParam == VK_DELETE)
-        {
-          c[0] = THE_KEY_DELETE;
-        }
-        else if (wParam == VK_ESCAPE)
-        {
-          c[0] = THE_KEY_ESCAPE;
-        }
-        // comma
-        else if (wParam == VK_OEM_COMMA)
-        {
-          c[0] = ',';
-        }
-        // dot
-        else if (wParam == VK_OEM_PERIOD)
-        {
-          c[0] = '.';
-        }
-        else if (wParam == VK_DIVIDE)
+        const double aTimeStamp = ViewerTest::CurrentEventManager()->EventTime();
+        if (theMsg == WM_KEYDOWN)
         {
-          c[0] = '/';
+          ViewerTest::CurrentEventManager()->KeyDown (aVKey, aTimeStamp);
         }
-        // dot
-        else if (wParam == VK_MULTIPLY)
+        else
         {
-          c[0] = '*';
+          ViewerTest::CurrentEventManager()->KeyUp (aVKey, aTimeStamp);
         }
-        VT_ProcessKeyPress (c);
+        ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), ViewerTest::CurrentView(), true);
       }
       break;
-
+    }
     case WM_LBUTTONUP:
     case WM_MBUTTONUP:
     case WM_RBUTTONUP:
-      Up = 1;
-      VT_ProcessButton3Release();
-      break;
-
     case WM_LBUTTONDOWN:
     case WM_MBUTTONDOWN:
     case WM_RBUTTONDOWN:
+    {
+      const Graphic3d_Vec2i aPos (LOWORD(lParam), HIWORD(lParam));
+      const Aspect_VKeyFlags aFlags = WNT_Window::MouseKeyFlagsFromEvent (wParam);
+      Aspect_VKeyMouse aButton = Aspect_VKeyMouse_NONE;
+      switch (theMsg)
       {
-        WPARAM fwKeys = wParam;
-
-        Up = 0;
-
-        X_ButtonPress = LOWORD(lParam);
-        Y_ButtonPress = HIWORD(lParam);
-
-        if (Msg == WM_LBUTTONDOWN)
-        {
-          if ((fwKeys & MK_CONTROL) != 0)
-          {
-            Ppick = VT_ProcessButton1Press (Pargc, Pargv, Ppick, (fwKeys & MK_SHIFT) != 0);
-          }
-          else
-          {
-            VT_ProcessButton1Press (Pargc, Pargv, Ppick, (fwKeys & MK_SHIFT) != 0);
-          }
-        }
-        else if (Msg == WM_RBUTTONDOWN)
-        {
-          // Start rotation
-          VT_ProcessButton3Press();
-        }
+        case WM_LBUTTONUP:
+        case WM_LBUTTONDOWN:
+          aButton = Aspect_VKeyMouse_LeftButton;
+          break;
+        case WM_MBUTTONUP:
+        case WM_MBUTTONDOWN:
+          aButton = Aspect_VKeyMouse_MiddleButton;
+          break;
+        case WM_RBUTTONUP:
+        case WM_RBUTTONDOWN:
+          aButton = Aspect_VKeyMouse_RightButton;
+          break;
       }
-      break;
-
-    case WM_MOUSEWHEEL:
-    {
-      int aDelta = GET_WHEEL_DELTA_WPARAM (wParam);
-      if (wParam & MK_CONTROL)
+      if (theMsg == WM_LBUTTONDOWN
+       || theMsg == WM_MBUTTONDOWN
+       || theMsg == WM_RBUTTONDOWN)
       {
-        if (aView->Camera()->IsStereo())
+        if (aButton == Aspect_VKeyMouse_LeftButton)
         {
-          Standard_Real aFocus = aView->Camera()->ZFocus() + (aDelta > 0 ? 0.05 : -0.05);
-          if (aFocus > 0.2
-           && aFocus < 2.0)
-          {
-            aView->Camera()->SetZFocus (aView->Camera()->ZFocusType(), aFocus);
-            aView->Redraw();
-          }
+          TheIsAnimating = Standard_False;
         }
+
+        SetFocus  (theWinHandle);
+        SetCapture(theWinHandle);
+        ViewerTest::CurrentEventManager()->PressMouseButton (aPos, aButton, aFlags, false);
       }
       else
       {
-        aView->Zoom (0, 0, aDelta / 40, aDelta / 40);
+        ReleaseCapture();
+        ViewerTest::CurrentEventManager()->ReleaseMouseButton (aPos, aButton, aFlags, false);
       }
+      ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), ViewerTest::CurrentView(), true);
       break;
     }
-
-    case WM_MOUSEMOVE:
+    case WM_MOUSEWHEEL:
+    {
+      const int aDelta = GET_WHEEL_DELTA_WPARAM (wParam);
+      const Standard_Real aDeltaF = Standard_Real(aDelta) / Standard_Real(WHEEL_DELTA);
+      const Aspect_VKeyFlags aFlags = WNT_Window::MouseKeyFlagsFromEvent (wParam);
+      Graphic3d_Vec2i aPos (int(short(LOWORD(lParam))), int(short(HIWORD(lParam))));
+      POINT aCursorPnt = { aPos.x(), aPos.y() };
+      if (ScreenToClient (theWinHandle, &aCursorPnt))
       {
-        //cout << "\t WM_MOUSEMOVE" << endl;
-        WPARAM fwKeys = wParam;
-        X_Motion = LOWORD(lParam);
-        Y_Motion = HIWORD(lParam);
+        aPos.SetValues (aCursorPnt.x, aCursorPnt.y);
+      }
 
-        if ( Up &&
-          (fwKeys & ( MK_LBUTTON|MK_MBUTTON|MK_RBUTTON )) != 0 )
-          {
-            Up = 0;
-            X_ButtonPress = LOWORD(lParam);
-            Y_ButtonPress = HIWORD(lParam);
+      ViewerTest::CurrentEventManager()->UpdateMouseScroll (Aspect_ScrollDelta (aPos, aDeltaF, aFlags));
+      ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), ViewerTest::CurrentView(), true);
+      break;
+    }
+    case WM_MOUSEMOVE:
+    {
+      Graphic3d_Vec2i aPos (LOWORD(lParam), HIWORD(lParam));
+      Aspect_VKeyMouse aButtons = WNT_Window::MouseButtonsFromEvent (wParam);
+      Aspect_VKeyFlags aFlags   = WNT_Window::MouseKeyFlagsFromEvent(wParam);
 
-            if ((fwKeys & MK_RBUTTON) != 0) {
-              // Start rotation
-              VT_ProcessButton3Press();
-            }
-          }
+      // don't make a slide-show from input events - fetch the actual mouse cursor position
+      CURSORINFO aCursor;
+      aCursor.cbSize = sizeof(aCursor);
+      if (::GetCursorInfo (&aCursor) != FALSE)
+      {
+        POINT aCursorPnt = { aCursor.ptScreenPos.x, aCursor.ptScreenPos.y };
+        if (ScreenToClient (theWinHandle, &aCursorPnt))
+        {
+          // as we override mouse position, we need overriding also mouse state
+          aPos.SetValues (aCursorPnt.x, aCursorPnt.y);
+          aButtons = WNT_Window::MouseButtonsAsync();
+          aFlags   = WNT_Window::MouseKeyFlagsAsync();
+        }
+      }
 
-          if ((fwKeys & MK_CONTROL) != 0)
-          {
-            if ((fwKeys & MK_LBUTTON) != 0)
-            {
-              ProcessControlButton1Motion();
-            }
-            else if ((fwKeys & MK_MBUTTON) != 0
-                 || ((fwKeys & MK_LBUTTON) != 0
-                  && (fwKeys & MK_RBUTTON) != 0))
-            {
-              VT_ProcessControlButton2Motion();
-            }
-            else if ((fwKeys & MK_RBUTTON) != 0)
-            {
-              VT_ProcessControlButton3Motion();
-            }
-          }
-          else if (GetWindowHandle (VT_GetWindow()) == hwnd)
-          {
-            VT_ProcessMotion();
-          }
+      if (VT_GetWindow().IsNull()
+      || (HWND )VT_GetWindow()->HWindow() != theWinHandle)
+      {
+        // mouse move events come also for inactive windows
+        break;
       }
-      break;
 
+      ViewerTest::CurrentEventManager()->UpdateMousePosition (aPos, aButtons, aFlags, false);
+      ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), aView, true);
+      break;
+    }
     default:
-      return DefWindowProcW (hwnd, Msg, wParam, lParam);
+    {
+      return DefWindowProcW (theWinHandle, theMsg, wParam, lParam);
     }
-    return 0L;
+  }
+  return 0L;
 }
 
 //==============================================================================
@@ -3861,32 +3205,33 @@ static LRESULT WINAPI ViewerWindowProc( HWND hwnd,
 //purpose  : Get a Event on the view and dispatch it
 //==============================================================================
 
-
-int ViewerMainLoop(Standard_Integer argc, const char** argv)
+int ViewerMainLoop (Standard_Integer theNbArgs, const char** theArgVec)
 {
-  Ppick = (argc > 0)? 1 : 0;
-  Pargc = argc;
-  Pargv = argv;
+  Handle(ViewerTest_EventManager) aViewCtrl = ViewerTest::CurrentEventManager();
+  if (aViewCtrl.IsNull()
+   || theNbArgs < 4)
+  {
+    return 0;
+  }
 
-  if ( Ppick ) {
-    MSG msg;
-    msg.wParam = 1;
+  aViewCtrl->StartPickPoint (theArgVec[1], theArgVec[2], theArgVec[3]);
 
-    cout << "Start picking" << endl;
+  std::cout << "Start picking\n";
 
-    while ( Ppick == 1 ) {
-      // Wait for a VT_ProcessButton1Press() to toggle pick to 1 or 0
-      if (GetMessageW (&msg, NULL, 0, 0))
-      {
-        TranslateMessage (&msg);
-        DispatchMessageW (&msg);
-      }
+  MSG aMsg;
+  aMsg.wParam = 1;
+  while (aViewCtrl->ToPickPoint())
+  {
+    // Wait for a VT_ProcessButton1Press() to toggle pick to 1 or 0
+    if (GetMessageW (&aMsg, NULL, 0, 0))
+    {
+      TranslateMessage (&aMsg);
+      DispatchMessageW (&aMsg);
     }
-
-    cout << "Picking done" << endl;
   }
 
-  return Ppick;
+  std::cout << "Picking done\n";
+  return 0;
 }
 
 #elif !defined(__APPLE__) || defined(MACOSX_USE_GLX)
@@ -3907,11 +3252,19 @@ int max( int a, int b )
     return b;
 }
 
-int ViewerMainLoop (Standard_Integer argc, const char** argv)
+int ViewerMainLoop (Standard_Integer theNbArgs, const char** theArgVec)
 {
   static XEvent aReport;
-  const Standard_Boolean toPick = argc > 0;
-  Standard_Boolean toPickMore = toPick;
+  const Standard_Boolean toPick = theNbArgs > 0;
+  if (theNbArgs > 0)
+  {
+    if (ViewerTest::CurrentEventManager().IsNull())
+    {
+      return 0;
+    }
+    ViewerTest::CurrentEventManager()->StartPickPoint (theArgVec[1], theArgVec[2], theArgVec[3]);
+  }
+
   Display* aDisplay = GetDisplayConnection()->GetDisplay();
   XNextEvent (aDisplay, &aReport);
 
@@ -3931,7 +3284,7 @@ int ViewerMainLoop (Standard_Integer argc, const char** argv)
     case FocusIn:
     {
       // Activate inactive view
-      Window aWindow = GetWindowHandle(VT_GetWindow());
+      Window aWindow = !VT_GetWindow().IsNull() ? VT_GetWindow()->XWindow() : 0;
       if (aWindow != aReport.xfocus.window)
       {
         ActivateView (FindViewIdByWindowHandle (aReport.xfocus.window));
@@ -3940,10 +3293,10 @@ int ViewerMainLoop (Standard_Integer argc, const char** argv)
     }
     case Expose:
     {
-      Window anXWindow = GetWindowHandle (VT_GetWindow());
+      Window anXWindow = !VT_GetWindow().IsNull() ? VT_GetWindow()->XWindow() : 0;
       if (anXWindow == aReport.xexpose.window)
       {
-        VT_ProcessExpose();
+        ViewerTest::CurrentEventManager()->ProcessExpose();
       }
 
       // remove all the ExposureMask and process them at once
@@ -3960,7 +3313,7 @@ int ViewerMainLoop (Standard_Integer argc, const char** argv)
     case ConfigureNotify:
     {
       // remove all the StructureNotifyMask and process them at once
-      Window anXWindow = GetWindowHandle (VT_GetWindow());
+      Window anXWindow = !VT_GetWindow().IsNull() ? VT_GetWindow()->XWindow() : 0;
       for (int aNbMaxEvents = XPending (aDisplay); aNbMaxEvents > 0; --aNbMaxEvents)
       {
         if (!XCheckWindowEvent (aDisplay, anXWindow, StructureNotifyMask, &aReport))
@@ -3971,106 +3324,92 @@ int ViewerMainLoop (Standard_Integer argc, const char** argv)
 
       if (anXWindow == aReport.xconfigure.window)
       {
-        VT_ProcessConfigure();
+        ViewerTest::CurrentEventManager()->ProcessConfigure();
       }
       break;
     }
     case KeyPress:
+    case KeyRelease:
     {
-      KeySym aKeySym;
-      char aKeyBuf[11];
-      XComposeStatus status_in_out;
-      int aKeyLen = XLookupString ((XKeyEvent* )&aReport, (char* )aKeyBuf, 10, &aKeySym, &status_in_out);
-      aKeyBuf[aKeyLen] = '\0';
-      if (aKeyLen != 0)
-      {
-        VT_ProcessKeyPress (aKeyBuf);
-      }
-      break;
-    }
-    case ButtonPress:
-    {
-      X_ButtonPress = aReport.xbutton.x;
-      Y_ButtonPress = aReport.xbutton.y;
-      if (aReport.xbutton.button == Button1)
+      XKeyEvent*   aKeyEvent = (XKeyEvent* )&aReport;
+      const KeySym aKeySym = XLookupKeysym (aKeyEvent, 0);
+      const Aspect_VKey aVKey = Xw_Window::VirtualKeyFromNative (aKeySym);
+      if (aVKey != Aspect_VKey_UNKNOWN)
       {
-        if (aReport.xbutton.state & ControlMask)
+        const double aTimeStamp = ViewerTest::CurrentEventManager()->EventTime();
+        if (aReport.type == KeyPress)
         {
-          toPickMore = VT_ProcessButton1Press (argc, argv, toPick, (aReport.xbutton.state & ShiftMask) != 0);
+          ViewerTest::CurrentEventManager()->KeyDown (aVKey, aTimeStamp);
         }
         else
         {
-          IsDragged = Standard_True;
-          DragFirst = Standard_True;
+          ViewerTest::CurrentEventManager()->KeyUp (aVKey, aTimeStamp);
         }
-      }
-      else if (aReport.xbutton.button == Button3)
-      {
-        // Start rotation
-        VT_ProcessButton3Press();
+        ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), ViewerTest::CurrentView(), true);
       }
       break;
     }
+    case ButtonPress:
     case ButtonRelease:
     {
-      if (!IsDragged)
+      const Graphic3d_Vec2i aPos (aReport.xbutton.x, aReport.xbutton.y);
+      Aspect_VKeyFlags aFlags = Aspect_VKeyFlags_NONE;
+      Aspect_VKeyMouse aButton = Aspect_VKeyMouse_NONE;
+      if (aReport.xbutton.button == Button1)
       {
-        VT_ProcessButton3Release();
-        break;
+        aButton = Aspect_VKeyMouse_LeftButton;
       }
-
-      Handle(AIS_InteractiveContext) aContext = ViewerTest::GetAISContext();
-      if (aContext.IsNull())
+      if (aReport.xbutton.button == Button2)
       {
-        std::cout << "Error: No active view.\n";
-        return 0;
+        aButton = Aspect_VKeyMouse_MiddleButton;
       }
-
-      if (!DragFirst
-        && aContext->IsDisplayed (GetRubberBand()))
+      if (aReport.xbutton.button == Button3)
       {
-        aContext->Remove (GetRubberBand(), Standard_False);
-        aContext->CurrentViewer()->RedrawImmediate();
+        aButton = Aspect_VKeyMouse_RightButton;
       }
 
-      if (aReport.xbutton.button != Button1)
+      if (aReport.xbutton.state & ControlMask)
       {
-        break;
+        aFlags |= Aspect_VKeyFlags_CTRL;
+      }
+      if (aReport.xbutton.state & ShiftMask)
+      {
+        aFlags |= Aspect_VKeyFlags_SHIFT;
+      }
+      if (ViewerTest::CurrentEventManager()->Keys().IsKeyDown (Aspect_VKey_Alt))
+      {
+        aFlags |= Aspect_VKeyFlags_ALT;
       }
 
-      const Standard_Boolean isShiftPressed = (aReport.xbutton.state & ShiftMask) != 0;
-      if (DragFirst)
+      if (aReport.xbutton.button == Button4
+       || aReport.xbutton.button == Button5)
       {
-        if (isShiftPressed)
+        if (aReport.type != ButtonPress)
         {
-          aContext->ShiftSelect (Standard_True);
+          break;
         }
-        else
+
+        const double aDeltaF = (aReport.xbutton.button == Button4 ? 1.0 : -1.0);
+        ViewerTest::CurrentEventManager()->UpdateMouseScroll (Aspect_ScrollDelta (aPos, aDeltaF, aFlags));
+      }
+      else if (aReport.type == ButtonPress)
+      {
+        if (aButton == Aspect_VKeyMouse_LeftButton)
         {
-          aContext->Select (Standard_True);
+          TheIsAnimating = Standard_False;
         }
+        ViewerTest::CurrentEventManager()->PressMouseButton (aPos, aButton, aFlags, false);
       }
       else
       {
-        if (isShiftPressed)
-        {
-          aContext->ShiftSelect (Min (X_ButtonPress, X_Motion), Min (Y_ButtonPress, Y_Motion),
-                                 Max (X_ButtonPress, X_Motion), Max (Y_ButtonPress, Y_Motion),
-                                 ViewerTest::CurrentView(), Standard_True);
-        }
-        else
-        {
-          aContext->Select (Min (X_ButtonPress, X_Motion), Min(Y_ButtonPress, Y_Motion),
-                            Max (X_ButtonPress, X_Motion), Max(Y_ButtonPress, Y_Motion),
-                            ViewerTest::CurrentView(), Standard_True);
-        }
+        ViewerTest::CurrentEventManager()->ReleaseMouseButton (aPos, aButton, aFlags, false);
       }
-      IsDragged = Standard_False;
+      ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), ViewerTest::CurrentView(), true);
       break;
     }
     case MotionNotify:
     {
-      Window anXWindow = GetWindowHandle (VT_GetWindow());
+      Window anXWindow = !VT_GetWindow().IsNull() ? VT_GetWindow()->XWindow() : 0;
       if (anXWindow != aReport.xmotion.window)
       {
         break;
@@ -4085,55 +3424,41 @@ int ViewerMainLoop (Standard_Integer argc, const char** argv)
         }
       }
 
-      if (IsDragged)
+      Graphic3d_Vec2i aPos (aReport.xmotion.x, aReport.xmotion.y);
+      Aspect_VKeyMouse aButtons = Aspect_VKeyMouse_NONE;
+      Aspect_VKeyFlags aFlags   = Aspect_VKeyFlags_NONE;
+      if ((aReport.xmotion.state & Button1Mask) != 0)
       {
-        if (!DragFirst
-          && ViewerTest::GetAISContext()->IsDisplayed (GetRubberBand()))
-        {
-          ViewerTest::GetAISContext()->Remove (GetRubberBand(), Standard_False);
-        }
-
-        X_Motion = aReport.xmotion.x;
-        Y_Motion = aReport.xmotion.y;
-        DragFirst = Standard_False;
+        aButtons |= Aspect_VKeyMouse_LeftButton;
+      }
+      else if ((aReport.xmotion.state & Button2Mask) != 0)
+      {
+        aButtons |= Aspect_VKeyMouse_MiddleButton;
+      }
+      else if ((aReport.xmotion.state & Button3Mask) != 0)
+      {
+        aButtons |= Aspect_VKeyMouse_RightButton;
+      }
 
-        Window aWindow = GetWindowHandle(VT_GetWindow());
-        Window aRoot;
-        int anX, anY;
-        unsigned int aWidth, aHeight, aBorderWidth, aDepth;
-        XGetGeometry (aDisplay, aWindow, &aRoot, &anX, &anY, &aWidth, &aHeight, &aBorderWidth, &aDepth);
-        GetRubberBand()->SetRectangle (X_ButtonPress, aHeight - Y_ButtonPress, X_Motion, aHeight - Y_Motion);
-        ViewerTest::GetAISContext()->Display (GetRubberBand(), 0, -1, Standard_False, AIS_DS_Displayed);
-        ViewerTest::GetAISContext()->CurrentViewer()->RedrawImmediate();
+      if (aReport.xmotion.state & ControlMask)
+      {
+        aFlags |= Aspect_VKeyFlags_CTRL;
       }
-      else
+      if (aReport.xmotion.state & ShiftMask)
       {
-        X_Motion = aReport.xmotion.x;
-        Y_Motion = aReport.xmotion.y;
-        if ((aReport.xmotion.state & ControlMask) != 0)
-        {
-          if ((aReport.xmotion.state & Button1Mask) != 0)
-          {
-            ProcessControlButton1Motion();
-          }
-          else if ((aReport.xmotion.state & Button2Mask) != 0)
-          {
-            VT_ProcessControlButton2Motion();
-          }
-          else if ((aReport.xmotion.state & Button3Mask) != 0)
-          {
-            VT_ProcessControlButton3Motion();
-          }
-        }
-        else
-        {
-          VT_ProcessMotion();
-        }
+        aFlags |= Aspect_VKeyFlags_SHIFT;
       }
+      if (ViewerTest::CurrentEventManager()->Keys().IsKeyDown (Aspect_VKey_Alt))
+      {
+        aFlags |= Aspect_VKeyFlags_ALT;
+      }
+
+      ViewerTest::CurrentEventManager()->UpdateMousePosition (aPos, aButtons, aFlags, false);
+      ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), ViewerTest::CurrentView(), true);
       break;
     }
   }
-  return (!toPick || toPickMore) ? 1 : 0;
+  return (!toPick || ViewerTest::CurrentEventManager()->ToPickPoint()) ? 1 : 0;
 }
 
 //==============================================================================
@@ -4224,7 +3549,7 @@ static void OSWindowSetup()
 
   XSetWMHints( aDisplay, window, &wmhints);
 
-  XSelectInput( aDisplay, window,  ExposureMask | KeyPressMask |
+  XSelectInput( aDisplay, window,  ExposureMask | KeyPressMask | KeyReleaseMask |
     ButtonPressMask | ButtonReleaseMask |
     StructureNotifyMask |
     PointerMotionMask |
@@ -4468,18 +3793,27 @@ static int VClear(Draw_Interpretor& , Standard_Integer , const char** )
 //purpose  :
 //==============================================================================
 
-static int VPick(Draw_Interpretor& di, Standard_Integer argc, const char** argv)
-{ if (ViewerTest::CurrentView().IsNull() ) return 1;
+static int VPick (Draw_Interpretor& ,
+                  Standard_Integer theNbArgs,
+                  const char** theArgVec)
+{
+  if (ViewerTest::CurrentView().IsNull())
+  {
+    return 1;
+  }
 
-if ( argc < 4 ) {
-  di << argv[0] << "Invalid number of arguments\n";
-  return 1;
-}
+  if (theNbArgs < 4)
+  {
+    std::cout << "Syntax error: Invalid number of arguments\n";
+    return 1;
+  }
 
-while (ViewerMainLoop( argc, argv)) {
-}
+  while (ViewerMainLoop (theNbArgs, theArgVec))
+  {
+    //
+  }
 
-return 0;
+  return 0;
 }
 
 namespace
@@ -7785,9 +7119,9 @@ static int VDiffImage (Draw_Interpretor& theDI, Standard_Integer theArgNb, const
     }
     else if (anArg == "-exitonclose")
     {
-      Draw_ToExitOnCloseView = true;
+      ViewerTest_EventManager::ToExitOnCloseView() = true;
       if (anArgIter + 1 < theArgNb
-       && ViewerTest::ParseOnOff (theArgVec[anArgIter + 1], Draw_ToExitOnCloseView))
+       && ViewerTest::ParseOnOff (theArgVec[anArgIter + 1], ViewerTest_EventManager::ToExitOnCloseView()))
       {
         ++anArgIter;
       }
@@ -7795,9 +7129,9 @@ static int VDiffImage (Draw_Interpretor& theDI, Standard_Integer theArgNb, const
     else if (anArg == "-closeonescape"
           || anArg == "-closeonesc")
     {
-      Draw_ToCloseViewOnEsc = true;
+      ViewerTest_EventManager::ToCloseViewOnEscape() = true;
       if (anArgIter + 1 < theArgNb
-       && ViewerTest::ParseOnOff (theArgVec[anArgIter + 1], Draw_ToCloseViewOnEsc))
+       && ViewerTest::ParseOnOff (theArgVec[anArgIter + 1], ViewerTest_EventManager::ToCloseViewOnEscape()))
       {
         ++anArgIter;
       }
@@ -7973,71 +7307,93 @@ static int VDiffImage (Draw_Interpretor& theDI, Standard_Integer theArgNb, const
 //           pixel positions (x1,y1),...,(xn,yn)
 //           4) any of these selections with shift button pressed
 //=======================================================================
-static Standard_Integer VSelect (Draw_Interpretor& di,
-                                 Standard_Integer argc,
-                                 const char ** argv)
+static Standard_Integer VSelect (Draw_Interpretor& ,
+                                 Standard_Integer theNbArgs,
+                                 const char** theArgVec)
 {
-  if(argc < 3)
-  {
-    di << "Usage : " << argv[0] << " x1 y1 [x2 y2 [... xn yn]] [shift_selection = 1|0]\n";
-    return 1;
-  }
-
-  Handle(AIS_InteractiveContext) myAIScontext = ViewerTest::GetAISContext();
-  if(myAIScontext.IsNull())
+  const Handle(AIS_InteractiveContext)& aCtx = ViewerTest::GetAISContext();
+  if (aCtx.IsNull())
   {
-    di << "use 'vinit' command before " << argv[0] << "\n";
+    std::cout << "Error: no active View\n";
     return 1;
   }
 
-  const Standard_Boolean isShiftSelection = (argc > 3 && !(argc % 2) && (atoi (argv[argc - 1]) == 1));
-  Standard_Integer aCoordsNb = isShiftSelection ? argc - 2 : argc - 1;
-  TCollection_AsciiString anArg;
-  anArg = isShiftSelection ? argv[argc - 3] : argv[argc - 2];
-  anArg.LowerCase();
-  if (anArg == "-allowoverlap")
+  NCollection_Sequence<Graphic3d_Vec2i> aPnts;
+  bool isShiftSelection = false, toAllowOverlap = false;
+  for (Standard_Integer anArgIter = 1; anArgIter < theNbArgs; ++anArgIter)
   {
-    Standard_Boolean isValidated = isShiftSelection ? argc == 8
-      : argc == 7;
-    if (!isValidated)
+    TCollection_AsciiString anArg (theArgVec[anArgIter]);
+    anArg.LowerCase();
+    if (anArg == "-allowoverlap")
+    {
+      toAllowOverlap = true;
+      if (anArgIter + 1 < theNbArgs
+       && ViewerTest::ParseOnOff (theArgVec[anArgIter + 1], toAllowOverlap))
+      {
+        ++anArgIter;
+      }
+    }
+    else if (anArgIter + 1 < theNbArgs
+          && anArg.IsIntegerValue()
+          && TCollection_AsciiString (theArgVec[anArgIter + 1]).IsIntegerValue())
     {
-      di << "Wrong number of arguments! -allowoverlap key is applied only for rectangle selection";
+      const TCollection_AsciiString anArgNext (theArgVec[++anArgIter]);
+      aPnts.Append (Graphic3d_Vec2i (anArg.IntegerValue(), anArgNext.IntegerValue()));
+    }
+    else if (anArgIter + 1 == theNbArgs
+          && anArg.IsIntegerValue())
+    {
+      isShiftSelection = anArg.IntegerValue() == 1;
+    }
+    else
+    {
+      std::cout << "Syntax error at '" << anArg << "'\n";
       return 1;
     }
-
-    Standard_Integer isToAllow = isShiftSelection ? Draw::Atoi(argv[argc - 2]) : Draw::Atoi(argv[argc - 1]);
-    myAIScontext->MainSelector()->AllowOverlapDetection (isToAllow != 0);
-    aCoordsNb -= 2;
+  }
+  if (toAllowOverlap
+   && aPnts.Length() != 2)
+  {
+    std::cout << "Syntax error: -allowoverlap key is applied only for rectangle selection\n";
+    return 1;
+  }
+  if (toAllowOverlap)
+  {
+    aCtx->MainSelector()->AllowOverlapDetection (toAllowOverlap);
   }
 
   Handle(ViewerTest_EventManager) aCurrentEventManager = ViewerTest::CurrentEventManager();
-  aCurrentEventManager->MoveTo(atoi(argv[1]),atoi(argv[2]));
-  if(aCoordsNb == 2)
+  if (aPnts.IsEmpty())
   {
-    if(isShiftSelection)
-      aCurrentEventManager->ShiftSelect();
+    if (isShiftSelection)
+    {
+      aCtx->ShiftSelect (false);
+    }
     else
-      aCurrentEventManager->Select();
+    {
+      aCtx->Select (false);
+    }
+    aCtx->CurrentViewer()->Invalidate();
   }
-  else if(aCoordsNb == 4)
+  else if (aPnts.Length() == 2)
   {
-    if(isShiftSelection)
-      aCurrentEventManager->ShiftSelect (atoi (argv[1]), atoi (argv[2]), atoi (argv[3]), atoi (argv[4]), Standard_False);
-    else
-      aCurrentEventManager->Select (atoi (argv[1]), atoi (argv[2]), atoi (argv[3]), atoi (argv[4]), Standard_False);
+    if (toAllowOverlap
+     && aPnts.First().y() < aPnts.Last().y())
+    {
+      std::swap (aPnts.ChangeFirst(), aPnts.ChangeLast());
+    }
+    else if (!toAllowOverlap
+           && aPnts.First().y() > aPnts.Last().y())
+    {
+      std::swap (aPnts.ChangeFirst(), aPnts.ChangeLast());
+    }
+    aCurrentEventManager->SelectInViewer (aPnts, isShiftSelection);
   }
   else
   {
-    TColgp_Array1OfPnt2d aPolyline (1,aCoordsNb / 2);
-
-    for(Standard_Integer i=1;i<=aCoordsNb / 2;++i)
-      aPolyline.SetValue(i,gp_Pnt2d(atoi(argv[2*i-1]),atoi(argv[2*i])));
-
-    if(isShiftSelection)
-      aCurrentEventManager->ShiftSelect(aPolyline);
-    else
-      aCurrentEventManager->Select(aPolyline);
+    aCurrentEventManager->SelectInViewer (aPnts, isShiftSelection);
   }
+  aCurrentEventManager->FlushViewEvents (aCtx, ViewerTest::CurrentView(), true);
   return 0;
 }
 
@@ -8107,7 +7463,10 @@ static Standard_Integer VMoveTo (Draw_Interpretor& theDI,
     return 1;
   }
 
-  ViewerTest::CurrentEventManager()->MoveTo (aMousePos.x(), aMousePos.y());
+  ViewerTest::CurrentEventManager()->ResetPreviousMoveTo();
+  ViewerTest::CurrentEventManager()->UpdateMousePosition (aMousePos, Aspect_VKeyMouse_NONE, Aspect_VKeyFlags_NONE, false);
+  ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), aView, true);
+
   gp_Pnt aTopPnt (RealLast(), RealLast(), RealLast());
   const Handle(SelectMgr_EntityOwner)& aDetOwner = aContext->DetectedOwner();
   for (Standard_Integer aDetIter = 1; aDetIter <= aContext->MainSelector()->NbPicked(); ++aDetIter)
@@ -12846,52 +12205,6 @@ static Standard_Integer VStatProfiler (Draw_Interpretor& theDI,
 }
 
 //=======================================================================
-//function : VProgressiveMode
-//purpose  :
-//=======================================================================
-#if defined(_WIN32)
-static Standard_Integer VProgressiveMode (Draw_Interpretor& /*theDI*/,
-                                          Standard_Integer  /*theNbArgs*/,
-                                          const char**      /*theArgs*/)
-{
-  Handle(V3d_View) aView = ViewerTest::CurrentView();
-  if (aView.IsNull())
-  {
-    std::cerr << "Error: no active viewer!\n";
-    return 1;
-  }
-
-  std::cout << "Press Enter or Escape key to exit progressive rendering mode" << std::endl;
-
-  for (;;)
-  {
-    aView->Redraw();
-
-    Standard_Boolean toExit = Standard_False;
-
-    MSG aMsg;
-    while (PeekMessageW (&aMsg, NULL, 0, 0, PM_REMOVE))
-    {
-      if (aMsg.message == WM_KEYDOWN && (aMsg.wParam == 0x0d || aMsg.wParam == 0x1b))
-      {
-        toExit = Standard_True;
-      }
-
-      TranslateMessage (&aMsg);
-      DispatchMessageW (&aMsg);
-    }
-
-    if (toExit)
-    {
-      break;
-    }
-  }
-
-  return 0;
-}
-#endif
-
-//=======================================================================
 //function : VXRotate
 //purpose  :
 //=======================================================================
@@ -13065,6 +12378,7 @@ static int VManipulator (Draw_Interpretor& theDi,
     std::cout << theArgVec[0] << ": AIS object \"" << aName << "\" has been created.\n";
 
     aManipulator = new ViewerTest_AISManipulator();
+    aManipulator->SetModeActivationOnDetection (true);
     aMapAIS.Bind (aManipulator, aName);
   }
   else
@@ -14492,11 +13806,5 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
                    "\n\t\t:   selMode     color of selection mode"
                    "\n\t\t:   entity      color of etected entity",
                    __FILE__, VDumpSelectionImage, group);
-
-#if defined(_WIN32)
-  theCommands.Add("vprogressive",
-    "vprogressive",
-    __FILE__, VProgressiveMode, group);
-#endif
 }
 
index 7d0eb93..f39dc12 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <Cocoa_Window.hxx>
 #include <ViewerTest.hxx>
+#include <ViewerTest_EventManager.hxx>
 #include <V3d_View.hxx>
 #include <V3d_Viewer.hxx>
 #include <AIS_InteractiveContext.hxx>
 
 extern void ActivateView (const TCollection_AsciiString& theViewName,
                           Standard_Boolean theToUpdate = Standard_True);
-extern void VT_ProcessExpose();
-extern void VT_ProcessConfigure();
-extern void VT_ProcessKeyPress (const char* theBuffer);
-extern void VT_ProcessMotion();
-extern void VT_ProcessButton3Press();
-extern void VT_ProcessButton3Release();
-extern void VT_ProcessControlButton2Motion();
-extern void VT_ProcessControlButton3Motion();
-extern Standard_Boolean VT_ProcessButton1Press (Standard_Integer theArgsNb,
-                                                const char**     theArgsVec,
-                                                Standard_Boolean theToPick,
-                                                Standard_Boolean theIsShift);
-extern void VT_ProcessButton1Release(Standard_Boolean theIsShift);
 
 extern NCollection_DoubleMap <TCollection_AsciiString, Handle(V3d_View)> ViewerTest_myViews;
-extern int X_Motion; // Current cursor position
-extern int Y_Motion;
-extern int X_ButtonPress; // Last ButtonPress position
-extern int Y_ButtonPress;
-extern Standard_Boolean IsDragged;
-
-// =======================================================================
-// function : SetCocoaWindowTitle
-// purpose  :
-// =======================================================================
-void SetCocoaWindowTitle (const Handle(Cocoa_Window)& theWindow, Standard_CString theTitle)
-{
-  NSView* aView = theWindow->HView();
-  NSWindow* aWindow = [aView window];
-
-  NSString* aTitleNS = [[NSString alloc] initWithUTF8String: theTitle];
-  [aWindow setTitle: aTitleNS];
-  [aTitleNS release];
-  
-}
 
 // =======================================================================
 // function : GetCocoaScreenResolution
@@ -170,20 +138,41 @@ void ViewerTest_SetCocoaEventManagerView (const Handle(Cocoa_Window)& theWindow)
   [aView release];
 }
 
-// =======================================================================
-// function : getMouseCoords
-// purpose  : Retrieve cursor position
-// =======================================================================
-static void getMouseCoords (NSView*           theView,
-                            NSEvent*          theEvent,
-                            Standard_Integer& theX,
-                            Standard_Integer& theY)
+//! Retrieve cursor position
+static Graphic3d_Vec2i getMouseCoords (NSView*  theView,
+                                       NSEvent* theEvent)
 {
   NSPoint aMouseLoc = [theView convertPoint: [theEvent locationInWindow] fromView: nil];
   NSRect  aBounds   = [theView bounds];
+  return Graphic3d_Vec2i (Standard_Integer(aMouseLoc.x),
+                          Standard_Integer(aBounds.size.height - aMouseLoc.y));
+}
 
-  theX = Standard_Integer(aMouseLoc.x);
-  theY = Standard_Integer(aBounds.size.height - aMouseLoc.y);
+//! Convert key flags from mouse event.
+static Aspect_VKeyFlags getMouseKeyFlags (NSEvent* theEvent)
+{
+  Aspect_VKeyFlags aFlags = Aspect_VKeyFlags_NONE;
+  if (([theEvent modifierFlags] & NSEventModifierFlagShift) != 0)
+  {
+    aFlags |= Aspect_VKeyFlags_SHIFT;
+  }
+  if (([theEvent modifierFlags] & NSEventModifierFlagControl) != 0)
+  {
+    aFlags |= Aspect_VKeyFlags_CTRL;
+  }
+  if (([theEvent modifierFlags] & NSEventModifierFlagOption) != 0)
+  {
+    aFlags |= Aspect_VKeyFlags_ALT;
+  }
+  if (([theEvent modifierFlags] & NSEventModifierFlagFunction) != 0)
+  {
+    //aFlags |= Aspect_VKeyFlags_FUNC;
+  }
+  if (([theEvent modifierFlags] & NSEventModifierFlagCommand) != 0)
+  {
+    //aFlags |= Aspect_VKeyFlags_CMD;
+  }
+  return aFlags;
 }
 
 @implementation ViewerTest_CocoaEventManagerView
@@ -195,7 +184,7 @@ static void getMouseCoords (NSView*           theView,
 - (void )setFrameSize: (NSSize )theNewSize
 {
   [super setFrameSize: theNewSize];
-  VT_ProcessConfigure();
+  ViewerTest::CurrentEventManager()->ProcessConfigure();
 }
 
 // =======================================================================
@@ -205,7 +194,10 @@ static void getMouseCoords (NSView*           theView,
 - (void )drawRect: (NSRect )theDirtyRect
 {
   (void )theDirtyRect;
-  VT_ProcessExpose();
+  if (!ViewerTest::CurrentEventManager().IsNull())
+  {
+    ViewerTest::CurrentEventManager()->ProcessExpose();
+  }
 }
 
 // =======================================================================
@@ -214,8 +206,11 @@ static void getMouseCoords (NSView*           theView,
 // =======================================================================
 - (void )mouseMoved: (NSEvent* )theEvent
 {
-  getMouseCoords (self, theEvent, X_Motion, Y_Motion);
-  VT_ProcessMotion();
+  const Graphic3d_Vec2i  aPos   = getMouseCoords (self, theEvent);
+  const Aspect_VKeyFlags aFlags = getMouseKeyFlags (theEvent);
+  const Aspect_VKeyMouse aButtons = ViewerTest::CurrentEventManager()->PressedMouseButtons();
+  ViewerTest::CurrentEventManager()->UpdateMousePosition (aPos, aButtons, aFlags, false);
+  ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), ViewerTest::CurrentView(), true);
 }
 
 // =======================================================================
@@ -233,8 +228,10 @@ static void getMouseCoords (NSView*           theView,
 // =======================================================================
 - (void )mouseDown: (NSEvent* )theEvent
 {
-  getMouseCoords (self, theEvent, X_ButtonPress, Y_ButtonPress);
-  VT_ProcessButton1Press (0, NULL, Standard_False, [theEvent modifierFlags] & NSEventModifierFlagShift);
+  const Graphic3d_Vec2i  aPos   = getMouseCoords (self, theEvent);
+  const Aspect_VKeyFlags aFlags = getMouseKeyFlags (theEvent);
+  ViewerTest::CurrentEventManager()->PressMouseButton (aPos, Aspect_VKeyMouse_LeftButton, aFlags, false);
+  ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), ViewerTest::CurrentView(), true);
 }
 
 // =======================================================================
@@ -243,23 +240,23 @@ static void getMouseCoords (NSView*           theView,
 // =======================================================================
 - (void )mouseUp: (NSEvent* )theEvent
 {
-  getMouseCoords (self, theEvent, X_Motion, Y_Motion);
-  VT_ProcessButton1Release([theEvent modifierFlags] & NSEventModifierFlagShift);
+  const Graphic3d_Vec2i  aPos   = getMouseCoords (self, theEvent);
+  const Aspect_VKeyFlags aFlags = getMouseKeyFlags (theEvent);
+  ViewerTest::CurrentEventManager()->ReleaseMouseButton (aPos, Aspect_VKeyMouse_LeftButton, aFlags, false);
+  ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), ViewerTest::CurrentView(), true);
 }
 
-
 // =======================================================================
 // function : mouseDragged
 // purpose  :
 // =======================================================================
 - (void )mouseDragged: (NSEvent* )theEvent
 {
-  IsDragged = Standard_True;
-  if ([theEvent modifierFlags] & NSEventModifierFlagControl)
-  {
-    getMouseCoords (self, theEvent, X_Motion, Y_Motion);
-    VT_ProcessControlButton2Motion();
-  }
+  const Graphic3d_Vec2i  aPos   = getMouseCoords (self, theEvent);
+  const Aspect_VKeyFlags aFlags = getMouseKeyFlags (theEvent);
+  const Aspect_VKeyMouse aButtons = ViewerTest::CurrentEventManager()->PressedMouseButtons();
+  ViewerTest::CurrentEventManager()->UpdateMousePosition (aPos, aButtons, aFlags, false);
+  ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), ViewerTest::CurrentView(), true);
 }
 
 // =======================================================================
@@ -268,8 +265,10 @@ static void getMouseCoords (NSView*           theView,
 // =======================================================================
 - (void )rightMouseDown: (NSEvent* )theEvent
 {
-  getMouseCoords (self, theEvent, X_ButtonPress, Y_ButtonPress);
-  VT_ProcessButton3Press(); // Start rotation
+  const Graphic3d_Vec2i  aPos   = getMouseCoords (self, theEvent);
+  const Aspect_VKeyFlags aFlags = getMouseKeyFlags (theEvent);
+  ViewerTest::CurrentEventManager()->PressMouseButton (aPos, Aspect_VKeyMouse_RightButton, aFlags, false);
+  ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), ViewerTest::CurrentView(), true);
 }
 
 // =======================================================================
@@ -278,8 +277,10 @@ static void getMouseCoords (NSView*           theView,
 // =======================================================================
 - (void )rightMouseUp: (NSEvent* )theEvent
 {
-  (void )theEvent;
-  VT_ProcessButton3Release();
+  const Graphic3d_Vec2i  aPos   = getMouseCoords (self, theEvent);
+  const Aspect_VKeyFlags aFlags = getMouseKeyFlags (theEvent);
+  ViewerTest::CurrentEventManager()->ReleaseMouseButton (aPos, Aspect_VKeyMouse_RightButton, aFlags, false);
+  ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), ViewerTest::CurrentView(), true);
 }
 
 // =======================================================================
@@ -288,11 +289,11 @@ static void getMouseCoords (NSView*           theView,
 // =======================================================================
 - (void )rightMouseDragged: (NSEvent* )theEvent
 {
-  if ([theEvent modifierFlags] & NSEventModifierFlagControl)
-  {
-    getMouseCoords (self, theEvent, X_Motion, Y_Motion);
-    VT_ProcessControlButton3Motion();
-  }
+  const Graphic3d_Vec2i  aPos   = getMouseCoords (self, theEvent);
+  const Aspect_VKeyFlags aFlags = getMouseKeyFlags (theEvent);
+  const Aspect_VKeyMouse aButtons = ViewerTest::CurrentEventManager()->PressedMouseButtons();
+  ViewerTest::CurrentEventManager()->UpdateMousePosition (aPos, aButtons, aFlags, false);
+  ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), ViewerTest::CurrentView(), true);
 }
 
 // =======================================================================
@@ -301,14 +302,18 @@ static void getMouseCoords (NSView*           theView,
 // =======================================================================
 - (void )scrollWheel: (NSEvent* )theEvent
 {
-  float aDelta = [theEvent deltaY];
+  const Graphic3d_Vec2i  aPos   = getMouseCoords (self, theEvent);
+  const Aspect_VKeyFlags aFlags = getMouseKeyFlags (theEvent);
+
+  const Standard_Real aDelta = [theEvent deltaY];
   if (Abs (aDelta) < 0.001)
   {
     // a lot of values near zero can be generated by touchpad
     return;
   }
 
-  ViewerTest::CurrentView()->Zoom (0, 0, aDelta, aDelta);
+  ViewerTest::CurrentEventManager()->UpdateMouseScroll (Aspect_ScrollDelta (aPos, aDelta, aFlags));
+  ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), ViewerTest::CurrentView(), true);
 }
 
 // =======================================================================
@@ -317,14 +322,36 @@ static void getMouseCoords (NSView*           theView,
 // =======================================================================
 - (void )keyDown: (NSEvent* )theEvent
 {
-  NSString* aStringNs = [theEvent characters];
-  if (aStringNs == NULL || [aStringNs length] == 0)
+  unsigned int aKeyCode = [theEvent keyCode];
+  const Aspect_VKey aVKey = Cocoa_Window::VirtualKeyFromNative (aKeyCode);
+  if (aVKey != Aspect_VKey_UNKNOWN)
   {
-    return;
+    const double aTimeStamp = [theEvent timestamp];
+    ViewerTest::CurrentEventManager()->KeyDown (aVKey, aTimeStamp);
+    ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), ViewerTest::CurrentView(), true);
   }
 
-  const Standard_CString aString = [aStringNs UTF8String];
-  VT_ProcessKeyPress (aString);
+  //NSString* aStringNs = [theEvent characters];
+  //if (aStringNs != NULL && [aStringNs length] != 1)
+  //{
+  //  const Standard_CString aString = [aStringNs UTF8String];
+  //}
+}
+
+// =======================================================================
+// function : keyUp
+// purpose  :
+// =======================================================================
+- (void )keyUp: (NSEvent* )theEvent
+{
+  unsigned int aKeyCode = [theEvent keyCode];
+  const Aspect_VKey aVKey = Cocoa_Window::VirtualKeyFromNative (aKeyCode);
+  if (aVKey != Aspect_VKey_UNKNOWN)
+  {
+    const double aTimeStamp = [theEvent timestamp];
+    ViewerTest::CurrentEventManager()->KeyUp (aVKey, aTimeStamp);
+    ViewerTest::CurrentEventManager()->FlushViewEvents (ViewerTest::GetAISContext(), ViewerTest::CurrentView(), true);
+  }
 }
 
 @end
index 88ebbea..9922043 100644 (file)
@@ -356,6 +356,16 @@ void WNT_Window::SetPos (const Standard_Integer theX,  const Standard_Integer th
 }
 
 // =======================================================================
+// function : SetTitle
+// purpose  :
+// =======================================================================
+void WNT_Window::SetTitle (const TCollection_AsciiString& theTitle)
+{
+  const TCollection_ExtendedString aTitleW (theTitle);
+  SetWindowTextW ((HWND )myHWindow, aTitleW.ToWideString());
+}
+
+// =======================================================================
 // function : InvalidateContent
 // purpose  :
 // =======================================================================
@@ -367,4 +377,270 @@ void WNT_Window::InvalidateContent (const Handle(Aspect_DisplayConnection)& )
   }
 }
 
+// =======================================================================
+// function : VirtualKeyFromNative
+// purpose  :
+// =======================================================================
+Aspect_VKey WNT_Window::VirtualKeyFromNative (Standard_Integer theKey)
+{
+  if (theKey >= Standard_Integer('0')
+   && theKey <= Standard_Integer('9'))
+  {
+    return Aspect_VKey((theKey - Standard_Integer('0')) + Aspect_VKey_0);
+  }
+  if (theKey >= Standard_Integer('A')
+   && theKey <= Standard_Integer('Z'))
+  {
+    // main latin alphabet keys
+    return Aspect_VKey((theKey - Standard_Integer('A')) + Aspect_VKey_A);
+  }
+  if (theKey >= VK_F1
+   && theKey <= VK_F24)
+  {
+    // special keys
+    if (theKey <= VK_F12)
+    {
+      return Aspect_VKey((theKey - VK_F1) + Aspect_VKey_F1);
+    }
+    return Aspect_VKey_UNKNOWN;
+  }
+  if (theKey >= VK_NUMPAD0
+   && theKey <= VK_NUMPAD9)
+  {
+    // numpad keys
+    return Aspect_VKey((theKey - VK_NUMPAD0) + Aspect_VKey_Numpad0);
+  }
+
+  switch (theKey)
+  {
+    case VK_LBUTTON:
+    case VK_RBUTTON:
+    case VK_CANCEL:
+    case VK_MBUTTON:
+    case VK_XBUTTON1:
+    case VK_XBUTTON2:
+      return Aspect_VKey_UNKNOWN;
+    case VK_BACK:
+      return Aspect_VKey_Backspace;
+    case VK_TAB:
+      return Aspect_VKey_Tab;
+    case VK_CLEAR:
+      return Aspect_VKey_UNKNOWN;
+    case VK_RETURN:
+      return Aspect_VKey_Enter;
+    case VK_SHIFT:
+      return Aspect_VKey_Shift;
+    case VK_CONTROL:
+      return Aspect_VKey_Control;
+    case VK_MENU:
+      return Aspect_VKey_Alt; //Aspect_VKey_Menu;
+    case VK_PAUSE:
+    case VK_CAPITAL:
+      return Aspect_VKey_UNKNOWN;
+    case VK_ESCAPE:
+      return Aspect_VKey_Escape;
+    case VK_CONVERT:
+    case VK_NONCONVERT:
+    case VK_ACCEPT:
+    case VK_MODECHANGE:
+      return Aspect_VKey_UNKNOWN;
+    case VK_SPACE:
+      return Aspect_VKey_Space;
+    case VK_PRIOR:
+      return Aspect_VKey_PageUp;
+    case VK_NEXT:
+      return Aspect_VKey_PageDown;
+    case VK_END:
+      return Aspect_VKey_End;
+    case VK_HOME:
+      return Aspect_VKey_Home;
+    case VK_LEFT:
+      return Aspect_VKey_Left;
+    case VK_UP:
+      return Aspect_VKey_Up;
+    case VK_DOWN:
+      return Aspect_VKey_Down;
+    case VK_RIGHT:
+      return Aspect_VKey_Right;
+    case VK_SELECT:
+    case VK_PRINT:
+    case VK_EXECUTE:
+    case VK_SNAPSHOT:
+      return Aspect_VKey_UNKNOWN;
+    case VK_INSERT:
+      return Aspect_VKey_UNKNOWN; // Aspect_VKey_Insert
+    case VK_DELETE:
+      return Aspect_VKey_Delete;
+    case VK_HELP:
+    case VK_LWIN:
+    case VK_RWIN:
+    case VK_APPS:
+    case VK_SLEEP:
+      return Aspect_VKey_UNKNOWN;
+    case VK_MULTIPLY:
+      return Aspect_VKey_NumpadMultiply;
+    case VK_ADD:
+      return Aspect_VKey_NumpadAdd;
+    case VK_SEPARATOR:
+    case VK_DECIMAL:
+      return Aspect_VKey_UNKNOWN;
+    case VK_SUBTRACT:
+      return Aspect_VKey_NumpadSubtract;
+    case VK_DIVIDE:
+      return Aspect_VKey_NumpadDivide;
+    case VK_NUMLOCK:
+      return Aspect_VKey_Numlock;
+    case VK_SCROLL:
+      return Aspect_VKey_Scroll;
+    case VK_LSHIFT:
+    case VK_RSHIFT:
+    case VK_LCONTROL:
+    case VK_RCONTROL:
+    case VK_LMENU:
+    case VK_RMENU:
+      return Aspect_VKey_UNKNOWN;
+    case VK_BROWSER_BACK:
+      return Aspect_VKey_BrowserBack;
+    case VK_BROWSER_FORWARD:
+      return Aspect_VKey_BrowserForward;
+    case VK_BROWSER_REFRESH:
+      return Aspect_VKey_BrowserRefresh;
+    case VK_BROWSER_STOP:
+      return Aspect_VKey_BrowserStop;
+    case VK_BROWSER_SEARCH:
+      return Aspect_VKey_BrowserSearch;
+    case VK_BROWSER_FAVORITES:
+      return Aspect_VKey_BrowserFavorites;
+    case VK_BROWSER_HOME:
+      return Aspect_VKey_BrowserHome;
+    case VK_VOLUME_MUTE:
+      return Aspect_VKey_VolumeMute;
+    case VK_VOLUME_DOWN:
+      return Aspect_VKey_VolumeDown;
+    case VK_VOLUME_UP:
+      return Aspect_VKey_VolumeUp;
+    case VK_MEDIA_NEXT_TRACK:
+      return Aspect_VKey_MediaNextTrack;
+    case VK_MEDIA_PREV_TRACK:
+      return Aspect_VKey_MediaPreviousTrack;
+    case VK_MEDIA_STOP:
+      return Aspect_VKey_MediaStop;
+    case VK_MEDIA_PLAY_PAUSE:
+      return Aspect_VKey_MediaPlayPause;
+    case VK_OEM_1:
+      return Aspect_VKey_Semicolon;
+    case VK_OEM_PLUS:
+      return Aspect_VKey_Plus;
+    case VK_OEM_COMMA:
+      return Aspect_VKey_Comma;
+    case VK_OEM_MINUS:
+      return Aspect_VKey_Minus;
+    case VK_OEM_PERIOD:
+      return Aspect_VKey_Period;
+    case VK_OEM_2:
+      return Aspect_VKey_Slash;
+    case VK_OEM_3:
+      return Aspect_VKey_Tilde;
+    case VK_OEM_4:
+      return Aspect_VKey_BracketLeft;
+    case VK_OEM_5:
+      return Aspect_VKey_Backslash;
+    case VK_OEM_6:
+      return Aspect_VKey_BracketRight;
+    case VK_OEM_7:
+      return Aspect_VKey_Apostrophe;
+  }
+  return Aspect_VKey_UNKNOWN;
+}
+
+// =======================================================================
+// function : MouseKeyFlagsFromEvent
+// purpose  :
+// =======================================================================
+Aspect_VKeyFlags WNT_Window::MouseKeyFlagsFromEvent (WPARAM theKeys)
+{
+  Aspect_VKeyFlags aFlags = Aspect_VKeyFlags_NONE;
+  if ((theKeys & MK_CONTROL) != 0)
+  {
+    aFlags |= Aspect_VKeyFlags_CTRL;
+  }
+  if ((theKeys & MK_SHIFT) != 0)
+  {
+    aFlags |= Aspect_VKeyFlags_SHIFT;
+  }
+  if (GetKeyState (VK_MENU) < 0)
+  {
+    aFlags |= Aspect_VKeyFlags_ALT;
+  }
+  return aFlags;
+}
+
+// =======================================================================
+// function : MouseKeyFlagsAsync
+// purpose  :
+// =======================================================================
+Aspect_VKeyFlags WNT_Window::MouseKeyFlagsAsync()
+{
+  Aspect_VKeyFlags aFlags = Aspect_VKeyFlags_NONE;
+  if ((GetAsyncKeyState (VK_CONTROL) & 0x8000) != 0)
+  {
+    aFlags |= Aspect_VKeyFlags_CTRL;
+  }
+  if ((GetAsyncKeyState (VK_SHIFT) & 0x8000) != 0)
+  {
+    aFlags |= Aspect_VKeyFlags_SHIFT;
+  }
+  if ((GetAsyncKeyState (VK_MENU) & 0x8000) != 0)
+  {
+    aFlags |= Aspect_VKeyFlags_ALT;
+  }
+  return aFlags;
+}
+
+// =======================================================================
+// function : MouseButtonsFromEvent
+// purpose  :
+// =======================================================================
+Aspect_VKeyMouse WNT_Window::MouseButtonsFromEvent (WPARAM theKeys)
+{
+  Aspect_VKeyMouse aButtons = Aspect_VKeyMouse_NONE;
+  if ((theKeys & MK_LBUTTON) != 0)
+  {
+    aButtons |= Aspect_VKeyMouse_LeftButton;
+  }
+  if ((theKeys & MK_MBUTTON) != 0)
+  {
+    aButtons |= Aspect_VKeyMouse_MiddleButton;
+  }
+  if ((theKeys & MK_RBUTTON) != 0)
+  {
+    aButtons |= Aspect_VKeyMouse_RightButton;
+  }
+  return aButtons;
+}
+
+// =======================================================================
+// function : MouseButtonsAsync
+// purpose  :
+// =======================================================================
+Aspect_VKeyMouse WNT_Window::MouseButtonsAsync()
+{
+  Aspect_VKeyMouse aButtons = Aspect_VKeyMouse_NONE;
+  const bool isSwapped = GetSystemMetrics (SM_SWAPBUTTON) != 0;
+
+  if ((GetAsyncKeyState (!isSwapped ? VK_LBUTTON : VK_RBUTTON) & 0x8000) != 0)
+  {
+    aButtons |= Aspect_VKeyMouse_LeftButton;
+  }
+  if ((GetAsyncKeyState (VK_MBUTTON) & 0x8000) != 0)
+  {
+    aButtons |= Aspect_VKeyMouse_MiddleButton;
+  }
+  if ((GetAsyncKeyState (!isSwapped ? VK_RBUTTON : VK_LBUTTON) & 0x8000) != 0)
+  {
+    aButtons |= Aspect_VKeyMouse_RightButton;
+  }
+  return aButtons;
+}
+
 #endif // _WIN32
index d031ab4..cbd6a8f 100644 (file)
 #ifndef _WNT_Window_HeaderFile
 #define _WNT_Window_HeaderFile
 
-#include <Standard.hxx>
+#include <Aspect_Window.hxx>
 
 #if defined(_WIN32) && !defined(OCCT_UWP)
 
-#include <Standard_Type.hxx>
-#include <Standard_Integer.hxx>
+#include <Aspect_Drawable.hxx>
+#include <Aspect_VKey.hxx>
 #include <Aspect_Handle.hxx>
-#include <Standard_Boolean.hxx>
-#include <Aspect_Window.hxx>
-#include <Standard_CString.hxx>
+#include <Standard_Type.hxx>
 #include <WNT_Dword.hxx>
-#include <Quantity_NameOfColor.hxx>
-#include <Standard_Address.hxx>
-#include <Aspect_TypeOfResize.hxx>
-#include <Aspect_Drawable.hxx>
-class WNT_WClass;
-class Aspect_WindowDefinitionError;
-class Aspect_WindowError;
 
+class WNT_WClass;
 
-class WNT_Window;
 DEFINE_STANDARD_HANDLE(WNT_Window, Aspect_Window)
 
 //! This class defines Windows NT window
 class WNT_Window : public Aspect_Window
 {
+public:
+
+  //! Convert WInAPI virtual key (VK_ enumeration) into Aspect_VKey.
+  Standard_EXPORT static Aspect_VKey VirtualKeyFromNative (Standard_Integer theKey);
+
+  //! Convert WPARAM from mouse event to key flags.
+  Standard_EXPORT static Aspect_VKeyFlags MouseKeyFlagsFromEvent (WPARAM theKeys);
+
+  //! Convert WPARAM from mouse event to mouse buttons bitmask.
+  Standard_EXPORT static Aspect_VKeyMouse MouseButtonsFromEvent (WPARAM theKeys);
+
+  //! Use GetAsyncKeyState() to fetch actual mouse key flags regardless of event loop.
+  Standard_EXPORT static Aspect_VKeyFlags MouseKeyFlagsAsync();
+
+  //! Use GetAsyncKeyState() to fetch actual mouse buttons state regardless of event loop.
+  Standard_EXPORT static Aspect_VKeyMouse MouseButtonsAsync();
 
 public:
 
-  
   //! Creates a Window defined by his position and size
   //! in pixles from the Parent Window.
   //! Trigger: Raises WindowDefinitionError if the Position out of the
@@ -115,6 +121,9 @@ public:
   //! Returns nothing on Windows
   virtual Aspect_FBConfig NativeFBConfig() const Standard_OVERRIDE { return NULL; }
 
+  //! Sets window title.
+  Standard_EXPORT virtual void SetTitle (const TCollection_AsciiString& theTitle) Standard_OVERRIDE;
+
   //! Invalidate entire window content by calling InvalidateRect() WinAPI function, resulting in WM_PAINT event put into window message loop.
   //! Method can be called from non-window thread, and system will also automatically aggregate multiple events into single one.
   Standard_EXPORT virtual void InvalidateContent (const Handle(Aspect_DisplayConnection)& theDisp = NULL) Standard_OVERRIDE;
index b811c5e..3622de9 100644 (file)
@@ -22,6 +22,8 @@
 #include <Message.hxx>
 #include <Message_Messenger.hxx>
 
+//#include <X11/XF86keysym.h>
+
 #if defined(HAVE_EGL) || defined(HAVE_GLES2)
   #include <EGL/egl.h>
   #ifndef EGL_OPENGL_ES3_BIT
@@ -501,6 +503,18 @@ void Xw_Window::Size (Standard_Integer& theWidth,
 }
 
 // =======================================================================
+// function : SetTitle
+// purpose  :
+// =======================================================================
+void Xw_Window::SetTitle (const TCollection_AsciiString& theTitle)
+{
+  if (myXWindow != 0)
+  {
+    XStoreName (myDisplay->GetDisplay(), myXWindow, theTitle.ToCString());
+  }
+}
+
+// =======================================================================
 // function : InvalidateContent
 // purpose  :
 // =======================================================================
@@ -522,4 +536,155 @@ void Xw_Window::InvalidateContent (const Handle(Aspect_DisplayConnection)& theDi
   XFlush (aDispX);
 }
 
+// =======================================================================
+// function : VirtualKeyFromNative
+// purpose  :
+// =======================================================================
+Aspect_VKey Xw_Window::VirtualKeyFromNative (unsigned long theKey)
+{
+  if (theKey >= XK_0
+   && theKey <= XK_9)
+  {
+    return Aspect_VKey(theKey - XK_0 + Aspect_VKey_0);
+  }
+
+  if (theKey >= XK_A
+   && theKey <= XK_Z)
+  {
+    return Aspect_VKey(theKey - XK_A + Aspect_VKey_A);
+  }
+
+  if (theKey >= XK_a
+   && theKey <= XK_z)
+  {
+    return Aspect_VKey(theKey - XK_a + Aspect_VKey_A);
+  }
+
+  if (theKey >= XK_F1
+   && theKey <= XK_F24)
+  {
+    if (theKey <= XK_F12)
+    {
+      return Aspect_VKey(theKey - XK_F1 + Aspect_VKey_F1);
+    }
+    return Aspect_VKey_UNKNOWN;
+  }
+
+  switch (theKey)
+  {
+    case XK_space:
+      return Aspect_VKey_Space;
+    case XK_apostrophe:
+      return Aspect_VKey_Apostrophe;
+    case XK_comma:
+      return Aspect_VKey_Comma;
+    case XK_minus:
+      return Aspect_VKey_Minus;
+    case XK_period:
+      return Aspect_VKey_Period;
+    case XK_semicolon:
+      return Aspect_VKey_Semicolon;
+    case XK_equal:
+      return Aspect_VKey_Equal;
+    case XK_bracketleft:
+      return Aspect_VKey_BracketLeft;
+    case XK_backslash:
+      return Aspect_VKey_Backslash;
+    case XK_bracketright:
+      return Aspect_VKey_BracketRight;
+    case XK_BackSpace:
+      return Aspect_VKey_Backspace;
+    case XK_Tab:
+      return Aspect_VKey_Tab;
+    //case XK_Linefeed:
+    case XK_Return:
+    case XK_KP_Enter:
+      return Aspect_VKey_Enter;
+    //case XK_Pause:
+    //  return Aspect_VKey_Pause;
+    case XK_Escape:
+      return Aspect_VKey_Escape;
+    case XK_Home:
+      return Aspect_VKey_Home;
+    case XK_Left:
+      return Aspect_VKey_Left;
+    case XK_Up:
+      return Aspect_VKey_Up;
+    case XK_Right:
+      return Aspect_VKey_Right;
+    case XK_Down:
+      return Aspect_VKey_Down;
+    case XK_Prior:
+      return Aspect_VKey_PageUp;
+    case XK_Next:
+      return Aspect_VKey_PageDown;
+    case XK_End:
+      return Aspect_VKey_End;
+    //case XK_Insert:
+    //  return Aspect_VKey_Insert;
+    case XK_Menu:
+      return Aspect_VKey_Menu;
+    case XK_Num_Lock:
+      return Aspect_VKey_Numlock;
+    //case XK_KP_Delete:
+    //  return Aspect_VKey_NumDelete;
+    case XK_KP_Multiply:
+      return Aspect_VKey_NumpadMultiply;
+    case XK_KP_Add:
+      return Aspect_VKey_NumpadAdd;
+    //case XK_KP_Separator:
+    //  return Aspect_VKey_Separator;
+    case XK_KP_Subtract:
+      return Aspect_VKey_NumpadSubtract;
+    //case XK_KP_Decimal:
+    //  return Aspect_VKey_Decimal;
+    case XK_KP_Divide:
+      return Aspect_VKey_NumpadDivide;
+    case XK_Shift_L:
+    case XK_Shift_R:
+      return Aspect_VKey_Shift;
+    case XK_Control_L:
+    case XK_Control_R:
+      return Aspect_VKey_Control;
+    //case XK_Caps_Lock:
+    //  return Aspect_VKey_CapsLock;
+    case XK_Alt_L:
+    case XK_Alt_R:
+      return Aspect_VKey_Alt;
+    //case XK_Super_L:
+    //case XK_Super_R:
+    //  return Aspect_VKey_Super;
+    case XK_Delete:
+      return Aspect_VKey_Delete;
+
+    case 0x1008FF11: // XF86AudioLowerVolume
+      return Aspect_VKey_VolumeDown;
+    case 0x1008FF12: // XF86AudioMute
+      return Aspect_VKey_VolumeMute;
+    case 0x1008FF13: // XF86AudioRaiseVolume
+      return Aspect_VKey_VolumeUp;
+
+    case 0x1008FF14: // XF86AudioPlay
+      return Aspect_VKey_MediaPlayPause;
+    case 0x1008FF15: // XF86AudioStop
+      return Aspect_VKey_MediaStop;
+    case 0x1008FF16: // XF86AudioPrev
+      return Aspect_VKey_MediaPreviousTrack;
+    case 0x1008FF17: // XF86AudioNext
+      return Aspect_VKey_MediaNextTrack;
+
+    case 0x1008FF18: // XF86HomePage
+      return Aspect_VKey_BrowserHome;
+    case 0x1008FF26: // XF86Back
+      return Aspect_VKey_BrowserBack;
+    case 0x1008FF27: // XF86Forward
+      return Aspect_VKey_BrowserForward;
+    case 0x1008FF28: // XF86Stop
+      return Aspect_VKey_BrowserStop;
+    case 0x1008FF29: // XF86Refresh
+      return Aspect_VKey_BrowserRefresh;
+  }
+  return Aspect_VKey_UNKNOWN;
+}
+
 #endif //  Win32 or Mac OS X
index 6ab4273..5356f07 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <Aspect_Window.hxx>
 
+#include <Aspect_VKey.hxx>
 #include <Aspect_DisplayConnection.hxx>
 #include <Aspect_FillMethod.hxx>
 #include <Aspect_GradientFillMethod.hxx>
@@ -38,6 +39,10 @@ class Aspect_GradientBackground;
 //! This class defines XLib window intended for creation of OpenGL context.
 class Xw_Window : public Aspect_Window
 {
+public:
+
+  //! Convert X11 virtual key (KeySym) into Aspect_VKey.
+  Standard_EXPORT static Aspect_VKey VirtualKeyFromNative (unsigned long theKey);
 
 public:
 
@@ -111,6 +116,9 @@ public:
     return myFBConfig;
   }
 
+  //! Sets window title.
+  Standard_EXPORT virtual void SetTitle (const TCollection_AsciiString& theTitle) Standard_OVERRIDE;
+
   //! Invalidate entire window content through generation of Expose event.
   //! This method does not aggregate multiple calls into single event - dedicated event will be sent on each call.
   //! When NULL display connection is specified, the connection specified on window creation will be used.
index 7f6cefd..2e3b657 100644 (file)
@@ -49,8 +49,8 @@ if {$aNbSelected3 != 4} {
 
 vselect 0 0
 
-vselect 75 230 235 320 -allowoverlap 1 1
-vselect 350 150 380 170 1
+vselect  75 230 235 320 -allowoverlap 1 1
+vselect 350 150 380 170 -allowOverlap 1 1
 vnbselected
 set aNbSelected4 [vnbselected]
 if {$aNbSelected4 != 13} {