diff options
Diffstat (limited to 'Source/QtDialog/QCMakeCacheView.cxx')
-rw-r--r-- | Source/QtDialog/QCMakeCacheView.cxx | 687 |
1 files changed, 687 insertions, 0 deletions
diff --git a/Source/QtDialog/QCMakeCacheView.cxx b/Source/QtDialog/QCMakeCacheView.cxx new file mode 100644 index 0000000..cc1f4aa --- /dev/null +++ b/Source/QtDialog/QCMakeCacheView.cxx @@ -0,0 +1,687 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "QCMakeCacheView.h" + +#include <QApplication> +#include <QEvent> +#include <QHBoxLayout> +#include <QHeaderView> +#include <QKeyEvent> +#include <QMetaProperty> +#include <QSortFilterProxyModel> +#include <QStyle> + +#include "QCMakeWidgets.h" + +// filter for searches +class QCMakeSearchFilter : public QSortFilterProxyModel +{ +public: + QCMakeSearchFilter(QObject* o) + : QSortFilterProxyModel(o) + { + } + +protected: + bool filterAcceptsRow(int row, const QModelIndex& p) const + { + QStringList strs; + const QAbstractItemModel* m = this->sourceModel(); + QModelIndex idx = m->index(row, 0, p); + + // if there are no children, get strings for column 0 and 1 + if (!m->hasChildren(idx)) { + strs.append(m->data(idx).toString()); + idx = m->index(row, 1, p); + strs.append(m->data(idx).toString()); + } else { + // get strings for children entries to compare with + // instead of comparing with the parent + int num = m->rowCount(idx); + for (int i = 0; i < num; i++) { + QModelIndex tmpidx = m->index(i, 0, idx); + strs.append(m->data(tmpidx).toString()); + tmpidx = m->index(i, 1, idx); + strs.append(m->data(tmpidx).toString()); + } + } + + // check all strings for a match + foreach (QString str, strs) { + if (str.contains(this->filterRegExp())) { + return true; + } + } + + return false; + } +}; + +// filter for searches +class QCMakeAdvancedFilter : public QSortFilterProxyModel +{ +public: + QCMakeAdvancedFilter(QObject* o) + : QSortFilterProxyModel(o) + , ShowAdvanced(false) + { + } + + void setShowAdvanced(bool f) + { + this->ShowAdvanced = f; + this->invalidate(); + } + bool showAdvanced() const { return this->ShowAdvanced; } + +protected: + bool ShowAdvanced; + + bool filterAcceptsRow(int row, const QModelIndex& p) const + { + const QAbstractItemModel* m = this->sourceModel(); + QModelIndex idx = m->index(row, 0, p); + + // if there are no children + if (!m->hasChildren(idx)) { + bool adv = m->data(idx, QCMakeCacheModel::AdvancedRole).toBool(); + if (!adv || (adv && this->ShowAdvanced)) { + return true; + } + return false; + } + + // check children + int num = m->rowCount(idx); + for (int i = 0; i < num; i++) { + bool accept = this->filterAcceptsRow(i, idx); + if (accept) { + return true; + } + } + return false; + } +}; + +QCMakeCacheView::QCMakeCacheView(QWidget* p) + : QTreeView(p) +{ + // hook up our model and search/filter proxies + this->CacheModel = new QCMakeCacheModel(this); + this->AdvancedFilter = new QCMakeAdvancedFilter(this); + this->AdvancedFilter->setSourceModel(this->CacheModel); + this->AdvancedFilter->setDynamicSortFilter(true); + this->SearchFilter = new QCMakeSearchFilter(this); + this->SearchFilter->setSourceModel(this->AdvancedFilter); + this->SearchFilter->setFilterCaseSensitivity(Qt::CaseInsensitive); + this->SearchFilter->setDynamicSortFilter(true); + this->setModel(this->SearchFilter); + + // our delegate for creating our editors + QCMakeCacheModelDelegate* delegate = new QCMakeCacheModelDelegate(this); + this->setItemDelegate(delegate); + + this->setUniformRowHeights(true); + + this->setEditTriggers(QAbstractItemView::AllEditTriggers); + + // tab, backtab doesn't step through items + this->setTabKeyNavigation(false); + + this->setRootIsDecorated(false); +} + +bool QCMakeCacheView::event(QEvent* e) +{ + if (e->type() == QEvent::Show) { + this->header()->setDefaultSectionSize(this->viewport()->width() / 2); + } + return QTreeView::event(e); +} + +QCMakeCacheModel* QCMakeCacheView::cacheModel() const +{ + return this->CacheModel; +} + +QModelIndex QCMakeCacheView::moveCursor(CursorAction act, + Qt::KeyboardModifiers mod) +{ + // want home/end to go to begin/end of rows, not columns + if (act == MoveHome) { + return this->model()->index(0, 1); + } else if (act == MoveEnd) { + return this->model()->index(this->model()->rowCount() - 1, 1); + } + return QTreeView::moveCursor(act, mod); +} + +void QCMakeCacheView::setShowAdvanced(bool s) +{ +#if QT_VERSION >= 040300 + // new 4.3 API that needs to be called. what about an older Qt? + this->SearchFilter->invalidate(); +#endif + + this->AdvancedFilter->setShowAdvanced(s); +} + +bool QCMakeCacheView::showAdvanced() const +{ + return this->AdvancedFilter->showAdvanced(); +} + +void QCMakeCacheView::setSearchFilter(const QString& s) +{ + this->SearchFilter->setFilterFixedString(s); +} + +QCMakeCacheModel::QCMakeCacheModel(QObject* p) + : QStandardItemModel(p) + , EditEnabled(true) + , NewPropertyCount(0) + , View(FlatView) +{ + this->ShowNewProperties = true; + QStringList labels; + labels << tr("Name") << tr("Value"); + this->setHorizontalHeaderLabels(labels); +} + +QCMakeCacheModel::~QCMakeCacheModel() +{ +} + +static uint qHash(const QCMakeProperty& p) +{ + return qHash(p.Key); +} + +void QCMakeCacheModel::setShowNewProperties(bool f) +{ + this->ShowNewProperties = f; +} + +void QCMakeCacheModel::clear() +{ + this->QStandardItemModel::clear(); + this->NewPropertyCount = 0; + + QStringList labels; + labels << tr("Name") << tr("Value"); + this->setHorizontalHeaderLabels(labels); +} + +void QCMakeCacheModel::setProperties(const QCMakePropertyList& props) +{ + QSet<QCMakeProperty> newProps, newProps2; + + if (this->ShowNewProperties) { + newProps = props.toSet(); + newProps2 = newProps; + QSet<QCMakeProperty> oldProps = this->properties().toSet(); + oldProps.intersect(newProps); + newProps.subtract(oldProps); + newProps2.subtract(newProps); + } else { + newProps2 = props.toSet(); + } + + bool b = this->blockSignals(true); + + this->clear(); + this->NewPropertyCount = newProps.size(); + + if (View == FlatView) { + QCMakePropertyList newP = newProps.toList(); + QCMakePropertyList newP2 = newProps2.toList(); + qSort(newP); + qSort(newP2); + int row_count = 0; + foreach (QCMakeProperty p, newP) { + this->insertRow(row_count); + this->setPropertyData(this->index(row_count, 0), p, true); + row_count++; + } + foreach (QCMakeProperty p, newP2) { + this->insertRow(row_count); + this->setPropertyData(this->index(row_count, 0), p, false); + row_count++; + } + } else if (this->View == GroupView) { + QMap<QString, QCMakePropertyList> newPropsTree; + this->breakProperties(newProps, newPropsTree); + QMap<QString, QCMakePropertyList> newPropsTree2; + this->breakProperties(newProps2, newPropsTree2); + + QStandardItem* root = this->invisibleRootItem(); + + foreach (QString key, newPropsTree.keys()) { + QCMakePropertyList props2 = newPropsTree[key]; + + QList<QStandardItem*> parentItems; + parentItems.append( + new QStandardItem(key.isEmpty() ? tr("Ungrouped Entries") : key)); + parentItems.append(new QStandardItem()); + parentItems[0]->setData(QBrush(QColor(255, 100, 100)), + Qt::BackgroundColorRole); + parentItems[1]->setData(QBrush(QColor(255, 100, 100)), + Qt::BackgroundColorRole); + parentItems[0]->setData(1, GroupRole); + parentItems[1]->setData(1, GroupRole); + root->appendRow(parentItems); + + int num = props2.size(); + for (int i = 0; i < num; i++) { + QCMakeProperty prop = props2[i]; + QList<QStandardItem*> items; + items.append(new QStandardItem()); + items.append(new QStandardItem()); + parentItems[0]->appendRow(items); + this->setPropertyData(this->indexFromItem(items[0]), prop, true); + } + } + + foreach (QString key, newPropsTree2.keys()) { + QCMakePropertyList props2 = newPropsTree2[key]; + + QStandardItem* parentItem = + new QStandardItem(key.isEmpty() ? tr("Ungrouped Entries") : key); + root->appendRow(parentItem); + parentItem->setData(1, GroupRole); + + int num = props2.size(); + for (int i = 0; i < num; i++) { + QCMakeProperty prop = props2[i]; + QList<QStandardItem*> items; + items.append(new QStandardItem()); + items.append(new QStandardItem()); + parentItem->appendRow(items); + this->setPropertyData(this->indexFromItem(items[0]), prop, false); + } + } + } + + this->blockSignals(b); + this->reset(); +} + +QCMakeCacheModel::ViewType QCMakeCacheModel::viewType() const +{ + return this->View; +} + +void QCMakeCacheModel::setViewType(QCMakeCacheModel::ViewType t) +{ + this->View = t; + + QCMakePropertyList props = this->properties(); + QCMakePropertyList oldProps; + int numNew = this->NewPropertyCount; + int numTotal = props.count(); + for (int i = numNew; i < numTotal; i++) { + oldProps.append(props[i]); + } + + bool b = this->blockSignals(true); + this->clear(); + this->setProperties(oldProps); + this->setProperties(props); + this->blockSignals(b); + this->reset(); +} + +void QCMakeCacheModel::setPropertyData(const QModelIndex& idx1, + const QCMakeProperty& prop, bool isNew) +{ + QModelIndex idx2 = idx1.sibling(idx1.row(), 1); + + this->setData(idx1, prop.Key, Qt::DisplayRole); + this->setData(idx1, prop.Help, QCMakeCacheModel::HelpRole); + this->setData(idx1, prop.Type, QCMakeCacheModel::TypeRole); + this->setData(idx1, prop.Advanced, QCMakeCacheModel::AdvancedRole); + + if (prop.Type == QCMakeProperty::BOOL) { + int check = prop.Value.toBool() ? Qt::Checked : Qt::Unchecked; + this->setData(idx2, check, Qt::CheckStateRole); + } else { + this->setData(idx2, prop.Value, Qt::DisplayRole); + } + this->setData(idx2, prop.Help, QCMakeCacheModel::HelpRole); + + if (!prop.Strings.isEmpty()) { + this->setData(idx1, prop.Strings, QCMakeCacheModel::StringsRole); + } + + if (isNew) { + this->setData(idx1, QBrush(QColor(255, 100, 100)), + Qt::BackgroundColorRole); + this->setData(idx2, QBrush(QColor(255, 100, 100)), + Qt::BackgroundColorRole); + } +} + +void QCMakeCacheModel::getPropertyData(const QModelIndex& idx1, + QCMakeProperty& prop) const +{ + QModelIndex idx2 = idx1.sibling(idx1.row(), 1); + + prop.Key = this->data(idx1, Qt::DisplayRole).toString(); + prop.Help = this->data(idx1, HelpRole).toString(); + prop.Type = static_cast<QCMakeProperty::PropertyType>( + this->data(idx1, TypeRole).toInt()); + prop.Advanced = this->data(idx1, AdvancedRole).toBool(); + prop.Strings = + this->data(idx1, QCMakeCacheModel::StringsRole).toStringList(); + if (prop.Type == QCMakeProperty::BOOL) { + int check = this->data(idx2, Qt::CheckStateRole).toInt(); + prop.Value = check == Qt::Checked; + } else { + prop.Value = this->data(idx2, Qt::DisplayRole).toString(); + } +} + +QString QCMakeCacheModel::prefix(const QString& s) +{ + QString prefix = s.section('_', 0, 0); + if (prefix == s) { + prefix = QString(); + } + return prefix; +} + +void QCMakeCacheModel::breakProperties( + const QSet<QCMakeProperty>& props, QMap<QString, QCMakePropertyList>& result) +{ + QMap<QString, QCMakePropertyList> tmp; + // return a map of properties grouped by prefixes, and sorted + foreach (QCMakeProperty p, props) { + QString prefix = QCMakeCacheModel::prefix(p.Key); + tmp[prefix].append(p); + } + // sort it and re-org any properties with only one sub item + QCMakePropertyList reorgProps; + QMap<QString, QCMakePropertyList>::iterator iter; + for (iter = tmp.begin(); iter != tmp.end();) { + if (iter->count() == 1) { + reorgProps.append((*iter)[0]); + iter = tmp.erase(iter); + } else { + qSort(*iter); + ++iter; + } + } + if (reorgProps.count()) { + tmp[QString()] += reorgProps; + } + result = tmp; +} + +QCMakePropertyList QCMakeCacheModel::properties() const +{ + QCMakePropertyList props; + + if (!this->rowCount()) { + return props; + } + + QList<QModelIndex> idxs; + idxs.append(this->index(0, 0)); + + // walk the entire model for property entries + // this works regardless of a flat view or a tree view + while (!idxs.isEmpty()) { + QModelIndex idx = idxs.last(); + if (this->hasChildren(idx) && this->rowCount(idx)) { + idxs.append(this->index(0, 0, idx)); + } else { + if (!data(idx, GroupRole).toInt()) { + // get data + QCMakeProperty prop; + this->getPropertyData(idx, prop); + props.append(prop); + } + + // go to the next in the tree + while (!idxs.isEmpty() && + ( +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) && \ + QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + (idxs.last().row() + 1) >= rowCount(idxs.last().parent()) || +#endif + !idxs.last().sibling(idxs.last().row() + 1, 0).isValid())) { + idxs.removeLast(); + } + if (!idxs.isEmpty()) { + idxs.last() = idxs.last().sibling(idxs.last().row() + 1, 0); + } + } + } + + return props; +} + +bool QCMakeCacheModel::insertProperty(QCMakeProperty::PropertyType t, + const QString& name, + const QString& description, + const QVariant& value, bool advanced) +{ + QCMakeProperty prop; + prop.Key = name; + prop.Value = value; + prop.Help = description; + prop.Type = t; + prop.Advanced = advanced; + + // insert at beginning + this->insertRow(0); + this->setPropertyData(this->index(0, 0), prop, true); + this->NewPropertyCount++; + return true; +} + +void QCMakeCacheModel::setEditEnabled(bool e) +{ + this->EditEnabled = e; +} + +bool QCMakeCacheModel::editEnabled() const +{ + return this->EditEnabled; +} + +int QCMakeCacheModel::newPropertyCount() const +{ + return this->NewPropertyCount; +} + +Qt::ItemFlags QCMakeCacheModel::flags(const QModelIndex& idx) const +{ + Qt::ItemFlags f = QStandardItemModel::flags(idx); + if (!this->EditEnabled) { + f &= ~Qt::ItemIsEditable; + return f; + } + if (QCMakeProperty::BOOL == this->data(idx, TypeRole).toInt()) { + f |= Qt::ItemIsUserCheckable; + } + return f; +} + +QModelIndex QCMakeCacheModel::buddy(const QModelIndex& idx) const +{ + if (!this->hasChildren(idx) && + this->data(idx, TypeRole).toInt() != QCMakeProperty::BOOL) { + return this->index(idx.row(), 1, idx.parent()); + } + return idx; +} + +QCMakeCacheModelDelegate::QCMakeCacheModelDelegate(QObject* p) + : QItemDelegate(p) + , FileDialogFlag(false) +{ +} + +void QCMakeCacheModelDelegate::setFileDialogFlag(bool f) +{ + this->FileDialogFlag = f; +} + +QWidget* QCMakeCacheModelDelegate::createEditor(QWidget* p, + const QStyleOptionViewItem&, + const QModelIndex& idx) const +{ + QModelIndex var = idx.sibling(idx.row(), 0); + int type = var.data(QCMakeCacheModel::TypeRole).toInt(); + if (type == QCMakeProperty::BOOL) { + return NULL; + } else if (type == QCMakeProperty::PATH) { + QCMakePathEditor* editor = + new QCMakePathEditor(p, var.data(Qt::DisplayRole).toString()); + QObject::connect(editor, SIGNAL(fileDialogExists(bool)), this, + SLOT(setFileDialogFlag(bool))); + return editor; + } else if (type == QCMakeProperty::FILEPATH) { + QCMakeFilePathEditor* editor = + new QCMakeFilePathEditor(p, var.data(Qt::DisplayRole).toString()); + QObject::connect(editor, SIGNAL(fileDialogExists(bool)), this, + SLOT(setFileDialogFlag(bool))); + return editor; + } else if (type == QCMakeProperty::STRING && + var.data(QCMakeCacheModel::StringsRole).isValid()) { + QCMakeComboBox* editor = new QCMakeComboBox( + p, var.data(QCMakeCacheModel::StringsRole).toStringList()); + editor->setFrame(false); + return editor; + } + + QLineEdit* editor = new QLineEdit(p); + editor->setFrame(false); + return editor; +} + +bool QCMakeCacheModelDelegate::editorEvent(QEvent* e, + QAbstractItemModel* model, + const QStyleOptionViewItem& option, + const QModelIndex& index) +{ + Qt::ItemFlags flags = model->flags(index); + if (!(flags & Qt::ItemIsUserCheckable) || + !(option.state & QStyle::State_Enabled) || + !(flags & Qt::ItemIsEnabled)) { + return false; + } + + QVariant value = index.data(Qt::CheckStateRole); + if (!value.isValid()) { + return false; + } + + if ((e->type() == QEvent::MouseButtonRelease) || + (e->type() == QEvent::MouseButtonDblClick)) { + // eat the double click events inside the check rect + if (e->type() == QEvent::MouseButtonDblClick) { + return true; + } + } else if (e->type() == QEvent::KeyPress) { + if (static_cast<QKeyEvent*>(e)->key() != Qt::Key_Space && + static_cast<QKeyEvent*>(e)->key() != Qt::Key_Select) { + return false; + } + } else { + return false; + } + + Qt::CheckState state = + (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked ? Qt::Unchecked + : Qt::Checked); + bool success = model->setData(index, state, Qt::CheckStateRole); + if (success) { + this->recordChange(model, index); + } + return success; +} + +// Issue 205903 fixed in Qt 4.5.0. +// Can remove this function and FileDialogFlag when minimum Qt version is 4.5 +bool QCMakeCacheModelDelegate::eventFilter(QObject* object, QEvent* evt) +{ + // workaround for what looks like a bug in Qt on Mac OS X + // where it doesn't create a QWidget wrapper for the native file dialog + // so the Qt library ends up assuming the focus was lost to something else + + if (evt->type() == QEvent::FocusOut && this->FileDialogFlag) { + return false; + } + return QItemDelegate::eventFilter(object, evt); +} + +void QCMakeCacheModelDelegate::setModelData(QWidget* editor, + QAbstractItemModel* model, + const QModelIndex& index) const +{ + QItemDelegate::setModelData(editor, model, index); + const_cast<QCMakeCacheModelDelegate*>(this)->recordChange(model, index); +} + +QSize QCMakeCacheModelDelegate::sizeHint(const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + QSize sz = QItemDelegate::sizeHint(option, index); + QStyle* style = QApplication::style(); + + // increase to checkbox size + QStyleOptionButton opt; + opt.QStyleOption::operator=(option); + sz = sz.expandedTo( + style->subElementRect(QStyle::SE_ViewItemCheckIndicator, &opt, NULL) + .size()); + + return sz; +} + +QSet<QCMakeProperty> QCMakeCacheModelDelegate::changes() const +{ + return mChanges; +} + +void QCMakeCacheModelDelegate::clearChanges() +{ + mChanges.clear(); +} + +void QCMakeCacheModelDelegate::recordChange(QAbstractItemModel* model, + const QModelIndex& index) +{ + QModelIndex idx = index; + QAbstractItemModel* mymodel = model; + while (qobject_cast<QAbstractProxyModel*>(mymodel)) { + idx = static_cast<QAbstractProxyModel*>(mymodel)->mapToSource(idx); + mymodel = static_cast<QAbstractProxyModel*>(mymodel)->sourceModel(); + } + QCMakeCacheModel* cache_model = qobject_cast<QCMakeCacheModel*>(mymodel); + if (cache_model && idx.isValid()) { + QCMakeProperty prop; + idx = idx.sibling(idx.row(), 0); + cache_model->getPropertyData(idx, prop); + + // clean out an old one + QSet<QCMakeProperty>::iterator iter = mChanges.find(prop); + if (iter != mChanges.end()) { + mChanges.erase(iter); + } + // now add the new item + mChanges.insert(prop); + } +} |