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 email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
20 #include "XGUI_ObjectsBrowser.h"
21 #include "XGUI_Tools.h"
22 #include "XGUI_DataModel.h"
24 #include <ModelAPI_Data.h>
25 #include <ModelAPI_Session.h>
26 #include <ModelAPI_Document.h>
27 #include <ModelAPI_Tools.h>
29 #include <ModuleBase_Tools.h>
35 #include <QMouseEvent>
37 #include <QStyledItemDelegate>
38 #include <QMessageBox>
41 /// Width of second column (minimum acceptable = 27)
42 #define SECOND_COL_WIDTH 30
47 * Tree item delegate for definition of data in column items editor
49 class XGUI_TreeViewItemDelegate: public QStyledItemDelegate
53 /// \param theParent a parent of the delegate
54 XGUI_TreeViewItemDelegate(XGUI_DataTree* theParent):QStyledItemDelegate(theParent),
55 myTreedView(theParent) {}
57 /// Set data for item editor (name of the item)
58 /// \param editor a widget of editor
59 /// \param index the tree item index
60 virtual void setEditorData ( QWidget* editor, const QModelIndex& index ) const
62 QLineEdit* aEditor = dynamic_cast<QLineEdit*>(editor);
64 XGUI_DataModel* aModel = myTreedView->dataModel();
65 ObjectPtr aObj = aModel->object(index);
66 if (aObj.get() != NULL) {
67 aEditor->setText(aObj->data()->name().c_str());
71 QStyledItemDelegate::setEditorData(editor, index);
75 XGUI_DataTree* myTreedView;
79 XGUI_DataTree::XGUI_DataTree(QWidget* theParent)
80 : QTreeView(theParent)
82 setHeaderHidden(true);
83 setEditTriggers(QAbstractItemView::NoEditTriggers);
84 setSelectionBehavior(QAbstractItemView::SelectRows);
85 setSelectionMode(QAbstractItemView::ExtendedSelection);
87 setItemDelegateForColumn(0, new XGUI_TreeViewItemDelegate(this));
89 connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
90 SLOT(onDoubleClick(const QModelIndex&)));
93 XGUI_DataTree::~XGUI_DataTree()
97 XGUI_DataModel* XGUI_DataTree::dataModel() const
99 return static_cast<XGUI_DataModel*>(model());
102 void XGUI_DataTree::contextMenuEvent(QContextMenuEvent* theEvent)
104 emit contextMenuRequested(theEvent);
107 void XGUI_DataTree::commitData(QWidget* theEditor)
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
114 QLineEdit* aEditor = dynamic_cast<QLineEdit*>(theEditor);
116 QString aName = aEditor->text();
117 QModelIndexList aIndexList = selectionModel()->selectedIndexes();
118 XGUI_DataModel* aModel = dataModel();
119 ObjectPtr aObj = aModel->object(aIndexList.first());
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();
132 void XGUI_DataTree::clear()
134 dataModel()->clear();
138 void XGUI_DataTree::resizeEvent(QResizeEvent* theEvent)
140 QTreeView::resizeEvent(theEvent);
141 QSize aSize = theEvent->size();
142 if (aSize.isValid()) {
143 setColumnWidth(0, aSize.width() - SECOND_COL_WIDTH - 7);
144 setColumnWidth(1, SECOND_COL_WIDTH);
148 void XGUI_DataTree::onDoubleClick(const QModelIndex& theIndex)
150 if (theIndex.column() != 1)
152 SessionPtr aMgr = ModelAPI_Session::get();
153 // When operation is opened then we can not change history
154 if (aMgr->isOperation())
156 XGUI_DataModel* aModel = dataModel();
157 if (aModel->flags(theIndex) == 0)
159 ObjectPtr aObj = aModel->object(theIndex);
161 DocumentPtr aDoc = aMgr->activeDocument();
163 std::string aOpName = tr("History change").toStdString();
165 if (aObj->document() != aDoc)
167 aMgr->startOperation(aOpName);
168 aDoc->setCurrentFeature(std::dynamic_pointer_cast<ModelAPI_Feature>(aObj), true);
169 aMgr->finishOperation();
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
175 if ((theIndex.internalId() != 0) && (aDoc.get() != theIndex.internalPointer()))
176 // Cliced not on active document folder
179 aMgr->startOperation(aOpName);
180 aDoc->setCurrentFeature(FeaturePtr(), true);
181 aMgr->finishOperation();
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));
193 //********************************************************************
194 //********************************************************************
195 //********************************************************************
196 XGUI_ActiveDocLbl::XGUI_ActiveDocLbl(const QString& theText, QWidget* theParent )
197 : QLabel(theText, theParent),
198 myPreSelectionStyle(""),
200 mySelectionStyle(""),
205 void XGUI_ActiveDocLbl::setTreeView(QTreeView* theView)
207 myTreeView = theView;
208 setFont(myTreeView->font());
210 QPalette aPalet = myTreeView->palette();
211 QColor aHighlight = aPalet.highlight().color();
212 QColor aHighlightText = aPalet.highlightedText().color();
214 myPreSelectionStyle = "QLabel {background-color: ";
215 myPreSelectionStyle += aHighlight.lighter(170).name() + "}";
216 //myPreSelectionStyle += "qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 white, stop:1 " +
217 // aHighlight.lighter(170).name() + ");";
218 //myPreSelectionStyle += "border: 1px solid lightblue; border-radius: 2px }";
220 QString aName = aPalet.color(QPalette::Base).name();
221 myNeutralStyle = "QLabel { border: 1px solid " + aName + " }";
224 #if (!defined HAVE_SALOME) && (defined WIN32)
225 mySelectionStyle = "QLabel {background-color: ";
226 mySelectionStyle += "rgb(205, 232, 255); ";
227 //mySelectionStyle += "qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(236, 245, 255)";
228 //mySelectionStyle += ", stop:1 rgb(208, 229, 255));";
229 //mySelectionStyle += "border: 1px solid rgb(132, 172, 221); border-radius: 2px }";
230 mySelectionStyle += "border: 1px solid rgb(153, 209, 255) }";
232 mySelectionStyle = "QLabel {background-color: " + aHighlight.name();
233 mySelectionStyle += "; color : " + aHighlightText.name() + "}";
236 myTreeView->viewport()->installEventFilter(this);
240 #if (!defined HAVE_SALOME) && (defined WIN32)
241 bool XGUI_ActiveDocLbl::event(QEvent* theEvent)
243 switch (theEvent->type()) {
246 setStyleSheet(myPreSelectionStyle);
250 setStyleSheet(myNeutralStyle);
253 return QLabel::event(theEvent);
257 bool XGUI_ActiveDocLbl::eventFilter(QObject* theObj, QEvent* theEvent)
259 if (theObj == myTreeView->viewport()) {
260 if (theEvent->type() == QEvent::MouseButtonRelease)
263 return QLabel::eventFilter(theObj, theEvent);
266 static bool MYClearing = false;
267 void XGUI_ActiveDocLbl::mouseReleaseEvent( QMouseEvent* e)
271 setStyleSheet(mySelectionStyle);
272 // We can not block signals because on
273 // clear selection the View state will not be updated
274 myTreeView->clearSelection();
275 QLabel::mouseReleaseEvent(e);
279 void XGUI_ActiveDocLbl::unselect()
282 myIsSelected = false;
283 setStyleSheet(myNeutralStyle);
288 //********************************************************************
289 //********************************************************************
290 //********************************************************************
291 XGUI_ObjectsBrowser::XGUI_ObjectsBrowser(QWidget* theParent)
292 : QWidget(theParent), myDocModel(0)
294 QVBoxLayout* aLayout = new QVBoxLayout(this);
295 ModuleBase_Tools::zeroMargins(aLayout);
296 aLayout->setSpacing(0);
298 QWidget* aLabelWgt = new QWidget(this);
299 aLabelWgt->setAutoFillBackground(true);
301 aLayout->addWidget(aLabelWgt);
302 QHBoxLayout* aLabelLay = new QHBoxLayout(aLabelWgt);
303 ModuleBase_Tools::zeroMargins(aLabelLay);
304 aLabelLay->setSpacing(0);
306 QLabel* aLbl = new QLabel(aLabelWgt);
307 aLbl->setPixmap(QPixmap(":pictures/assembly.png"));
309 // Do not paint background of the label (in order to show icon)
310 aLbl->setAutoFillBackground(false);
312 aLabelLay->addWidget(aLbl);
314 SessionPtr aMgr = ModelAPI_Session::get();
315 DocumentPtr aDoc = aMgr->moduleDocument();
317 myActiveDocLbl = new XGUI_ActiveDocLbl(tr("Part set"), aLabelWgt);
318 // myActiveDocLbl->setReadOnly(true);
319 // myActiveDocLbl->setFrame(false);
320 myActiveDocLbl->setContextMenuPolicy(Qt::CustomContextMenu);
322 aLabelLay->addWidget(myActiveDocLbl);
323 aLabelLay->setStretch(1, 1);
325 myTreeView = new XGUI_DataTree(this);
326 myTreeView->setFrameShape(QFrame::NoFrame);
327 aLayout->addWidget(myTreeView);
329 QPalette aTreePalet = myTreeView->palette();
330 QColor aTreeBack = aTreePalet.color(QPalette::Base);
333 aPalet.setColor(QPalette::Base, aTreeBack);
334 aPalet.setColor(QPalette::Window, aTreeBack);
335 aLabelWgt->setPalette(aPalet);
337 myDocModel = new XGUI_DataModel(this);
338 connect(myDocModel, SIGNAL(modelAboutToBeReset()), SLOT(onBeforeReset()));
339 connect(myDocModel, SIGNAL(treeRebuilt()), SLOT(onAfterModelReset()));
341 connect(myTreeView, SIGNAL(contextMenuRequested(QContextMenuEvent*)), this,
342 SLOT(onContextMenuRequested(QContextMenuEvent*)));
345 //***************************************************
346 XGUI_ObjectsBrowser::~XGUI_ObjectsBrowser()
350 void XGUI_ObjectsBrowser::setXMLReader(Config_DataModelReader* theReader)
352 myDocModel->setXMLReader(theReader);
353 myTreeView->setModel(myDocModel);
355 // It has to be done after setting of model
356 myActiveDocLbl->setTreeView(myTreeView);
358 QItemSelectionModel* aSelMod = myTreeView->selectionModel();
359 connect(aSelMod, SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
360 this, SLOT(onSelectionChanged(const QItemSelection&, const QItemSelection&)));
363 //***************************************************
364 void XGUI_ObjectsBrowser::onContextMenuRequested(QContextMenuEvent* theEvent)
366 QModelIndexList aIndexes;
367 QObjectPtrList aSelectedData = selectedObjects(&aIndexes);
368 bool toEnable = false;
370 if (aSelectedData.size() == 1) {
371 QModelIndex aSelected = myTreeView->indexAt(theEvent->pos());
372 if (!aIndexes.contains(aSelected))
373 return; // menu is called on non selected item
375 Qt::ItemFlags aFlags = dataModel()->flags(aIndexes.first());
376 toEnable = ((aFlags & Qt::ItemIsEditable) != 0);
378 foreach(QAction* aCmd, actions()) {
379 aCmd->setEnabled(toEnable);
381 emit contextMenuRequested(theEvent);
384 //***************************************************
385 void XGUI_ObjectsBrowser::onLabelContextMenuRequested(const QPoint& thePnt)
387 myTreeView->selectionModel()->clearSelection();
388 //Empty feature pointer means that selected root document
389 foreach(QAction* aCmd, actions()) {
390 aCmd->setEnabled(true);
392 QContextMenuEvent aEvent(QContextMenuEvent::Mouse, thePnt, myActiveDocLbl->mapToGlobal(thePnt));
393 emit contextMenuRequested(&aEvent);
396 //***************************************************
397 void XGUI_ObjectsBrowser::onEditItem()
399 QObjectPtrList aSelectedData = selectedObjects();
400 if (aSelectedData.size() > 0) {
401 ObjectPtr anObject = aSelectedData.first();
402 if (anObject.get()) { // Selection happens in TreeView
403 // check whether the object can be renamed. There should not be parts which are not loaded
404 std::set<FeaturePtr> aFeatures;
405 aFeatures.insert(ModelAPI_Feature::feature(anObject));
406 if (!XGUI_Tools::canRemoveOrRename((QWidget*)parent(), aFeatures))
409 // Find index which corresponds the feature
411 foreach(QModelIndex aIdx, selectedIndexes()) {
412 ObjectPtr aFea = dataModel()->object(aIdx);
413 if (dataModel()->object(aIdx)->isSame(anObject)) {
418 if (aIndex.isValid()) {
419 myTreeView->setCurrentIndex(aIndex);
420 myTreeView->edit(aIndex);
427 //***************************************************
428 QModelIndexList XGUI_ObjectsBrowser::expandedItems(const QModelIndex& theParent) const
430 QModelIndexList aIndexes;
432 for (int i = 0; i < myDocModel->rowCount(theParent); i++) {
433 aIndex = myDocModel->index(i, 0, theParent);
434 if (myDocModel->hasChildren(aIndex)) {
435 if (myTreeView->isExpanded(aIndex)) {
436 aIndexes.append(aIndex);
437 QModelIndexList aSubIndexes = expandedItems(aIndex);
438 if (!aSubIndexes.isEmpty())
439 aIndexes.append(aSubIndexes);
446 //***************************************************
447 void XGUI_ObjectsBrowser::rebuildDataTree()
449 myDocModel->rebuildDataTree();
453 //***************************************************
454 void XGUI_ObjectsBrowser::setObjectsSelected(const QObjectPtrList& theObjects)
456 QList<QModelIndex> theIndexes;
457 QItemSelectionModel* aSelectModel = myTreeView->selectionModel();
458 aSelectModel->clear();
460 foreach(ObjectPtr aFeature, theObjects)
462 QModelIndex aIndex = myDocModel->objectIndex(aFeature);
463 if (aIndex.isValid()) {
464 aSelectModel->select(aIndex, QItemSelectionModel::Select);
469 //***************************************************
470 void XGUI_ObjectsBrowser::clearContent()
475 void XGUI_ObjectsBrowser::onSelectionChanged(const QItemSelection& theSelected,
476 const QItemSelection& theDeselected)
478 emit selectionChanged();
481 QObjectPtrList XGUI_ObjectsBrowser::selectedObjects(QModelIndexList* theIndexes) const
483 QObjectPtrList aList;
484 QModelIndexList aIndexes = selectedIndexes();
485 XGUI_DataModel* aModel = dataModel();
486 QModelIndexList::const_iterator aIt;
487 for (aIt = aIndexes.constBegin(); aIt != aIndexes.constEnd(); ++aIt) {
488 if ((*aIt).column() == 0) {
489 ObjectPtr aObject = aModel->object(*aIt);
491 aList.append(aObject);
493 theIndexes->append(*aIt);
500 void XGUI_ObjectsBrowser::onBeforeReset()
502 myExpandedItems = expandedItems();
505 void XGUI_ObjectsBrowser::onAfterModelReset()
507 foreach(QModelIndex aIndex, myExpandedItems) {
508 myTreeView->setExpanded(aIndex, true);
512 std::list<bool> XGUI_ObjectsBrowser::getStateForDoc(DocumentPtr theDoc) const
514 std::list<bool> aStates;
515 XGUI_DataModel* aModel = dataModel();
516 QModelIndex aRootIdx = aModel->documentRootIndex(theDoc);
517 int aNbChild = aModel->rowCount(aRootIdx);
518 for (int i = 0; i < aNbChild; i++) {
519 QModelIndex aIdx = aModel->index(i, 0, aRootIdx);
520 aStates.push_back(myTreeView->isExpanded(aIdx));
525 void XGUI_ObjectsBrowser::setStateForDoc(DocumentPtr theDoc, const std::list<bool>& theStates)
527 if (theStates.size() == 0)
529 XGUI_DataModel* aModel = dataModel();
530 QModelIndex aRootIdx = aModel->documentRootIndex(theDoc);
531 int aNbChild = aModel->rowCount(aRootIdx);
533 std::list<bool>::const_iterator aIt;
535 for (aIt = theStates.cbegin(); aIt != theStates.cend(); aIt++, i++) {
538 QModelIndex aIdx = aModel->index(i, 0, aRootIdx);
539 myTreeView->setExpanded(aIdx, (*aIt));