0029748: Samples - Inspector tool - use recently opened files in TInspectorEXE
[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.Bind ("geometry",  TreeModel_Tools::ToString (myMainWindow->saveState()).toStdString().c_str());
171
172   QMap<QString, QString> anItems;
173   TreeModel_Tools::SaveState (myTreeView, anItems);
174   View_Tools::SaveState(myViewWindow, anItems);
175   for (QMap<QString, QString>::const_iterator anItemsIt = anItems.begin(); anItemsIt != anItems.end(); anItemsIt++)
176     theItem.Bind (anItemsIt.key().toStdString().c_str(), anItemsIt.value().toStdString().c_str());
177 }
178
179 // =======================================================================
180 // function : SetPreferences
181 // purpose :
182 // =======================================================================
183 void ShapeView_Window::SetPreferences (const TInspectorAPI_PreferencesDataMap& theItem)
184 {
185   if (theItem.IsEmpty())
186   {
187     TreeModel_Tools::SetDefaultHeaderSections (myTreeView);
188     return;
189   }
190
191   for (TInspectorAPI_IteratorOfPreferencesDataMap anItemIt (theItem); anItemIt.More(); anItemIt.Next())
192   {
193     if (anItemIt.Key().IsEqual ("geometry"))
194       myMainWindow->restoreState (TreeModel_Tools::ToByteArray (anItemIt.Value().ToCString()));
195     else if (TreeModel_Tools::RestoreState (myTreeView, anItemIt.Key().ToCString(), anItemIt.Value().ToCString()))
196       continue;
197     else if (View_Tools::RestoreState(myViewWindow, anItemIt.Key().ToCString(), anItemIt.Value().ToCString()))
198       continue;
199   }
200 }
201
202 // =======================================================================
203 // function : UpdateContent
204 // purpose :
205 // =======================================================================
206 void ShapeView_Window::UpdateContent()
207 {
208   TCollection_AsciiString aName = "TKShapeView";
209   if (myParameters->FindParameters (aName))
210   {
211     NCollection_List<Handle(Standard_Transient)> aParameters = myParameters->Parameters (aName);
212     // Init will remove from parameters those, that are processed only one time (TShape)
213     Init(aParameters);
214     myParameters->SetParameters (aName, aParameters);
215   }
216   if (myParameters->FindFileNames(aName))
217   {
218     for (NCollection_List<TCollection_AsciiString>::Iterator aFilesIt(myParameters->FileNames(aName));
219          aFilesIt.More(); aFilesIt.Next())
220       OpenFile (aFilesIt.Value());
221
222     NCollection_List<TCollection_AsciiString> aNames;
223     myParameters->SetFileNames (aName, aNames);
224   }
225   // make TopoDS_TShape selected if exist in select parameters
226   NCollection_List<Handle(Standard_Transient)> anObjects;
227   if (myParameters->GetSelectedObjects(aName, anObjects))
228   {
229     ShapeView_TreeModel* aModel = dynamic_cast<ShapeView_TreeModel*> (myTreeView->model());
230     QItemSelectionModel* aSelectionModel = myTreeView->selectionModel();
231     aSelectionModel->clear();
232     for (NCollection_List<Handle(Standard_Transient)>::Iterator aParamsIt (anObjects);
233          aParamsIt.More(); aParamsIt.Next())
234     {
235       Handle(Standard_Transient) anObject = aParamsIt.Value();
236       Handle(TopoDS_TShape) aShapePointer = Handle(TopoDS_TShape)::DownCast (anObject);
237       if (aShapePointer.IsNull())
238         continue;
239
240       TopoDS_Shape aShape;
241       aShape.TShape (aShapePointer);
242
243       QModelIndex aShapeIndex = aModel->FindIndex (aShape);
244       if (!aShapeIndex.isValid())
245         continue;
246        aSelectionModel->select (aShapeIndex, QItemSelectionModel::Select);
247        myTreeView->scrollTo (aShapeIndex);
248     }
249     myParameters->SetSelected (aName, NCollection_List<Handle(Standard_Transient)>());
250   }
251 }
252
253 // =======================================================================
254 // function : Init
255 // purpose :
256 // =======================================================================
257 void ShapeView_Window::Init (NCollection_List<Handle(Standard_Transient)>& theParameters)
258 {
259   Handle(AIS_InteractiveContext) aContext;
260   NCollection_List<Handle(Standard_Transient)> aParameters;
261
262   TCollection_AsciiString aPluginName ("TKShapeView");
263   NCollection_List<TCollection_AsciiString> aSelectedParameters;
264   if (myParameters->FindSelectedNames (aPluginName)) // selected names have TShape parameters
265     aSelectedParameters = myParameters->GetSelectedNames (aPluginName);
266
267   NCollection_List<TCollection_AsciiString>::Iterator aParamsIt (aSelectedParameters);
268   for (NCollection_List<Handle(Standard_Transient)>::Iterator anObjectsIt (theParameters);
269        anObjectsIt.More(); anObjectsIt.Next())
270   {
271     Handle(Standard_Transient) anObject = anObjectsIt.Value();
272     Handle(TopoDS_TShape) aShapePointer = Handle(TopoDS_TShape)::DownCast (anObject);
273     if (!aShapePointer.IsNull())
274     {
275       TopoDS_Shape aShape;
276       aShape.TShape (aShapePointer);
277       if (aParamsIt.More())
278       {
279         // each Transient object has own location/orientation description
280         TInspectorAPI_PluginParameters::ParametersToShape (aParamsIt.Value(), aShape);
281         aParamsIt.Next();
282       }
283       addShape (aShape);
284     }
285     else
286     {
287       aParameters.Append (anObject);
288       if (aContext.IsNull())
289         aContext = Handle(AIS_InteractiveContext)::DownCast (anObject);
290     }
291   }
292   if (!aContext.IsNull())
293     myViewWindow->SetContext (View_ContextType_External, aContext);
294
295   theParameters = aParameters;
296   myParameters->SetSelectedNames (aPluginName, NCollection_List<TCollection_AsciiString>());
297 }
298
299 // =======================================================================
300 // function : OpenFile
301 // purpose :
302 // =======================================================================
303 void ShapeView_Window::OpenFile(const TCollection_AsciiString& theFileName)
304 {
305   TopoDS_Shape aShape = ShapeView_Tools::ReadShape (theFileName);
306   if (!aShape.IsNull())
307     addShape(aShape);
308 }
309
310 // =======================================================================
311 // function : RemoveAllShapes
312 // purpose :
313 // =======================================================================
314 void ShapeView_Window::RemoveAllShapes()
315 {
316   ShapeView_TreeModel* aModel = dynamic_cast<ShapeView_TreeModel*> (myTreeView->model());
317   aModel->RemoveAllShapes();
318
319   onCloseAllBREPViews();
320 }
321
322 // =======================================================================
323 // function : addShape
324 // purpose :
325 // =======================================================================
326 void ShapeView_Window::addShape (const TopoDS_Shape& theShape)
327 {
328   ShapeView_TreeModel* aModel = dynamic_cast<ShapeView_TreeModel*> (myTreeView->model());
329   aModel->AddShape (theShape);
330 }
331
332 // =======================================================================
333 // function : onTreeViewContextMenuRequested
334 // purpose :
335 // =======================================================================
336 void ShapeView_Window::onTreeViewContextMenuRequested (const QPoint& thePosition)
337 {
338   QItemSelectionModel* aModel = myTreeView->selectionModel();
339   if (!aModel)
340     return;
341
342   QModelIndex anIndex = TreeModel_ModelBase::SingleSelected (aModel->selectedIndexes(), 0);
343   TreeModel_ItemBasePtr anItemBase = TreeModel_ModelBase::GetItemByIndex (anIndex);
344   if (!anItemBase)
345     return;
346
347   QMenu* aMenu = new QMenu(myMainWindow);
348   ShapeView_ItemRootPtr aRootItem = itemDynamicCast<ShapeView_ItemRoot> (anItemBase);
349   if (aRootItem) {
350     aMenu->addAction (ViewControl_Tools::CreateAction ("Load BREP file", SLOT (onLoadFile()), myMainWindow, this));
351     aMenu->addAction (ViewControl_Tools::CreateAction ("Remove all shape items", SLOT (onClearView()), myMainWindow, this));
352   }
353   else {
354     if (!GetTemporaryDirectory().IsEmpty())
355       aMenu->addAction (ViewControl_Tools::CreateAction ("BREP view", SLOT (onBREPView()), myMainWindow, this));
356     aMenu->addAction (ViewControl_Tools::CreateAction ("Close All BREP views", SLOT (onCloseAllBREPViews()), myMainWindow, this));
357     aMenu->addAction (ViewControl_Tools::CreateAction ("BREP directory", SLOT (onBREPDirectory()), myMainWindow, this));
358   }
359
360   QPoint aPoint = myTreeView->mapToGlobal (thePosition);
361   aMenu->exec (aPoint);
362 }
363
364 // =======================================================================
365 // function : onEraseAllPerformed
366 // purpose :
367 // =======================================================================
368 void ShapeView_Window::onEraseAllPerformed()
369 {
370   ShapeView_TreeModel* aTreeModel = dynamic_cast<ShapeView_TreeModel*> (myTreeView->model());
371
372   // TODO: provide update for only visibility state for better performance  TopoDS_Shape myCustomShape;
373
374   aTreeModel->Reset();
375   aTreeModel->EmitLayoutChanged();
376 }
377
378 // =======================================================================
379 // function : onBREPDirectory
380 // purpose :
381 // =======================================================================
382 void ShapeView_Window::onBREPDirectory()
383 {
384   QString aFilter (tr ("BREP file (*.brep*)"));
385   QString aSelectedFilter;
386   QString aFileName = QFileDialog::getOpenFileName (0, tr ("Export shape to BREP file"),
387                                                     GetTemporaryDirectory().ToCString(), aSelectedFilter);
388   if (!aFileName.isEmpty())
389     viewFile (aFileName);
390 }
391
392 // =======================================================================
393 // function : onLoadFile
394 // purpose :
395 // =======================================================================
396 void ShapeView_Window::onLoadFile()
397 {
398   QString aDataDirName = QDir::currentPath();
399
400   QString aFileName = ShapeView_OpenFileDialog::OpenFile(0, aDataDirName);
401   aFileName = QDir().toNativeSeparators (aFileName);
402   if (!aFileName.isEmpty())
403     onOpenFile(aFileName);
404 }
405
406 // =======================================================================
407 // function : onBREPView
408 // purpose :
409 // =======================================================================
410 void ShapeView_Window::onBREPView()
411 {
412   if (GetTemporaryDirectory().IsEmpty())
413     return;
414
415   QItemSelectionModel* aModel = myTreeView->selectionModel();
416   if (!aModel)
417     return;
418
419   QModelIndexList aSelectedRows = aModel->selectedRows();
420   if (aSelectedRows.size() == 0)
421     return;
422
423   QModelIndex aSelectedIndex = aSelectedRows.at (0);
424   TreeModel_ItemBasePtr anItemBase = TreeModel_ModelBase::GetItemByIndex (aSelectedIndex);
425   if (!anItemBase)
426     return;
427
428   ShapeView_ItemShapePtr anItem = itemDynamicCast<ShapeView_ItemShape>(anItemBase);
429   if (!anItem)
430     return;
431
432   QString aFileName = anItem->GetFileName();
433   QDir aDir;
434   if (aFileName.isEmpty() || !aDir.exists (aFileName))
435   {
436     TCollection_AsciiString aFileNameIndiced = GetTemporaryDirectory() + TCollection_AsciiString ("\\") +
437                                                getNextTmpName (anItem->TShapePointer());
438     const TopoDS_Shape& aShape = anItem->GetItemShape();
439     BRepTools::Write (aShape, aFileNameIndiced.ToCString());
440     anItem->SetFileName (aFileNameIndiced.ToCString());
441     aFileName = aFileNameIndiced.ToCString();
442   }
443   viewFile (aFileName);
444 }
445
446 // =======================================================================
447 // function : onCloseAllBREPViews
448 // purpose :
449 // =======================================================================
450 void ShapeView_Window::onCloseAllBREPViews()
451 {
452   removeBREPFiles();
453
454   for (int aViewId = myBREPViews.size()-1; aViewId >= 0; aViewId--)
455     delete myBREPViews[aViewId];
456
457   myBREPViews.clear();
458 }
459
460 // =======================================================================
461 // function : onEditorDestroyed
462 // purpose :
463 // =======================================================================
464 void ShapeView_Window::onEditorDestroyed(QObject* theObject)
465 {
466   QWidget* aWidget = dynamic_cast<QWidget*> (theObject);
467
468   for (int aViewId = myBREPViews.size()-1; aViewId >= 0; aViewId--)
469   {
470     if (myBREPViews[aViewId] == aWidget)
471       myBREPViews.removeAll(aWidget);
472   }
473 }
474
475 // =======================================================================
476 // function : viewFile
477 // purpose :
478 // =======================================================================
479 void ShapeView_Window::viewFile (const QString& theFileName)
480 {
481   QApplication::setOverrideCursor (Qt::WaitCursor);
482   QString aFileText;
483   QFile aFile (theFileName);
484   if (aFile.open (QIODevice::ReadOnly | QIODevice::Text))
485   {
486     QTextStream aStream(&aFile);
487     QString aLine = aStream.readLine();
488     while (!aLine.isNull())
489     {
490       aLine = aStream.readLine();
491       aFileText.append (aLine + QString ("\n"));
492     }
493     if (!aFileText.isEmpty())
494     {
495       QPlainTextEdit* anEditor = new QPlainTextEdit (0);
496       anEditor->setAttribute (Qt::WA_DeleteOnClose, true);
497       connect (anEditor, SIGNAL (destroyed(QObject*)), this, SLOT (onEditorDestroyed(QObject*)));
498       anEditor->setPlainText (aFileText);
499       anEditor->resize (DEFAULT_TEXT_VIEW_WIDTH, DEFAULT_TEXT_VIEW_HEIGHT);
500       anEditor->move (DEFAULT_TEXT_VIEW_POSITION_X + myNextPosition, DEFAULT_TEXT_VIEW_POSITION_Y);
501       myNextPosition += DEFAULT_TEXT_VIEW_DELTA;
502       anEditor->show();
503       myBREPViews.append (anEditor);
504     }
505   }
506   QApplication::restoreOverrideCursor();
507 }
508
509 // =======================================================================
510 // function : removeBREPFiles
511 // purpose :
512 // =======================================================================
513 void ShapeView_Window::removeBREPFiles()
514 {
515   QDir aDir (GetTemporaryDirectory().ToCString());
516
517   QStringList anEntries = aDir.entryList();
518   QString aPrefix(viewBREPPrefix().ToCString());
519   for (int anEntryId = 0, aSize = anEntries.size(); anEntryId < aSize; anEntryId++)
520   {
521     if (anEntries[anEntryId].contains (aPrefix))
522       aDir.remove (anEntries[anEntryId]);
523   }
524 }
525
526 // =======================================================================
527 // function : getNextTmpName
528 // purpose :
529 // =======================================================================
530 TCollection_AsciiString ShapeView_Window::getNextTmpName (const TCollection_AsciiString& thePointerInfo)
531 {
532   TCollection_AsciiString aTmpName(viewBREPPrefix());
533   aTmpName += thePointerInfo;
534   return aTmpName;
535 }