Salome HOME
Porting on new SALOME
[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 <QLineEdit>
16 #include <QPixmap>
17 #include <QEvent>
18 #include <QMouseEvent>
19 #include <QAction>
20 #include <QStyledItemDelegate>
21 #include <QMessageBox>
22
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),
38     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
95     // recursively by Qt 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   QTreeView::resizeEvent(theEvent);
124   QSize aSize = theEvent->size();
125   if (aSize.isValid()) {
126     setColumnWidth(0, aSize.width() - SECOND_COL_WIDTH - 7);
127     setColumnWidth(1, SECOND_COL_WIDTH);
128   }
129 }
130
131 void XGUI_DataTree::onDoubleClick(const QModelIndex& theIndex)
132 {
133   if (theIndex.column() != 1)
134     return;
135   SessionPtr aMgr = ModelAPI_Session::get();
136   // When operation is opened then we can not change history
137   if (aMgr->isOperation())
138     return;
139   XGUI_DataModel* aModel = dataModel();
140   if (aModel->flags(theIndex) == 0)
141     return;
142   ObjectPtr aObj = aModel->object(theIndex);
143
144   DocumentPtr aDoc = aMgr->activeDocument();
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() == 0) && (aDoc != aMgr->moduleDocument()))
156       // Clicked folder under root but active document is another
157       return;
158     if ((theIndex.internalId() != 0) && (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 aSize = aModel->rowCount(aParent);
169   for (int i = 0; i < aSize; i++) {
170     update(aModel->index(i, 0, aParent));
171     update(aModel->index(i, 1, aParent));
172   }
173 }
174
175
176 //********************************************************************
177 //********************************************************************
178 //********************************************************************
179 XGUI_ActiveDocLbl::XGUI_ActiveDocLbl(const QString& theText, QWidget* theParent )
180   : QLabel(theText, theParent),
181   myPreSelectionStyle(""),
182   myNeutralStyle(""),
183   mySelectionStyle(""),
184   myIsSelected(false)
185 {
186 }
187
188 void XGUI_ActiveDocLbl::setTreeView(QTreeView* theView)
189 {
190   myTreeView = theView;
191   setFont(myTreeView->font());
192
193   QPalette aPalet = myTreeView->palette();
194   QColor aHighlight = aPalet.highlight().color();
195   QColor aHighlightText = aPalet.highlightedText().color();
196
197   myPreSelectionStyle = "QLabel {background-color: ";
198   myPreSelectionStyle += aHighlight.lighter(170).name() + "}";
199   //myPreSelectionStyle += "qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 white, stop:1 " +
200   //  aHighlight.lighter(170).name() + ");";
201   //myPreSelectionStyle += "border: 1px solid lightblue; border-radius: 2px }";
202
203   QString aName = aPalet.color(QPalette::Base).name();
204   myNeutralStyle = "QLabel { border: 1px solid " + aName + " }";
205
206
207 #if (!defined HAVE_SALOME) && (defined WIN32)
208   mySelectionStyle = "QLabel {background-color: ";
209   mySelectionStyle += "rgb(205, 232, 255); ";
210   //mySelectionStyle += "qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(236, 245, 255)";
211   //mySelectionStyle += ", stop:1 rgb(208, 229, 255));";
212   //mySelectionStyle += "border: 1px solid rgb(132, 172, 221); border-radius: 2px }";
213   mySelectionStyle += "border: 1px solid rgb(153, 209, 255) }";
214 #else
215   mySelectionStyle = "QLabel {background-color: " + aHighlight.name();
216   mySelectionStyle += "; color : " + aHighlightText.name() + "}";
217 #endif
218
219   myTreeView->viewport()->installEventFilter(this);
220 }
221
222
223 #if (!defined HAVE_SALOME) && (defined WIN32)
224 bool XGUI_ActiveDocLbl::event(QEvent* theEvent)
225 {
226   switch (theEvent->type()) {
227     case QEvent::Enter:
228       if (!myIsSelected)
229         setStyleSheet(myPreSelectionStyle);
230       break;
231     case QEvent::Leave:
232       if (!myIsSelected)
233         setStyleSheet(myNeutralStyle);
234       break;
235   }
236   return QLabel::event(theEvent);
237 }
238 #endif
239
240 bool XGUI_ActiveDocLbl::eventFilter(QObject* theObj, QEvent* theEvent)
241 {
242   if (theObj == myTreeView->viewport()) {
243     if (theEvent->type() == QEvent::MouseButtonRelease)
244       unselect();
245   }
246   return QLabel::eventFilter(theObj, theEvent);
247 }
248
249 static bool MYClearing = false;
250 void XGUI_ActiveDocLbl::mouseReleaseEvent( QMouseEvent* e)
251 {
252   MYClearing = true;
253   myIsSelected = true;
254   setStyleSheet(mySelectionStyle);
255   // We can not block signals because on
256   // clear selection the View state will not be updated
257   myTreeView->clearSelection();
258   QLabel::mouseReleaseEvent(e);
259   MYClearing = false;
260 }
261
262 void XGUI_ActiveDocLbl::unselect()
263 {
264   if (!MYClearing) {
265     myIsSelected = false;
266     setStyleSheet(myNeutralStyle);
267   }
268 }
269
270
271 //********************************************************************
272 //********************************************************************
273 //********************************************************************
274 XGUI_ObjectsBrowser::XGUI_ObjectsBrowser(QWidget* theParent)
275     : QWidget(theParent), myDocModel(0)
276 {
277   QVBoxLayout* aLayout = new QVBoxLayout(this);
278   ModuleBase_Tools::zeroMargins(aLayout);
279   aLayout->setSpacing(0);
280
281   QWidget* aLabelWgt = new QWidget(this);
282   aLabelWgt->setAutoFillBackground(true);
283
284   aLayout->addWidget(aLabelWgt);
285   QHBoxLayout* aLabelLay = new QHBoxLayout(aLabelWgt);
286   ModuleBase_Tools::zeroMargins(aLabelLay);
287   aLabelLay->setSpacing(0);
288
289   QLabel* aLbl = new QLabel(aLabelWgt);
290   aLbl->setPixmap(QPixmap(":pictures/assembly.png"));
291   aLbl->setMargin(2);
292   // Do not paint background of the label (in order to show icon)
293   aLbl->setAutoFillBackground(false);
294
295   aLabelLay->addWidget(aLbl);
296
297   SessionPtr aMgr = ModelAPI_Session::get();
298   DocumentPtr aDoc = aMgr->moduleDocument();
299
300   myActiveDocLbl = new XGUI_ActiveDocLbl(tr("Part set"), aLabelWgt);
301 //  myActiveDocLbl->setReadOnly(true);
302 //  myActiveDocLbl->setFrame(false);
303   myActiveDocLbl->setContextMenuPolicy(Qt::CustomContextMenu);
304
305   aLabelLay->addWidget(myActiveDocLbl);
306   aLabelLay->setStretch(1, 1);
307
308   myTreeView = new XGUI_DataTree(this);
309   myTreeView->setFrameShape(QFrame::NoFrame);
310   aLayout->addWidget(myTreeView);
311
312   QPalette aTreePalet = myTreeView->palette();
313   QColor aTreeBack = aTreePalet.color(QPalette::Base);
314
315   QPalette aPalet;
316   aPalet.setColor(QPalette::Base, aTreeBack);
317   aPalet.setColor(QPalette::Window, aTreeBack);
318   aLabelWgt->setPalette(aPalet);
319
320   myDocModel = new XGUI_DataModel(this);
321   connect(myDocModel, SIGNAL(modelAboutToBeReset()), SLOT(onBeforeReset()));
322   connect(myDocModel, SIGNAL(treeRebuilt()), SLOT(onAfterModelReset()));
323
324   connect(myTreeView, SIGNAL(contextMenuRequested(QContextMenuEvent*)), this,
325           SLOT(onContextMenuRequested(QContextMenuEvent*)));
326 }
327
328 //***************************************************
329 XGUI_ObjectsBrowser::~XGUI_ObjectsBrowser()
330 {
331 }
332
333 void XGUI_ObjectsBrowser::setXMLReader(Config_DataModelReader* theReader)
334 {
335   myDocModel->setXMLReader(theReader);
336   myTreeView->setModel(myDocModel);
337
338   // It has to be done after setting of model
339   myActiveDocLbl->setTreeView(myTreeView);
340
341   QItemSelectionModel* aSelMod = myTreeView->selectionModel();
342   connect(aSelMod, SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
343           this, SLOT(onSelectionChanged(const QItemSelection&, const QItemSelection&)));
344 }
345
346 //***************************************************
347 void XGUI_ObjectsBrowser::onContextMenuRequested(QContextMenuEvent* theEvent)
348 {
349   QModelIndexList aIndexes;
350   QObjectPtrList aSelectedData = selectedObjects(&aIndexes);
351   bool toEnable = false;
352
353   if (aSelectedData.size() == 1) {
354     QModelIndex aSelected = myTreeView->indexAt(theEvent->pos());
355     if (!aIndexes.contains(aSelected))
356       return; // menu is called on non selected item
357
358     Qt::ItemFlags aFlags = dataModel()->flags(aIndexes.first());
359     toEnable = ((aFlags & Qt::ItemIsEditable) != 0);
360   }
361   foreach(QAction* aCmd, actions()) {
362     aCmd->setEnabled(toEnable);
363   }
364   emit contextMenuRequested(theEvent);
365 }
366
367 //***************************************************
368 void XGUI_ObjectsBrowser::onLabelContextMenuRequested(const QPoint& thePnt)
369 {
370   myTreeView->selectionModel()->clearSelection();
371   //Empty feature pointer means that selected root document
372   foreach(QAction* aCmd, actions()) {
373     aCmd->setEnabled(true);
374   }
375   QContextMenuEvent aEvent(QContextMenuEvent::Mouse, thePnt, myActiveDocLbl->mapToGlobal(thePnt));
376   emit contextMenuRequested(&aEvent);
377 }
378
379 //***************************************************
380 void XGUI_ObjectsBrowser::onEditItem()
381 {
382   QObjectPtrList aSelectedData = selectedObjects();
383   if (aSelectedData.size() > 0) {
384     ObjectPtr anObject = aSelectedData.first();
385     if (anObject.get()) {  // Selection happens in TreeView
386       // check whether the object can be renamed. There should not be parts which are not loaded
387       std::set<FeaturePtr> aFeatures;
388       aFeatures.insert(ModelAPI_Feature::feature(anObject));
389       if (!XGUI_Tools::canRemoveOrRename((QWidget*)parent(), aFeatures))
390         return;
391
392       // Find index which corresponds the feature
393       QModelIndex aIndex;
394       foreach(QModelIndex aIdx, selectedIndexes()) {
395         ObjectPtr aFea = dataModel()->object(aIdx);
396         if (dataModel()->object(aIdx)->isSame(anObject)) {
397           aIndex = aIdx;
398           break;
399         }
400       }
401       if (aIndex.isValid()) {
402         myTreeView->setCurrentIndex(aIndex);
403         myTreeView->edit(aIndex);
404       }
405       return;
406     }
407   }
408 }
409
410 //***************************************************
411 QModelIndexList XGUI_ObjectsBrowser::expandedItems(const QModelIndex& theParent) const
412 {
413   QModelIndexList aIndexes;
414   QModelIndex aIndex;
415   for (int i = 0; i < myDocModel->rowCount(theParent); i++) {
416     aIndex = myDocModel->index(i, 0, theParent);
417     if (myDocModel->hasChildren(aIndex)) {
418       if (myTreeView->isExpanded(aIndex)) {
419         aIndexes.append(aIndex);
420         QModelIndexList aSubIndexes = expandedItems(aIndex);
421         if (!aSubIndexes.isEmpty())
422           aIndexes.append(aSubIndexes);
423       }
424     }
425   }
426   return aIndexes;
427 }
428
429 //***************************************************
430 void XGUI_ObjectsBrowser::rebuildDataTree()
431 {
432   myDocModel->rebuildDataTree();
433   update();
434 }
435
436 //***************************************************
437 void XGUI_ObjectsBrowser::setObjectsSelected(const QObjectPtrList& theObjects)
438 {
439   QList<QModelIndex> theIndexes;
440   QItemSelectionModel* aSelectModel = myTreeView->selectionModel();
441   aSelectModel->clear();
442
443   foreach(ObjectPtr aFeature, theObjects)
444   {
445     QModelIndex aIndex = myDocModel->objectIndex(aFeature);
446     if (aIndex.isValid()) {
447       aSelectModel->select(aIndex, QItemSelectionModel::Select);
448     }
449   }
450 }
451
452 //***************************************************
453 void XGUI_ObjectsBrowser::clearContent()
454 {
455   myTreeView->clear();
456 }
457
458 void XGUI_ObjectsBrowser::onSelectionChanged(const QItemSelection& theSelected,
459                                        const QItemSelection& theDeselected)
460 {
461   emit selectionChanged();
462 }
463
464 QObjectPtrList XGUI_ObjectsBrowser::selectedObjects(QModelIndexList* theIndexes) const
465 {
466   QObjectPtrList aList;
467   QModelIndexList aIndexes = selectedIndexes();
468   XGUI_DataModel* aModel = dataModel();
469   QModelIndexList::const_iterator aIt;
470   for (aIt = aIndexes.constBegin(); aIt != aIndexes.constEnd(); ++aIt) {
471     if ((*aIt).column() == 0) {
472       ObjectPtr aObject = aModel->object(*aIt);
473       if (aObject) {
474         aList.append(aObject);
475         if (theIndexes)
476           theIndexes->append(*aIt);
477       }
478     }
479   }
480   return aList;
481 }
482
483 void XGUI_ObjectsBrowser::onBeforeReset()
484 {
485   myExpandedItems = expandedItems();
486 }
487
488 void XGUI_ObjectsBrowser::onAfterModelReset()
489 {
490   foreach(QModelIndex aIndex, myExpandedItems) {
491     myTreeView->setExpanded(aIndex, true);
492   }
493 }
494
495 std::list<bool> XGUI_ObjectsBrowser::getStateForDoc(DocumentPtr theDoc) const
496 {
497   std::list<bool> aStates;
498   XGUI_DataModel* aModel = dataModel();
499   QModelIndex aRootIdx = aModel->documentRootIndex(theDoc);
500   int aNbChild = aModel->rowCount(aRootIdx);
501   for (int i = 0; i < aNbChild; i++) {
502     QModelIndex aIdx = aModel->index(i, 0, aRootIdx);
503     aStates.push_back(myTreeView->isExpanded(aIdx));
504   }
505   return aStates;
506 }
507
508 void XGUI_ObjectsBrowser::setStateForDoc(DocumentPtr theDoc, const std::list<bool>& theStates)
509 {
510   if (theStates.size() == 0)
511     return;
512   XGUI_DataModel* aModel = dataModel();
513   QModelIndex aRootIdx = aModel->documentRootIndex(theDoc);
514   int aNbChild = aModel->rowCount(aRootIdx);
515
516   std::list<bool>::const_iterator aIt;
517   int i = 0;
518   for (aIt = theStates.cbegin(); aIt != theStates.cend(); aIt++, i++) {
519     if (i >= aNbChild )
520       break;
521     QModelIndex aIdx = aModel->index(i, 0, aRootIdx);
522     myTreeView->setExpanded(aIdx, (*aIt));
523   }
524 }