1 // Created on: 2017-06-16
2 // Created by: Natalia ERMOLAEVA
3 // Copyright (c) 2017 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15 
16 #include <inspector/ShapeView_Window.hxx>
17 #include <inspector/ShapeView_ItemRoot.hxx>
18 #include <inspector/ShapeView_ItemShape.hxx>
19 #include <inspector/ShapeView_TreeModel.hxx>
20 #include <inspector/ShapeView_VisibilityState.hxx>
21 
22 #include <inspector/Convert_Tools.hxx>
23 
24 #include <inspector/TreeModel_Tools.hxx>
25 #include <inspector/TreeModel_ContextMenu.hxx>
26 
27 #include <inspector/ViewControl_PropertyView.hxx>
28 #include <inspector/ViewControl_Tools.hxx>
29 #include <inspector/ViewControl_TreeView.hxx>
30 
31 #include <inspector/View_Displayer.hxx>
32 #include <inspector/View_PresentationType.hxx>
33 #include <inspector/View_ToolBar.hxx>
34 #include <inspector/View_Window.hxx>
35 #include <inspector/View_Viewer.hxx>
36 
37 #include <inspector/ShapeView_Window.hxx>
38 #include <inspector/ShapeView_OpenFileDialog.hxx>
39 #include <inspector/ShapeView_Tools.hxx>
40 
41 #include <BRep_Builder.hxx>
42 #include <BRepTools.hxx>
43 
44 #include <Standard_WarningsDisable.hxx>
45 #include <QApplication>
46 #include <QAction>
47 #include <QComboBox>
48 #include <QDockWidget>
49 #include <QDir>
50 #include <QFile>
51 #include <QFileDialog>
52 #include <QMainWindow>
53 #include <QMenu>
54 #include <QPlainTextEdit>
55 #include <QPushButton>
56 #include <QTextEdit>
57 #include <QTextStream>
58 #include <QToolBar>
59 #include <QToolButton>
60 #include <QTreeView>
61 #include <QWidget>
62 #include <QVBoxLayout>
63 #include <Standard_WarningsRestore.hxx>
64 
65 static const int DEFAULT_SHAPE_VIEW_WIDTH = 900;
66 static const int DEFAULT_SHAPE_VIEW_HEIGHT = 450;
67 static const int DEFAULT_SHAPE_VIEW_POSITION_X = 60;
68 static const int DEFAULT_SHAPE_VIEW_POSITION_Y = 60;
69 
70 static const int SHAPEVIEW_DEFAULT_TREE_VIEW_WIDTH = 600;
71 static const int SHAPEVIEW_DEFAULT_TREE_VIEW_HEIGHT = 500;
72 
73 static const int SHAPEVIEW_DEFAULT_VIEW_WIDTH = 300;
74 static const int SHAPEVIEW_DEFAULT_VIEW_HEIGHT = 1000;
75 
76 // =======================================================================
77 // function : Constructor
78 // purpose :
79 // =======================================================================
ShapeView_Window(QWidget * theParent)80 ShapeView_Window::ShapeView_Window (QWidget* theParent)
81 : QObject (theParent)
82 {
83   myMainWindow = new QMainWindow (theParent);
84 
85   myTreeView = new ViewControl_TreeView (myMainWindow);
86   ((ViewControl_TreeView*)myTreeView)->SetPredefinedSize (QSize (SHAPEVIEW_DEFAULT_TREE_VIEW_WIDTH,
87                                                                  SHAPEVIEW_DEFAULT_TREE_VIEW_HEIGHT));
88   myTreeView->setContextMenuPolicy (Qt::CustomContextMenu);
89   connect (myTreeView, SIGNAL (customContextMenuRequested (const QPoint&)),
90           this, SLOT (onTreeViewContextMenuRequested (const QPoint&)));
91   new TreeModel_ContextMenu (myTreeView);
92   ShapeView_TreeModel* aModel = new ShapeView_TreeModel (myTreeView);
93   aModel->InitColumns();
94 
95   myTreeView->setModel (aModel);
96   ShapeView_VisibilityState* aVisibilityState = new ShapeView_VisibilityState (aModel);
97   aModel->SetVisibilityState (aVisibilityState);
98   TreeModel_Tools::UseVisibilityColumn (myTreeView);
99   QObject::connect (myTreeView, SIGNAL (clicked (const QModelIndex&)),
100                     aVisibilityState, SLOT (OnClicked(const QModelIndex&)));
101 
102   QItemSelectionModel* aSelModel = new QItemSelectionModel (myTreeView->model(), myTreeView);
103   myTreeView->setSelectionModel (aSelModel);
104   connect (aSelModel, SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)),
105            this, SLOT (onTreeViewSelectionChanged (const QItemSelection&, const QItemSelection&)));
106 
107   QModelIndex aParentIndex = myTreeView->model()->index (0, 0);
108   myTreeView->setExpanded (aParentIndex, true);
109   myMainWindow->setCentralWidget (myTreeView);
110 
111   // property view
112   myPropertyView = new ViewControl_PropertyView (myMainWindow,
113     QSize(SHAPEVIEW_DEFAULT_VIEW_WIDTH, SHAPEVIEW_DEFAULT_VIEW_HEIGHT));
114   myPropertyPanelWidget = new QDockWidget (tr ("PropertyPanel"), myMainWindow);
115   myPropertyPanelWidget->setObjectName (myPropertyPanelWidget->windowTitle());
116   myPropertyPanelWidget->setTitleBarWidget (new QWidget(myMainWindow));
117   myPropertyPanelWidget->setWidget (myPropertyView->GetControl());
118   myMainWindow->addDockWidget (Qt::RightDockWidgetArea, myPropertyPanelWidget);
119 
120   // view
121   myViewWindow = new View_Window (myMainWindow, NULL, false);
122   connect (myViewWindow, SIGNAL(eraseAllPerformed()), this, SLOT(onEraseAllPerformed()));
123   aVisibilityState->SetDisplayer (myViewWindow->Displayer());
124   aVisibilityState->SetPresentationType (View_PresentationType_Main);
125   myViewWindow->SetPredefinedSize (SHAPEVIEW_DEFAULT_VIEW_WIDTH, SHAPEVIEW_DEFAULT_VIEW_HEIGHT);
126 
127   QDockWidget* aViewDockWidget = new QDockWidget (tr ("View"), myMainWindow);
128   aViewDockWidget->setObjectName (aViewDockWidget->windowTitle());
129   aViewDockWidget->setWidget (myViewWindow);
130   aViewDockWidget->setTitleBarWidget (myViewWindow->ViewToolBar()->GetControl());
131   myMainWindow->addDockWidget (Qt::RightDockWidgetArea, aViewDockWidget);
132 
133   myMainWindow->splitDockWidget(myPropertyPanelWidget, aViewDockWidget, Qt::Vertical);
134 
135   myMainWindow->resize (DEFAULT_SHAPE_VIEW_WIDTH, DEFAULT_SHAPE_VIEW_HEIGHT);
136   myMainWindow->move (DEFAULT_SHAPE_VIEW_POSITION_X, DEFAULT_SHAPE_VIEW_POSITION_Y);
137 }
138 
139 // =======================================================================
140 // function : Destructor
141 // purpose :
142 // =======================================================================
~ShapeView_Window()143 ShapeView_Window::~ShapeView_Window()
144 {
145 }
146 
147 // =======================================================================
148 // function : SetParent
149 // purpose :
150 // =======================================================================
SetParent(void * theParent)151 void ShapeView_Window::SetParent (void* theParent)
152 {
153   QWidget* aParent = (QWidget*)theParent;
154   if (aParent)
155   {
156     QLayout* aLayout = aParent->layout();
157     if (aLayout)
158       aLayout->addWidget (GetMainWindow());
159   }
160 }
161 
162 // =======================================================================
163 // function : FillActionsMenu
164 // purpose :
165 // =======================================================================
FillActionsMenu(void * theMenu)166 void ShapeView_Window::FillActionsMenu (void* theMenu)
167 {
168   QMenu* aMenu = (QMenu*)theMenu;
169   QList<QDockWidget*> aDockwidgets = myMainWindow->findChildren<QDockWidget*>();
170   for (QList<QDockWidget*>::iterator it = aDockwidgets.begin(); it != aDockwidgets.end(); ++it)
171   {
172     QDockWidget* aDockWidget = *it;
173     if (aDockWidget->parentWidget() == myMainWindow)
174       aMenu->addAction (aDockWidget->toggleViewAction());
175   }
176 }
177 
178 // =======================================================================
179 // function : GetPreferences
180 // purpose :
181 // =======================================================================
GetPreferences(TInspectorAPI_PreferencesDataMap & theItem)182 void ShapeView_Window::GetPreferences (TInspectorAPI_PreferencesDataMap& theItem)
183 {
184   theItem.Bind ("geometry",  TreeModel_Tools::ToString (myMainWindow->saveState()).toStdString().c_str());
185 
186   QMap<QString, QString> anItems;
187   TreeModel_Tools::SaveState (myTreeView, anItems);
188   View_Window::SaveState(myViewWindow, anItems);
189   for (QMap<QString, QString>::const_iterator anItemsIt = anItems.begin(); anItemsIt != anItems.end(); anItemsIt++)
190     theItem.Bind (anItemsIt.key().toStdString().c_str(), anItemsIt.value().toStdString().c_str());
191 }
192 
193 // =======================================================================
194 // function : SetPreferences
195 // purpose :
196 // =======================================================================
SetPreferences(const TInspectorAPI_PreferencesDataMap & theItem)197 void ShapeView_Window::SetPreferences (const TInspectorAPI_PreferencesDataMap& theItem)
198 {
199   if (theItem.IsEmpty())
200   {
201     TreeModel_Tools::SetDefaultHeaderSections (myTreeView);
202     return;
203   }
204 
205   for (TInspectorAPI_IteratorOfPreferencesDataMap anItemIt (theItem); anItemIt.More(); anItemIt.Next())
206   {
207     if (anItemIt.Key().IsEqual ("geometry"))
208       myMainWindow->restoreState (TreeModel_Tools::ToByteArray (anItemIt.Value().ToCString()));
209     else if (TreeModel_Tools::RestoreState (myTreeView, anItemIt.Key().ToCString(), anItemIt.Value().ToCString()))
210       continue;
211     else if (View_Window::RestoreState(myViewWindow, anItemIt.Key().ToCString(), anItemIt.Value().ToCString()))
212       continue;
213   }
214 }
215 
216 // =======================================================================
217 // function : UpdateContent
218 // purpose :
219 // =======================================================================
UpdateContent()220 void ShapeView_Window::UpdateContent()
221 {
222   TCollection_AsciiString aName = "TKShapeView";
223   if (myParameters->FindParameters (aName))
224   {
225     NCollection_List<Handle(Standard_Transient)> aParameters = myParameters->Parameters (aName);
226     // Init will remove from parameters those, that are processed only one time (TShape)
227     Init(aParameters);
228     myParameters->SetParameters (aName, aParameters);
229   }
230   if (myParameters->FindFileNames(aName))
231   {
232     for (NCollection_List<TCollection_AsciiString>::Iterator aFilesIt(myParameters->FileNames(aName));
233          aFilesIt.More(); aFilesIt.Next())
234       OpenFile (aFilesIt.Value());
235 
236     NCollection_List<TCollection_AsciiString> aNames;
237     myParameters->SetFileNames (aName, aNames);
238   }
239   // make TopoDS_TShape selected if exist in select parameters
240   NCollection_List<Handle(Standard_Transient)> anObjects;
241   if (myParameters->GetSelectedObjects(aName, anObjects))
242   {
243     ShapeView_TreeModel* aModel = dynamic_cast<ShapeView_TreeModel*> (myTreeView->model());
244     QItemSelectionModel* aSelectionModel = myTreeView->selectionModel();
245     aSelectionModel->clear();
246     for (NCollection_List<Handle(Standard_Transient)>::Iterator aParamsIt (anObjects);
247          aParamsIt.More(); aParamsIt.Next())
248     {
249       Handle(Standard_Transient) anObject = aParamsIt.Value();
250       Handle(TopoDS_TShape) aShapePointer = Handle(TopoDS_TShape)::DownCast (anObject);
251       if (aShapePointer.IsNull())
252         continue;
253 
254       TopoDS_Shape aShape;
255       aShape.TShape (aShapePointer);
256 
257       QModelIndex aShapeIndex = aModel->FindIndex (aShape);
258       if (!aShapeIndex.isValid())
259         continue;
260        aSelectionModel->select (aShapeIndex, QItemSelectionModel::Select);
261        myTreeView->scrollTo (aShapeIndex);
262     }
263     myParameters->SetSelected (aName, NCollection_List<Handle(Standard_Transient)>());
264   }
265 }
266 
267 // =======================================================================
268 // function : Init
269 // purpose :
270 // =======================================================================
Init(NCollection_List<Handle (Standard_Transient)> & theParameters)271 void ShapeView_Window::Init (NCollection_List<Handle(Standard_Transient)>& theParameters)
272 {
273   Handle(AIS_InteractiveContext) aContext;
274   NCollection_List<Handle(Standard_Transient)> aParameters;
275 
276   TCollection_AsciiString aPluginName ("TKShapeView");
277   NCollection_List<TCollection_AsciiString> aSelectedParameters;
278   if (myParameters->FindSelectedNames (aPluginName)) // selected names have TShape parameters
279     aSelectedParameters = myParameters->GetSelectedNames (aPluginName);
280 
281   NCollection_List<TCollection_AsciiString>::Iterator aParamsIt (aSelectedParameters);
282   for (NCollection_List<Handle(Standard_Transient)>::Iterator anObjectsIt (theParameters);
283        anObjectsIt.More(); anObjectsIt.Next())
284   {
285     Handle(Standard_Transient) anObject = anObjectsIt.Value();
286     Handle(TopoDS_TShape) aShapePointer = Handle(TopoDS_TShape)::DownCast (anObject);
287     if (!aShapePointer.IsNull())
288     {
289       TopoDS_Shape aShape;
290       aShape.TShape (aShapePointer);
291       if (aParamsIt.More())
292       {
293         // each Transient object has own location/orientation description
294         TInspectorAPI_PluginParameters::ParametersToShape (aParamsIt.Value(), aShape);
295         aParamsIt.Next();
296       }
297       addShape (aShape);
298     }
299     else
300     {
301       aParameters.Append (anObject);
302       if (aContext.IsNull())
303         aContext = Handle(AIS_InteractiveContext)::DownCast (anObject);
304     }
305   }
306   if (!aContext.IsNull())
307     myViewWindow->SetContext (View_ContextType_External, aContext);
308 
309   theParameters = aParameters;
310   myParameters->SetSelectedNames (aPluginName, NCollection_List<TCollection_AsciiString>());
311 }
312 
313 // =======================================================================
314 // function : OpenFile
315 // purpose :
316 // =======================================================================
OpenFile(const TCollection_AsciiString & theFileName)317 void ShapeView_Window::OpenFile(const TCollection_AsciiString& theFileName)
318 {
319   TopoDS_Shape aShape = Convert_Tools::ReadShape (theFileName);
320   if (!aShape.IsNull())
321     addShape(aShape);
322 }
323 
324 // =======================================================================
325 // function : RemoveAllShapes
326 // purpose :
327 // =======================================================================
RemoveAllShapes()328 void ShapeView_Window::RemoveAllShapes()
329 {
330   ShapeView_TreeModel* aModel = dynamic_cast<ShapeView_TreeModel*> (myTreeView->model());
331   aModel->RemoveAllShapes();
332 }
333 
334 // =======================================================================
335 // function : addShape
336 // purpose :
337 // =======================================================================
addShape(const TopoDS_Shape & theShape)338 void ShapeView_Window::addShape (const TopoDS_Shape& theShape)
339 {
340   ShapeView_TreeModel* aModel = dynamic_cast<ShapeView_TreeModel*> (myTreeView->model());
341   aModel->AddShape (theShape);
342 }
343 
344 // =======================================================================
345 // function : onTreeViewContextMenuRequested
346 // purpose :
347 // =======================================================================
onTreeViewContextMenuRequested(const QPoint & thePosition)348 void ShapeView_Window::onTreeViewContextMenuRequested (const QPoint& thePosition)
349 {
350   QItemSelectionModel* aModel = myTreeView->selectionModel();
351   if (!aModel)
352     return;
353 
354   QModelIndex anIndex = TreeModel_ModelBase::SingleSelected (aModel->selectedIndexes(), 0);
355   TreeModel_ItemBasePtr anItemBase = TreeModel_ModelBase::GetItemByIndex (anIndex);
356   if (!anItemBase)
357     return;
358 
359   QMenu* aMenu = new QMenu(myMainWindow);
360   ShapeView_ItemRootPtr aRootItem = itemDynamicCast<ShapeView_ItemRoot> (anItemBase);
361   if (aRootItem) {
362     aMenu->addAction (ViewControl_Tools::CreateAction ("Load BREP file", SLOT (onLoadFile()), myMainWindow, this));
363     aMenu->addAction (ViewControl_Tools::CreateAction ("Remove all shape items", SLOT (onClearView()), myMainWindow, this));
364   }
365   else {
366     aMenu->addAction (ViewControl_Tools::CreateAction ("Export to BREP", SLOT (onExportToBREP()), myMainWindow, this));
367     ShapeView_ItemShapePtr aShapeItem = itemDynamicCast<ShapeView_ItemShape>(anItemBase);
368     const TopoDS_Shape& aShape = aShapeItem->GetItemShape();
369     TopAbs_ShapeEnum anExplodeType = aShapeItem->ExplodeType();
370     NCollection_List<TopAbs_ShapeEnum> anExplodeTypes;
371     ShapeView_Tools::IsPossibleToExplode (aShape, anExplodeTypes);
372     if (anExplodeTypes.Size() > 0)
373     {
374       QMenu* anExplodeMenu = aMenu->addMenu ("Explode");
375       for (NCollection_List<TopAbs_ShapeEnum>::Iterator anExpIterator (anExplodeTypes); anExpIterator.More();
376         anExpIterator.Next())
377       {
378         TopAbs_ShapeEnum aType = anExpIterator.Value();
379         QAction* anAction = ViewControl_Tools::CreateAction (TopAbs::ShapeTypeToString (aType), SLOT (onExplode()), myMainWindow, this);
380         anExplodeMenu->addAction (anAction);
381         if (anExplodeType == aType)
382         {
383           anAction->setCheckable (true);
384           anAction->setChecked (true);
385         }
386       }
387       QAction* anAction = ViewControl_Tools::CreateAction ("NONE", SLOT (onExplode()), myMainWindow, this);
388       anExplodeMenu->addSeparator();
389       anExplodeMenu->addAction (anAction);
390     }
391   }
392 
393   QPoint aPoint = myTreeView->mapToGlobal (thePosition);
394   aMenu->exec (aPoint);
395 }
396 
397 // =======================================================================
398 // function : onTreeViewSelectionChanged
399 // purpose :
400 // =======================================================================
onTreeViewSelectionChanged(const QItemSelection &,const QItemSelection &)401 void ShapeView_Window::onTreeViewSelectionChanged (const QItemSelection&,
402                                                    const QItemSelection&)
403 {
404   QApplication::setOverrideCursor (Qt::WaitCursor);
405 
406   if (myPropertyPanelWidget->toggleViewAction()->isChecked())
407     myPropertyView->Init (ViewControl_Tools::CreateTableModelValues (myTreeView->selectionModel()));
408 
409   QApplication::restoreOverrideCursor();
410 }
411 
412 // =======================================================================
413 // function : onEraseAllPerformed
414 // purpose :
415 // =======================================================================
onEraseAllPerformed()416 void ShapeView_Window::onEraseAllPerformed()
417 {
418   ShapeView_TreeModel* aTreeModel = dynamic_cast<ShapeView_TreeModel*> (myTreeView->model());
419 
420   // TODO: provide update for only visibility state for better performance  TopoDS_Shape myCustomShape;
421 
422   aTreeModel->Reset();
423   aTreeModel->EmitLayoutChanged();
424 }
425 
426 // =======================================================================
427 // function : onExplode
428 // purpose :
429 // =======================================================================
onExplode()430 void ShapeView_Window::onExplode()
431 {
432   QItemSelectionModel* aModel = myTreeView->selectionModel();
433   if (!aModel)
434     return;
435 
436   QModelIndex anIndex = TreeModel_ModelBase::SingleSelected(aModel->selectedIndexes(), 0);
437   TreeModel_ItemBasePtr anItemBase = TreeModel_ModelBase::GetItemByIndex(anIndex);
438   if (!anItemBase)
439     return;
440 
441   ShapeView_ItemShapePtr aShapeItem = itemDynamicCast<ShapeView_ItemShape>(anItemBase);
442   if (!aShapeItem)
443     return;
444 
445   QAction* anAction = (QAction*)sender();
446   if (!anAction)
447     return;
448 
449   QApplication::setOverrideCursor (Qt::WaitCursor);
450   TopAbs_ShapeEnum aShapeType;
451   if (anAction->text() == "NONE")
452     aShapeType = TopAbs_SHAPE;
453   else
454     aShapeType = TopAbs::ShapeTypeFromString(anAction->text().toStdString().c_str());
455 
456   myViewWindow->Displayer()->EraseAllPresentations();
457   aShapeItem->SetExplodeType(aShapeType);
458 
459   //anItemBase->Parent()->Reset(); - TODO (update only modified sub-tree)
460   ShapeView_TreeModel* aTreeModel = dynamic_cast<ShapeView_TreeModel*> (myTreeView->model());
461   aTreeModel->Reset();
462   aTreeModel->EmitLayoutChanged();
463   QApplication::restoreOverrideCursor();
464 }
465 
466 // =======================================================================
467 // function : onLoadFile
468 // purpose :
469 // =======================================================================
onLoadFile()470 void ShapeView_Window::onLoadFile()
471 {
472   QString aDataDirName = QDir::currentPath();
473 
474   QString aFileName = ShapeView_OpenFileDialog::OpenFile(0, aDataDirName);
475   aFileName = QDir().toNativeSeparators (aFileName);
476   if (aFileName.isEmpty())
477     return;
478 
479   QApplication::setOverrideCursor (Qt::WaitCursor);
480   onOpenFile(aFileName);
481   QApplication::restoreOverrideCursor();
482 }
483 
484 // =======================================================================
485 // function : onExportToBREP
486 // purpose :
487 // =======================================================================
onExportToBREP()488 void ShapeView_Window::onExportToBREP()
489 {
490   QString aFilter (tr ("Boundary representation file (*.brep *)"));
491   QString aSelectedFilter;
492   QString aFileName = QFileDialog::getSaveFileName (0, tr ("Export shape to file"), QString(), aFilter, &aSelectedFilter);
493 
494   QItemSelectionModel* aModel = myTreeView->selectionModel();
495   if (!aModel)
496     return;
497 
498   QModelIndexList aSelectedRows = aModel->selectedRows();
499   if (aSelectedRows.size() == 0)
500     return;
501 
502   QModelIndex aSelectedIndex = aSelectedRows.at (0);
503   TreeModel_ItemBasePtr anItemBase = TreeModel_ModelBase::GetItemByIndex (aSelectedIndex);
504   if (!anItemBase)
505     return;
506 
507   ShapeView_ItemShapePtr anItem = itemDynamicCast<ShapeView_ItemShape>(anItemBase);
508   if (!anItem)
509     return;
510 
511   TCollection_AsciiString aFileNameIndiced = aFileName.toStdString().c_str();
512   const TopoDS_Shape& aShape = anItem->GetItemShape();
513   BRepTools::Write (aShape, aFileNameIndiced.ToCString());
514   anItem->SetFileName (aFileNameIndiced.ToCString());
515   aFileName = aFileNameIndiced.ToCString();
516 }
517