// Created on: 2017-06-16 // Created by: Natalia ERMOLAEVA // Copyright (c) 2017 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. #if !defined _WIN32 #define QT_CLEAN_NAMESPACE /* avoid definition of INT32 and INT8 */ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #elif defined(__APPLE__) && !defined(MACOSX_USE_GLX) #include #else #include #endif // the key for multi selection : #define MULTISELECTIONKEY Qt::ShiftModifier // the key for shortcut (use to activate dynamic onRotate, panning) #define CASCADESHORTCUTKEY Qt::ControlModifier // for elastic bean selection #define ValZWMin 1 // ======================================================================= // function : Constructor // purpose : // ======================================================================= View_Widget::View_Widget (QWidget* theParent) : QWidget (theParent), myCurrentMode (View_CurrentAction3d_Nothing), myFirst (true), myDefaultWidth (-1), myDefaultHeight (-1), myViewIsEnabled (true), myXmin (0), myYmin (0), myXmax (0), myYmax (0), myDragButtonDownX (0), myDragButtonDownY (0), myDragMultiButtonDownX (0), myDragMultiButtonDownY (0), myIsRectVisible (false), myRectBand (0) { myViewer = new View_Viewer (View_Viewer::DefaultColor()); myViewer->InitStandardViewer(); setAttribute (Qt::WA_PaintOnScreen); setAttribute (Qt::WA_NoSystemBackground); setMouseTracking (true); setBackgroundRole (QPalette::NoRole); // set focus policy to threat QContextMenuEvent from keyboard setFocusPolicy (Qt::StrongFocus); initViewActions(); initCursors(); } // ======================================================================= // function : SetPredefinedSize // purpose : // ======================================================================= void View_Widget::SetPredefinedSize (int theDefaultWidth, int theDefaultHeight) { myDefaultWidth = theDefaultWidth; myDefaultHeight = theDefaultHeight; } // ======================================================================= // function : Init // purpose : // ======================================================================= void View_Widget::Init() { myViewer->CreateView(); #ifdef _WIN32 Aspect_Handle aWindowHandle = (Aspect_Handle)winId(); Handle(Aspect_Window) aWnd = new WNT_Window (aWindowHandle); #if OCC_VERSION_HEX <= 0x060901 myViewer->GetView()->SetZClippingDepth (0.5); myViewer->GetView()->SetZClippingWidth (0.5); #endif #elif defined (__APPLE__) && !defined (MACOSX_USE_GLX) NSView* aViewHandle = (NSView*)winId(); Handle(Aspect_Window) aWnd = new Cocoa_Window (aViewHandle); #else Window aWindowHandle = (Window)winId(); Handle(Aspect_DisplayConnection) aDispConnection = myViewer->GetContext()->CurrentViewer()->Driver()->GetDisplayConnection(); Handle(Aspect_Window) aWnd = new Xw_Window (aDispConnection, aWindowHandle); #endif myViewer->SetWindow (aWnd); myViewer->GetView()->SetBackgroundColor (View_Viewer::DefaultColor()); myViewer->GetView()->MustBeResized(); } // ======================================================================= // function : GetDisplayMode // purpose : // ======================================================================= int View_Widget::GetDisplayMode() const { return myViewActions[View_ViewActionType_DisplayModeId]->isChecked() ? AIS_Shaded : AIS_WireFrame; } // ======================================================================= // function : paintEvent // purpose : // ======================================================================= void View_Widget::paintEvent (QPaintEvent* /*theEvent*/) { #if QT_VERSION < 0x050000 if (myFirst) { Init(); myFirst = false; } #endif if (myViewer->GetView()) myViewer->GetView()->Redraw(); } // ======================================================================= // function : resizeEvent // purpose : // ======================================================================= void View_Widget::resizeEvent (QResizeEvent* /*theEvent*/) { #if QT_VERSION > 0x050000 if (myFirst) { Init(); myFirst = false; } #endif if (myViewer->GetView()) myViewer->GetView()->MustBeResized(); } // ======================================================================= // function : sizeHint // purpose : // ======================================================================= QSize View_Widget::sizeHint() const { if (myDefaultWidth > 0 && myDefaultHeight > 0) return QSize (myDefaultWidth, myDefaultHeight); return QWidget::sizeHint(); } // ======================================================================= // function : SetEnabledView // purpose : // ======================================================================= void View_Widget::SetEnabledView (const bool theIsEnabled) { myViewIsEnabled = theIsEnabled; if (myViewer->GetView()) myViewer->GetView()->SetBackgroundColor (theIsEnabled ? View_Viewer::DefaultColor() : View_Viewer::DisabledColor()); for (int anActionId = View_ViewActionType_FitAllId; anActionId <= View_ViewActionType_DisplayModeId; anActionId++) GetViewAction ((View_ViewActionType)anActionId)->setEnabled (theIsEnabled); } // ======================================================================= // function : OnUpdateToggled // purpose : // ======================================================================= void View_Widget::OnUpdateToggled (bool isOn) { QAction* sentBy = (QAction*)sender(); if (sentBy == myViewActions[View_ViewActionType_DisplayModeId]) { sentBy->setIcon (isOn ? QIcon (":/icons/view_dm_wireframe.png") : QIcon (":/icons/view_dm_shading.png")); return; } if (!isOn) return; for (int anActionId = View_ViewActionType_FitAllId; anActionId <= View_ViewActionType_RotationId; anActionId++) { QAction* anAction = myViewActions[(View_ViewActionType)anActionId]; if ((anAction == myViewActions[View_ViewActionType_FitAreaId]) || (anAction == myViewActions[View_ViewActionType_ZoomId]) || (anAction == myViewActions[View_ViewActionType_PanId]) || (anAction == myViewActions[View_ViewActionType_RotationId])) { if (anAction && (anAction != sentBy)) { anAction->setChecked (false); } else { if (sentBy == myViewActions[View_ViewActionType_FitAreaId]) setActiveCursor (View_CursorMode_HandCursor); else if (sentBy == myViewActions[View_ViewActionType_ZoomId]) setActiveCursor (View_CursorMode_ZoomCursor); else if (sentBy == myViewActions[View_ViewActionType_PanId]) setActiveCursor (View_CursorMode_PanCursor); else if (sentBy == myViewActions[View_ViewActionType_RotationId]) setActiveCursor (View_CursorMode_RotationCursor); else setActiveCursor (View_CursorMode_DefaultCursor); } } } } // ======================================================================= // function : initViewActions // purpose : // ======================================================================= void View_Widget::initViewActions() { if (!myViewActions.empty()) return; createAction (View_ViewActionType_FitAllId, ":/icons/view_fitall.png", tr ("Fit All"), SLOT (OnFitAll())); createAction (View_ViewActionType_FitAreaId, ":/icons/view_fitarea.png", tr ("Fit Area"), SLOT (OnFitArea()), true); createAction (View_ViewActionType_ZoomId, ":/icons/view_zoom.png", tr ("Zoom"), SLOT (OnZoom()), true); createAction (View_ViewActionType_PanId, ":/icons/view_pan.png", tr ("Pan"), SLOT (OnPan()), true); createAction (View_ViewActionType_RotationId, ":/icons/view_rotate.png", tr ("Rotation"), SLOT (OnRotate()), true); createAction (View_ViewActionType_DisplayModeId, ":/icons/view_dm_shading.png", tr ("Display Mode"), SIGNAL (displayModeClicked()), true); for (int anActionId = View_ViewActionType_FitAreaId; anActionId <= View_ViewActionType_RotationId; anActionId++) connect (myViewActions[(View_ViewActionType)anActionId], SIGNAL (toggled(bool)), this, SLOT (OnUpdateToggled(bool))); } // ======================================================================= // function : initCursors // purpose : // ======================================================================= void View_Widget::initCursors() { if (!myCursors.empty()) return; myCursors[View_CursorMode_DefaultCursor] = QCursor (Qt::ArrowCursor); myCursors[View_CursorMode_HandCursor] = QCursor (Qt::PointingHandCursor); myCursors[View_CursorMode_PanCursor] = QCursor (Qt::SizeAllCursor); myCursors[View_CursorMode_ZoomCursor] = QCursor(QIcon (":/icons/cursor_zoom.png").pixmap (20, 20)); myCursors[View_CursorMode_RotationCursor] = QCursor(QIcon (":/icons/cursor_rotate.png").pixmap (20, 20)); } // ======================================================================= // function : mousePressEvent // purpose : // ======================================================================= void View_Widget::mousePressEvent (QMouseEvent* theEvent) { if (theEvent->button() == Qt::LeftButton) processLeftButtonDown (theEvent->buttons() | theEvent->modifiers(), theEvent->pos()); else if (theEvent->button() == Qt::MidButton) processMiddleButtonDown (theEvent->buttons() | theEvent->modifiers(), theEvent->pos()); else if (theEvent->button() == Qt::RightButton) processRightButtonDown (theEvent->buttons() | theEvent->modifiers(), theEvent->pos()); } // ======================================================================= // function : mouseReleaseEvent // purpose : // ======================================================================= void View_Widget::mouseReleaseEvent (QMouseEvent* theEvent) { if (theEvent->button() == Qt::LeftButton) processLeftButtonUp (theEvent->buttons() | theEvent->modifiers(), theEvent->pos()); else if (theEvent->button() == Qt::MidButton) processMiddleButtonUp (theEvent->buttons() | theEvent->modifiers(), theEvent->pos()); else if (theEvent->button() == Qt::RightButton) processRightButtonUp (theEvent->buttons() | theEvent->modifiers(), theEvent->pos()); } // ======================================================================= // function : mouseMoveEvent // purpose : // ======================================================================= void View_Widget::mouseMoveEvent (QMouseEvent* theEvent) { processMouseMove (theEvent->buttons() | theEvent->modifiers(), theEvent->pos()); } // ======================================================================= // function : activateCursor // purpose : // ======================================================================= void View_Widget::activateCursor (const View_CurrentAction3d theMode) { switch (theMode) { case View_CurrentAction3d_DynamicPanning: { setActiveCursor (View_CursorMode_PanCursor); break; } case View_CurrentAction3d_DynamicZooming: { setActiveCursor (View_CursorMode_ZoomCursor); break; } case View_CurrentAction3d_DynamicRotation: { setActiveCursor (View_CursorMode_RotationCursor); break; } case View_CurrentAction3d_WindowZooming: { setActiveCursor (View_CursorMode_HandCursor); break; } case View_CurrentAction3d_Nothing: default: { setActiveCursor (View_CursorMode_DefaultCursor); break; } } } // ======================================================================= // function : processLeftButtonDown // purpose : // ======================================================================= void View_Widget::processLeftButtonDown (const int theFlags, const QPoint thePoint) { // save the current mouse coordinate in min myXmin = thePoint.x(); myYmin = thePoint.y(); myXmax = thePoint.x(); myYmax = thePoint.y(); if (theFlags & CASCADESHORTCUTKEY) { myCurrentMode = View_CurrentAction3d_DynamicZooming; OnUpdateToggled(true); } else { switch (myCurrentMode) { case View_CurrentAction3d_Nothing: { if (theFlags & MULTISELECTIONKEY) processDragMultiEvent (myXmax, myYmax, View_DragMode_ButtonDown); else processDragEvent (myXmax, myYmax, View_DragMode_ButtonDown); break; } case View_CurrentAction3d_DynamicZooming: case View_CurrentAction3d_WindowZooming: case View_CurrentAction3d_DynamicPanning: break; case View_CurrentAction3d_DynamicRotation: { myViewer->GetView()->StartRotation (thePoint.x(), thePoint.y()); break; } default: { throw Standard_ProgramError ("View_Widget::processLeftButtonDown : Incompatible Current Mode"); break; } } } activateCursor (myCurrentMode); } // ======================================================================= // function : processMiddleButtonDown // purpose : // ======================================================================= void View_Widget::processMiddleButtonDown (const int theFlags, const QPoint /*thePoint*/) { if (theFlags & CASCADESHORTCUTKEY) { myCurrentMode = View_CurrentAction3d_DynamicPanning; OnUpdateToggled(true); } activateCursor (myCurrentMode); } // ======================================================================= // function : processRightButtonDown // purpose : // ======================================================================= void View_Widget::processRightButtonDown (const int theFlags, const QPoint thePoint) { if (theFlags & CASCADESHORTCUTKEY) { myCurrentMode = View_CurrentAction3d_DynamicRotation; myViewer->GetView()->StartRotation (thePoint.x(), thePoint.y()); OnUpdateToggled(true); } else { popup (thePoint.x(), thePoint.y()); } activateCursor (myCurrentMode); } // ======================================================================= // function : processLeftButtonUp // purpose : // ======================================================================= void View_Widget::processLeftButtonUp (const int theFlags, const QPoint thePoint) { switch (myCurrentMode) { case View_CurrentAction3d_Nothing: { if (thePoint.x() == myXmin && thePoint.y() == myYmin) { // no offset between down and up --> selectEvent myXmax = thePoint.x(); myYmax = thePoint.y(); if (theFlags & MULTISELECTIONKEY) processInputMultiEvent (thePoint.x(), thePoint.y()); else processInputEvent (thePoint.x(), thePoint.y()); } else { drawRectangle (myXmin, myYmin, myXmax, myYmax, Standard_False); myXmax = thePoint.x(); myYmax = thePoint.y(); if (theFlags & MULTISELECTIONKEY) processDragMultiEvent (thePoint.x(), thePoint.y(), View_DragMode_ButtonUp); else processDragEvent (thePoint.x(), thePoint.y(), View_DragMode_ButtonUp); } break; } case View_CurrentAction3d_DynamicZooming: break; case View_CurrentAction3d_WindowZooming: { drawRectangle (myXmin, myYmin, myXmax, myYmax, Standard_False); myXmax = thePoint.x(); myYmax = thePoint.y(); if ((abs(myXmin - myXmax) > ValZWMin) || (abs(myYmin - myYmax) > ValZWMin)) myViewer->GetView()->WindowFitAll (myXmin, myYmin, myXmax, myYmax); break; } case View_CurrentAction3d_DynamicPanning: break; case View_CurrentAction3d_DynamicRotation: break; default: { throw Standard_ProgramError("View_Widget::processLeftButtonUp : Incompatible Current Mode"); break; } } myDragButtonDownX = 0; myDragButtonDownY = 0; myDragMultiButtonDownX = 0; myDragMultiButtonDownY = 0; activateCursor (myCurrentMode); emit selectionChanged(); } // ======================================================================= // function : processMiddleButtonUp // purpose : // ======================================================================= void View_Widget::processMiddleButtonUp (const int /*theFlags*/, const QPoint /*thePoint*/) { myCurrentMode = View_CurrentAction3d_Nothing; activateCursor (myCurrentMode); } // ======================================================================= // function : processRightButtonUp // purpose : // ======================================================================= void View_Widget::processRightButtonUp (const int /*theFlags*/, const QPoint thePoint) { if (myCurrentMode == View_CurrentAction3d_Nothing) { popup (thePoint.x(), thePoint.y()); } else myCurrentMode = View_CurrentAction3d_Nothing; activateCursor (myCurrentMode); } // ======================================================================= // function : processMouseMove // purpose : // ======================================================================= void View_Widget::processMouseMove (const int theFlags, const QPoint thePoint) { if (theFlags & Qt::LeftButton || theFlags & Qt::RightButton || theFlags & Qt::MidButton) { switch (myCurrentMode) { case View_CurrentAction3d_Nothing: { myXmax = thePoint.x(); myYmax = thePoint.y(); drawRectangle (myXmin, myYmin, myXmax, myYmax, Standard_False); if (theFlags & MULTISELECTIONKEY) processDragMultiEvent (myXmax, myYmax, View_DragMode_ButtonMove); else processDragEvent (myXmax, myYmax, View_DragMode_ButtonMove); drawRectangle (myXmin, myYmin, myXmax, myYmax, Standard_True); break; } case View_CurrentAction3d_DynamicZooming: { myViewer->GetView()->Zoom (myXmax, myYmax, thePoint.x(), thePoint.y()); myXmax = thePoint.x(); myYmax = thePoint.y(); break; } case View_CurrentAction3d_WindowZooming: { myXmax = thePoint.x(); myYmax = thePoint.y(); drawRectangle (myXmin, myYmin, myXmax, myYmax, Standard_False); drawRectangle (myXmin, myYmin, myXmax, myYmax, Standard_True); break; } case View_CurrentAction3d_DynamicPanning: { myViewer->GetView()->Pan (thePoint.x() - myXmax, myYmax - thePoint.y()); myXmax = thePoint.x(); myYmax = thePoint.y(); break; } case View_CurrentAction3d_DynamicRotation: { myViewer->GetView()->Rotation (thePoint.x(), thePoint.y()); myViewer->GetView()->Redraw(); break; } default: { throw Standard_ProgramError("View_Widget::processMouseMove : Incompatible Current Mode"); break; } } } else { myXmax = thePoint.x(); myYmax = thePoint.y(); if (theFlags & MULTISELECTIONKEY) processMoveMultiEvent (thePoint.x(), thePoint.y()); else processMoveEvent (thePoint.x(), thePoint.y()); } } // ======================================================================= // function : processDragEvent // purpose : // ======================================================================= void View_Widget::processDragEvent (const Standard_Integer theX, const Standard_Integer theY, const View_DragMode& theState) { //myDragButtonDownX = 0; //myDragButtonDownY = 0; switch (theState) { case View_DragMode_ButtonDown: { myDragButtonDownX = theX; myDragButtonDownY = theY; break; } case View_DragMode_ButtonMove: break; case View_DragMode_ButtonUp: { myViewer->GetContext()->Select (myDragButtonDownX, myDragButtonDownY, theX, theY, myViewer->GetView(), Standard_True); emit selectionChanged(); break; } default: break; } } // ======================================================================= // function : processInputEvent // purpose : // ======================================================================= void View_Widget::processInputEvent (const Standard_Integer/* theX*/, const Standard_Integer/* theY*/) { myViewer->GetContext()->Select (Standard_True); emit selectionChanged(); } // ======================================================================= // function : processMoveEvent // purpose : // ======================================================================= void View_Widget::processMoveEvent (const Standard_Integer theX, const Standard_Integer theY) { myViewer->GetContext()->MoveTo (theX, theY, myViewer->GetView(), Standard_True); } // ======================================================================= // function : processDragMultiEvent // purpose : // ======================================================================= void View_Widget::processDragMultiEvent (const Standard_Integer theX, const Standard_Integer theY, const View_DragMode& theState) { switch (theState) { case View_DragMode_ButtonDown: { myDragMultiButtonDownX = theX; myDragMultiButtonDownY = theY; break; } case View_DragMode_ButtonMove: { myViewer->GetContext()->ShiftSelect (myDragMultiButtonDownX, myDragMultiButtonDownY, theX, theY, myViewer->GetView(), Standard_True); emit selectionChanged(); break; } case View_DragMode_ButtonUp: default: break; } } // ======================================================================= // function : processInputMultiEvent // purpose : // ======================================================================= void View_Widget::processInputMultiEvent (const Standard_Integer /*theX*/, const Standard_Integer /*theY*/) { myViewer->GetContext()->ShiftSelect (Standard_True); emit selectionChanged(); } // ======================================================================= // function : drawRectangle // purpose : // ======================================================================= void View_Widget::drawRectangle (const Standard_Integer theMinX, const Standard_Integer MinY, const Standard_Integer MaxX, const Standard_Integer MaxY, const Standard_Boolean theToDraw) { Standard_Integer StoredMinX, StoredMaxX, StoredMinY, StoredMaxY; StoredMinX = (theMinX < MaxX) ? theMinX : MaxX; StoredMinY = (MinY < MaxY) ? MinY : MaxY; StoredMaxX = (theMinX > MaxX) ? theMinX : MaxX; StoredMaxY = (MinY > MaxY) ? MinY : MaxY; QRect aRect; aRect.setRect(StoredMinX, StoredMinY, abs (StoredMaxX-StoredMinX), abs (StoredMaxY-StoredMinY)); if (!myRectBand) { myRectBand = new QRubberBand (QRubberBand::Rectangle, this); myRectBand->setStyle (QStyleFactory::create ("windows")); myRectBand->setGeometry (aRect); myRectBand->show(); } if (myIsRectVisible && !theToDraw) // move or up : erase at the old position { myRectBand->hide(); delete myRectBand; myRectBand = 0; myIsRectVisible = false; } if (theToDraw) // move : draw { myIsRectVisible = true; myRectBand->setGeometry (aRect); } } // ======================================================================= // function : createAction // purpose : // ======================================================================= void View_Widget::createAction (const View_ViewActionType theActionId, const QString& theIcon, const QString& theText, const char* theSlot, const bool isCheckable, const QString& theToolBar, const QString& theStatusBar) { QAction* anAction = new QAction (QIcon (theIcon), theText, this); anAction->setToolTip (!theToolBar.isEmpty() ? theToolBar : theText); anAction->setStatusTip (!theStatusBar.isEmpty() ? theStatusBar : theText); if (isCheckable) anAction->setCheckable (true); connect(anAction, SIGNAL (triggered()) , this, theSlot); myViewActions[theActionId] = anAction; } // ======================================================================= // function : setActiveCursor // purpose : // ======================================================================= void View_Widget::setActiveCursor (const View_CursorMode& theMode) { QCursor aCursor = myCursors[theMode]; setCursor (myCursors[theMode]); }