7ec1c822a67feec8f67d38513d386483081b7f9b
[occt.git] / tools / ShapeView / ShapeView_Window.cxx
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/TreeModel_Tools.hxx>
23 #include <inspector/TreeModel_ContextMenu.hxx>
24
25 #include <inspector/ViewControl_Tools.hxx>
26 #include <inspector/ViewControl_TreeView.hxx>
27
28 #include <inspector/View_Displayer.hxx>
29 #include <inspector/View_PresentationType.hxx>
30 #include <inspector/View_Tools.hxx>
31 #include <inspector/View_ToolBar.hxx>
32 #include <inspector/View_Widget.hxx>
33 #include <inspector/View_Window.hxx>
34 #include <inspector/View_Viewer.hxx>
35
36 #include <inspector/ShapeView_Window.hxx>
37 #include <inspector/ShapeView_OpenFileDialog.hxx>
38 #include <inspector/ShapeView_Tools.hxx>
39
40 #include <BRep_Builder.hxx>
41 #include <BRepTools.hxx>
42
43 #include <Standard_WarningsDisable.hxx>
44 #include <QApplication>
45 #include <QAction>
46 #include <QComboBox>
47 #include <QDockWidget>
48 #include <QDir>
49 #include <QFile>
50 #include <QFileDialog>
51 #include <QMainWindow>
52 #include <QMenu>
53 #include <QPlainTextEdit>
54 #include <QPushButton>
55 #include <QTextEdit>
56 #include <QTextStream>
57 #include <QToolBar>
58 #include <QToolButton>
59 #include <QTreeView>
60 #include <QWidget>
61 #include <QVBoxLayout>
62 #include <Standard_WarningsRestore.hxx>
63
64 const int DEFAULT_TEXT_VIEW_WIDTH = 800;
65 const int DEFAULT_TEXT_VIEW_HEIGHT = 700;
66 const int DEFAULT_TEXT_VIEW_POSITION_X = 430;
67 const int DEFAULT_TEXT_VIEW_POSITION_Y = 30;
68 const int DEFAULT_TEXT_VIEW_DELTA = 100;
69
70 const int DEFAULT_SHAPE_VIEW_WIDTH = 900;
71 const int DEFAULT_SHAPE_VIEW_HEIGHT = 450;
72 const int DEFAULT_SHAPE_VIEW_POSITION_X = 60;
73 const int DEFAULT_SHAPE_VIEW_POSITION_Y = 60;
74
75 const int SHAPEVIEW_DEFAULT_TREE_VIEW_WIDTH = 600;
76 const int SHAPEVIEW_DEFAULT_TREE_VIEW_HEIGHT = 500;
77
78 const int SHAPEVIEW_DEFAULT_VIEW_WIDTH = 300;
79 const int SHAPEVIEW_DEFAULT_VIEW_HEIGHT = 1000;
80
81 // =======================================================================
82 // function : Constructor
83 // purpose :
84 // =======================================================================
85 ShapeView_Window::ShapeView_Window (QWidget* theParent)
86 : QObject (theParent), myNextPosition (0)
87 {
88   myMainWindow = new QMainWindow (theParent);
89
90   myTreeView = new ViewControl_TreeView (myMainWindow);
91   ((ViewControl_TreeView*)myTreeView)->SetPredefinedSize (QSize (SHAPEVIEW_DEFAULT_TREE_VIEW_WIDTH,
92                                                                  SHAPEVIEW_DEFAULT_TREE_VIEW_HEIGHT));
93   myTreeView->setContextMenuPolicy (Qt::CustomContextMenu);
94   connect (myTreeView, SIGNAL (customContextMenuRequested (const QPoint&)),
95           this, SLOT (onTreeViewContextMenuRequested (const QPoint&)));
96   new TreeModel_ContextMenu (myTreeView);
97   ShapeView_TreeModel* aModel = new ShapeView_TreeModel (myTreeView);
98   myTreeView->setModel (aModel);
99   ShapeView_VisibilityState* aVisibilityState = new ShapeView_VisibilityState (aModel);
100   aModel->SetVisibilityState (aVisibilityState);
101   TreeModel_Tools::UseVisibilityColumn (myTreeView);
102
103   QModelIndex aParentIndex = myTreeView->model()->index (0, 0);
104   myTreeView->setExpanded (aParentIndex, true);
105   myMainWindow->setCentralWidget (myTreeView);
106
107   // view
108   myViewWindow = new View_Window (myMainWindow, false);
109   connect (myViewWindow, SIGNAL(eraseAllPerformed()), this, SLOT(onEraseAllPerformed()));
110   aVisibilityState->SetDisplayer (myViewWindow->GetDisplayer());
111   aVisibilityState->SetPresentationType (View_PresentationType_Main);
112   myViewWindow->GetView()->SetPredefinedSize (SHAPEVIEW_DEFAULT_VIEW_WIDTH, SHAPEVIEW_DEFAULT_VIEW_HEIGHT);
113
114   QDockWidget* aViewDockWidget = new QDockWidget (tr ("View"), myMainWindow);
115   aViewDockWidget->setObjectName (aViewDockWidget->windowTitle());
116   aViewDockWidget->setWidget (myViewWindow);
117   aViewDockWidget->setTitleBarWidget (myViewWindow->GetViewToolBar()->GetControl());
118   myMainWindow->addDockWidget (Qt::RightDockWidgetArea, aViewDockWidget);
119
120   myMainWindow->resize (DEFAULT_SHAPE_VIEW_WIDTH, DEFAULT_SHAPE_VIEW_HEIGHT);
121   myMainWindow->move (DEFAULT_SHAPE_VIEW_POSITION_X, DEFAULT_SHAPE_VIEW_POSITION_Y);
122 }
123
124 // =======================================================================
125 // function : Destructor
126 // purpose :
127 // =======================================================================
128 ShapeView_Window::~ShapeView_Window()
129 {
130   onCloseAllBREPViews();
131 }
132
133 // =======================================================================
134 // function : SetParent
135 // purpose :
136 // =======================================================================
137 void ShapeView_Window::SetParent (void* theParent)
138 {
139   QWidget* aParent = (QWidget*)theParent;
140   if (aParent)
141   {
142     QLayout* aLayout = aParent->layout();
143     if (aLayout)
144       aLayout->addWidget (GetMainWindow());
145   }
146 }
147
148 // =======================================================================
149 // function : FillActionsMenu
150 // purpose :
151 // =======================================================================
152 void ShapeView_Window::FillActionsMenu (void* theMenu)
153 {
154   QMenu* aMenu = (QMenu*)theMenu;
155   QList<QDockWidget*> aDockwidgets = myMainWindow->findChildren<QDockWidget*>();
156   for (QList<QDockWidget*>::iterator it = aDockwidgets.begin(); it != aDockwidgets.end(); ++it)
157   {
158     QDockWidget* aDockWidget = *it;
159     if (aDockWidget->parentWidget() == myMainWindow)
160       aMenu->addAction (aDockWidget->toggleViewAction());
161   }
162 }
163
164 // =======================================================================
165 // function : GetPreferences
166 // purpose :
167 // =======================================================================
168 void ShapeView_Window::GetPreferences (TInspectorAPI_PreferencesDataMap& theItem)
169 {
170   theItem.Clear();
171   theItem.Bind ("geometry",  TreeModel_Tools::ToString (myMainWindow->saveState()).toStdString().c_str());
172
173   QMap<QString, QString> anItems;
174   TreeModel_Tools::SaveState (myTreeView, anItems);
175   View_Tools::SaveState(myViewWindow, anItems);
176   for (QMap<QString, QString>::const_iterator anItemsIt = anItems.begin(); anItemsIt != anItems.end(); anItemsIt++)
177     theItem.Bind (anItemsIt.key().toStdString().c_str(), anItemsIt.value().toStdString().c_str());
178 }
179
180 // =======================================================================
181 // function : SetPreferences
182 // purpose :
183 // =======================================================================
184 void ShapeView_Window::SetPreferences (const TInspectorAPI_PreferencesDataMap& theItem)
185 {
186   if (theItem.IsEmpty())
187   {
188     TreeModel_Tools::SetDefaultHeaderSections (myTreeView);
189     return;
190   }
191
192   for (TInspectorAPI_IteratorOfPreferencesDataMap anItemIt (theItem); anItemIt.More(); anItemIt.Next())
193   {
194     if (anItemIt.Key().IsEqual ("geometry"))
195       myMainWindow->restoreState (TreeModel_Tools::ToByteArray (anItemIt.Value().ToCString()));
196     else if (TreeModel_Tools::RestoreState (myTreeView, anItemIt.Key().ToCString(), anItemIt.Value().ToCString()))
197       continue;
198     else if (View_Tools::RestoreState(myViewWindow, anItemIt.Key().ToCString(), anItemIt.Value().ToCString()))
199       continue;
200   }
201 }
202
203 // =======================================================================
204 // function : UpdateContent
205 // purpose :
206 // =======================================================================
207 void ShapeView_Window::UpdateContent()
208 {
209   TCollection_AsciiString aName = "TKShapeView";
210   if (myParameters->FindParameters (aName))
211   {
212     NCollection_List<Handle(Standard_Transient)> aParameters = myParameters->Parameters (aName);
213     // Init will remove from parameters those, that are processed only one time (TShape)
214     Init(aParameters);
215     myParameters->SetParameters (aName, aParameters);
216   }
217   if (myParameters->FindFileNames(aName))
218   {
219     for (NCollection_List<TCollection_AsciiString>::Iterator aFilesIt(myParameters->FileNames(aName));
220          aFilesIt.More(); aFilesIt.Next())
221       OpenFile (aFilesIt.Value());
222
223     NCollection_List<TCollection_AsciiString> aNames;
224     myParameters->SetFileNames (aName, aNames);
225   }
226   // make TopoDS_TShape selected if exist in select parameters
227   NCollection_List<Handle(Standard_Transient)> anObjects;
228   if (myParameters->GetSelectedObjects(aName, anObjects))
229   {
230     ShapeView_TreeModel* aModel = dynamic_cast<ShapeView_TreeModel*> (myTreeView->model());
231     QItemSelectionModel* aSelectionModel = myTreeView->selectionModel();
232     aSelectionModel->clear();
233     for (NCollection_List<Handle(Standard_Transient)>::Iterator aParamsIt (anObjects);
234          aParamsIt.More(); aParamsIt.Next())
235     {
236       Handle(Standard_Transient) anObject = aParamsIt.Value();
237       Handle(TopoDS_TShape) aShapePointer = Handle(TopoDS_TShape)::DownCast (anObject);
238       if (aShapePointer.IsNull())
239         continue;
240
241       TopoDS_Shape aShape;
242       aShape.TShape (aShapePointer);
243
244       QModelIndex aShapeIndex = aModel->FindIndex (aShape);
245       if (!aShapeIndex.isValid())
246         continue;
247        aSelectionModel->select (aShapeIndex, QItemSelectionModel::Select);
248        myTreeView->scrollTo (aShapeIndex);
249     }
250     myParameters->SetSelected (aName, NCollection_List<Handle(Standard_Transient)>());
251   }
252 }
253
254 // =======================================================================
255 // function : Init
256 // purpose :
257 // =======================================================================
258 void ShapeView_Window::Init (NCollection_List<Handle(Standard_Transient)>& theParameters)
259 {
260   Handle(AIS_InteractiveContext) aContext;
261   NCollection_List<Handle(Standard_Transient)> aParameters;
262
263   TCollection_AsciiString aPluginName ("TKShapeView");
264   NCollection_List<TCollection_AsciiString> aSelectedParameters;
265   if (myParameters->FindSelectedNames (aPluginName)) // selected names have TShape parameters
266     aSelectedParameters = myParameters->GetSelectedNames (aPluginName);
267
268   NCollection_List<TCollection_AsciiString>::Iterator aParamsIt (aSelectedParameters);
269   for (NCollection_List<Handle(Standard_Transient)>::Iterator anObjectsIt (theParameters);
270        anObjectsIt.More(); anObjectsIt.Next())
271   {
272     Handle(Standard_Transient) anObject = anObjectsIt.Value();
273     Handle(TopoDS_TShape) aShapePointer = Handle(TopoDS_TShape)::DownCast (anObject);
274     if (!aShapePointer.IsNull())
275     {
276       TopoDS_Shape aShape;
277       aShape.TShape (aShapePointer);
278       if (aParamsIt.More())
279       {
280         // each Transient object has own location/orientation description
281         TInspectorAPI_PluginParameters::ParametersToShape (aParamsIt.Value(), aShape);
282         aParamsIt.Next();
283       }
284       addShape (aShape);
285     }
286     else
287     {
288       aParameters.Append (anObject);
289       if (aContext.IsNull())
290         aContext = Handle(AIS_InteractiveContext)::DownCast (anObject);
291     }
292   }
293   if (!aContext.IsNull())
294     myViewWindow->SetContext (View_ContextType_External, aContext);
295
296   theParameters = aParameters;
297   myParameters->SetSelectedNames (aPluginName, NCollection_List<TCollection_AsciiString>());
298 }
299
300 // =======================================================================
301 // function : OpenFile
302 // purpose :
303 // =======================================================================
304 void ShapeView_Window::OpenFile(const TCollection_AsciiString& theFileName)
305 {
306   TopoDS_Shape aShape = ShapeView_Tools::ReadShape (theFileName);
307   if (!aShape.IsNull())
308     addShape(aShape);
309 }
310
311 // =======================================================================
312 // function : RemoveAllShapes
313 // purpose :
314 // =======================================================================
315 void ShapeView_Window::RemoveAllShapes()
316 {
317   ShapeView_TreeModel* aModel = dynamic_cast<ShapeView_TreeModel*> (myTreeView->model());
318   aModel->RemoveAllShapes();
319
320   onCloseAllBREPViews();
321 }
322
323 // =======================================================================
324 // function : addShape
325 // purpose :
326 // =======================================================================
327 void ShapeView_Window::addShape (const TopoDS_Shape& theShape)
328 {
329   ShapeView_TreeModel* aModel = dynamic_cast<ShapeView_TreeModel*> (myTreeView->model());
330   aModel->AddShape (theShape);
331 }
332
333 // =======================================================================
334 // function : onTreeViewContextMenuRequested
335 // purpose :
336 // =======================================================================
337 void ShapeView_Window::onTreeViewContextMenuRequested (const QPoint& thePosition)
338 {
339   QItemSelectionModel* aModel = myTreeView->selectionModel();
340   if (!aModel)
341     return;
342
343   QModelIndex anIndex = TreeModel_ModelBase::SingleSelected (aModel->selectedIndexes(), 0);
344   TreeModel_ItemBasePtr anItemBase = TreeModel_ModelBase::GetItemByIndex (anIndex);
345   if (!anItemBase)
346     return;
347
348   QMenu* aMenu = new QMenu(myMainWindow);
349   ShapeView_ItemRootPtr aRootItem = itemDynamicCast<ShapeView_ItemRoot> (anItemBase);
350   if (aRootItem) {
351     aMenu->addAction (ViewControl_Tools::CreateAction ("Load BREP file", SLOT (onLoadFile()), myMainWindow, this));
352     aMenu->addAction (ViewControl_Tools::CreateAction ("Remove all shape items", SLOT (onClearView()), myMainWindow, this));
353   }
354   else {
355     if (!GetTemporaryDirectory().IsEmpty())
356       aMenu->addAction (ViewControl_Tools::CreateAction ("BREP view", SLOT (onBREPView()), myMainWindow, this));
357     aMenu->addAction (ViewControl_Tools::CreateAction ("Close All BREP views", SLOT (onCloseAllBREPViews()), myMainWindow, this));
358     aMenu->addAction (ViewControl_Tools::CreateAction ("BREP directory", SLOT (onBREPDirectory()), myMainWindow, this));
359   }
360
361   QPoint aPoint = myTreeView->mapToGlobal (thePosition);
362   aMenu->exec (aPoint);
363 }
364
365 // =======================================================================
366 // function : onEraseAllPerformed
367 // purpose :
368 // =======================================================================
369 void ShapeView_Window::onEraseAllPerformed()
370 {
371   ShapeView_TreeModel* aTreeModel = dynamic_cast<ShapeView_TreeModel*> (myTreeView->model());
372
373   // TODO: provide update for only visibility state for better performance  TopoDS_Shape myCustomShape;
374
375   aTreeModel->Reset();
376   aTreeModel->EmitLayoutChanged();
377 }
378
379 // =======================================================================
380 // function : onBREPDirectory
381 // purpose :
382 // =======================================================================
383 void ShapeView_Window::onBREPDirectory()
384 {
385   QString aFilter (tr ("BREP file (*.brep*)"));
386   QString aSelectedFilter;
387   QString aFileName = QFileDialog::getOpenFileName (0, tr ("Export shape to BREP file"),
388                                                     GetTemporaryDirectory().ToCString(), aSelectedFilter);
389   if (!aFileName.isEmpty())
390     viewFile (aFileName);
391 }
392
393 // =======================================================================
394 // function : onLoadFile
395 // purpose :
396 // =======================================================================
397 void ShapeView_Window::onLoadFile()
398 {
399   QString aDataDirName = QDir::currentPath();
400
401   QString aFileName = ShapeView_OpenFileDialog::OpenFile(0, aDataDirName);
402   aFileName = QDir().toNativeSeparators (aFileName);
403   if (!aFileName.isEmpty())
404     onOpenFile(aFileName);
405 }
406
407 // =======================================================================
408 // function : onBREPView
409 // purpose :
410 // =======================================================================
411 void ShapeView_Window::onBREPView()
412 {
413   if (GetTemporaryDirectory().IsEmpty())
414     return;
415
416   QItemSelectionModel* aModel = myTreeView->selectionModel();
417   if (!aModel)
418     return;
419
420   QModelIndexList aSelectedRows = aModel->selectedRows();
421   if (aSelectedRows.size() == 0)
422     return;
423
424   QModelIndex aSelectedIndex = aSelectedRows.at (0);
425   TreeModel_ItemBasePtr anItemBase = TreeModel_ModelBase::GetItemByIndex (aSelectedIndex);
426   if (!anItemBase)
427     return;
428
429   ShapeView_ItemShapePtr anItem = itemDynamicCast<ShapeView_ItemShape>(anItemBase);
430   if (!anItem)
431     return;
432
433   QString aFileName = anItem->GetFileName();
434   QDir aDir;
435   if (aFileName.isEmpty() || !aDir.exists (aFileName))
436   {
437     TCollection_AsciiString aFileNameIndiced = GetTemporaryDirectory() + TCollection_AsciiString ("\\") +
438                                                getNextTmpName (anItem->TShapePointer());
439     const TopoDS_Shape& aShape = anItem->GetItemShape();
440     BRepTools::Write (aShape, aFileNameIndiced.ToCString());
441     anItem->SetFileName (aFileNameIndiced.ToCString());
442     aFileName = aFileNameIndiced.ToCString();
443   }
444   viewFile (aFileName);
445 }
446
447 // =======================================================================
448 // function : onCloseAllBREPViews
449 // purpose :
450 // =======================================================================
451 void ShapeView_Window::onCloseAllBREPViews()
452 {
453   removeBREPFiles();
454
455   for (int aViewId = myBREPViews.size()-1; aViewId >= 0; aViewId--)
456     delete myBREPViews[aViewId];
457
458   myBREPViews.clear();
459 }
460
461 // =======================================================================
462 // function : onEditorDestroyed
463 // purpose :
464 // =======================================================================
465 void ShapeView_Window::onEditorDestroyed(QObject* theObject)
466 {
467   QWidget* aWidget = dynamic_cast<QWidget*> (theObject);
468
469   for (int aViewId = myBREPViews.size()-1; aViewId >= 0; aViewId--)
470   {
471     if (myBREPViews[aViewId] == aWidget)
472       myBREPViews.removeAll(aWidget);
473   }
474 }
475
476 // =======================================================================
477 // function : viewFile
478 // purpose :
479 // =======================================================================
480 void ShapeView_Window::viewFile (const QString& theFileName)
481 {
482   QApplication::setOverrideCursor (Qt::WaitCursor);
483   QString aFileText;
484   QFile aFile (theFileName);
485   if (aFile.open (QIODevice::ReadOnly | QIODevice::Text))
486   {
487     QTextStream aStream(&aFile);
488     QString aLine = aStream.readLine();
489     while (!aLine.isNull())
490     {
491       aLine = aStream.readLine();
492       aFileText.append (aLine + QString ("\n"));
493     }
494     if (!aFileText.isEmpty())
495     {
496       QPlainTextEdit* anEditor = new QPlainTextEdit (0);
497       anEditor->setAttribute (Qt::WA_DeleteOnClose, true);
498       connect (anEditor, SIGNAL (destroyed(QObject*)), this, SLOT (onEditorDestroyed(QObject*)));
499       anEditor->setPlainText (aFileText);
500       anEditor->resize (DEFAULT_TEXT_VIEW_WIDTH, DEFAULT_TEXT_VIEW_HEIGHT);
501       anEditor->move (DEFAULT_TEXT_VIEW_POSITION_X + myNextPosition, DEFAULT_TEXT_VIEW_POSITION_Y);
502       myNextPosition += DEFAULT_TEXT_VIEW_DELTA;
503       anEditor->show();
504       myBREPViews.append (anEditor);
505     }
506   }
507   QApplication::restoreOverrideCursor();
508 }
509
510 // =======================================================================
511 // function : removeBREPFiles
512 // purpose :
513 // =======================================================================
514 void ShapeView_Window::removeBREPFiles()
515 {
516   QDir aDir (GetTemporaryDirectory().ToCString());
517
518   QStringList anEntries = aDir.entryList();
519   QString aPrefix(viewBREPPrefix().ToCString());
520   for (int anEntryId = 0, aSize = anEntries.size(); anEntryId < aSize; anEntryId++)
521   {
522     if (anEntries[anEntryId].contains (aPrefix))
523       aDir.remove (anEntries[anEntryId]);
524   }
525 }
526
527 // =======================================================================
528 // function : getNextTmpName
529 // purpose :
530 // =======================================================================
531 TCollection_AsciiString ShapeView_Window::getNextTmpName (const TCollection_AsciiString& thePointerInfo)
532 {
533   TCollection_AsciiString aTmpName(viewBREPPrefix());
534   aTmpName += thePointerInfo;
535   return aTmpName;
536 }