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