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