Salome HOME
Fix of Delete operation bug and Rename improvement
[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 #include <Events_Error.h>
12
13 #include <ModuleBase_Tools.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       XGUI_DataModel* 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   connect(this, SIGNAL(doubleClicked(const QModelIndex&)), 
73     SLOT(onDoubleClick(const QModelIndex&)));
74 }
75
76 XGUI_DataTree::~XGUI_DataTree()
77 {
78 }
79
80 XGUI_DataModel* XGUI_DataTree::dataModel() const
81 {
82   return static_cast<XGUI_DataModel*>(model());
83 }
84
85 void XGUI_DataTree::contextMenuEvent(QContextMenuEvent* theEvent)
86 {
87   emit contextMenuRequested(theEvent);
88 }
89
90 void XGUI_DataTree::commitData(QWidget* theEditor)
91 {
92   static int aEntrance = 0;
93   if (aEntrance == 0) {
94     // We have to check number of enter and exit of this function because it can be called recursively by Qt
95     // in order to avoid double modifying of a data
96     aEntrance = 1;
97     QLineEdit* aEditor = dynamic_cast<QLineEdit*>(theEditor);
98     if (aEditor) {
99       QString aName = aEditor->text();
100       QModelIndexList aIndexList = selectionModel()->selectedIndexes();
101       XGUI_DataModel* aModel = dataModel();
102       ObjectPtr aObj = aModel->object(aIndexList.first());
103
104       if (XGUI_Tools::canRename(aObj, aName)) {
105         SessionPtr aMgr = ModelAPI_Session::get();
106         aMgr->startOperation("Rename");
107         aObj->data()->setName(qPrintable(aName));
108         aMgr->finishOperation();
109       }
110     }
111   }
112   aEntrance = 0;
113 }
114
115 void XGUI_DataTree::clear() 
116 {
117   dataModel()->clear();
118   reset();
119 }
120
121 void XGUI_DataTree::resizeEvent(QResizeEvent* theEvent)
122 {
123   QSize aSize = theEvent->size();
124   if (aSize.isValid()) {
125     setColumnWidth(0, aSize.width() - SECOND_COL_WIDTH);
126     setColumnWidth(1, SECOND_COL_WIDTH);
127   }
128 }
129
130 void XGUI_DataTree::onDoubleClick(const QModelIndex& theIndex)
131 {
132   if (theIndex.column() != 1)
133     return;
134   SessionPtr aMgr = ModelAPI_Session::get();
135   // When operation is opened then we can not change history
136   if (aMgr->isOperation())
137     return;
138   XGUI_DataModel* aModel = dataModel();
139   if (aModel->flags(theIndex) == 0)
140     return;
141   ObjectPtr aObj = aModel->object(theIndex);
142
143   DocumentPtr aDoc = aMgr->activeDocument();
144   
145   std::string aOpName = tr("History change").toStdString();
146   if (aObj.get()) {
147     if (aObj->document() != aDoc)
148       return;
149     aMgr->startOperation(aOpName);
150     aDoc->setCurrentFeature(std::dynamic_pointer_cast<ModelAPI_Feature>(aObj), true);
151     aMgr->finishOperation();
152   } else {
153     // Ignore clicks on folders outside current document
154     if ((theIndex.internalId() == -1) && (aDoc != aMgr->moduleDocument()))
155       // Clicked folder under root but active document is another
156       return;
157     if ((theIndex.internalId() != -1) && (aDoc.get() != theIndex.internalPointer()))
158       // Cliced not on active document folder
159       return;
160
161     aMgr->startOperation(aOpName);
162     aDoc->setCurrentFeature(FeaturePtr(), true);
163     aMgr->finishOperation();
164   }
165   QModelIndex aNewIndex = aModel->lastHistoryIndex();
166   QModelIndex aParent = theIndex.parent();
167   int aSize = aModel->rowCount(aParent);
168   for (int i = 0; i < aSize; i++) {
169     update(aModel->index(i, 0, aParent));
170   }
171 }
172
173 //********************************************************************
174 //********************************************************************
175 //********************************************************************
176 XGUI_ObjectsBrowser::XGUI_ObjectsBrowser(QWidget* theParent)
177     : QWidget(theParent), myDocModel(0)
178 {
179   QVBoxLayout* aLayout = new QVBoxLayout(this);
180   ModuleBase_Tools::zeroMargins(aLayout);
181   aLayout->setSpacing(0);
182
183   QFrame* aLabelWgt = new QFrame(this);
184   //QWidget* aLabelWgt = new QWidget(this);
185   aLabelWgt->setAutoFillBackground(true);
186   QPalette aPalet = aLabelWgt->palette();
187   aPalet.setColor(QPalette::Window, Qt::white);
188   aLabelWgt->setPalette(aPalet);
189
190   aLayout->addWidget(aLabelWgt);
191   QHBoxLayout* aLabelLay = new QHBoxLayout(aLabelWgt);
192   ModuleBase_Tools::zeroMargins(aLabelLay);
193   aLabelLay->setSpacing(0);
194
195   QLabel* aLbl = new QLabel(aLabelWgt);
196   aLbl->setPixmap(QPixmap(":pictures/assembly.png"));
197   aLbl->setMargin(2);
198
199   aLbl->setAutoFillBackground(true);
200
201   aLabelLay->addWidget(aLbl);
202
203   SessionPtr aMgr = ModelAPI_Session::get();
204   DocumentPtr aDoc = aMgr->moduleDocument();
205   // TODO: Find a name of the root document
206
207   myActiveDocLbl = new QLineEdit(tr("Part set"), aLabelWgt);
208   myActiveDocLbl->setReadOnly(true);
209   myActiveDocLbl->setFrame(false);
210   //myActiveDocLbl->setMargin(2);
211   myActiveDocLbl->setContextMenuPolicy(Qt::CustomContextMenu);
212
213   myActiveDocLbl->installEventFilter(this);
214
215   aLabelLay->addWidget(myActiveDocLbl);
216   aLabelLay->setStretch(1, 1);
217
218   myTreeView = new XGUI_DataTree(this);
219   //myTreeView->setFrameShape(QFrame::NoFrame);
220   aLayout->addWidget(myTreeView);
221
222   aLabelWgt->setFrameShape(myTreeView->frameShape());
223   aLabelWgt->setFrameShadow(myTreeView->frameShadow());
224
225   myDocModel = new XGUI_DataModel(this);
226   myTreeView->setModel(myDocModel);
227   QItemSelectionModel* aSelMod = myTreeView->selectionModel();
228   connect(aSelMod, SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
229           this, SLOT(onSelectionChanged(const QItemSelection&, const QItemSelection&)));
230
231   connect(myActiveDocLbl, SIGNAL(customContextMenuRequested(const QPoint&)), this,
232           SLOT(onLabelContextMenuRequested(const QPoint&)));
233   connect(myTreeView, SIGNAL(contextMenuRequested(QContextMenuEvent*)), this,
234           SLOT(onContextMenuRequested(QContextMenuEvent*)));
235 }
236
237 //***************************************************
238 XGUI_ObjectsBrowser::~XGUI_ObjectsBrowser()
239 {
240 }
241
242 //***************************************************
243 bool XGUI_ObjectsBrowser::eventFilter(QObject* obj, QEvent* theEvent)
244 {
245   if (obj == myActiveDocLbl) {
246     if (!myActiveDocLbl->isReadOnly()) {
247       // End of editing by mouse click
248       if (theEvent->type() == QEvent::MouseButtonRelease) {
249         QMouseEvent* aEvent = (QMouseEvent*) theEvent;
250         QPoint aPnt = mapFromGlobal(aEvent->globalPos());
251         if (childAt(aPnt) != myActiveDocLbl) {
252           closeDocNameEditing(true);
253         }
254       } else if (theEvent->type() == QEvent::KeyRelease) {
255         QKeyEvent* aEvent = (QKeyEvent*) theEvent;
256         switch (aEvent->key()) {
257           case Qt::Key_Return:
258           case Qt::Key_Enter:  // Accept current input
259             closeDocNameEditing(true);
260             break;
261           case Qt::Key_Escape:  // Cancel the input
262             closeDocNameEditing(false);
263             break;
264         }
265       }
266     } else {
267       if (theEvent->type() == QEvent::MouseButtonDblClick) {
268         emit headerMouseDblClicked(QModelIndex());
269         return true;
270       }  
271     }
272   }
273   return QWidget::eventFilter(obj, theEvent);
274 }
275
276 //***************************************************
277 void XGUI_ObjectsBrowser::closeDocNameEditing(bool toSave)
278 {
279   myActiveDocLbl->deselect();
280   myActiveDocLbl->clearFocus();
281   myActiveDocLbl->releaseMouse();
282   myActiveDocLbl->setReadOnly(true);
283   if (toSave) {
284     // TODO: Save the name of root document
285     SessionPtr aMgr = ModelAPI_Session::get();
286     DocumentPtr aDoc = aMgr->moduleDocument();
287   } else {
288     myActiveDocLbl->setText(myActiveDocLbl->property("OldText").toString());
289   }
290 }
291
292 //***************************************************
293 void XGUI_ObjectsBrowser::onContextMenuRequested(QContextMenuEvent* theEvent)
294 {
295   QModelIndexList aIndexes;
296   QObjectPtrList aSelectedData = selectedObjects(&aIndexes);
297   bool toEnable = false;
298
299   if (aSelectedData.size() == 1) {
300     QModelIndex aSelected = myTreeView->indexAt(theEvent->pos());
301     if (!aIndexes.contains(aSelected))
302       return; // menu is called on non selected item
303
304     Qt::ItemFlags aFlags = dataModel()->flags(aIndexes.first());
305     toEnable = ((aFlags & Qt::ItemIsEditable) != 0);
306   }
307   foreach(QAction* aCmd, actions()) {
308     aCmd->setEnabled(toEnable);
309   }
310   emit contextMenuRequested(theEvent);
311 }
312
313 //***************************************************
314 void XGUI_ObjectsBrowser::onLabelContextMenuRequested(const QPoint& thePnt)
315 {
316   myTreeView->selectionModel()->clearSelection();
317   //Empty feature pointer means that selected root document
318   foreach(QAction* aCmd, actions()) {
319     aCmd->setEnabled(true);
320   }
321   QContextMenuEvent aEvent(QContextMenuEvent::Mouse, thePnt, myActiveDocLbl->mapToGlobal(thePnt));
322   emit contextMenuRequested(&aEvent);
323 }
324
325 //***************************************************
326 void XGUI_ObjectsBrowser::onEditItem()
327 {
328   QObjectPtrList aSelectedData = selectedObjects();
329   if (aSelectedData.size() > 0) {
330     ObjectPtr aFeature = aSelectedData.first();
331     if (aFeature) {  // Selection happens in TreeView
332       QObjectPtrList aList;
333       aList.append(aFeature);
334       // check whether the object can be deleted. There should not be parts which are not loaded
335       if (!XGUI_Tools::canRemoveOrRename((QWidget*)parent(), aList))
336         return;
337
338       // Find index which corresponds the feature
339       QModelIndex aIndex;
340       foreach(QModelIndex aIdx, selectedIndexes()) {
341         ObjectPtr aFea = dataModel()->object(aIdx);
342         if (dataModel()->object(aIdx)->isSame(aFeature)) {
343           aIndex = aIdx;
344           break;
345         }
346       }
347       if (aIndex.isValid()) {
348         myTreeView->setCurrentIndex(aIndex);
349         myTreeView->edit(aIndex);
350       }
351       return;
352     }
353   }
354   //Selection happens in Upper label
355   myActiveDocLbl->setReadOnly(false);
356   myActiveDocLbl->setFocus();
357   myActiveDocLbl->selectAll();
358   myActiveDocLbl->grabMouse();
359   myActiveDocLbl->setProperty("OldText", myActiveDocLbl->text());
360 }
361
362 //***************************************************
363 void XGUI_ObjectsBrowser::rebuildDataTree()
364 {
365   myDocModel->rebuildDataTree();
366   update();
367 }
368
369 //***************************************************
370 void XGUI_ObjectsBrowser::setObjectsSelected(const QObjectPtrList& theObjects)
371 {
372   QList<QModelIndex> theIndexes;
373   QItemSelectionModel* aSelectModel = myTreeView->selectionModel();
374   aSelectModel->clear();
375
376   foreach(ObjectPtr aFeature, theObjects)
377   {
378     QModelIndex aIndex = myDocModel->objectIndex(aFeature);
379     if (aIndex.isValid()) {
380       aSelectModel->select(aIndex, QItemSelectionModel::Select);
381     }
382   }
383 }
384
385 //***************************************************
386 void XGUI_ObjectsBrowser::clearContent()  
387
388   myTreeView->clear(); 
389 }
390
391 void XGUI_ObjectsBrowser::onSelectionChanged(const QItemSelection& theSelected,
392                                        const QItemSelection& theDeselected)
393 {
394   emit selectionChanged();
395 }
396
397 QObjectPtrList XGUI_ObjectsBrowser::selectedObjects(QModelIndexList* theIndexes) const
398 {
399   QObjectPtrList aList;
400   QModelIndexList aIndexes = selectedIndexes();
401   XGUI_DataModel* aModel = dataModel();
402   QModelIndexList::const_iterator aIt;
403   for (aIt = aIndexes.constBegin(); aIt != aIndexes.constEnd(); ++aIt) {
404     if ((*aIt).column() == 0) {
405       ObjectPtr aObject = aModel->object(*aIt);
406       if (aObject) {
407         aList.append(aObject);
408         if (theIndexes)
409           theIndexes->append(*aIt);
410       }
411     }
412   }
413   return aList;
414 }