// File: BoilerTreeView.cpp // // CLASSES: // class BoilerTreeView : public QTreeView // class BoilerTreeView::ItemModel : public QAbstractItemModel // class BoilerTreeView::ItemDelegate : public QStyledItemDelegate // class BoilerTreeViewTest : public QWidget // // DESCRIPTION -- SEE ALSO "SAMPLE DESCRIPTION" BELOW // This boiler plate Qt4 QTreeView class with internal "model" and // "delegate" subclasses is intended for adaptation for application // QTreeView classes. It is concrete (i.e. it can be instantiated). // // If column sorting is desired, I recommend handling sorting directly // in the item model class rather than by introducing a QSortFilterProxyModel // subclass. Use of that class creates ambiguities in interpreting // QModelIndex values, and itself has some problems. However, if you // really want to use a QSortFilterProxyModel, I recommend use of our // own "RwQSortFilterProxyModel" subclass to fix those problems. // (See the comments at the head of RwQSortFilterProxyModel.cpp). // // To use as a basis for a new QTreeView subclass, copy this file, and the // the accompanying header file, and do a global substitution within the // new files of "Boiler", "Entity", "Entities", "entity". // // (1) BoilerTreeView.cpp (this file). // (2) BoilerTreeView.hpp (header file). // // This module was adapted from the Qt4 model/view replacement of a Qt3 // GenListView-based QListView instance in the RiverWare 6.1 Multiple Run // Configuration Editor dialog. See QtRun/MrmPolicyTreeView.hpp and .cpp. // // This module contains "QObject" classes (all three classes listed above). // So this module needs to be supported as such in the make files; // On Windows: in the two ".pro" files in the containing project (library). // // ==================================== // // SAMPLE DESCRIPTION: // This module presents an editable GUI list of Entity values. // Additional entity items can be appended. Existing items // can be removed. Entity values can be directly edited (inline) // and can be edited in an an open Entity dialog. // // The client can set and retrieve the Entity file path list using: // void setEntityList (const EntityList&); // const EntityList& entityListRef() const; // // Notifications of user-edits are issued with this Qt signal: // void entityListEdited(); // Qt Signal // //-- // Column Index Symbols static const int COL_Ord (0); // Ordinal Row Number static const int COL_Icon (1); // Entity Icon static const int COL_StartEd (2); // Start Edit Icon (pencil) static const int COL_Entity (3); // Entity value string static const int COL_Open (4); // Open external editor button static const int COL_RemButs (5); // Open external editor button static const int COL_COUNT (6); // --- NUMBER OF COLUMNS --- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- #ifndef BoilerTreeViewINCLUDED #include "BoilerTreeView.hpp" #endif #include #include #include #include #include #include #include #include #include // for std::max #ifndef RwQPixmap16INCLUDED #include "RwQPixmap16.hpp" #endif #ifndef rwErrorINCLUDED #include "rwError.hpp" //--- for rwAssert #endif //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // ****************************************** // *** class BoilerTreeView : QTreeView *** // ****************************************** // constructor 1 of 1 BoilerTreeView::BoilerTreeView (QWidget* parentWid) : QTreeView (parentWid), _itemModel (NULL), _itemDelegate (NULL), _contextMenu (NULL), _contextRow (-1), _contextCol (-1), _appendEntity_CtxAction (NULL), _removeEntity_CtxAction (NULL), _clientSetEntityList_inProgress (false) { static const char* mname ("BoilerTreeView ctor"); // std::cout << mname << std::endl; _itemModel = new ItemModel (this); setModel (_itemModel); _itemDelegate = new ItemDelegate (this); setItemDelegate (_itemDelegate); QHeaderView* hdrView = header(); hdrView->setSortIndicatorShown (false); hdrView->setMovable (false); hdrView->setStretchLastSection (false); hdrView->setResizeMode (QHeaderView::ResizeToContents); setSortingEnabled (false); setAllColumnsShowFocus (true); setMouseTracking (true); // For Status Tips setRootIsDecorated (false); const QSize maxIconSize = _itemModel->maxIconSize(); setIconSize (maxIconSize); initContextMenu(); initConnections(); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // destructor BoilerTreeView::~BoilerTreeView() { static const char* mname ("BoilerTreeView dtor"); // std::cout << mname << std::endl; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void BoilerTreeView::setEntityList (const EntityList& entityList) { // This method should be called only by external clients to initialize // (or set, at any time) the Entity list for operations which // do _not_ represent a user edit. Changes to the list made by this // method DO NOT result in the generation of a "entityListEdited()" // notification (signal emission). //---------------------------------------------------------- const bool saveInProg = _clientSetEntityList_inProgress; _clientSetEntityList_inProgress = true; //---------------------------------------------------------- if (rwAssert (_itemModel)) { _itemModel->setModelEntityList (entityList); } //---------------------------------------------------------- _clientSetEntityList_inProgress = saveInProg; //---------------------------------------------------------- } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- const EntityList& BoilerTreeView::entityListRef() const { if (rwAssert (_itemModel)) { return (_itemModel->modelEntityListRef()); } static const EntityList EmptyEntityList; return (EmptyEntityList); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void BoilerTreeView::appendEntityItem (const QString& entityName) { if (rwAssert (_itemModel)) { const int newRow = _itemModel->appendEntityItem (entityName); if (newRow >= 0) { // Select the new row const QModelIndex inx1 = _itemModel->index (newRow, 0); const QModelIndex inx2 = _itemModel->index (newRow, COL_COUNT-1); setCurrentIndex (inx1); const QItemSelection itemSel (inx1, inx2); // topLeft, botRight selectionModel()->select (itemSel, QItemSelectionModel::ClearAndSelect); // Insure that the new item is visible scrollTo (inx1, QAbstractItemView::EnsureVisible); } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void BoilerTreeView::showRemoveColumn (bool doShow) { setColumnHidden (COL_RemButs, !doShow); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void BoilerTreeView::processModelDataChanged() { static const char* mname ("BoilerTreeView::processModelDataChanged"); static int callCnt (0); ++callCnt; if (!_clientSetEntityList_inProgress) { // std::cout << mname << " [#" << callCnt << "]" << std::endl; emit entityListEdited(); } update(); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void BoilerTreeView::initContextMenu() { _contextMenu = new QMenu (this); _appendEntity_CtxAction = _contextMenu->addAction (tr ("Append Entity")); _removeEntity_CtxAction = _contextMenu->addAction (tr ("Remove Entity")); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void BoilerTreeView::initConnections() { // ************************************* // *** Context Menu / Menu Actions *** // ************************************* setContextMenuPolicy (Qt::CustomContextMenu); connect (this, SIGNAL (customContextMenuRequested (const QPoint&)), this, SLOT (contextMenuRequested (const QPoint&))); connect (_appendEntity_CtxAction, SIGNAL (triggered (bool)), SLOT (appendEntity_ctxTriggered())); connect (_removeEntity_CtxAction, SIGNAL (triggered (bool)), SLOT (removeEntity_ctxTriggered())); // *********************************************** // *** QTreeView (QAbstractItemView) Signals *** // *********************************************** connect (this, SIGNAL (clicked (const QModelIndex&)), SLOT (cell_clicked (const QModelIndex&))); connect (this, SIGNAL (doubleClicked (const QModelIndex&)), SLOT (cell_doubleClicked (const QModelIndex&))); connect (this, SIGNAL (entered (const QModelIndex&)), SLOT (cell_entered (const QModelIndex&))); connect (this, SIGNAL (pressed (const QModelIndex&)), SLOT (cell_pressed (const QModelIndex&))); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // NOTE: This is just for "Boiler Plate" demonstration purposes, and will // likely NOT be useful in this form for an actual application use. #include // Boiler Plate Stub Thing void BoilerTreeView::openEntity (int row) { EntityList editEntityList = entityListRef(); // copy const int rowCnt = editEntityList.size(); if ((row < 0) || (row >= rowCnt)) return; //------------------------------------->> const QString oldStr = editEntityList [row]; static const QString title (tr ("Edit Entity")); static const QString label (tr ("Entity Name")); // Show Entity Editor bool editOk (false); const QString newStr = QInputDialog::getText (NULL, // QWidget* parent title, // const QString& title label, // const QString& label QLineEdit::Normal, // QLineEdit::EchoMode oldStr, // const QString& text &editOk); // bool* ok if (editOk && (newStr != oldStr)) { // Update QTreeView Model editEntityList [row] = newStr; if (rwAssert (_itemModel)) { _itemModel->setModelEntityList (editEntityList); // Select the row const QModelIndex inx1 = _itemModel->index (row, 0); const QModelIndex inx2 = _itemModel->index (row, COL_COUNT-1); const QItemSelection itemSel (inx1, inx2); // topLeft, botRight selectionModel()->select ( itemSel, QItemSelectionModel::ClearAndSelect); } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void BoilerTreeView::removeEntityItem (int row) { // Retrieve the model's Entity List EntityList curEntityList = entityListRef(); // copy const int curRowCnt = curEntityList.size(); if ( (curRowCnt <= 0) || (row < 0) || (row >= curRowCnt) ) { QApplication::beep(); return; //--------->> } // Construct a new vector, without the indicated row EntityList newEntityList; // newEntityList.reserve (curRowCnt); for (int oldInx = 0; oldInx < curRowCnt; ++oldInx) { if (oldInx != row) { newEntityList.push_back (curEntityList [oldInx]); } } // Set the edited Entity List on the model _itemModel->setModelEntityList (newEntityList); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void BoilerTreeView::contextMenuRequested (const QPoint& pos) { static const char* mname ("BoilerTreeView::contextMenuRequested"); const QModelIndex posIndex = indexAt (pos); const bool isOnCell = posIndex.isValid(); _appendEntity_CtxAction->setEnabled (true); _removeEntity_CtxAction->setEnabled (isOnCell); _contextRow = isOnCell ? posIndex.row() : (-1); _contextCol = isOnCell ? posIndex.column() : (-1); const QPoint globalPos = viewport()->mapToGlobal (pos); // **************************** // *** Popup Context Menu *** // **************************** _contextMenu->popup (globalPos); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void BoilerTreeView::appendEntity_ctxTriggered() { appendEntityItem (""); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void BoilerTreeView::removeEntity_ctxTriggered() { // Note: this method checks the range of the 'row' parameter. removeEntityItem (_contextRow); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void BoilerTreeView::cell_clicked (const QModelIndex& modelInx) { static const char* mname ("BoilerTreeView::cell_clicked"); const int row = modelInx.row(); const int col = modelInx.column(); const int rowCnt = entityListRef().size(); // std::cout << mname // << " [" << row << "," << col << "]" // << std::endl; // Note: [With Qt 4.6.3] This Qt slot (connected to the QAbstractItemView // "clicked" signal) does not actually get called for out-of-cell clicks. // So, this is just a non-essential "safety". The 'clearSelection' // effect of clicking outside of a cell is actually implemented in this // class' QWidget mousePressEvent() virtual method reimplementation. // if ( (row < 0) || (row >= rowCnt) || (col < 0) || (col >= COL_COUNT) ) { clearSelection(); return; //----->> } if (col == COL_Open) { // Open Entity Editor openEntity (row); } else if (col == COL_StartEd) { if (rwAssert (_itemModel)) { // Start an In-Cell Edit in the Entity value column. const QModelIndex editInx = _itemModel->index (row, COL_Entity); edit (editInx); // [QAbstractItemView] } } else if (col == COL_RemButs) { removeEntityItem (row); } } void BoilerTreeView::cell_doubleClicked (const QModelIndex&) { } void BoilerTreeView::cell_entered (const QModelIndex&) { } void BoilerTreeView::cell_pressed (const QModelIndex&) { } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // virtual from QWidget void BoilerTreeView::mousePressEvent (QMouseEvent* evt) { static const char* mname ("BoilerTreeView::mousePressEvent"); // Call base class method QTreeView::mousePressEvent (evt); const QModelIndex posIndex = indexAt (evt->pos()); const int row = posIndex.row(); const int col = posIndex.column(); const int rowCnt = entityListRef().size(); // std::cout << mname // << " [" << row << "," << col << "]" // << std::endl; if ( (row < 0) || (row >= rowCnt) || (col < 0) || (col >= COL_COUNT) ) { clearSelection(); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void BoilerTreeView::mouseReleaseEvent (QMouseEvent* evt) { static const char* mname ("BoilerTreeView::mouseReleaseEvent"); // Call base class method QTreeView::mouseReleaseEvent (evt); // const QModelIndex posIndex = indexAt (evt->pos()); // const int row = posIndex.row(); // const int col = posIndex.column(); // const int rowCnt = entityListRef().size(); // std::cout << mname // << " [" << row << "," << col << "]" // << std::endl; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // ************************************************************** // *** class BoilerTreeView::ItemModel : QAbstractItemModel *** // ************************************************************** // constructor 1 of 1 BoilerTreeView::ItemModel::ItemModel (BoilerTreeView* parentTreeView) : QAbstractItemModel (parentTreeView), _parentTreeView (parentTreeView), _entityList(), _entityIcon (NULL), _entityIconSize (0,0), _pencilIcon (NULL), _pencilIconSize (0,0), _ellipsisIcon (NULL), _ellipsisIconSize (0,0), _removeIcon (NULL), _removeIconSize (0,0) { static const char* mname ("BoilerTreeView::ItemModel ctor"); // std::cout << mname << std::endl; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // destructor BoilerTreeView::ItemModel::~ItemModel() { static const char* mname ("BoilerTreeView::ItemModel dtor"); // std::cout << mname << std::endl; _entityList.clear(); if (_entityIcon) { delete _entityIcon; _entityIcon = NULL; } if (_pencilIcon) { delete _pencilIcon; _pencilIcon = NULL; } if (_ellipsisIcon) { delete _ellipsisIcon; _ellipsisIcon = NULL; } if (_removeIcon) { delete _removeIcon; _removeIcon = NULL; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void BoilerTreeView::ItemModel::setModelEntityList ( const EntityList& entityList) { if (&entityList != &_entityList) { beginResetModel(); // [QAbstractItemModel] _entityList.clear(); _entityList = entityList; // copy list endResetModel(); // [QAbstractItemModel] if (rwAssert (_parentTreeView)) { _parentTreeView->processModelDataChanged(); } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- int BoilerTreeView::ItemModel::appendEntityItem (const QString& entityName) { beginResetModel(); // [QAbstractItemModel] _entityList.push_back (entityName); endResetModel(); // [QAbstractItemModel] if (rwAssert (_parentTreeView)) { _parentTreeView->processModelDataChanged(); } const int newRow = _entityList.size() - 1; return (newRow); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- QString BoilerTreeView::ItemModel::cellStr (int row, int col) const { const int rowCnt = _entityList.size(); if ((row < 0) || (row >= rowCnt)) return (""); //------------------------------------------>> QString dispStr (""); switch (col) { case COL_Ord: { static const QString fmt1 (" %1: "); dispStr = fmt1.arg (QString::number (row+1)); break; } case COL_Entity: { static const QString fmt2 (" %1 "); dispStr = fmt2.arg (_entityList [row]); break; } } return (dispStr); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- const QIcon& BoilerTreeView::ItemModel::cellIcon ( int row, int col, QSize& retSize) const { // A note about Icon Sizes. QIcons don't convey a 'natural' size. // The natural size needs to be measured from the original QPixmap. // Icon sizes are cached in seperate QSize fields. retSize = QSize (0,0); // returned size const QIcon* ri (NULL); // returned icon static const QIcon NullIcon; const int rowCnt = _entityList.size(); if ((row < 0) || (row >= rowCnt)) return (NullIcon); //------------------------------------------------>> switch (col) { case COL_Icon: ri = &entityIcon(); retSize = _entityIconSize; break; case COL_StartEd: ri = &pencilIcon(); retSize = _pencilIconSize; break; case COL_Open: ri = &ellipsisIcon(); retSize = _ellipsisIconSize + QSize (6,0); break; case COL_RemButs: ri = &removeIcon(); retSize = _removeIconSize + QSize (6,0); break; } if (ri) return (*ri); // const QIcon& cellIcon //----------------->> return (NullIcon); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- QSize BoilerTreeView::ItemModel::cellSize (int row, int col) const { static const char* mname ("BoilerTreeView::ItemModel::cellSize"); static const int TopPlusBotMargin (2); // Note: Since the custom delegate is based on QStyledItemDelegate, // probably the gap between the icon and text (for cells containing both) // is based on some QStyle pixel metrics property. But I don't see it. // (See http://doc.qt.nokia.com/4.6/qstyle.html#PixelMetric-enum). // These values were measured (visually, precisely) on Windows XP and // Solaris with Qt 4.6.3. [January 2011]. // #ifdef Q_WS_X11 static const int TextWidthPad (4); static const int IconTextGap (10); #else static const int TextWidthPad (6); static const int IconTextGap (7); #endif QSize retSize (0,0); const int rowCnt = _entityList.size(); if ((row < 0) || (row >= rowCnt)) return (retSize); //----------------------------------------------->> QSize iconSize (0,0); (void) cellIcon (row, col, iconSize); // set QSize rawTextSize (0,0); QSize textSize (0,0); const QWidget* fontRefWidget = _parentTreeView; const QString str = cellStr (row, col); if (fontRefWidget && !str.isEmpty()) { // enum Qt::TextFlag (BITS) // SEE: http://doc.qt.nokia.com/4.6/qt.html#TextFlag-enum static const int TextFlags (Qt::IncludeTrailingSpaces); const QFontMetrics fm (fontRefWidget->fontMetrics()); rawTextSize = fm.size (TextFlags, str); textSize = rawTextSize + QSize (TextWidthPad, 0); } const bool bothIconAndText = (iconSize.width() && textSize.width()); const int iconTextGap = bothIconAndText ? IconTextGap : 0; const int cellWidth = iconSize.width() + textSize.width() + iconTextGap; const int cellHeight = TopPlusBotMargin + std::max (iconSize.height(), textSize.height()); // if ((row == 0) && ((col == COL_Icon) || (col == COL_Entity))) // { // std::cout << mname // << " [" << row << "," << col << "]" // << " iconWid " << iconSize.width() // << " rawTextWid " << rawTextSize.width() // << " textWid " << textSize.width() // << " cellWid " << cellWidth // << std::endl; // } return QSize (cellWidth, cellHeight); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- const QIcon& BoilerTreeView::ItemModel::entityIcon() const { if (_entityIcon == NULL) { _entityIcon = new QIcon (RwQPixmap16::rwLogoSoftFrm()); _entityIconSize = RwQPixmap16::rwLogoSoftFrm().size(); } return *_entityIcon; } const QIcon& BoilerTreeView::ItemModel::pencilIcon() const { if (_pencilIcon == NULL) { _pencilIcon = new QIcon (RwQPixmap16::pencil()); _pencilIconSize = RwQPixmap16::pencil().size(); } return *_pencilIcon; } const QIcon& BoilerTreeView::ItemModel::ellipsisIcon() const { if (_ellipsisIcon == NULL) { _ellipsisIcon = new QIcon (RwQPixmap16::ellipsisBut()); _ellipsisIconSize = RwQPixmap16::ellipsisBut().size(); } return *_ellipsisIcon; } const QIcon& BoilerTreeView::ItemModel::removeIcon() const { if (_removeIcon == NULL) { _removeIcon = new QIcon (RwQPixmap16::redMinus()); _removeIconSize = RwQPixmap16::redMinus().size(); } return *_removeIcon; } QSize BoilerTreeView::ItemModel::maxIconSize() const { // Ensure icons and sizes are instantiated and computed (void) entityIcon(); (void) pencilIcon(); (void) ellipsisIcon(); (void) removeIcon(); const QSize retSize = _entityIconSize .expandedTo (_pencilIconSize) .expandedTo (_ellipsisIconSize) .expandedTo (_removeIconSize); return (retSize); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // virtual from QAbstractItemModel Qt::ItemFlags BoilerTreeView::ItemModel::flags ( const QModelIndex& modelInx) const { // Qt::NoItemFlags -- Does not have any properties set. // Qt::ItemIsSelectable -- Can be selected. // Qt::ItemIsEditable -- Can be edited. // Qt::ItemIsDragEnabled -- Can be dragged. // Qt::ItemIsDropEnabled -- Can be used as a drop target. // Qt::ItemIsUserCheckable -- Can be checked or unchecked by the user. // Qt::ItemIsEnabled -- The user can interact with the item. // Qt::ItemIsTristate -- Item is checkable with three separate states. Qt::ItemFlags retFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; const int col = modelInx.column(); if (col == COL_Entity) { retFlags |= Qt::ItemIsEditable; } return (retFlags); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // virtual from QAbstractItemModel QModelIndex BoilerTreeView::ItemModel::index (int row, int col, const QModelIndex& parent /*=QModelIndex()*/) const { if (parent == QModelIndex()) { return createIndex (row, col); // [QAbstractItemModel] } return (QModelIndex()); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // virtual from QAbstractItemModel int BoilerTreeView::ItemModel::rowCount ( const QModelIndex& parent /*=QModelIndex()*/) const { int rowCnt (0); if (parent == QModelIndex()) { rowCnt = _entityList.size(); } return (rowCnt); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // virtual from QAbstractItemModel int BoilerTreeView::ItemModel::columnCount ( const QModelIndex& parent /*=QModelIndex()*/) const { int colCnt (0); if (parent == QModelIndex()) { colCnt = COL_COUNT; } return (colCnt); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // virtual from QAbstractItemModel QVariant BoilerTreeView::ItemModel::data ( const QModelIndex& modelInx, int role /*=Qt::DisplayRole*/) const { // Note: See enum Qt::ItemDataRole for role values. // http://doc.trolltech.com/4.6/qt.html#ItemDataRole-enum static const QString SP (" "); const int row = modelInx.row(); const int col = modelInx.column(); const int rowCnt = _entityList.size(); if ( (row < 0) || (row >= rowCnt) || (col < 0) || (col >= COL_COUNT) ) { return QVariant(); //-------------->> } if (role == Qt::DisplayRole) { return QVariant (cellStr (row, col)); } else if (role == Qt::EditRole) { QString editStr (""); if (col == COL_Entity) { editStr = _entityList [row].trimmed(); } return QVariant (editStr); } // else if (role == Qt::ForegroundRole) // Text Brush (color, etc). // { // static const QBrush redTextBrush (QColor (0xB9, 0x00, 0x00)); // switch (col) // { // } // // return QVariant(); // } else if (role == Qt::DecorationRole) { QSize iconSize (0,0); QIcon decoIcon = cellIcon (row, col, iconSize); if (!decoIcon.isNull()) { return QVariant (decoIcon); } return QVariant(); } else if (role == Qt::ToolTipRole) { QString tipStr (""); switch (col) { case COL_StartEd: tipStr = QString (tr ("Edit Entity name")); break; case COL_Open: tipStr = QString (tr ("Open Entity editor...")); break; case COL_RemButs: tipStr = QString (tr ("Remove Entity")); break; } if (!tipStr.isEmpty()) { return QVariant (tipStr); } return QVariant(); } else if (role == Qt::TextAlignmentRole) { switch (col) { case COL_Ord: return QVariant (Qt::AlignRight | Qt::AlignVCenter); } return QVariant (Qt::AlignLeft | Qt::AlignVCenter); } else if (role == Qt::SizeHintRole) { const QSize retSize = cellSize (row, col); return QVariant (retSize); } return QVariant(); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // virtual from QAbstractItemModel QVariant BoilerTreeView::ItemModel::headerData ( int section, Qt::Orientation orient, int role /*=Qt::DisplayRole*/) const { if (orient == Qt::Horizontal) { if (role == Qt::DisplayRole) { const int col (section); // section is column index [0..] QString retStr (""); switch (col) { case COL_Ord: retStr = ""; break; case COL_Icon: retStr = ""; break; case COL_Entity: retStr = "Entities"; break; case COL_StartEd: retStr = ""; break; case COL_Open: retStr = ""; break; } return QVariant (retStr); } } return QVariant(); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // virtual from QAbstractItemModel bool BoilerTreeView::ItemModel::setData (const QModelIndex& modelInx, const QVariant& val, int role /*=Qt::EditRole*/) { static const char* mname ("BoilerTreeView::ItemModel::setData"); //-------------------------------------------------------------------- // Qt Notes: // (1) This method sets the "role" data for the item at index to value. // (2) Returns true if successful; otherwise returns false. // (3) The dataChanged() signal should be emitted if the data was // successfully set. // (4) The base class implementation returns false. This function and // data() must be reimplemented for editable models. //-------------------------------------------------------------------- const bool roleIsEdit = (role == Qt::EditRole); const int row = modelInx.row(); const int col = modelInx.column(); const QString newStr = val.toString().trimmed(); const int rowCnt = _entityList.size(); // std::cout << mname // << " [" << row << "," << col << "]" // << " " << (roleIsEdit ? "EDIT" : "not-edit") // << " '" << qPrintable (newStr) << "'" // << std::endl; const bool changeValid = roleIsEdit && (row >= 0) && (row < rowCnt) && (col == COL_Entity); if (!changeValid) { QApplication::beep(); return (false); //----------------->> } const QString oldStr = _entityList [row]; const bool diff = (newStr != oldStr); if (diff) { _entityList [row] = newStr; const QModelIndex inx1 = index (row, 0); const QModelIndex inx2 = index (row, COL_COUNT-1); emit dataChanged (inx1, inx2); // topLeft, botRight if (rwAssert (_parentTreeView)) { _parentTreeView->processModelDataChanged(); } } return (diff); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // ****************************************************************** // *** class BoilerTreeView::ItemDelegate : QStyledItemDelegate *** // ****************************************************************** // constructor BoilerTreeView::ItemDelegate::ItemDelegate ( BoilerTreeView* parentTreeView) : QStyledItemDelegate (parentTreeView), _parentTreeView (parentTreeView) { static const char* mname ("BoilerTreeView::ItemDelegate ctor"); // std::cout << mname << std::endl; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // destructor BoilerTreeView::ItemDelegate::~ItemDelegate() { static const char* mname ("BoilerTreeView::ItemDelegate dtor"); // std::cout << mname << std::endl; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // virtual from QAbstractItemDelegate QWidget* BoilerTreeView::ItemDelegate::createEditor ( QWidget* parentWid, const QStyleOptionViewItem& styleItem, const QModelIndex& modelInx) const { // Note: when defering to the base class implementation, make sure // to call the direct base class method, i.e. of QStyledItemDelegate. QWidget* customEditor = QStyledItemDelegate::createEditor (parentWid, styleItem, modelInx); return (customEditor); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // virtual from QAbstractItemDelegate void BoilerTreeView::ItemDelegate::setEditorData ( QWidget* customEditor, const QModelIndex& modelInx) const { // Note: when defering to the base class implementation, make sure // to call the direct base class method, i.e. of QStyledItemDelegate. QStyledItemDelegate::setEditorData (customEditor, modelInx); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // virtual from QAbstractItemDelegate void BoilerTreeView::ItemDelegate::setModelData ( QWidget* customEditor, QAbstractItemModel* itemModel, const QModelIndex& modelInx) const { // Note: when defering to the base class implementation, make sure // to call the direct base class method, i.e. of QStyledItemDelegate. QStyledItemDelegate::setModelData (customEditor, itemModel, modelInx); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // ******************************************** // *** class BoilerTreeViewTest : QWidget *** // ******************************************** #include #include #include #include #include BoilerTreeViewTest::BoilerTreeViewTest (QWidget* parentWid) : QWidget (parentWid), _mainVBox (NULL), _boilerTreeView (NULL), _buttonHBox (NULL), _unlockButton (NULL), _addEntityButton (NULL), _addEntityTitleLabel (NULL) { _mainVBox = new QVBoxLayout (this); _boilerTreeView = new BoilerTreeView (this); _mainVBox->addWidget (_boilerTreeView); _unlockButton = new QPushButton (this); _unlockButton->setCheckable (true); _unlockButton->setIcon (RwQPixmap16::unlockedIconSet()); _unlockButton->setText (QString::null); _unlockButton->setToolTip (tr ("Enable Append and Remove Entity Buttons")); _unlockButton->setMaximumSize ( RwQPixmap16::unlocked().size() + QSize (4,4)); _addEntityButton = new QPushButton (this); _addEntityButton->setPixmap (RwQPixmap16::greenPlus()); _addEntityButton->setText (QString::null); _addEntityButton->setMaximumSize ( RwQPixmap16::greenPlus().size() + QSize (4,4)); _addEntityTitleLabel = new QLabel (this); _addEntityTitleLabel->setText (tr ("Append Entity")); _buttonHBox = new QHBoxLayout(); _buttonHBox->addWidget (_unlockButton); _buttonHBox->addWidget (_addEntityButton); _buttonHBox->addWidget (_addEntityTitleLabel); _buttonHBox->addSpacerItem ( new QSpacerItem (0, 0, QSizePolicy::Expanding, QSizePolicy::Preferred)); _mainVBox->addLayout(_buttonHBox); // Connections connect (_boilerTreeView, SIGNAL (entityListEdited()), SLOT (boilerTreeView_entityListEdited())); connect (_unlockButton, SIGNAL (clicked()), SLOT (unlock_clicked())); connect (_addEntityButton, SIGNAL (clicked()), SLOT (addEntity_clicked())); // Initial Test Data QStringList items; items << "One" << "Two" << "Three"; _boilerTreeView->setEntityList (items); updateButtons(); } // private slot void BoilerTreeViewTest::boilerTreeView_entityListEdited() { updateButtons(); } // private slot void BoilerTreeViewTest::unlock_clicked() { updateButtons(); } // private slot void BoilerTreeViewTest::addEntity_clicked() { _boilerTreeView->appendEntityItem (""); } // private void BoilerTreeViewTest::updateButtons() { const int itemCnt = _boilerTreeView->entityListRef().size(); if (itemCnt == 0) { // Force the add/remove ruleset operations enabled (unlocked) _unlockButton->setChecked (true); _unlockButton->setEnabled (false); } else { _unlockButton->setEnabled (true); } const bool rulesUnlocked = _unlockButton->isChecked(); _addEntityButton->setEnabled (rulesUnlocked); _addEntityTitleLabel->setEnabled (rulesUnlocked); _boilerTreeView->showRemoveColumn (rulesUnlocked); } //--- (end BoilerTreeView.cpp) ---