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