123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443 |
- /*=========================================================================
- Library: CTK
- Copyright (c) Kitware Inc.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0.txt
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- =========================================================================*/
- // Qt includes
- #include <QAction>
- #include <QDebug>
- #include <QHeaderView>
- #include <QPainter>
- #include <QSortFilterProxyModel>
- #include <QStandardItem>
- #include <QStandardItemModel>
- #include <QTextDocument>
- #include <QTreeView>
- #include <QVBoxLayout>
- // CTK includes
- #include "ctkActionsWidget.h"
- //-----------------------------------------------------------------------------
- class ctkActionsWidgetPrivate
- {
- Q_DECLARE_PUBLIC(ctkActionsWidget);
- protected:
- ctkActionsWidget* const q_ptr;
- public:
- ctkActionsWidgetPrivate(ctkActionsWidget& object);
- void setupUI();
- void setupHeaders();
- void updateItems(QList<QStandardItem*>& items, QAction* action);
- QStandardItemModel* ActionsModel;
- ctkSortFilterActionsProxyModel* SortFilterActionsProxyModel;
- QTreeView* ActionsTreeView;
- };
- //-----------------------------------------------------------------------------
- ctkActionsWidgetPrivate::ctkActionsWidgetPrivate(ctkActionsWidget& object)
- :q_ptr(&object)
- {
- this->ActionsModel = 0;
- this->SortFilterActionsProxyModel = 0;
- this->ActionsTreeView = 0;
- }
- //-----------------------------------------------------------------------------
- void ctkActionsWidgetPrivate::setupUI()
- {
- Q_Q(ctkActionsWidget);
- this->ActionsModel = new QStandardItemModel(q);
- this->setupHeaders();
- this->SortFilterActionsProxyModel = new ctkSortFilterActionsProxyModel(q);
- this->SortFilterActionsProxyModel->setSourceModel(this->ActionsModel);
- this->ActionsTreeView = new QTreeView(q);
- QVBoxLayout* layout = new QVBoxLayout(q);
- layout->addWidget(this->ActionsTreeView);
- layout->setContentsMargins(0,0,0,0);
- q->setLayout(layout);
- this->ActionsTreeView->setModel(this->SortFilterActionsProxyModel);
- this->ActionsTreeView->setAlternatingRowColors(true);
- //this->ActionsTreeView->setItemDelegate(new ctkRichTextItemDelegate);
- }
- //-----------------------------------------------------------------------------
- void ctkActionsWidgetPrivate::setupHeaders()
- {
- this->ActionsModel->setColumnCount(4);
- QStringList headers;
- headers << "Action" << "Shortcut(s)" << "Context" << "Details";
- this->ActionsModel->setHorizontalHeaderLabels(headers);
- }
- //-----------------------------------------------------------------------------
- void ctkActionsWidgetPrivate
- ::updateItems(QList<QStandardItem*>& items, QAction* action)
- {
- Q_ASSERT(items.size() == 4);
- // Name
- QString actionText = action->text();
- if (actionText.indexOf('&') != -1)
- {
- actionText = actionText.remove(actionText.indexOf('&'),1); // remove mnemonic
- }
- items[ctkActionsWidget::NameColumn]->setText(actionText);
- items[ctkActionsWidget::NameColumn]->setIcon(action->icon());
- // Shortcut
- QStringList shortcuts;
- foreach(const QKeySequence& keySequence, action->shortcuts())
- {
- shortcuts << keySequence.toString(QKeySequence::NativeText);
- }
- items[ctkActionsWidget::ShortcutColumn]->setText(
- shortcuts.join("; "));
- // Context
- QString shortcutContext;
- switch (action->shortcutContext())
- {
- case Qt::WidgetShortcut:
- case Qt::WidgetWithChildrenShortcut:
- shortcutContext = "Widget";
- break;
- case Qt::WindowShortcut:
- case Qt::ApplicationShortcut:
- default:
- shortcutContext = "Application";
- }
- items[ctkActionsWidget::ContextColumn]->setText(shortcutContext);
- items[ctkActionsWidget::DetailsColumn]->setText(action->toolTip() != actionText
- ? action->toolTip() : QString(""));
- }
- //-----------------------------------------------------------------------------
- ctkActionsWidget::ctkActionsWidget(QWidget* parentWidget)
- :QWidget(parentWidget)
- , d_ptr(new ctkActionsWidgetPrivate(*this))
- {
- Q_D(ctkActionsWidget);
- d->setupUI();
- }
- //-----------------------------------------------------------------------------
- ctkActionsWidget::~ctkActionsWidget()
- {
- }
- //-----------------------------------------------------------------------------
- void ctkActionsWidget::addAction(QAction* action, const QString& group)
- {
- Q_D(ctkActionsWidget);
- QStandardItem* actionGroupItem = this->groupItem(group);
- Q_ASSERT(actionGroupItem);
- QList<QStandardItem*> actionItems;
- for (int i = 0; i < 4; ++i)
- {
- QStandardItem* item = new QStandardItem;
- item->setData(qVariantFromValue(qobject_cast<QObject*>(action)));
- item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
- actionItems << item;
- }
- d->updateItems(actionItems, action);
-
- bool expandGroupItem = (actionGroupItem->rowCount() == 0);
- actionGroupItem->appendRow(actionItems);
- // if the group didn't exist yet or was empty, then open/expand it
- // automatcally to show its contents. If the group was not empty, then let
- // it as is (maybe the user closed/collapsed it for a good reason...
- if (expandGroupItem)
- {
- d->ActionsTreeView->expand(
- d->SortFilterActionsProxyModel->mapFromSource(d->ActionsModel->indexFromItem(actionGroupItem)));
- }
- d->ActionsTreeView->resizeColumnToContents(ctkActionsWidget::NameColumn);
- d->ActionsTreeView->resizeColumnToContents(ctkActionsWidget::DetailsColumn);
- connect(action, SIGNAL(changed()), this, SLOT(updateAction()));
- }
- //-----------------------------------------------------------------------------
- void ctkActionsWidget::addActions(QList<QAction*> actions, const QString& group)
- {
- Q_D(ctkActionsWidget);
- bool wasSortinEnabled = d->ActionsTreeView->isSortingEnabled();
- d->ActionsTreeView->setSortingEnabled(false);
- foreach(QAction* action, actions)
- {
- this->addAction(action, group);
- }
- d->ActionsTreeView->setSortingEnabled(wasSortinEnabled);
- }
- //-----------------------------------------------------------------------------
- void ctkActionsWidget::clear()
- {
- Q_D(ctkActionsWidget);
- d->ActionsModel->clear();
- d->setupHeaders();
- }
- //-----------------------------------------------------------------------------
- QStandardItem* ctkActionsWidget::groupItem(const QString& group)
- {
- Q_D(ctkActionsWidget);
- if (group.isEmpty())
- {
- return d->ActionsModel->invisibleRootItem();
- }
- // check if the group already exists
- QList<QStandardItem *> foundGroup =
- d->ActionsModel->findItems(group);
- if (foundGroup.size() > 0)
- {
- return foundGroup[0];
- }
- QStandardItem* groupItem = new QStandardItem(group);
- groupItem->setFlags(Qt::ItemIsEnabled);
- d->ActionsModel->appendRow(groupItem);
- return groupItem;
- }
- //-----------------------------------------------------------------------------
- QStandardItemModel* ctkActionsWidget::model()const
- {
- Q_D(const ctkActionsWidget);
- return d->ActionsModel;
- }
- //-----------------------------------------------------------------------------
- void ctkActionsWidget::setActionsWithNoShortcutVisible(bool show)
- {
- Q_D(ctkActionsWidget);
- d->SortFilterActionsProxyModel->setActionsWithNoShortcutVisible(show);
- }
- //-----------------------------------------------------------------------------
- bool ctkActionsWidget::areActionsWithNoShortcutVisible()const
- {
- Q_D(const ctkActionsWidget);
- return d->SortFilterActionsProxyModel->areActionsWithNoShortcutVisible();
- }
- //-----------------------------------------------------------------------------
- void ctkActionsWidget::setMenuActionsVisible(bool show)
- {
- Q_D(ctkActionsWidget);
- d->SortFilterActionsProxyModel->setMenuActionsVisible(show);
- }
- //-----------------------------------------------------------------------------
- bool ctkActionsWidget::areMenuActionsVisible()const
- {
- Q_D(const ctkActionsWidget);
- return d->SortFilterActionsProxyModel->areMenuActionsVisible();
- }
- //-----------------------------------------------------------------------------
- void ctkActionsWidget::setSortColumn(int column)
- {
- Q_D(ctkActionsWidget);
- d->ActionsTreeView->sortByColumn(column, Qt::AscendingOrder);
- d->ActionsTreeView->setSortingEnabled(column != -1);
- }
- //-----------------------------------------------------------------------------
- int ctkActionsWidget::sortColumn()const
- {
- Q_D(const ctkActionsWidget);
- return d->ActionsTreeView->isSortingEnabled() ?
- d->ActionsTreeView->header()->sortIndicatorSection() : -1;
- }
- //-----------------------------------------------------------------------------
- void ctkActionsWidget::updateAction()
- {
- Q_D(ctkActionsWidget);
- QAction* action = qobject_cast<QAction*>(this->sender());
- Q_ASSERT(action);
- QModelIndexList foundActions =
- d->ActionsModel->match(d->ActionsModel->index(0,0),
- Qt::UserRole + 1, qVariantFromValue(qobject_cast<QObject*>(action)),
- -1, Qt::MatchExactly | Qt::MatchRecursive);
- Q_ASSERT(foundActions.size());
- foreach (QModelIndex actionIndex, foundActions)
- {
- QModelIndex parentIndex = actionIndex.parent();
- QStandardItem* parent = parentIndex.isValid()
- ? d->ActionsModel->itemFromIndex(parentIndex)
- : d->ActionsModel->invisibleRootItem();
- int actionRow = actionIndex.row();
- Q_ASSERT(actionRow >= 0);
- QList<QStandardItem*> actionItems;
- for(int i = 0; i < 4; ++i)
- {
- actionItems << parent->child(actionRow, i);
- }
- d->updateItems(actionItems, action);
- }
- }
- //-----------------------------------------------------------------------------
- QTreeView* ctkActionsWidget::view()const
- {
- Q_D(const ctkActionsWidget);
- return d->ActionsTreeView;
- }
- //-----------------------------------------------------------------------------
- class ctkSortFilterActionsProxyModelPrivate
- {
- protected:
- ctkSortFilterActionsProxyModel* const q_ptr;
- public:
- ctkSortFilterActionsProxyModelPrivate(ctkSortFilterActionsProxyModel& object);
- bool ActionsWithNoShortcutVisible;
- bool MenuActionsVisible;
- };
- //-----------------------------------------------------------------------------
- ctkSortFilterActionsProxyModelPrivate::ctkSortFilterActionsProxyModelPrivate(ctkSortFilterActionsProxyModel& object)
- :q_ptr(&object)
- {
- this->ActionsWithNoShortcutVisible = true;
- this->MenuActionsVisible = true;
- }
- //-----------------------------------------------------------------------------
- ctkSortFilterActionsProxyModel::~ctkSortFilterActionsProxyModel()
- {
- }
- //-----------------------------------------------------------------------------
- ctkSortFilterActionsProxyModel::ctkSortFilterActionsProxyModel(QObject* parentObject)
- :QSortFilterProxyModel(parentObject)
- , d_ptr(new ctkSortFilterActionsProxyModelPrivate(*this))
- {
- }
- //-----------------------------------------------------------------------------
- void ctkSortFilterActionsProxyModel::setActionsWithNoShortcutVisible(bool visible)
- {
- Q_D(ctkSortFilterActionsProxyModel);
- d->ActionsWithNoShortcutVisible = visible;
- this->invalidateFilter();
- }
- //-----------------------------------------------------------------------------
- bool ctkSortFilterActionsProxyModel::areActionsWithNoShortcutVisible()const
- {
- Q_D(const ctkSortFilterActionsProxyModel);
- return d->ActionsWithNoShortcutVisible;
- }
- //-----------------------------------------------------------------------------
- void ctkSortFilterActionsProxyModel::setMenuActionsVisible(bool visible)
- {
- Q_D(ctkSortFilterActionsProxyModel);
- d->MenuActionsVisible = visible;
- this->invalidateFilter();
- }
- //-----------------------------------------------------------------------------
- bool ctkSortFilterActionsProxyModel::areMenuActionsVisible()const
- {
- Q_D(const ctkSortFilterActionsProxyModel);
- return d->MenuActionsVisible;
- }
- //-----------------------------------------------------------------------------
- bool ctkSortFilterActionsProxyModel::filterAcceptsRow(int source_row, const QModelIndex & source_parent) const
- {
- Q_D(const ctkSortFilterActionsProxyModel);
- QModelIndex shortcutIndex = this->sourceModel()->index(source_row, ctkActionsWidget::ShortcutColumn, source_parent);
- QStandardItem* shortcutItem = qobject_cast<QStandardItemModel*>(
- this->sourceModel())->itemFromIndex(shortcutIndex);
- QAction* action = shortcutItem ?
- qobject_cast<QAction*>(shortcutItem->data().value<QObject*>()) : 0;
- if (!action)
- {
- return true;
- }
- if (action->isSeparator())
- {
- return false;
- }
- if (action->text().isEmpty())
- {// not sure what the empty text actions are
- return false;
- }
- if (!d->ActionsWithNoShortcutVisible && shortcutItem->text().isEmpty())
- {
- return false;
- }
- if (!d->MenuActionsVisible && action->menu())
- {
- return false;
- }
- return true;
- }
- //---------------------------------------------------------------------------
- void ctkRichTextItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem & option,
- const QModelIndex &index) const
- {
- QStyleOptionViewItemV4 options = option;
- initStyleOption(&options, index);
- if (! Qt::mightBeRichText(options.text))
- {
- this->QStyledItemDelegate::paint(painter, option, index);
- return;
- }
- painter->save();
- QTextDocument doc;
- doc.setHtml(options.text);
- /* Call this to get the focus rect and selection background. */
- options.text = "";
- options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter, options.widget);
- /* Draw using our rich text document. */
- painter->translate(options.rect.left(), options.rect.top());
- QRect clip(0, 0, options.rect.width(), options.rect.height());
- doc.drawContents(painter, clip);
- painter->restore();
- }
- //---------------------------------------------------------------------------
- QSize ctkRichTextItemDelegate::sizeHint(const QStyleOptionViewItem & option,
- const QModelIndex & index)const
- {
- QStyleOptionViewItemV4 options = option;
- initStyleOption(&options, index);
- if (! Qt::mightBeRichText(options.text))
- {
- return this->QStyledItemDelegate::sizeHint(option, index);;
- }
- QTextDocument doc;
- doc.setHtml(options.text);
- doc.setTextWidth(options.rect.width());
- return QSize(doc.idealWidth(), doc.size().height());
- }
|