]> SALOME platform Git repositories - modules/shaper.git/blob - src/XGUI/XGUI_ObjectsBrowser.cpp
Salome HOME
Porting to SALOME_8.2.0 : SHAPERGUI module
[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 #ifdef WIN32
24 #ifdef HAVE_SALOME
25 // PORTING_TO_SALOME_8
26 //#include <QWindowsStyle>
27 #include <QCommonStyle>
28 #endif
29 #endif
30
31
32 /// Width of second column (minimum acceptable = 27)
33 #define SECOND_COL_WIDTH 30
34
35
36 /**
37 * \ingroup GUI
38 * Tree item delegate for definition of data in column items editor
39 */
40 class XGUI_TreeViewItemDelegate: public QStyledItemDelegate
41 {
42 public:
43   /// Constructor
44   /// \param theParent a parent of the delegate
45   XGUI_TreeViewItemDelegate(XGUI_DataTree* theParent):QStyledItemDelegate(theParent),
46     myTreedView(theParent) {}
47
48   /// Set data for item editor (name of the item)
49   /// \param editor a widget of editor
50   /// \param index the tree item index
51   virtual void  setEditorData ( QWidget* editor, const QModelIndex& index ) const
52   {
53     QLineEdit* aEditor = dynamic_cast<QLineEdit*>(editor);
54     if (aEditor) {
55       XGUI_DataModel* aModel = myTreedView->dataModel();
56       ObjectPtr aObj = aModel->object(index);
57       if (aObj.get() != NULL) {
58         aEditor->setText(aObj->data()->name().c_str());
59         return;
60       }
61     }
62     QStyledItemDelegate::setEditorData(editor, index);
63   }
64
65 private:
66   XGUI_DataTree* myTreedView;
67 };
68
69
70 XGUI_DataTree::XGUI_DataTree(QWidget* theParent)
71     : QTreeView(theParent)
72 {
73 #ifdef WIN32
74 #ifdef HAVE_SALOME
75   setStyle(new QCommonStyle());
76 #else
77   myStyle = new XGUI_TreeViewStyle();
78   setStyle(myStyle);
79 #endif
80 #endif
81
82   setHeaderHidden(true);
83   setEditTriggers(QAbstractItemView::NoEditTriggers);
84   setSelectionBehavior(QAbstractItemView::SelectRows);
85   setSelectionMode(QAbstractItemView::ExtendedSelection);
86
87   setItemDelegateForColumn(0, new XGUI_TreeViewItemDelegate(this));
88
89   connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
90     SLOT(onDoubleClick(const QModelIndex&)));
91 }
92
93 XGUI_DataTree::~XGUI_DataTree()
94 {
95 }
96
97 XGUI_DataModel* XGUI_DataTree::dataModel() const
98 {
99   return static_cast<XGUI_DataModel*>(model());
100 }
101
102 void XGUI_DataTree::contextMenuEvent(QContextMenuEvent* theEvent)
103 {
104   emit contextMenuRequested(theEvent);
105 }
106
107 void XGUI_DataTree::commitData(QWidget* theEditor)
108 {
109   static int aEntrance = 0;
110   if (aEntrance == 0) {
111     // We have to check number of enter and exit of this function because it can be called
112     // recursively by Qt in order to avoid double modifying of a data
113     aEntrance = 1;
114     QLineEdit* aEditor = dynamic_cast<QLineEdit*>(theEditor);
115     if (aEditor) {
116       QString aName = aEditor->text();
117       QModelIndexList aIndexList = selectionModel()->selectedIndexes();
118       XGUI_DataModel* aModel = dataModel();
119       ObjectPtr aObj = aModel->object(aIndexList.first());
120
121       if (XGUI_Tools::canRename(aObj, aName)) {
122         SessionPtr aMgr = ModelAPI_Session::get();
123         aMgr->startOperation("Rename");
124         aObj->data()->setName(qPrintable(aName));
125         aMgr->finishOperation();
126       }
127     }
128   }
129   aEntrance = 0;
130 }
131
132 void XGUI_DataTree::clear()
133 {
134   dataModel()->clear();
135   reset();
136 }
137
138 void XGUI_DataTree::resizeEvent(QResizeEvent* theEvent)
139 {
140   QTreeView::resizeEvent(theEvent);
141   QSize aSize = theEvent->size();
142   if (aSize.isValid()) {
143     setColumnWidth(0, aSize.width() - SECOND_COL_WIDTH - 6);
144     setColumnWidth(1, SECOND_COL_WIDTH);
145   }
146 }
147
148 void XGUI_DataTree::onDoubleClick(const QModelIndex& theIndex)
149 {
150   if (theIndex.column() != 1)
151     return;
152   SessionPtr aMgr = ModelAPI_Session::get();
153   // When operation is opened then we can not change history
154   if (aMgr->isOperation())
155     return;
156   XGUI_DataModel* aModel = dataModel();
157   if (aModel->flags(theIndex) == 0)
158     return;
159   ObjectPtr aObj = aModel->object(theIndex);
160
161   DocumentPtr aDoc = aMgr->activeDocument();
162
163   std::string aOpName = tr("History change").toStdString();
164   if (aObj.get()) {
165     if (aObj->document() != aDoc)
166       return;
167     aMgr->startOperation(aOpName);
168     aDoc->setCurrentFeature(std::dynamic_pointer_cast<ModelAPI_Feature>(aObj), true);
169     aMgr->finishOperation();
170   } else {
171     // Ignore clicks on folders outside current document
172     if ((theIndex.internalId() == -1) && (aDoc != aMgr->moduleDocument()))
173       // Clicked folder under root but active document is another
174       return;
175     if ((theIndex.internalId() != -1) && (aDoc.get() != theIndex.internalPointer()))
176       // Cliced not on active document folder
177       return;
178
179     aMgr->startOperation(aOpName);
180     aDoc->setCurrentFeature(FeaturePtr(), true);
181     aMgr->finishOperation();
182   }
183   QModelIndex aNewIndex = aModel->lastHistoryIndex();
184   QModelIndex aParent = theIndex.parent();
185   int aSize = aModel->rowCount(aParent);
186   for (int i = 0; i < aSize; i++) {
187     update(aModel->index(i, 0, aParent));
188     update(aModel->index(i, 1, aParent));
189   }
190 }
191
192 #if (!defined HAVE_SALOME) && (defined WIN32)
193 void XGUI_DataTree::drawRow(QPainter* thePainter,
194                             const QStyleOptionViewItem& theOptions,
195                             const QModelIndex& theIndex) const
196 {
197   QStyleOptionViewItemV4 aOptions = theOptions;
198   myStyle->setIndex(theIndex);
199   QTreeView::drawRow(thePainter, aOptions, theIndex);
200 }
201
202 //********************************************************************
203 //********************************************************************
204 //********************************************************************
205 void XGUI_TreeViewStyle::drawPrimitive(PrimitiveElement theElement,
206                                        const QStyleOption* theOption,
207                                        QPainter* thePainter, const QWidget* theWidget) const
208 {
209   if ((theElement == QStyle::PE_PanelItemViewRow) || (theElement == QStyle::PE_PanelItemViewItem)) {
210     const QStyleOptionViewItemV4* aOptions =
211       qstyleoption_cast<const QStyleOptionViewItemV4 *>(theOption);
212     if (myIndex.isValid() && ((myIndex.flags() & Qt::ItemIsSelectable) == 0)) {
213       QStyle::State aState = aOptions->state;
214       if ((aState & QStyle::State_MouseOver) != 0)
215         aState &= ~QStyle::State_MouseOver;
216       QStyleOptionViewItemV4* aOpt = (QStyleOptionViewItemV4*) aOptions;
217       aOpt->state = aState;
218       QCommonStyle/*QWindowsVistaStyle*/::drawPrimitive(theElement, aOpt, thePainter, theWidget);
219     }
220   }
221   QCommonStyle/*QWindowsVistaStyle*/::drawPrimitive(theElement, theOption, thePainter, theWidget);
222 }
223 #endif
224
225
226 //********************************************************************
227 //********************************************************************
228 //********************************************************************
229 XGUI_ActiveDocLbl::XGUI_ActiveDocLbl(const QString& theText, QWidget* theParent )
230   : QLabel(theText, theParent),
231   myPreSelectionStyle(""),
232   myNeutralStyle(""),
233   mySelectionStyle(""),
234   myIsSelected(false)
235 {
236 }
237
238 void XGUI_ActiveDocLbl::setTreeView(QTreeView* theView)
239 {
240   myTreeView = theView;
241   setFont(myTreeView->font());
242
243   QPalette aPalet = myTreeView->palette();
244   QColor aHighlight = aPalet.highlight().color();
245   QColor aHighlightText = aPalet.highlightedText().color();
246
247   myPreSelectionStyle = "QLabel {background-color: ";
248   myPreSelectionStyle += "qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 white, stop:1 " +
249     aHighlight.lighter(170).name() + ");";
250   myPreSelectionStyle += "border: 1px solid lightblue; border-radius: 2px }";
251
252   QString aName = aPalet.color(QPalette::Base).name();
253   myNeutralStyle = "QLabel { border: 1px solid " + aName + " }";
254
255
256 #if (!defined HAVE_SALOME) && (defined WIN32)
257   mySelectionStyle = "QLabel {background-color: ";
258   mySelectionStyle += "qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(236, 245, 255)";
259   mySelectionStyle += ", stop:1 rgb(208, 229, 255));";
260   mySelectionStyle += "border: 1px solid rgb(132, 172, 221); border-radius: 2px }";
261 #else
262   mySelectionStyle = "QLabel {background-color: " + aHighlight.name();
263   mySelectionStyle += "; color : " + aHighlightText.name() + "}";
264 #endif
265
266   myTreeView->viewport()->installEventFilter(this);
267 }
268
269
270 #if (!defined HAVE_SALOME) && (defined WIN32)
271 bool XGUI_ActiveDocLbl::event(QEvent* theEvent)
272 {
273   switch (theEvent->type()) {
274     case QEvent::Enter:
275       if (!myIsSelected)
276         setStyleSheet(myPreSelectionStyle);
277       break;
278     case QEvent::Leave:
279       if (!myIsSelected)
280         setStyleSheet(myNeutralStyle);
281       break;
282   }
283   return QLabel::event(theEvent);
284 }
285 #endif
286
287 bool XGUI_ActiveDocLbl::eventFilter(QObject* theObj, QEvent* theEvent)
288 {
289   if (theObj == myTreeView->viewport()) {
290     if (theEvent->type() == QEvent::MouseButtonRelease)
291       unselect();
292   }
293   return QLabel::eventFilter(theObj, theEvent);
294 }
295
296 static bool MYClearing = false;
297 void XGUI_ActiveDocLbl::mouseReleaseEvent( QMouseEvent* e)
298 {
299   MYClearing = true;
300   myIsSelected = true;
301   setStyleSheet(mySelectionStyle);
302   // We can not block signals because on
303   // clear selection the View state will not be updated
304   myTreeView->clearSelection();
305   QLabel::mouseReleaseEvent(e);
306   MYClearing = false;
307 }
308
309 void XGUI_ActiveDocLbl::unselect()
310 {
311   if (!MYClearing) {
312     myIsSelected = false;
313     setStyleSheet(myNeutralStyle);
314   }
315 }
316
317
318 //********************************************************************
319 //********************************************************************
320 //********************************************************************
321 XGUI_ObjectsBrowser::XGUI_ObjectsBrowser(QWidget* theParent)
322     : QWidget(theParent), myDocModel(0)
323 {
324   QVBoxLayout* aLayout = new QVBoxLayout(this);
325   ModuleBase_Tools::zeroMargins(aLayout);
326   aLayout->setSpacing(0);
327
328   QWidget* aLabelWgt = new QWidget(this);
329   aLabelWgt->setAutoFillBackground(true);
330
331   aLayout->addWidget(aLabelWgt);
332   QHBoxLayout* aLabelLay = new QHBoxLayout(aLabelWgt);
333   ModuleBase_Tools::zeroMargins(aLabelLay);
334   aLabelLay->setSpacing(0);
335
336   QLabel* aLbl = new QLabel(aLabelWgt);
337   aLbl->setPixmap(QPixmap(":pictures/assembly.png"));
338   aLbl->setMargin(2);
339   // Do not paint background of the label (in order to show icon)
340   aLbl->setAutoFillBackground(false);
341
342   aLabelLay->addWidget(aLbl);
343
344   SessionPtr aMgr = ModelAPI_Session::get();
345   DocumentPtr aDoc = aMgr->moduleDocument();
346
347   myActiveDocLbl = new XGUI_ActiveDocLbl(tr("Part set"), aLabelWgt);
348 //  myActiveDocLbl->setReadOnly(true);
349 //  myActiveDocLbl->setFrame(false);
350   myActiveDocLbl->setContextMenuPolicy(Qt::CustomContextMenu);
351
352   aLabelLay->addWidget(myActiveDocLbl);
353   aLabelLay->setStretch(1, 1);
354
355   myTreeView = new XGUI_DataTree(this);
356   myTreeView->setFrameShape(QFrame::NoFrame);
357   aLayout->addWidget(myTreeView);
358
359   QPalette aTreePalet = myTreeView->palette();
360   QColor aTreeBack = aTreePalet.color(QPalette::Base);
361
362   QPalette aPalet;
363   aPalet.setColor(QPalette::Base, aTreeBack);
364   aPalet.setColor(QPalette::Window, aTreeBack);
365   aLabelWgt->setPalette(aPalet);
366
367   myDocModel = new XGUI_DataModel(this);
368   connect(myDocModel, SIGNAL(modelAboutToBeReset()), SLOT(onBeforeReset()));
369   connect(myDocModel, SIGNAL(treeRebuilt()), SLOT(onAfterModelReset()));
370
371   connect(myTreeView, SIGNAL(contextMenuRequested(QContextMenuEvent*)), this,
372           SLOT(onContextMenuRequested(QContextMenuEvent*)));
373 }
374
375 //***************************************************
376 XGUI_ObjectsBrowser::~XGUI_ObjectsBrowser()
377 {
378 }
379
380 void XGUI_ObjectsBrowser::setXMLReader(Config_DataModelReader* theReader)
381 {
382   myDocModel->setXMLReader(theReader);
383   myTreeView->setModel(myDocModel);
384
385   // It has to be done after setting of model
386   myActiveDocLbl->setTreeView(myTreeView);
387
388   QItemSelectionModel* aSelMod = myTreeView->selectionModel();
389   connect(aSelMod, SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
390           this, SLOT(onSelectionChanged(const QItemSelection&, const QItemSelection&)));
391 }
392
393 //***************************************************
394 void XGUI_ObjectsBrowser::onContextMenuRequested(QContextMenuEvent* theEvent)
395 {
396   QModelIndexList aIndexes;
397   QObjectPtrList aSelectedData = selectedObjects(&aIndexes);
398   bool toEnable = false;
399
400   if (aSelectedData.size() == 1) {
401     QModelIndex aSelected = myTreeView->indexAt(theEvent->pos());
402     if (!aIndexes.contains(aSelected))
403       return; // menu is called on non selected item
404
405     Qt::ItemFlags aFlags = dataModel()->flags(aIndexes.first());
406     toEnable = ((aFlags & Qt::ItemIsEditable) != 0);
407   }
408   foreach(QAction* aCmd, actions()) {
409     aCmd->setEnabled(toEnable);
410   }
411   emit contextMenuRequested(theEvent);
412 }
413
414 //***************************************************
415 void XGUI_ObjectsBrowser::onLabelContextMenuRequested(const QPoint& thePnt)
416 {
417   myTreeView->selectionModel()->clearSelection();
418   //Empty feature pointer means that selected root document
419   foreach(QAction* aCmd, actions()) {
420     aCmd->setEnabled(true);
421   }
422   QContextMenuEvent aEvent(QContextMenuEvent::Mouse, thePnt, myActiveDocLbl->mapToGlobal(thePnt));
423   emit contextMenuRequested(&aEvent);
424 }
425
426 //***************************************************
427 void XGUI_ObjectsBrowser::onEditItem()
428 {
429   QObjectPtrList aSelectedData = selectedObjects();
430   if (aSelectedData.size() > 0) {
431     ObjectPtr anObject = aSelectedData.first();
432     if (anObject.get()) {  // Selection happens in TreeView
433       // check whether the object can be renamed. There should not be parts which are not loaded
434       std::set<FeaturePtr> aFeatures;
435       aFeatures.insert(ModelAPI_Feature::feature(anObject));
436       if (!XGUI_Tools::canRemoveOrRename((QWidget*)parent(), aFeatures))
437         return;
438
439       // Find index which corresponds the feature
440       QModelIndex aIndex;
441       foreach(QModelIndex aIdx, selectedIndexes()) {
442         ObjectPtr aFea = dataModel()->object(aIdx);
443         if (dataModel()->object(aIdx)->isSame(anObject)) {
444           aIndex = aIdx;
445           break;
446         }
447       }
448       if (aIndex.isValid()) {
449         myTreeView->setCurrentIndex(aIndex);
450         myTreeView->edit(aIndex);
451       }
452       return;
453     }
454   }
455 }
456
457 //***************************************************
458 QModelIndexList XGUI_ObjectsBrowser::expandedItems(const QModelIndex& theParent) const
459 {
460   QModelIndexList aIndexes;
461   QModelIndex aIndex;
462   for (int i = 0; i < myDocModel->rowCount(theParent); i++) {
463     aIndex = myDocModel->index(i, 0, theParent);
464     if (myDocModel->hasChildren(aIndex)) {
465       if (myTreeView->isExpanded(aIndex)) {
466         aIndexes.append(aIndex);
467         QModelIndexList aSubIndexes = expandedItems(aIndex);
468         if (!aSubIndexes.isEmpty())
469           aIndexes.append(aSubIndexes);
470       }
471     }
472   }
473   return aIndexes;
474 }
475
476 //***************************************************
477 void XGUI_ObjectsBrowser::rebuildDataTree()
478 {
479   myDocModel->rebuildDataTree();
480   update();
481 }
482
483 //***************************************************
484 void XGUI_ObjectsBrowser::setObjectsSelected(const QObjectPtrList& theObjects)
485 {
486   QList<QModelIndex> theIndexes;
487   QItemSelectionModel* aSelectModel = myTreeView->selectionModel();
488   aSelectModel->clear();
489
490   foreach(ObjectPtr aFeature, theObjects)
491   {
492     QModelIndex aIndex = myDocModel->objectIndex(aFeature);
493     if (aIndex.isValid()) {
494       aSelectModel->select(aIndex, QItemSelectionModel::Select);
495     }
496   }
497 }
498
499 //***************************************************
500 void XGUI_ObjectsBrowser::clearContent()
501 {
502   myTreeView->clear();
503 }
504
505 void XGUI_ObjectsBrowser::onSelectionChanged(const QItemSelection& theSelected,
506                                        const QItemSelection& theDeselected)
507 {
508   emit selectionChanged();
509 }
510
511 QObjectPtrList XGUI_ObjectsBrowser::selectedObjects(QModelIndexList* theIndexes) const
512 {
513   QObjectPtrList aList;
514   QModelIndexList aIndexes = selectedIndexes();
515   XGUI_DataModel* aModel = dataModel();
516   QModelIndexList::const_iterator aIt;
517   for (aIt = aIndexes.constBegin(); aIt != aIndexes.constEnd(); ++aIt) {
518     if ((*aIt).column() == 0) {
519       ObjectPtr aObject = aModel->object(*aIt);
520       if (aObject) {
521         aList.append(aObject);
522         if (theIndexes)
523           theIndexes->append(*aIt);
524       }
525     }
526   }
527   return aList;
528 }
529
530 void XGUI_ObjectsBrowser::onBeforeReset()
531 {
532   myExpandedItems = expandedItems();
533 }
534
535 void XGUI_ObjectsBrowser::onAfterModelReset()
536 {
537   foreach(QModelIndex aIndex, myExpandedItems) {
538     myTreeView->setExpanded(aIndex, true);
539   }
540 }
541
542 std::list<bool> XGUI_ObjectsBrowser::getStateForDoc(DocumentPtr theDoc) const
543 {
544   std::list<bool> aStates;
545   XGUI_DataModel* aModel = dataModel();
546   QModelIndex aRootIdx = aModel->documentRootIndex(theDoc);
547   int aNbChild = aModel->rowCount(aRootIdx);
548   for (int i = 0; i < aNbChild; i++) {
549     QModelIndex aIdx = aModel->index(i, 0, aRootIdx);
550     aStates.push_back(myTreeView->isExpanded(aIdx));
551   }
552   return aStates;
553 }
554
555 void XGUI_ObjectsBrowser::setStateForDoc(DocumentPtr theDoc, const std::list<bool>& theStates)
556 {
557   if (theStates.size() == 0)
558     return;
559   XGUI_DataModel* aModel = dataModel();
560   QModelIndex aRootIdx = aModel->documentRootIndex(theDoc);
561   int aNbChild = aModel->rowCount(aRootIdx);
562
563   std::list<bool>::const_iterator aIt;
564   int i = 0;
565   for (aIt = theStates.cbegin(); aIt != theStates.cend(); aIt++, i++) {
566     if (i >= aNbChild )
567       break;
568     QModelIndex aIdx = aModel->index(i, 0, aRootIdx);
569     myTreeView->setExpanded(aIdx, (*aIt));
570   }
571 }