Salome HOME
Do not change history in ObjectBrowser if an operation is opened
[modules/shaper.git] / src / XGUI / XGUI_ObjectsBrowser.cpp
1 // Copyright (C) 2014-20xx CEA/DEN, EDF R&D -->
2
3 #include "XGUI_ObjectsBrowser.h"
4 #include "XGUI_Tools.h"
5 #include "XGUI_DataModel.h"
6
7 #include <ModelAPI_Data.h>
8 #include <ModelAPI_Session.h>
9 #include <ModelAPI_Document.h>
10 #include <ModelAPI_Tools.h>
11
12 #include <ModuleBase_Tools.h>
13 #include <ModuleBase_IDocumentDataModel.h>
14
15 #include <QLayout>
16 #include <QLabel>
17 #include <QLineEdit>
18 #include <QPixmap>
19 #include <QEvent>
20 #include <QMouseEvent>
21 #include <QAction>
22 #include <QStyledItemDelegate>
23 #include <QMessageBox>
24
25 /// Width of second column (minimum acceptable = 27)
26 #define SECOND_COL_WIDTH 30
27
28
29 /**
30 * \ingroup GUI
31 * Tree item delegate for definition of data in column items editor
32 */
33 class XGUI_TreeViewItemDelegate: public QStyledItemDelegate
34 {
35 public:
36   /// Constructor
37   /// \param theParent a parent of the delegate
38   XGUI_TreeViewItemDelegate(XGUI_DataTree* theParent):QStyledItemDelegate(theParent), myTreedView(theParent) {}
39
40   /// Set data for item editor (name of the item)
41   /// \param editor a widget of editor
42   /// \param index the tree item index
43   virtual void  setEditorData ( QWidget* editor, const QModelIndex& index ) const
44   {
45     QLineEdit* aEditor = dynamic_cast<QLineEdit*>(editor);
46     if (aEditor) {
47       ModuleBase_IDocumentDataModel* aModel = myTreedView->dataModel();
48       ObjectPtr aObj = aModel->object(index);
49       if (aObj.get() != NULL) {
50         aEditor->setText(aObj->data()->name().c_str());
51         return;
52       }
53     }
54     QStyledItemDelegate::setEditorData(editor, index);
55   }
56
57 private:
58   XGUI_DataTree* myTreedView;
59 };
60
61
62 XGUI_DataTree::XGUI_DataTree(QWidget* theParent)
63     : QTreeView(theParent)
64 {
65   setHeaderHidden(true);
66   setEditTriggers(QAbstractItemView::NoEditTriggers);
67   setSelectionBehavior(QAbstractItemView::SelectRows);
68   setSelectionMode(QAbstractItemView::ExtendedSelection);
69
70   setItemDelegateForColumn(0, new XGUI_TreeViewItemDelegate(this));
71
72 #ifndef ModuleDataModel
73   connect(this, SIGNAL(doubleClicked(const QModelIndex&)), 
74     SLOT(onDoubleClick(const QModelIndex&)));
75 #endif
76 }
77
78 XGUI_DataTree::~XGUI_DataTree()
79 {
80 }
81
82 ModuleBase_IDocumentDataModel* XGUI_DataTree::dataModel() const
83 {
84   return static_cast<ModuleBase_IDocumentDataModel*>(model());
85 }
86
87 void XGUI_DataTree::contextMenuEvent(QContextMenuEvent* theEvent)
88 {
89   emit contextMenuRequested(theEvent);
90 }
91
92 void XGUI_DataTree::commitData(QWidget* theEditor)
93 {
94   QLineEdit* aEditor = dynamic_cast<QLineEdit*>(theEditor);
95   if (aEditor) {
96     QString aName = aEditor->text();
97     QModelIndexList aIndexList = selectionModel()->selectedIndexes();
98     ModuleBase_IDocumentDataModel* aModel = dataModel();
99     ObjectPtr aObj = aModel->object(aIndexList.first());
100     SessionPtr aMgr = ModelAPI_Session::get();
101     aMgr->startOperation("Rename");
102
103     if (!XGUI_Tools::canRename(this, aObj, aName)) {
104       aMgr->abortOperation();
105       return;
106     }
107
108     aObj->data()->setName(qPrintable(aName));
109     aMgr->finishOperation();
110   }
111 }
112
113 void XGUI_DataTree::clear() 
114 {
115   ModuleBase_IDocumentDataModel* aModel = dataModel();
116   aModel->clear();
117   reset();
118 }
119
120 void XGUI_DataTree::resizeEvent(QResizeEvent* theEvent)
121 {
122   QSize aSize = theEvent->size();
123   if (aSize.isValid()) {
124     setColumnWidth(0, aSize.width() - SECOND_COL_WIDTH);
125     setColumnWidth(1, SECOND_COL_WIDTH);
126   }
127 }
128
129 void XGUI_DataTree::onDoubleClick(const QModelIndex& theIndex)
130 {
131   if (theIndex.column() != 1)
132     return;
133   SessionPtr aMgr = ModelAPI_Session::get();
134   // When operation is opened then we can not change history
135   if (aMgr->isOperation())
136     return;
137   ModuleBase_IDocumentDataModel* aModel = dataModel();
138   if (aModel->flags(theIndex) == 0)
139     return;
140   ObjectPtr aObj = aModel->object(theIndex);
141
142   DocumentPtr aDoc = aMgr->activeDocument();
143   
144   QModelIndex aOldIndex = aModel->lastHistoryIndex();
145
146   std::string aOpName = tr("History change").toStdString();
147   if (aObj.get()) {
148     if (aObj->document() != aDoc)
149       return;
150     aMgr->startOperation(aOpName);
151     aDoc->setCurrentFeature(std::dynamic_pointer_cast<ModelAPI_Feature>(aObj), true);
152     aMgr->finishOperation();
153   } else {
154     // Ignore clicks on folders outside current document
155     if ((theIndex.internalId() == -1) && (aDoc != aMgr->moduleDocument()))
156       // Clicked folder under root but active document is another
157       return;
158     if ((theIndex.internalId() != -1) && (aDoc.get() != theIndex.internalPointer()))
159       // Cliced not on active document folder
160       return;
161
162     aMgr->startOperation(aOpName);
163     aDoc->setCurrentFeature(FeaturePtr(), true);
164     aMgr->finishOperation();
165   }
166   QModelIndex aNewIndex = aModel->lastHistoryIndex();
167   QModelIndex aParent = theIndex.parent();
168   int aStartRow = std::min(aOldIndex.row(), aNewIndex.row());
169   int aEndRow = std::max(aOldIndex.row(), aNewIndex.row());
170   for (int i = aStartRow; i <= aEndRow; i++) {
171     update(aModel->index(i, 0, aParent));
172   }
173   update(aOldIndex);
174   update(aNewIndex);
175 }
176
177 //********************************************************************
178 //********************************************************************
179 //********************************************************************
180 XGUI_ObjectsBrowser::XGUI_ObjectsBrowser(QWidget* theParent)
181     : QWidget(theParent), myDocModel(0)
182 {
183   QVBoxLayout* aLayout = new QVBoxLayout(this);
184   ModuleBase_Tools::zeroMargins(aLayout);
185   aLayout->setSpacing(0);
186
187   QFrame* aLabelWgt = new QFrame(this);
188   aLabelWgt->setAutoFillBackground(true);
189   QPalette aPalet = aLabelWgt->palette();
190   aPalet.setColor(QPalette::Window, Qt::white);
191   aLabelWgt->setPalette(aPalet);
192
193   aLayout->addWidget(aLabelWgt);
194   QHBoxLayout* aLabelLay = new QHBoxLayout(aLabelWgt);
195   ModuleBase_Tools::zeroMargins(aLabelLay);
196   aLabelLay->setSpacing(0);
197
198   QLabel* aLbl = new QLabel(aLabelWgt);
199   aLbl->setPixmap(QPixmap(":pictures/assembly.png"));
200   aLbl->setMargin(2);
201
202   aLbl->setAutoFillBackground(true);
203
204   aLabelLay->addWidget(aLbl);
205
206   SessionPtr aMgr = ModelAPI_Session::get();
207   DocumentPtr aDoc = aMgr->moduleDocument();
208   // TODO: Find a name of the root document
209
210   myActiveDocLbl = new QLineEdit(tr("Part set"), aLabelWgt);
211   myActiveDocLbl->setReadOnly(true);
212   myActiveDocLbl->setFrame(false);
213   //myActiveDocLbl->setMargin(2);
214   myActiveDocLbl->setContextMenuPolicy(Qt::CustomContextMenu);
215
216   myActiveDocLbl->installEventFilter(this);
217
218   aLabelLay->addWidget(myActiveDocLbl);
219   aLabelLay->setStretch(1, 1);
220
221   myTreeView = new XGUI_DataTree(this);
222   aLayout->addWidget(myTreeView);
223
224   aLabelWgt->setFrameShape(myTreeView->frameShape());
225   aLabelWgt->setFrameShadow(myTreeView->frameShadow());
226
227 #ifndef ModuleDataModel
228   myDocModel = new XGUI_DataModel(this);
229   myTreeView->setModel(myDocModel);
230   QItemSelectionModel* aSelMod = myTreeView->selectionModel();
231   connect(aSelMod, SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
232           this, SLOT(onSelectionChanged(const QItemSelection&, const QItemSelection&)));
233 #endif
234
235   connect(myActiveDocLbl, SIGNAL(customContextMenuRequested(const QPoint&)), this,
236           SLOT(onLabelContextMenuRequested(const QPoint&)));
237   connect(myTreeView, SIGNAL(contextMenuRequested(QContextMenuEvent*)), this,
238           SLOT(onContextMenuRequested(QContextMenuEvent*)));
239 }
240
241 //***************************************************
242 XGUI_ObjectsBrowser::~XGUI_ObjectsBrowser()
243 {
244 }
245
246 //***************************************************
247 bool XGUI_ObjectsBrowser::eventFilter(QObject* obj, QEvent* theEvent)
248 {
249   if (obj == myActiveDocLbl) {
250     if (!myActiveDocLbl->isReadOnly()) {
251       // End of editing by mouse click
252       if (theEvent->type() == QEvent::MouseButtonRelease) {
253         QMouseEvent* aEvent = (QMouseEvent*) theEvent;
254         QPoint aPnt = mapFromGlobal(aEvent->globalPos());
255         if (childAt(aPnt) != myActiveDocLbl) {
256           closeDocNameEditing(true);
257         }
258       } else if (theEvent->type() == QEvent::KeyRelease) {
259         QKeyEvent* aEvent = (QKeyEvent*) theEvent;
260         switch (aEvent->key()) {
261           case Qt::Key_Return:
262           case Qt::Key_Enter:  // Accept current input
263             closeDocNameEditing(true);
264             break;
265           case Qt::Key_Escape:  // Cancel the input
266             closeDocNameEditing(false);
267             break;
268         }
269       }
270     } else {
271       if (theEvent->type() == QEvent::MouseButtonDblClick) {
272         emit headerMouseDblClicked(QModelIndex());
273         return true;
274       }  
275     }
276   }
277   return QWidget::eventFilter(obj, theEvent);
278 }
279
280 //***************************************************
281 void XGUI_ObjectsBrowser::closeDocNameEditing(bool toSave)
282 {
283   myActiveDocLbl->deselect();
284   myActiveDocLbl->clearFocus();
285   myActiveDocLbl->releaseMouse();
286   myActiveDocLbl->setReadOnly(true);
287   if (toSave) {
288     // TODO: Save the name of root document
289     SessionPtr aMgr = ModelAPI_Session::get();
290     DocumentPtr aDoc = aMgr->moduleDocument();
291   } else {
292     myActiveDocLbl->setText(myActiveDocLbl->property("OldText").toString());
293   }
294 }
295
296 //***************************************************
297 void XGUI_ObjectsBrowser::onContextMenuRequested(QContextMenuEvent* theEvent)
298 {
299   QModelIndexList aIndexes;
300   QObjectPtrList aSelectedData = selectedObjects(&aIndexes);
301   bool toEnable = false;
302   if (aSelectedData.size() == 1) {
303     Qt::ItemFlags aFlags = dataModel()->flags(aIndexes.first());
304     toEnable = ((aFlags & Qt::ItemIsEditable) != 0);
305   }
306   foreach(QAction* aCmd, actions()) {
307     aCmd->setEnabled(toEnable);
308   }
309   emit contextMenuRequested(theEvent);
310 }
311
312 //***************************************************
313 void XGUI_ObjectsBrowser::onLabelContextMenuRequested(const QPoint& thePnt)
314 {
315   myTreeView->selectionModel()->clearSelection();
316   //Empty feature pointer means that selected root document
317   foreach(QAction* aCmd, actions()) {
318     aCmd->setEnabled(true);
319   }
320   QContextMenuEvent aEvent(QContextMenuEvent::Mouse, thePnt, myActiveDocLbl->mapToGlobal(thePnt));
321   emit contextMenuRequested(&aEvent);
322 }
323
324 //***************************************************
325 void XGUI_ObjectsBrowser::onEditItem()
326 {
327   QObjectPtrList aSelectedData = selectedObjects();
328   if (aSelectedData.size() > 0) {
329     ObjectPtr aFeature = aSelectedData.first();
330     if (aFeature) {  // Selection happens in TreeView
331       QObjectPtrList aList;
332       aList.append(aFeature);
333       // check whether the object can be deleted. There should not be parts which are not loaded
334       if (!XGUI_Tools::canRemoveOrRename((QWidget*)parent(), aList))
335         return;
336
337       // Find index which corresponds the feature
338       QModelIndex aIndex;
339       foreach(QModelIndex aIdx, selectedIndexes()) {
340         ObjectPtr aFea = dataModel()->object(aIdx);
341         if (dataModel()->object(aIdx)->isSame(aFeature)) {
342           aIndex = aIdx;
343           break;
344         }
345       }
346       if (aIndex.isValid()) {
347         myTreeView->setCurrentIndex(aIndex);
348         myTreeView->edit(aIndex);
349       }
350       return;
351     }
352   }
353   //Selection happens in Upper label
354   myActiveDocLbl->setReadOnly(false);
355   myActiveDocLbl->setFocus();
356   myActiveDocLbl->selectAll();
357   myActiveDocLbl->grabMouse();
358   myActiveDocLbl->setProperty("OldText", myActiveDocLbl->text());
359 }
360
361 //***************************************************
362 void XGUI_ObjectsBrowser::rebuildDataTree()
363 {
364   myDocModel->rebuildDataTree();
365   update();
366 }
367
368 //***************************************************
369 void XGUI_ObjectsBrowser::setObjectsSelected(const QObjectPtrList& theObjects)
370 {
371   QList<QModelIndex> theIndexes;
372   QItemSelectionModel* aSelectModel = myTreeView->selectionModel();
373   aSelectModel->clear();
374
375   foreach(ObjectPtr aFeature, theObjects)
376   {
377     QModelIndex aIndex = myDocModel->objectIndex(aFeature);
378     if (aIndex.isValid()) {
379       aSelectModel->select(aIndex, QItemSelectionModel::Select);
380     }
381   }
382 }
383
384 //***************************************************
385 void XGUI_ObjectsBrowser::clearContent()  
386
387   myTreeView->clear(); 
388 }
389
390 #ifdef ModuleDataModel
391 void XGUI_ObjectsBrowser::setDataModel(ModuleBase_IDocumentDataModel* theModel)
392 {
393   myDocModel = theModel;
394   //myDocModel = new XGUI_DataModel(this);
395   myTreeView->setModel(myDocModel);
396   QItemSelectionModel* aSelMod = myTreeView->selectionModel();
397   connect(aSelMod, SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
398           this, SLOT(onSelectionChanged(const QItemSelection&, const QItemSelection&)));
399 }
400 #endif
401
402 void XGUI_ObjectsBrowser::onSelectionChanged(const QItemSelection& theSelected,
403                                        const QItemSelection& theDeselected)
404 {
405   emit selectionChanged();
406 }
407
408 QObjectPtrList XGUI_ObjectsBrowser::selectedObjects(QModelIndexList* theIndexes) const
409 {
410   QObjectPtrList aList;
411   QModelIndexList aIndexes = selectedIndexes();
412 #ifdef ModuleDataModel
413   ModuleBase_IDocumentDataModel* aModel = dataModel();
414 #else
415   XGUI_DataModel* aModel = dataModel();
416 #endif
417   QModelIndexList::const_iterator aIt;
418   for (aIt = aIndexes.constBegin(); aIt != aIndexes.constEnd(); ++aIt) {
419     if ((*aIt).column() == 0) {
420       ObjectPtr aObject = aModel->object(*aIt);
421       if (aObject) {
422         aList.append(aObject);
423         if (theIndexes)
424           theIndexes->append(*aIt);
425       }
426     }
427   }
428   return aList;
429 }