1 // Copyright (C) 2014-2017 CEA/DEN, EDF R&D
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.
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.
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
17 // See http://www.salome-platform.org/ or
18 // email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
21 #include "XGUI_ObjectsBrowser.h"
22 #include "XGUI_Tools.h"
23 #include "XGUI_DataModel.h"
25 #include <ModelAPI_Data.h>
26 #include <ModelAPI_Session.h>
27 #include <ModelAPI_Document.h>
28 #include <ModelAPI_Tools.h>
30 #include <ModuleBase_Tools.h>
36 #include <QMouseEvent>
38 #include <QStyledItemDelegate>
39 #include <QMessageBox>
42 /// Width of second column (minimum acceptable = 27)
43 #define SECOND_COL_WIDTH 30
48 * Tree item delegate for definition of data in column items editor
50 class XGUI_TreeViewItemDelegate: public QStyledItemDelegate
54 /// \param theParent a parent of the delegate
55 XGUI_TreeViewItemDelegate(XGUI_DataTree* theParent):QStyledItemDelegate(theParent),
56 myTreedView(theParent) {}
58 /// Set data for item editor (name of the item)
59 /// \param editor a widget of editor
60 /// \param index the tree item index
61 virtual void setEditorData ( QWidget* editor, const QModelIndex& index ) const
63 QLineEdit* aEditor = dynamic_cast<QLineEdit*>(editor);
65 XGUI_DataModel* aModel = myTreedView->dataModel();
66 ObjectPtr aObj = aModel->object(index);
67 if (aObj.get() != NULL) {
68 aEditor->setText(aObj->data()->name().c_str());
72 QStyledItemDelegate::setEditorData(editor, index);
76 XGUI_DataTree* myTreedView;
80 XGUI_DataTree::XGUI_DataTree(QWidget* theParent)
81 : QTreeView(theParent)
83 setHeaderHidden(true);
84 setEditTriggers(QAbstractItemView::NoEditTriggers);
85 setSelectionBehavior(QAbstractItemView::SelectRows);
86 setSelectionMode(QAbstractItemView::ExtendedSelection);
88 setItemDelegateForColumn(0, new XGUI_TreeViewItemDelegate(this));
90 connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
91 SLOT(onDoubleClick(const QModelIndex&)));
94 XGUI_DataTree::~XGUI_DataTree()
98 XGUI_DataModel* XGUI_DataTree::dataModel() const
100 return static_cast<XGUI_DataModel*>(model());
103 void XGUI_DataTree::contextMenuEvent(QContextMenuEvent* theEvent)
105 emit contextMenuRequested(theEvent);
108 void XGUI_DataTree::commitData(QWidget* theEditor)
110 static int aEntrance = 0;
111 if (aEntrance == 0) {
112 // We have to check number of enter and exit of this function because it can be called
113 // recursively by Qt in order to avoid double modifying of a data
115 QLineEdit* aEditor = dynamic_cast<QLineEdit*>(theEditor);
117 QString aName = aEditor->text();
118 QModelIndexList aIndexList = selectionModel()->selectedIndexes();
119 XGUI_DataModel* aModel = dataModel();
120 ObjectPtr aObj = aModel->object(aIndexList.first());
122 if (XGUI_Tools::canRename(aObj, aName)) {
123 SessionPtr aMgr = ModelAPI_Session::get();
124 aMgr->startOperation("Rename");
125 aObj->data()->setName(qPrintable(aName));
126 aMgr->finishOperation();
133 void XGUI_DataTree::clear()
135 dataModel()->clear();
139 void XGUI_DataTree::resizeEvent(QResizeEvent* theEvent)
141 QTreeView::resizeEvent(theEvent);
142 QSize aSize = theEvent->size();
143 if (aSize.isValid()) {
144 setColumnWidth(0, aSize.width() - SECOND_COL_WIDTH - 7);
145 setColumnWidth(1, SECOND_COL_WIDTH);
149 void XGUI_DataTree::onDoubleClick(const QModelIndex& theIndex)
151 if (theIndex.column() != 1)
153 SessionPtr aMgr = ModelAPI_Session::get();
154 // When operation is opened then we can not change history
155 if (aMgr->isOperation())
157 XGUI_DataModel* aModel = dataModel();
158 if (aModel->flags(theIndex) == 0)
160 ObjectPtr aObj = aModel->object(theIndex);
162 DocumentPtr aDoc = aMgr->activeDocument();
164 std::string aOpName = tr("History change").toStdString();
166 if (aObj->document() != aDoc)
168 aMgr->startOperation(aOpName);
169 aDoc->setCurrentFeature(std::dynamic_pointer_cast<ModelAPI_Feature>(aObj), true);
170 aMgr->finishOperation();
172 // Ignore clicks on folders outside current document
173 if ((theIndex.internalId() == 0) && (aDoc != aMgr->moduleDocument()))
174 // Clicked folder under root but active document is another
176 if ((theIndex.internalId() != 0) && (aDoc.get() != theIndex.internalPointer()))
177 // Cliced not on active document folder
180 aMgr->startOperation(aOpName);
181 aDoc->setCurrentFeature(FeaturePtr(), true);
182 aMgr->finishOperation();
184 QModelIndex aNewIndex = aModel->lastHistoryIndex();
185 QModelIndex aParent = theIndex.parent();
186 int aSize = aModel->rowCount(aParent);
187 for (int i = 0; i < aSize; i++) {
188 update(aModel->index(i, 0, aParent));
189 update(aModel->index(i, 1, aParent));
194 //********************************************************************
195 //********************************************************************
196 //********************************************************************
197 XGUI_ActiveDocLbl::XGUI_ActiveDocLbl(const QString& theText, QWidget* theParent )
198 : QLabel(theText, theParent),
199 myPreSelectionStyle(""),
201 mySelectionStyle(""),
206 void XGUI_ActiveDocLbl::setTreeView(QTreeView* theView)
208 myTreeView = theView;
209 setFont(myTreeView->font());
211 QPalette aPalet = myTreeView->palette();
212 QColor aHighlight = aPalet.highlight().color();
213 QColor aHighlightText = aPalet.highlightedText().color();
215 myPreSelectionStyle = "QLabel {background-color: ";
216 myPreSelectionStyle += aHighlight.lighter(170).name() + "}";
217 //myPreSelectionStyle += "qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 white, stop:1 " +
218 // aHighlight.lighter(170).name() + ");";
219 //myPreSelectionStyle += "border: 1px solid lightblue; border-radius: 2px }";
221 QString aName = aPalet.color(QPalette::Base).name();
222 myNeutralStyle = "QLabel { border: 1px solid " + aName + " }";
225 #if (!defined HAVE_SALOME) && (defined WIN32)
226 mySelectionStyle = "QLabel {background-color: ";
227 mySelectionStyle += "rgb(205, 232, 255); ";
228 //mySelectionStyle += "qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(236, 245, 255)";
229 //mySelectionStyle += ", stop:1 rgb(208, 229, 255));";
230 //mySelectionStyle += "border: 1px solid rgb(132, 172, 221); border-radius: 2px }";
231 mySelectionStyle += "border: 1px solid rgb(153, 209, 255) }";
233 mySelectionStyle = "QLabel {background-color: " + aHighlight.name();
234 mySelectionStyle += "; color : " + aHighlightText.name() + "}";
237 myTreeView->viewport()->installEventFilter(this);
241 #if (!defined HAVE_SALOME) && (defined WIN32)
242 bool XGUI_ActiveDocLbl::event(QEvent* theEvent)
244 switch (theEvent->type()) {
247 setStyleSheet(myPreSelectionStyle);
251 setStyleSheet(myNeutralStyle);
254 return QLabel::event(theEvent);
258 bool XGUI_ActiveDocLbl::eventFilter(QObject* theObj, QEvent* theEvent)
260 if (theObj == myTreeView->viewport()) {
261 if (theEvent->type() == QEvent::MouseButtonRelease)
264 return QLabel::eventFilter(theObj, theEvent);
267 static bool MYClearing = false;
268 void XGUI_ActiveDocLbl::mouseReleaseEvent( QMouseEvent* e)
272 setStyleSheet(mySelectionStyle);
273 // We can not block signals because on
274 // clear selection the View state will not be updated
275 myTreeView->clearSelection();
276 QLabel::mouseReleaseEvent(e);
280 void XGUI_ActiveDocLbl::unselect()
283 myIsSelected = false;
284 setStyleSheet(myNeutralStyle);
289 //********************************************************************
290 //********************************************************************
291 //********************************************************************
292 XGUI_ObjectsBrowser::XGUI_ObjectsBrowser(QWidget* theParent)
293 : QWidget(theParent), myDocModel(0)
295 QVBoxLayout* aLayout = new QVBoxLayout(this);
296 ModuleBase_Tools::zeroMargins(aLayout);
297 aLayout->setSpacing(0);
299 QWidget* aLabelWgt = new QWidget(this);
300 aLabelWgt->setAutoFillBackground(true);
302 aLayout->addWidget(aLabelWgt);
303 QHBoxLayout* aLabelLay = new QHBoxLayout(aLabelWgt);
304 ModuleBase_Tools::zeroMargins(aLabelLay);
305 aLabelLay->setSpacing(0);
307 QLabel* aLbl = new QLabel(aLabelWgt);
308 aLbl->setPixmap(QPixmap(":pictures/assembly.png"));
310 // Do not paint background of the label (in order to show icon)
311 aLbl->setAutoFillBackground(false);
313 aLabelLay->addWidget(aLbl);
315 SessionPtr aMgr = ModelAPI_Session::get();
316 DocumentPtr aDoc = aMgr->moduleDocument();
318 myActiveDocLbl = new XGUI_ActiveDocLbl(tr("Part set"), aLabelWgt);
319 // myActiveDocLbl->setReadOnly(true);
320 // myActiveDocLbl->setFrame(false);
321 myActiveDocLbl->setContextMenuPolicy(Qt::CustomContextMenu);
323 aLabelLay->addWidget(myActiveDocLbl);
324 aLabelLay->setStretch(1, 1);
326 myTreeView = new XGUI_DataTree(this);
327 myTreeView->setFrameShape(QFrame::NoFrame);
328 aLayout->addWidget(myTreeView);
330 QPalette aTreePalet = myTreeView->palette();
331 QColor aTreeBack = aTreePalet.color(QPalette::Base);
334 aPalet.setColor(QPalette::Base, aTreeBack);
335 aPalet.setColor(QPalette::Window, aTreeBack);
336 aLabelWgt->setPalette(aPalet);
338 myDocModel = new XGUI_DataModel(this);
339 connect(myDocModel, SIGNAL(modelAboutToBeReset()), SLOT(onBeforeReset()));
340 connect(myDocModel, SIGNAL(treeRebuilt()), SLOT(onAfterModelReset()));
342 connect(myTreeView, SIGNAL(contextMenuRequested(QContextMenuEvent*)), this,
343 SLOT(onContextMenuRequested(QContextMenuEvent*)));
346 //***************************************************
347 XGUI_ObjectsBrowser::~XGUI_ObjectsBrowser()
351 void XGUI_ObjectsBrowser::setXMLReader(Config_DataModelReader* theReader)
353 myDocModel->setXMLReader(theReader);
354 myTreeView->setModel(myDocModel);
356 // It has to be done after setting of model
357 myActiveDocLbl->setTreeView(myTreeView);
359 QItemSelectionModel* aSelMod = myTreeView->selectionModel();
360 connect(aSelMod, SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
361 this, SLOT(onSelectionChanged(const QItemSelection&, const QItemSelection&)));
364 //***************************************************
365 void XGUI_ObjectsBrowser::onContextMenuRequested(QContextMenuEvent* theEvent)
367 QModelIndexList aIndexes;
368 QObjectPtrList aSelectedData = selectedObjects(&aIndexes);
369 bool toEnable = false;
371 if (aSelectedData.size() == 1) {
372 QModelIndex aSelected = myTreeView->indexAt(theEvent->pos());
373 if (!aIndexes.contains(aSelected))
374 return; // menu is called on non selected item
376 Qt::ItemFlags aFlags = dataModel()->flags(aIndexes.first());
377 toEnable = ((aFlags & Qt::ItemIsEditable) != 0);
379 foreach(QAction* aCmd, actions()) {
380 aCmd->setEnabled(toEnable);
382 emit contextMenuRequested(theEvent);
385 //***************************************************
386 void XGUI_ObjectsBrowser::onLabelContextMenuRequested(const QPoint& thePnt)
388 myTreeView->selectionModel()->clearSelection();
389 //Empty feature pointer means that selected root document
390 foreach(QAction* aCmd, actions()) {
391 aCmd->setEnabled(true);
393 QContextMenuEvent aEvent(QContextMenuEvent::Mouse, thePnt, myActiveDocLbl->mapToGlobal(thePnt));
394 emit contextMenuRequested(&aEvent);
397 //***************************************************
398 void XGUI_ObjectsBrowser::onEditItem()
400 QObjectPtrList aSelectedData = selectedObjects();
401 if (aSelectedData.size() > 0) {
402 ObjectPtr anObject = aSelectedData.first();
403 if (anObject.get()) { // Selection happens in TreeView
404 // check whether the object can be renamed. There should not be parts which are not loaded
405 std::set<FeaturePtr> aFeatures;
406 aFeatures.insert(ModelAPI_Feature::feature(anObject));
407 if (!XGUI_Tools::canRemoveOrRename((QWidget*)parent(), aFeatures))
410 // Find index which corresponds the feature
412 foreach(QModelIndex aIdx, selectedIndexes()) {
413 ObjectPtr aFea = dataModel()->object(aIdx);
414 if (dataModel()->object(aIdx)->isSame(anObject)) {
419 if (aIndex.isValid()) {
420 myTreeView->setCurrentIndex(aIndex);
421 myTreeView->edit(aIndex);
428 //***************************************************
429 QModelIndexList XGUI_ObjectsBrowser::expandedItems(const QModelIndex& theParent) const
431 QModelIndexList aIndexes;
433 for (int i = 0; i < myDocModel->rowCount(theParent); i++) {
434 aIndex = myDocModel->index(i, 0, theParent);
435 if (myDocModel->hasChildren(aIndex)) {
436 if (myTreeView->isExpanded(aIndex)) {
437 aIndexes.append(aIndex);
438 QModelIndexList aSubIndexes = expandedItems(aIndex);
439 if (!aSubIndexes.isEmpty())
440 aIndexes.append(aSubIndexes);
447 //***************************************************
448 void XGUI_ObjectsBrowser::rebuildDataTree()
450 myDocModel->rebuildDataTree();
454 //***************************************************
455 void XGUI_ObjectsBrowser::setObjectsSelected(const QObjectPtrList& theObjects)
457 QList<QModelIndex> theIndexes;
458 QItemSelectionModel* aSelectModel = myTreeView->selectionModel();
459 aSelectModel->clear();
461 foreach(ObjectPtr aFeature, theObjects)
463 QModelIndex aIndex = myDocModel->objectIndex(aFeature);
464 if (aIndex.isValid()) {
465 aSelectModel->select(aIndex, QItemSelectionModel::Select);
470 //***************************************************
471 void XGUI_ObjectsBrowser::clearContent()
476 void XGUI_ObjectsBrowser::onSelectionChanged(const QItemSelection& theSelected,
477 const QItemSelection& theDeselected)
479 emit selectionChanged();
482 QObjectPtrList XGUI_ObjectsBrowser::selectedObjects(QModelIndexList* theIndexes) const
484 QObjectPtrList aList;
485 QModelIndexList aIndexes = selectedIndexes();
486 XGUI_DataModel* aModel = dataModel();
487 QModelIndexList::const_iterator aIt;
488 for (aIt = aIndexes.constBegin(); aIt != aIndexes.constEnd(); ++aIt) {
489 if ((*aIt).column() == 0) {
490 ObjectPtr aObject = aModel->object(*aIt);
492 aList.append(aObject);
494 theIndexes->append(*aIt);
501 void XGUI_ObjectsBrowser::onBeforeReset()
503 myExpandedItems = expandedItems();
506 void XGUI_ObjectsBrowser::onAfterModelReset()
508 foreach(QModelIndex aIndex, myExpandedItems) {
509 myTreeView->setExpanded(aIndex, true);
513 std::list<bool> XGUI_ObjectsBrowser::getStateForDoc(DocumentPtr theDoc) const
515 std::list<bool> aStates;
516 XGUI_DataModel* aModel = dataModel();
517 QModelIndex aRootIdx = aModel->documentRootIndex(theDoc);
518 int aNbChild = aModel->rowCount(aRootIdx);
519 for (int i = 0; i < aNbChild; i++) {
520 QModelIndex aIdx = aModel->index(i, 0, aRootIdx);
521 aStates.push_back(myTreeView->isExpanded(aIdx));
526 void XGUI_ObjectsBrowser::setStateForDoc(DocumentPtr theDoc, const std::list<bool>& theStates)
528 if (theStates.size() == 0)
530 XGUI_DataModel* aModel = dataModel();
531 QModelIndex aRootIdx = aModel->documentRootIndex(theDoc);
532 int aNbChild = aModel->rowCount(aRootIdx);
534 std::list<bool>::const_iterator aIt;
536 for (aIt = theStates.cbegin(); aIt != theStates.cend(); aIt++, i++) {
539 QModelIndex aIdx = aModel->index(i, 0, aRootIdx);
540 myTreeView->setExpanded(aIdx, (*aIt));