Salome HOME
Porting on Qt5
[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() == 0) && (aDoc != aMgr->moduleDocument()))
173       // Clicked folder under root but active document is another
174       return;
175     if ((theIndex.internalId() != 0) && (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::drawPrimitive(theElement, aOpt, thePainter, theWidget);
219 //    }
220 //  }
221 //  QCommonStyle::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 += aHighlight.lighter(170).name() + "}";
249   //myPreSelectionStyle += "qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 white, stop:1 " +
250   //  aHighlight.lighter(170).name() + ");";
251   //myPreSelectionStyle += "border: 1px solid lightblue; border-radius: 2px }";
252
253   QString aName = aPalet.color(QPalette::Base).name();
254   myNeutralStyle = "QLabel { border: 1px solid " + aName + " }";
255
256
257 #if (!defined HAVE_SALOME) && (defined WIN32)
258   mySelectionStyle = "QLabel {background-color: ";
259   mySelectionStyle += "rgb(205, 232, 255); ";
260   //mySelectionStyle += "qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(236, 245, 255)";
261   //mySelectionStyle += ", stop:1 rgb(208, 229, 255));";
262   //mySelectionStyle += "border: 1px solid rgb(132, 172, 221); border-radius: 2px }";
263   mySelectionStyle += "border: 1px solid rgb(153, 209, 255) }";
264 #else
265   mySelectionStyle = "QLabel {background-color: " + aHighlight.name();
266   mySelectionStyle += "; color : " + aHighlightText.name() + "}";
267 #endif
268
269   myTreeView->viewport()->installEventFilter(this);
270 }
271
272
273 #if (!defined HAVE_SALOME) && (defined WIN32)
274 bool XGUI_ActiveDocLbl::event(QEvent* theEvent)
275 {
276   switch (theEvent->type()) {
277     case QEvent::Enter:
278       if (!myIsSelected)
279         setStyleSheet(myPreSelectionStyle);
280       break;
281     case QEvent::Leave:
282       if (!myIsSelected)
283         setStyleSheet(myNeutralStyle);
284       break;
285   }
286   return QLabel::event(theEvent);
287 }
288 #endif
289
290 bool XGUI_ActiveDocLbl::eventFilter(QObject* theObj, QEvent* theEvent)
291 {
292   if (theObj == myTreeView->viewport()) {
293     if (theEvent->type() == QEvent::MouseButtonRelease)
294       unselect();
295   }
296   return QLabel::eventFilter(theObj, theEvent);
297 }
298
299 static bool MYClearing = false;
300 void XGUI_ActiveDocLbl::mouseReleaseEvent( QMouseEvent* e)
301 {
302   MYClearing = true;
303   myIsSelected = true;
304   setStyleSheet(mySelectionStyle);
305   // We can not block signals because on
306   // clear selection the View state will not be updated
307   myTreeView->clearSelection();
308   QLabel::mouseReleaseEvent(e);
309   MYClearing = false;
310 }
311
312 void XGUI_ActiveDocLbl::unselect()
313 {
314   if (!MYClearing) {
315     myIsSelected = false;
316     setStyleSheet(myNeutralStyle);
317   }
318 }
319
320
321 //********************************************************************
322 //********************************************************************
323 //********************************************************************
324 XGUI_ObjectsBrowser::XGUI_ObjectsBrowser(QWidget* theParent)
325     : QWidget(theParent), myDocModel(0)
326 {
327   QVBoxLayout* aLayout = new QVBoxLayout(this);
328   ModuleBase_Tools::zeroMargins(aLayout);
329   aLayout->setSpacing(0);
330
331   QWidget* aLabelWgt = new QWidget(this);
332   aLabelWgt->setAutoFillBackground(true);
333
334   aLayout->addWidget(aLabelWgt);
335   QHBoxLayout* aLabelLay = new QHBoxLayout(aLabelWgt);
336   ModuleBase_Tools::zeroMargins(aLabelLay);
337   aLabelLay->setSpacing(0);
338
339   QLabel* aLbl = new QLabel(aLabelWgt);
340   aLbl->setPixmap(QPixmap(":pictures/assembly.png"));
341   aLbl->setMargin(2);
342   // Do not paint background of the label (in order to show icon)
343   aLbl->setAutoFillBackground(false);
344
345   aLabelLay->addWidget(aLbl);
346
347   SessionPtr aMgr = ModelAPI_Session::get();
348   DocumentPtr aDoc = aMgr->moduleDocument();
349
350   myActiveDocLbl = new XGUI_ActiveDocLbl(tr("Part set"), aLabelWgt);
351 //  myActiveDocLbl->setReadOnly(true);
352 //  myActiveDocLbl->setFrame(false);
353   myActiveDocLbl->setContextMenuPolicy(Qt::CustomContextMenu);
354
355   aLabelLay->addWidget(myActiveDocLbl);
356   aLabelLay->setStretch(1, 1);
357
358   myTreeView = new XGUI_DataTree(this);
359   myTreeView->setFrameShape(QFrame::NoFrame);
360   aLayout->addWidget(myTreeView);
361
362   QPalette aTreePalet = myTreeView->palette();
363   QColor aTreeBack = aTreePalet.color(QPalette::Base);
364
365   QPalette aPalet;
366   aPalet.setColor(QPalette::Base, aTreeBack);
367   aPalet.setColor(QPalette::Window, aTreeBack);
368   aLabelWgt->setPalette(aPalet);
369
370   myDocModel = new XGUI_DataModel(this);
371   connect(myDocModel, SIGNAL(modelAboutToBeReset()), SLOT(onBeforeReset()));
372   connect(myDocModel, SIGNAL(treeRebuilt()), SLOT(onAfterModelReset()));
373
374   connect(myTreeView, SIGNAL(contextMenuRequested(QContextMenuEvent*)), this,
375           SLOT(onContextMenuRequested(QContextMenuEvent*)));
376 }
377
378 //***************************************************
379 XGUI_ObjectsBrowser::~XGUI_ObjectsBrowser()
380 {
381 }
382
383 void XGUI_ObjectsBrowser::setXMLReader(Config_DataModelReader* theReader)
384 {
385   myDocModel->setXMLReader(theReader);
386   myTreeView->setModel(myDocModel);
387
388   // It has to be done after setting of model
389   myActiveDocLbl->setTreeView(myTreeView);
390
391   QItemSelectionModel* aSelMod = myTreeView->selectionModel();
392   connect(aSelMod, SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
393           this, SLOT(onSelectionChanged(const QItemSelection&, const QItemSelection&)));
394 }
395
396 //***************************************************
397 void XGUI_ObjectsBrowser::onContextMenuRequested(QContextMenuEvent* theEvent)
398 {
399   QModelIndexList aIndexes;
400   QObjectPtrList aSelectedData = selectedObjects(&aIndexes);
401   bool toEnable = false;
402
403   if (aSelectedData.size() == 1) {
404     QModelIndex aSelected = myTreeView->indexAt(theEvent->pos());
405     if (!aIndexes.contains(aSelected))
406       return; // menu is called on non selected item
407
408     Qt::ItemFlags aFlags = dataModel()->flags(aIndexes.first());
409     toEnable = ((aFlags & Qt::ItemIsEditable) != 0);
410   }
411   foreach(QAction* aCmd, actions()) {
412     aCmd->setEnabled(toEnable);
413   }
414   emit contextMenuRequested(theEvent);
415 }
416
417 //***************************************************
418 void XGUI_ObjectsBrowser::onLabelContextMenuRequested(const QPoint& thePnt)
419 {
420   myTreeView->selectionModel()->clearSelection();
421   //Empty feature pointer means that selected root document
422   foreach(QAction* aCmd, actions()) {
423     aCmd->setEnabled(true);
424   }
425   QContextMenuEvent aEvent(QContextMenuEvent::Mouse, thePnt, myActiveDocLbl->mapToGlobal(thePnt));
426   emit contextMenuRequested(&aEvent);
427 }
428
429 //***************************************************
430 void XGUI_ObjectsBrowser::onEditItem()
431 {
432   QObjectPtrList aSelectedData = selectedObjects();
433   if (aSelectedData.size() > 0) {
434     ObjectPtr anObject = aSelectedData.first();
435     if (anObject.get()) {  // Selection happens in TreeView
436       // check whether the object can be renamed. There should not be parts which are not loaded
437       std::set<FeaturePtr> aFeatures;
438       aFeatures.insert(ModelAPI_Feature::feature(anObject));
439       if (!XGUI_Tools::canRemoveOrRename((QWidget*)parent(), aFeatures))
440         return;
441
442       // Find index which corresponds the feature
443       QModelIndex aIndex;
444       foreach(QModelIndex aIdx, selectedIndexes()) {
445         ObjectPtr aFea = dataModel()->object(aIdx);
446         if (dataModel()->object(aIdx)->isSame(anObject)) {
447           aIndex = aIdx;
448           break;
449         }
450       }
451       if (aIndex.isValid()) {
452         myTreeView->setCurrentIndex(aIndex);
453         myTreeView->edit(aIndex);
454       }
455       return;
456     }
457   }
458 }
459
460 //***************************************************
461 QModelIndexList XGUI_ObjectsBrowser::expandedItems(const QModelIndex& theParent) const
462 {
463   QModelIndexList aIndexes;
464   QModelIndex aIndex;
465   for (int i = 0; i < myDocModel->rowCount(theParent); i++) {
466     aIndex = myDocModel->index(i, 0, theParent);
467     if (myDocModel->hasChildren(aIndex)) {
468       if (myTreeView->isExpanded(aIndex)) {
469         aIndexes.append(aIndex);
470         QModelIndexList aSubIndexes = expandedItems(aIndex);
471         if (!aSubIndexes.isEmpty())
472           aIndexes.append(aSubIndexes);
473       }
474     }
475   }
476   return aIndexes;
477 }
478
479 //***************************************************
480 void XGUI_ObjectsBrowser::rebuildDataTree()
481 {
482   myDocModel->rebuildDataTree();
483   update();
484 }
485
486 //***************************************************
487 void XGUI_ObjectsBrowser::setObjectsSelected(const QObjectPtrList& theObjects)
488 {
489   QList<QModelIndex> theIndexes;
490   QItemSelectionModel* aSelectModel = myTreeView->selectionModel();
491   aSelectModel->clear();
492
493   foreach(ObjectPtr aFeature, theObjects)
494   {
495     QModelIndex aIndex = myDocModel->objectIndex(aFeature);
496     if (aIndex.isValid()) {
497       aSelectModel->select(aIndex, QItemSelectionModel::Select);
498     }
499   }
500 }
501
502 //***************************************************
503 void XGUI_ObjectsBrowser::clearContent()
504 {
505   myTreeView->clear();
506 }
507
508 void XGUI_ObjectsBrowser::onSelectionChanged(const QItemSelection& theSelected,
509                                        const QItemSelection& theDeselected)
510 {
511   emit selectionChanged();
512 }
513
514 QObjectPtrList XGUI_ObjectsBrowser::selectedObjects(QModelIndexList* theIndexes) const
515 {
516   QObjectPtrList aList;
517   QModelIndexList aIndexes = selectedIndexes();
518   XGUI_DataModel* aModel = dataModel();
519   QModelIndexList::const_iterator aIt;
520   for (aIt = aIndexes.constBegin(); aIt != aIndexes.constEnd(); ++aIt) {
521     if ((*aIt).column() == 0) {
522       ObjectPtr aObject = aModel->object(*aIt);
523       if (aObject) {
524         aList.append(aObject);
525         if (theIndexes)
526           theIndexes->append(*aIt);
527       }
528     }
529   }
530   return aList;
531 }
532
533 void XGUI_ObjectsBrowser::onBeforeReset()
534 {
535   myExpandedItems = expandedItems();
536 }
537
538 void XGUI_ObjectsBrowser::onAfterModelReset()
539 {
540   foreach(QModelIndex aIndex, myExpandedItems) {
541     myTreeView->setExpanded(aIndex, true);
542   }
543 }
544
545 std::list<bool> XGUI_ObjectsBrowser::getStateForDoc(DocumentPtr theDoc) const
546 {
547   std::list<bool> aStates;
548   XGUI_DataModel* aModel = dataModel();
549   QModelIndex aRootIdx = aModel->documentRootIndex(theDoc);
550   int aNbChild = aModel->rowCount(aRootIdx);
551   for (int i = 0; i < aNbChild; i++) {
552     QModelIndex aIdx = aModel->index(i, 0, aRootIdx);
553     aStates.push_back(myTreeView->isExpanded(aIdx));
554   }
555   return aStates;
556 }
557
558 void XGUI_ObjectsBrowser::setStateForDoc(DocumentPtr theDoc, const std::list<bool>& theStates)
559 {
560   if (theStates.size() == 0)
561     return;
562   XGUI_DataModel* aModel = dataModel();
563   QModelIndex aRootIdx = aModel->documentRootIndex(theDoc);
564   int aNbChild = aModel->rowCount(aRootIdx);
565
566   std::list<bool>::const_iterator aIt;
567   int i = 0;
568   for (aIt = theStates.cbegin(); aIt != theStates.cend(); aIt++, i++) {
569     if (i >= aNbChild )
570       break;
571     QModelIndex aIdx = aModel->index(i, 0, aRootIdx);
572     myTreeView->setExpanded(aIdx, (*aIt));
573   }
574 }