]> SALOME platform Git repositories - modules/shaper.git/blob - src/XGUI/XGUI_ObjectsBrowser.cpp
Salome HOME
Issue #888: Do not highlight non selectable tree items in Object Browser
[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 <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   myStyle = new XGUI_TreeViewStyle();
65   setStyle(myStyle);
66   setHeaderHidden(true);
67   setEditTriggers(QAbstractItemView::NoEditTriggers);
68   setSelectionBehavior(QAbstractItemView::SelectRows);
69   setSelectionMode(QAbstractItemView::ExtendedSelection);
70
71   setItemDelegateForColumn(0, new XGUI_TreeViewItemDelegate(this));
72
73   connect(this, SIGNAL(doubleClicked(const QModelIndex&)), 
74     SLOT(onDoubleClick(const QModelIndex&)));
75 }
76
77 XGUI_DataTree::~XGUI_DataTree()
78 {
79 }
80
81 XGUI_DataModel* XGUI_DataTree::dataModel() const
82 {
83   return static_cast<XGUI_DataModel*>(model());
84 }
85
86 void XGUI_DataTree::contextMenuEvent(QContextMenuEvent* theEvent)
87 {
88   emit contextMenuRequested(theEvent);
89 }
90
91 void XGUI_DataTree::commitData(QWidget* theEditor)
92 {
93   static int aEntrance = 0;
94   if (aEntrance == 0) {
95     // We have to check number of enter and exit of this function because it can be called recursively by Qt
96     // in order to avoid double modifying of a data
97     aEntrance = 1;
98     QLineEdit* aEditor = dynamic_cast<QLineEdit*>(theEditor);
99     if (aEditor) {
100       QString aName = aEditor->text();
101       QModelIndexList aIndexList = selectionModel()->selectedIndexes();
102       XGUI_DataModel* aModel = dataModel();
103       ObjectPtr aObj = aModel->object(aIndexList.first());
104
105       if (XGUI_Tools::canRename(aObj, aName)) {
106         SessionPtr aMgr = ModelAPI_Session::get();
107         aMgr->startOperation("Rename");
108         aObj->data()->setName(qPrintable(aName));
109         aMgr->finishOperation();
110       }
111     }
112   }
113   aEntrance = 0;
114 }
115
116 void XGUI_DataTree::clear() 
117 {
118   dataModel()->clear();
119   reset();
120 }
121
122 void XGUI_DataTree::resizeEvent(QResizeEvent* theEvent)
123 {
124   QSize aSize = theEvent->size();
125   if (aSize.isValid()) {
126     setColumnWidth(0, aSize.width() - SECOND_COL_WIDTH);
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() == -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 aSize = aModel->rowCount(aParent);
169   for (int i = 0; i < aSize; i++) {
170     update(aModel->index(i, 0, aParent));
171   }
172 }
173
174 void XGUI_DataTree::drawRow(QPainter* thePainter,
175                             const QStyleOptionViewItem& theOptions,
176                             const QModelIndex& theIndex) const
177 {
178   QStyleOptionViewItemV4 aOptions = theOptions;
179   myStyle->setIndex(theIndex);
180   QTreeView::drawRow(thePainter, aOptions, theIndex);
181 }
182
183 //********************************************************************
184 //********************************************************************
185 //********************************************************************
186 void XGUI_TreeViewStyle::drawPrimitive(PrimitiveElement theElement, 
187                                        const QStyleOption* theOption,
188                                        QPainter* thePainter, const QWidget* theWidget) const
189 {
190   if ((theElement == QStyle::PE_PanelItemViewRow) || (theElement == QStyle::PE_PanelItemViewItem)) {
191     const QStyleOptionViewItemV4* aOptions = qstyleoption_cast<const QStyleOptionViewItemV4 *>(theOption);
192     if (myIndex.isValid() && ((myIndex.flags() & Qt::ItemIsSelectable) == 0)) {
193       QStyle::State aState = aOptions->state;
194       if ((aState & QStyle::State_MouseOver) != 0)
195         aState &= ~QStyle::State_MouseOver;
196       QStyleOptionViewItemV4* aOpt = (QStyleOptionViewItemV4*) aOptions;
197       aOpt->state = aState;
198       QWindowsVistaStyle::drawPrimitive(theElement, aOpt, thePainter, theWidget);
199     }
200   }
201   QWindowsVistaStyle::drawPrimitive(theElement, theOption, thePainter, theWidget);
202 }
203
204
205 //********************************************************************
206 //********************************************************************
207 //********************************************************************
208 XGUI_ActiveDocLbl::XGUI_ActiveDocLbl(const QString& theText, QWidget* theParent )
209   : QLineEdit(theText, theParent), 
210   myPreSelectionStyle(""), 
211   myNeutralStyle(""), 
212   mySelectionStyle(""),
213   myIsSelected(false)
214 {
215 }
216
217 void XGUI_ActiveDocLbl::setTreeView(QTreeView* theView)
218 {
219   myTreeView = theView;
220   QPalette aPalet = myTreeView->palette();
221   QColor aHighlight = aPalet.highlight().color();
222
223   myPreSelectionStyle = "QLineEdit {background-color: ";
224   myPreSelectionStyle += "qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 white, stop:1 " + aHighlight.lighter(170).name() + ");"; 
225   myPreSelectionStyle += "border: 1px solid lightblue; border-radius: 2px }";
226
227   QString aName = aPalet.color(QPalette::Base).name();
228   myNeutralStyle = "QLineEdit { border: 1px solid " + aName + " }";
229
230   mySelectionStyle = "QLineEdit {background-color: ";
231   mySelectionStyle += "qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 " + aHighlight.lighter(170).name();
232   mySelectionStyle += ", stop:1 " + aHighlight.lighter().name() + ");"; 
233   mySelectionStyle += "border: 1px solid lightblue; border-radius: 2px }";
234
235   myTreeView->viewport()->installEventFilter(this);
236 }
237
238
239 bool XGUI_ActiveDocLbl::event(QEvent* theEvent)
240 {
241   switch (theEvent->type()) {
242     case QEvent::Enter:
243       if (!myIsSelected)
244         setStyleSheet(myPreSelectionStyle);
245       break;
246     case QEvent::Leave:
247       if (!myIsSelected)
248         setStyleSheet(myNeutralStyle);
249       break;
250   }
251   return QLineEdit::event(theEvent);
252 }
253
254 bool XGUI_ActiveDocLbl::eventFilter(QObject* theObj, QEvent* theEvent)
255 {
256   if (theObj == myTreeView->viewport()) {
257     if (theEvent->type() == QEvent::MouseButtonRelease)
258       unselect();
259   }
260   return QLineEdit::eventFilter(theObj, theEvent);
261 }
262
263 static bool MYClearing = false;
264 void XGUI_ActiveDocLbl::mouseReleaseEvent( QMouseEvent* e)
265 {
266   MYClearing = true;
267   myIsSelected = true;
268   setStyleSheet(mySelectionStyle);
269   // We can not block signals because on 
270   // clear selection the View state will not be updated
271   myTreeView->clearSelection();
272   QLineEdit::mouseReleaseEvent(e);
273   MYClearing = false;
274 }
275
276 void XGUI_ActiveDocLbl::unselect()
277 {
278   if (!MYClearing) {
279     myIsSelected = false;
280     setStyleSheet(myNeutralStyle);
281   }
282 }
283
284
285 //********************************************************************
286 //********************************************************************
287 //********************************************************************
288 XGUI_ObjectsBrowser::XGUI_ObjectsBrowser(QWidget* theParent)
289     : QWidget(theParent), myDocModel(0)
290 {
291   QVBoxLayout* aLayout = new QVBoxLayout(this);
292   ModuleBase_Tools::zeroMargins(aLayout);
293   aLayout->setSpacing(0);
294
295   QWidget* aLabelWgt = new QWidget(this);
296   aLabelWgt->setAutoFillBackground(true);
297
298   aLayout->addWidget(aLabelWgt);
299   QHBoxLayout* aLabelLay = new QHBoxLayout(aLabelWgt);
300   ModuleBase_Tools::zeroMargins(aLabelLay);
301   aLabelLay->setSpacing(0);
302
303   QLabel* aLbl = new QLabel(aLabelWgt);
304   aLbl->setPixmap(QPixmap(":pictures/assembly.png"));
305   aLbl->setMargin(2);
306   // Do not paint background of the label (in order to show icon)
307   aLbl->setAutoFillBackground(false); 
308
309   aLabelLay->addWidget(aLbl);
310
311   SessionPtr aMgr = ModelAPI_Session::get();
312   DocumentPtr aDoc = aMgr->moduleDocument();
313
314   myActiveDocLbl = new XGUI_ActiveDocLbl(tr("Part set"), aLabelWgt);
315   myActiveDocLbl->setReadOnly(true);
316   myActiveDocLbl->setFrame(false);
317   myActiveDocLbl->setContextMenuPolicy(Qt::CustomContextMenu);
318
319   aLabelLay->addWidget(myActiveDocLbl);
320   aLabelLay->setStretch(1, 1);
321
322   myTreeView = new XGUI_DataTree(this);
323   myTreeView->setFrameShape(QFrame::NoFrame);
324   aLayout->addWidget(myTreeView);
325
326   QPalette aTreePalet = myTreeView->palette();
327   QColor aTreeBack = aTreePalet.color(QPalette::Base);
328
329   QPalette aPalet;
330   aPalet.setColor(QPalette::Base, aTreeBack);
331   aPalet.setColor(QPalette::Window, aTreeBack);
332   aLabelWgt->setPalette(aPalet);
333
334   myDocModel = new XGUI_DataModel(this);
335   myTreeView->setModel(myDocModel);
336
337   // It has to be done after setting of model
338   myActiveDocLbl->setTreeView(myTreeView);
339
340   QItemSelectionModel* aSelMod = myTreeView->selectionModel();
341   connect(aSelMod, SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
342           this, SLOT(onSelectionChanged(const QItemSelection&, const QItemSelection&)));
343
344   connect(myTreeView, SIGNAL(contextMenuRequested(QContextMenuEvent*)), this,
345           SLOT(onContextMenuRequested(QContextMenuEvent*)));
346 }
347
348 //***************************************************
349 XGUI_ObjectsBrowser::~XGUI_ObjectsBrowser()
350 {
351 }
352
353
354 //***************************************************
355 void XGUI_ObjectsBrowser::onContextMenuRequested(QContextMenuEvent* theEvent)
356 {
357   QModelIndexList aIndexes;
358   QObjectPtrList aSelectedData = selectedObjects(&aIndexes);
359   bool toEnable = false;
360
361   if (aSelectedData.size() == 1) {
362     QModelIndex aSelected = myTreeView->indexAt(theEvent->pos());
363     if (!aIndexes.contains(aSelected))
364       return; // menu is called on non selected item
365
366     Qt::ItemFlags aFlags = dataModel()->flags(aIndexes.first());
367     toEnable = ((aFlags & Qt::ItemIsEditable) != 0);
368   }
369   foreach(QAction* aCmd, actions()) {
370     aCmd->setEnabled(toEnable);
371   }
372   emit contextMenuRequested(theEvent);
373 }
374
375 //***************************************************
376 void XGUI_ObjectsBrowser::onLabelContextMenuRequested(const QPoint& thePnt)
377 {
378   myTreeView->selectionModel()->clearSelection();
379   //Empty feature pointer means that selected root document
380   foreach(QAction* aCmd, actions()) {
381     aCmd->setEnabled(true);
382   }
383   QContextMenuEvent aEvent(QContextMenuEvent::Mouse, thePnt, myActiveDocLbl->mapToGlobal(thePnt));
384   emit contextMenuRequested(&aEvent);
385 }
386
387 //***************************************************
388 void XGUI_ObjectsBrowser::onEditItem()
389 {
390   QObjectPtrList aSelectedData = selectedObjects();
391   if (aSelectedData.size() > 0) {
392     ObjectPtr aFeature = aSelectedData.first();
393     if (aFeature) {  // Selection happens in TreeView
394       QObjectPtrList aList;
395       aList.append(aFeature);
396       // check whether the object can be deleted. There should not be parts which are not loaded
397       if (!XGUI_Tools::canRemoveOrRename((QWidget*)parent(), aList))
398         return;
399
400       // Find index which corresponds the feature
401       QModelIndex aIndex;
402       foreach(QModelIndex aIdx, selectedIndexes()) {
403         ObjectPtr aFea = dataModel()->object(aIdx);
404         if (dataModel()->object(aIdx)->isSame(aFeature)) {
405           aIndex = aIdx;
406           break;
407         }
408       }
409       if (aIndex.isValid()) {
410         myTreeView->setCurrentIndex(aIndex);
411         myTreeView->edit(aIndex);
412       }
413       return;
414     }
415   }
416 }
417
418 //***************************************************
419 void XGUI_ObjectsBrowser::rebuildDataTree()
420 {
421   myDocModel->rebuildDataTree();
422   update();
423 }
424
425 //***************************************************
426 void XGUI_ObjectsBrowser::setObjectsSelected(const QObjectPtrList& theObjects)
427 {
428   QList<QModelIndex> theIndexes;
429   QItemSelectionModel* aSelectModel = myTreeView->selectionModel();
430   aSelectModel->clear();
431
432   foreach(ObjectPtr aFeature, theObjects)
433   {
434     QModelIndex aIndex = myDocModel->objectIndex(aFeature);
435     if (aIndex.isValid()) {
436       aSelectModel->select(aIndex, QItemSelectionModel::Select);
437     }
438   }
439 }
440
441 //***************************************************
442 void XGUI_ObjectsBrowser::clearContent()  
443
444   myTreeView->clear(); 
445 }
446
447 void XGUI_ObjectsBrowser::onSelectionChanged(const QItemSelection& theSelected,
448                                        const QItemSelection& theDeselected)
449 {
450   emit selectionChanged();
451 }
452
453 QObjectPtrList XGUI_ObjectsBrowser::selectedObjects(QModelIndexList* theIndexes) const
454 {
455   QObjectPtrList aList;
456   QModelIndexList aIndexes = selectedIndexes();
457   XGUI_DataModel* aModel = dataModel();
458   QModelIndexList::const_iterator aIt;
459   for (aIt = aIndexes.constBegin(); aIt != aIndexes.constEnd(); ++aIt) {
460     if ((*aIt).column() == 0) {
461       ObjectPtr aObject = aModel->object(*aIt);
462       if (aObject) {
463         aList.append(aObject);
464         if (theIndexes)
465           theIndexes->append(*aIt);
466       }
467     }
468   }
469   return aList;
470 }