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