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