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