Salome HOME
Merge Dev_2.1.0 with PythonAPI branch
[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 <QLineEdit>
17 #include <QPixmap>
18 #include <QEvent>
19 #include <QMouseEvent>
20 #include <QAction>
21 #include <QStyledItemDelegate>
22 #include <QMessageBox>
23
24 #ifdef WIN32
25 #ifdef HAVE_SALOME
26 #include <QWindowsStyle>
27 #endif
28 #endif
29
30
31 /// Width of second column (minimum acceptable = 27)
32 #define SECOND_COL_WIDTH 30
33
34
35 /**
36 * \ingroup GUI
37 * Tree item delegate for definition of data in column items editor
38 */
39 class XGUI_TreeViewItemDelegate: public QStyledItemDelegate
40 {
41 public:
42   /// Constructor
43   /// \param theParent a parent of the delegate
44   XGUI_TreeViewItemDelegate(XGUI_DataTree* theParent):QStyledItemDelegate(theParent), 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 recursively by Qt
110     // 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   QSize aSize = theEvent->size();
139   if (aSize.isValid()) {
140     setColumnWidth(0, aSize.width() - SECOND_COL_WIDTH);
141     setColumnWidth(1, SECOND_COL_WIDTH);
142   }
143   QTreeView::resizeEvent(theEvent);
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 = qstyleoption_cast<const QStyleOptionViewItemV4 *>(theOption);
209     if (myIndex.isValid() && ((myIndex.flags() & Qt::ItemIsSelectable) == 0)) {
210       QStyle::State aState = aOptions->state;
211       if ((aState & QStyle::State_MouseOver) != 0)
212         aState &= ~QStyle::State_MouseOver;
213       QStyleOptionViewItemV4* aOpt = (QStyleOptionViewItemV4*) aOptions;
214       aOpt->state = aState;
215       QWindowsVistaStyle::drawPrimitive(theElement, aOpt, thePainter, theWidget);
216     }
217   }
218   QWindowsVistaStyle::drawPrimitive(theElement, theOption, thePainter, theWidget);
219 }
220 #endif
221
222
223 //********************************************************************
224 //********************************************************************
225 //********************************************************************
226 XGUI_ActiveDocLbl::XGUI_ActiveDocLbl(const QString& theText, QWidget* theParent )
227   : QLabel(theText, theParent), 
228   myPreSelectionStyle(""), 
229   myNeutralStyle(""), 
230   mySelectionStyle(""),
231   myIsSelected(false)
232 {
233 }
234
235 void XGUI_ActiveDocLbl::setTreeView(QTreeView* theView)
236 {
237   myTreeView = theView;
238   setFont(myTreeView->font());
239
240   QPalette aPalet = myTreeView->palette();
241   QColor aHighlight = aPalet.highlight().color();
242   QColor aHighlightText = aPalet.highlightedText().color();
243
244   myPreSelectionStyle = "QLabel {background-color: ";
245   myPreSelectionStyle += "qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 white, stop:1 " + aHighlight.lighter(170).name() + ");"; 
246   myPreSelectionStyle += "border: 1px solid lightblue; border-radius: 2px }";
247
248   QString aName = aPalet.color(QPalette::Base).name();
249   myNeutralStyle = "QLabel { border: 1px solid " + aName + " }";
250
251
252 #if (!defined HAVE_SALOME) && (defined WIN32)
253   mySelectionStyle = "QLabel {background-color: ";
254   mySelectionStyle += "qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(236, 245, 255)";
255   mySelectionStyle += ", stop:1 rgb(208, 229, 255));"; 
256   mySelectionStyle += "border: 1px solid rgb(132, 172, 221); border-radius: 2px }";
257 #else
258   mySelectionStyle = "QLabel {background-color: " + aHighlight.name();
259   mySelectionStyle += "; color : " + aHighlightText.name() + "}";
260 #endif
261
262   myTreeView->viewport()->installEventFilter(this);
263 }
264
265
266 #if (!defined HAVE_SALOME) && (defined WIN32)
267 bool XGUI_ActiveDocLbl::event(QEvent* theEvent)
268 {
269   switch (theEvent->type()) {
270     case QEvent::Enter:
271       if (!myIsSelected)
272         setStyleSheet(myPreSelectionStyle);
273       break;
274     case QEvent::Leave:
275       if (!myIsSelected)
276         setStyleSheet(myNeutralStyle);
277       break;
278   }
279   return QLabel::event(theEvent);
280 }
281 #endif
282
283 bool XGUI_ActiveDocLbl::eventFilter(QObject* theObj, QEvent* theEvent)
284 {
285   if (theObj == myTreeView->viewport()) {
286     if (theEvent->type() == QEvent::MouseButtonRelease)
287       unselect();
288   }
289   return QLabel::eventFilter(theObj, theEvent);
290 }
291
292 static bool MYClearing = false;
293 void XGUI_ActiveDocLbl::mouseReleaseEvent( QMouseEvent* e)
294 {
295   MYClearing = true;
296   myIsSelected = true;
297   setStyleSheet(mySelectionStyle);
298   // We can not block signals because on 
299   // clear selection the View state will not be updated
300   myTreeView->clearSelection();
301   QLabel::mouseReleaseEvent(e);
302   MYClearing = false;
303 }
304
305 void XGUI_ActiveDocLbl::unselect()
306 {
307   if (!MYClearing) {
308     myIsSelected = false;
309     setStyleSheet(myNeutralStyle);
310   }
311 }
312
313
314 //********************************************************************
315 //********************************************************************
316 //********************************************************************
317 XGUI_ObjectsBrowser::XGUI_ObjectsBrowser(QWidget* theParent)
318     : QWidget(theParent), myDocModel(0)
319 {
320   QVBoxLayout* aLayout = new QVBoxLayout(this);
321   ModuleBase_Tools::zeroMargins(aLayout);
322   aLayout->setSpacing(0);
323
324   QWidget* aLabelWgt = new QWidget(this);
325   aLabelWgt->setAutoFillBackground(true);
326
327   aLayout->addWidget(aLabelWgt);
328   QHBoxLayout* aLabelLay = new QHBoxLayout(aLabelWgt);
329   ModuleBase_Tools::zeroMargins(aLabelLay);
330   aLabelLay->setSpacing(0);
331
332   QLabel* aLbl = new QLabel(aLabelWgt);
333   aLbl->setPixmap(QPixmap(":pictures/assembly.png"));
334   aLbl->setMargin(2);
335   // Do not paint background of the label (in order to show icon)
336   aLbl->setAutoFillBackground(false); 
337
338   aLabelLay->addWidget(aLbl);
339
340   SessionPtr aMgr = ModelAPI_Session::get();
341   DocumentPtr aDoc = aMgr->moduleDocument();
342
343   myActiveDocLbl = new XGUI_ActiveDocLbl(tr("Part set"), aLabelWgt);
344 //  myActiveDocLbl->setReadOnly(true);
345 //  myActiveDocLbl->setFrame(false);
346   myActiveDocLbl->setContextMenuPolicy(Qt::CustomContextMenu);
347
348   aLabelLay->addWidget(myActiveDocLbl);
349   aLabelLay->setStretch(1, 1);
350
351   myTreeView = new XGUI_DataTree(this);
352   myTreeView->setFrameShape(QFrame::NoFrame);
353   aLayout->addWidget(myTreeView);
354
355   QPalette aTreePalet = myTreeView->palette();
356   QColor aTreeBack = aTreePalet.color(QPalette::Base);
357
358   QPalette aPalet;
359   aPalet.setColor(QPalette::Base, aTreeBack);
360   aPalet.setColor(QPalette::Window, aTreeBack);
361   aLabelWgt->setPalette(aPalet);
362
363   myDocModel = new XGUI_DataModel(this);
364   myTreeView->setModel(myDocModel);
365
366   // It has to be done after setting of model
367   myActiveDocLbl->setTreeView(myTreeView);
368
369   QItemSelectionModel* aSelMod = myTreeView->selectionModel();
370   connect(aSelMod, SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
371           this, SLOT(onSelectionChanged(const QItemSelection&, const QItemSelection&)));
372
373   connect(myTreeView, SIGNAL(contextMenuRequested(QContextMenuEvent*)), this,
374           SLOT(onContextMenuRequested(QContextMenuEvent*)));
375 }
376
377 //***************************************************
378 XGUI_ObjectsBrowser::~XGUI_ObjectsBrowser()
379 {
380 }
381
382
383 //***************************************************
384 void XGUI_ObjectsBrowser::onContextMenuRequested(QContextMenuEvent* theEvent)
385 {
386   QModelIndexList aIndexes;
387   QObjectPtrList aSelectedData = selectedObjects(&aIndexes);
388   bool toEnable = false;
389
390   if (aSelectedData.size() == 1) {
391     QModelIndex aSelected = myTreeView->indexAt(theEvent->pos());
392     if (!aIndexes.contains(aSelected))
393       return; // menu is called on non selected item
394
395     Qt::ItemFlags aFlags = dataModel()->flags(aIndexes.first());
396     toEnable = ((aFlags & Qt::ItemIsEditable) != 0);
397   }
398   foreach(QAction* aCmd, actions()) {
399     aCmd->setEnabled(toEnable);
400   }
401   emit contextMenuRequested(theEvent);
402 }
403
404 //***************************************************
405 void XGUI_ObjectsBrowser::onLabelContextMenuRequested(const QPoint& thePnt)
406 {
407   myTreeView->selectionModel()->clearSelection();
408   //Empty feature pointer means that selected root document
409   foreach(QAction* aCmd, actions()) {
410     aCmd->setEnabled(true);
411   }
412   QContextMenuEvent aEvent(QContextMenuEvent::Mouse, thePnt, myActiveDocLbl->mapToGlobal(thePnt));
413   emit contextMenuRequested(&aEvent);
414 }
415
416 //***************************************************
417 void XGUI_ObjectsBrowser::onEditItem()
418 {
419   QObjectPtrList aSelectedData = selectedObjects();
420   if (aSelectedData.size() > 0) {
421     ObjectPtr aFeature = aSelectedData.first();
422     if (aFeature) {  // Selection happens in TreeView
423       QObjectPtrList aList;
424       aList.append(aFeature);
425       // check whether the object can be deleted. There should not be parts which are not loaded
426       if (!XGUI_Tools::canRemoveOrRename((QWidget*)parent(), aList))
427         return;
428
429       // Find index which corresponds the feature
430       QModelIndex aIndex;
431       foreach(QModelIndex aIdx, selectedIndexes()) {
432         ObjectPtr aFea = dataModel()->object(aIdx);
433         if (dataModel()->object(aIdx)->isSame(aFeature)) {
434           aIndex = aIdx;
435           break;
436         }
437       }
438       if (aIndex.isValid()) {
439         myTreeView->setCurrentIndex(aIndex);
440         myTreeView->edit(aIndex);
441       }
442       return;
443     }
444   }
445 }
446
447 //***************************************************
448 QModelIndexList XGUI_ObjectsBrowser::expandedItems(const QModelIndex& theParent) const
449 {
450   QModelIndexList aIndexes;
451   QModelIndex aIndex;
452   for (int i = 0; i < myDocModel->rowCount(); i++) {
453     aIndex = myDocModel->index(i, 0, theParent);
454     if (myDocModel->hasChildren(aIndex)) {
455       if (myTreeView->isExpanded(aIndex)) {
456         aIndexes.append(aIndex);
457         QModelIndexList aSubIndexes = expandedItems(aIndex);
458         if (!aSubIndexes.isEmpty())
459           aIndexes.append(aSubIndexes);
460       }
461     }
462   }
463   return aIndexes;
464 }
465
466 //***************************************************
467 void XGUI_ObjectsBrowser::rebuildDataTree()
468 {
469   QModelIndexList aIndexList = expandedItems();
470   myDocModel->rebuildDataTree();
471   foreach(QModelIndex aIndex, aIndexList) {
472     myTreeView->setExpanded(aIndex, true);
473   }
474   update();
475 }
476
477 //***************************************************
478 void XGUI_ObjectsBrowser::setObjectsSelected(const QObjectPtrList& theObjects)
479 {
480   QList<QModelIndex> theIndexes;
481   QItemSelectionModel* aSelectModel = myTreeView->selectionModel();
482   aSelectModel->clear();
483
484   foreach(ObjectPtr aFeature, theObjects)
485   {
486     QModelIndex aIndex = myDocModel->objectIndex(aFeature);
487     if (aIndex.isValid()) {
488       aSelectModel->select(aIndex, QItemSelectionModel::Select);
489     }
490   }
491 }
492
493 //***************************************************
494 void XGUI_ObjectsBrowser::clearContent()  
495
496   myTreeView->clear(); 
497 }
498
499 void XGUI_ObjectsBrowser::onSelectionChanged(const QItemSelection& theSelected,
500                                        const QItemSelection& theDeselected)
501 {
502   emit selectionChanged();
503 }
504
505 QObjectPtrList XGUI_ObjectsBrowser::selectedObjects(QModelIndexList* theIndexes) const
506 {
507   QObjectPtrList aList;
508   QModelIndexList aIndexes = selectedIndexes();
509   XGUI_DataModel* aModel = dataModel();
510   QModelIndexList::const_iterator aIt;
511   for (aIt = aIndexes.constBegin(); aIt != aIndexes.constEnd(); ++aIt) {
512     if ((*aIt).column() == 0) {
513       ObjectPtr aObject = aModel->object(*aIt);
514       if (aObject) {
515         aList.append(aObject);
516         if (theIndexes)
517           theIndexes->append(*aIt);
518       }
519     }
520   }
521   return aList;
522 }