diff options
-rw-r--r-- | src/plugins/help/centralwidget.cpp | 13 | ||||
-rw-r--r-- | src/plugins/help/centralwidget.h | 5 | ||||
-rw-r--r-- | src/plugins/help/helpplugin.cpp | 8 | ||||
-rw-r--r-- | src/shared/help/indexwindow.cpp | 271 | ||||
-rw-r--r-- | src/shared/help/indexwindow.h | 65 |
5 files changed, 292 insertions, 70 deletions
diff --git a/src/plugins/help/centralwidget.cpp b/src/plugins/help/centralwidget.cpp index 23e4d98292..fa052a69d8 100644 --- a/src/plugins/help/centralwidget.cpp +++ b/src/plugins/help/centralwidget.cpp @@ -31,6 +31,7 @@ #include "helpviewer.h" #include "localhelpmanager.h" +#include "openpagesmanager.h" #include "topicchooser.h" #include <utils/qtcassert.h> @@ -76,10 +77,18 @@ CentralWidget *CentralWidget::instance() return gStaticCentralWidget; } +void CentralWidget::open(const QUrl &url, bool newPage) +{ + if (newPage) + OpenPagesManager::instance().createPage(url); + else + setSource(url); +} + void CentralWidget::showTopicChooser(const QMap<QString, QUrl> &links, - const QString &keyword) + const QString &keyword, bool newPage) { TopicChooser tc(this, keyword, links); if (tc.exec() == QDialog::Accepted) - setSource(tc.link()); + open(tc.link(), newPage); } diff --git a/src/plugins/help/centralwidget.h b/src/plugins/help/centralwidget.h index 37d310459e..808175a0c9 100644 --- a/src/plugins/help/centralwidget.h +++ b/src/plugins/help/centralwidget.h @@ -50,8 +50,11 @@ public: static CentralWidget *instance(); + void open(const QUrl &url, bool newPage = false); + public slots: - void showTopicChooser(const QMap<QString, QUrl> &links, const QString &key); + void showTopicChooser(const QMap<QString, QUrl> &links, const QString &key, + bool newPage = false); }; diff --git a/src/plugins/help/helpplugin.cpp b/src/plugins/help/helpplugin.cpp index 1a1a3e0bb1..429541278b 100644 --- a/src/plugins/help/helpplugin.cpp +++ b/src/plugins/help/helpplugin.cpp @@ -299,10 +299,10 @@ void HelpPlugin::setupUi() indexWindow->setWindowTitle(tr(SB_INDEX)); m_indexItem = new SideBarItem(indexWindow, QLatin1String(SB_INDEX)); - connect(indexWindow, SIGNAL(linkActivated(QUrl)), m_centralWidget, - SLOT(setSource(QUrl))); - connect(indexWindow, SIGNAL(linksActivated(QMap<QString,QUrl>,QString)), - m_centralWidget, SLOT(showTopicChooser(QMap<QString,QUrl>,QString))); + connect(indexWindow, &IndexWindow::linkActivated, + m_centralWidget, &CentralWidget::open); + connect(indexWindow, &IndexWindow::linksActivated, + m_centralWidget, &CentralWidget::showTopicChooser); QMap<QString, Command*> shortcutMap; QAction *action = new QAction(tr("Activate Index in Help mode"), m_splitter); diff --git a/src/shared/help/indexwindow.cpp b/src/shared/help/indexwindow.cpp index 529bfb2e83..6f5029a927 100644 --- a/src/shared/help/indexwindow.cpp +++ b/src/shared/help/indexwindow.cpp @@ -38,18 +38,19 @@ #include <utils/fancylineedit.h> #include <utils/hostosinfo.h> +#include <utils/navigationtreeview.h> #include <utils/styledbar.h> +#include <QAbstractItemModel> #include <QLayout> #include <QLabel> #include <QLineEdit> #include <QKeyEvent> #include <QMenu> #include <QContextMenuEvent> -#include <QListWidgetItem> #include <QHelpEngine> -#include <QHelpIndexWidget> +#include <QHelpIndexModel> using namespace Help::Internal; @@ -63,9 +64,10 @@ IndexWindow::IndexWindow() m_searchLineEdit->setPlaceholderText(QString()); m_searchLineEdit->setFiltering(true); setFocusProxy(m_searchLineEdit); - connect(m_searchLineEdit, SIGNAL(textChanged(QString)), this, - SLOT(filterIndices(QString))); + connect(m_searchLineEdit, &QLineEdit::textChanged, + this, &IndexWindow::filterIndices); m_searchLineEdit->installEventFilter(this); + m_searchLineEdit->setAttribute(Qt::WA_MacShowFocusRect, false); QLabel *l = new QLabel(tr("&Look for:")); l->setBuddy(m_searchLineEdit); @@ -83,20 +85,22 @@ IndexWindow::IndexWindow() toolbar->setLayout(tbLayout); layout->addWidget(toolbar); - QHelpEngine *engine = &LocalHelpManager::helpEngine(); - m_indexWidget = engine->indexWidget(); + QHelpIndexModel *indexModel = LocalHelpManager::helpEngine().indexModel(); + m_filteredIndexModel = new IndexFilterModel(this); + m_filteredIndexModel->setSourceModel(indexModel); + m_indexWidget = new Utils::NavigationTreeView(this); + m_indexWidget->setModel(m_filteredIndexModel); + m_indexWidget->setRootIsDecorated(false); + m_indexWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); m_indexWidget->installEventFilter(this); - connect(engine->indexModel(), SIGNAL(indexCreationStarted()), this, - SLOT(disableSearchLineEdit())); - connect(engine->indexModel(), SIGNAL(indexCreated()), this, - SLOT(enableSearchLineEdit())); - connect(m_indexWidget, SIGNAL(linkActivated(QUrl,QString)), this, - SIGNAL(linkActivated(QUrl))); - connect(m_indexWidget, SIGNAL(linksActivated(QMap<QString,QUrl>,QString)), - this, SIGNAL(linksActivated(QMap<QString,QUrl>,QString))); - connect(m_searchLineEdit, SIGNAL(returnPressed()), m_indexWidget, - SLOT(activateCurrentItem())); - m_indexWidget->setFrameStyle(QFrame::NoFrame); + connect(indexModel, &QHelpIndexModel::indexCreationStarted, + this, &IndexWindow::disableSearchLineEdit); + connect(indexModel, &QHelpIndexModel::indexCreated, + this, &IndexWindow::enableSearchLineEdit); + connect(m_indexWidget, &Utils::NavigationTreeView::activated, + this, [this](const QModelIndex &index) { open(index); }); + connect(m_searchLineEdit, &QLineEdit::returnPressed, + m_indexWidget, [this]() { open(m_indexWidget->currentIndex()); }); layout->addWidget(m_indexWidget); m_indexWidget->viewport()->installEventFilter(this); @@ -108,10 +112,15 @@ IndexWindow::~IndexWindow() void IndexWindow::filterIndices(const QString &filter) { + QModelIndex bestMatch; if (filter.contains(QLatin1Char('*'))) - m_indexWidget->filterIndices(filter, filter); + bestMatch = m_filteredIndexModel->filter(filter, filter); else - m_indexWidget->filterIndices(filter, QString()); + bestMatch = m_filteredIndexModel->filter(filter, QString()); + if (bestMatch.isValid()) { + m_indexWidget->setCurrentIndex(bestMatch); + m_indexWidget->scrollTo(bestMatch); + } } bool IndexWindow::eventFilter(QObject *obj, QEvent *e) @@ -150,9 +159,9 @@ bool IndexWindow::eventFilter(QObject *obj, QEvent *e) QAction *action = menu.exec(); if (curTab == action) - m_indexWidget->activateCurrentItem(); + open(idx); else if (newTab == action) - open(m_indexWidget, idx); + open(idx, true/*newPage*/); } } else if (m_indexWidget && obj == m_indexWidget->viewport() && e->type() == QEvent::MouseButtonRelease) { @@ -162,16 +171,10 @@ bool IndexWindow::eventFilter(QObject *obj, QEvent *e) Qt::MouseButtons button = mouseEvent->button(); if (((button == Qt::LeftButton) && (mouseEvent->modifiers() & Qt::ControlModifier)) || (button == Qt::MidButton)) { - open(m_indexWidget, idx); + open(idx); } } } - else if (Utils::HostOsInfo::isMacHost() && obj == m_indexWidget - && e->type() == QEvent::KeyPress) { - QKeyEvent *ke = static_cast<QKeyEvent*>(e); - if (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter) - m_indexWidget->activateCurrentItem(); - } return QWidget::eventFilter(obj, e); } @@ -187,37 +190,203 @@ void IndexWindow::disableSearchLineEdit() m_searchLineEdit->setDisabled(true); } -void IndexWindow::setSearchLineEditText(const QString &text) +void IndexWindow::open(const QModelIndex &index, bool newPage) { - m_searchLineEdit->setText(text); + QString keyword = m_filteredIndexModel->data(index, Qt::DisplayRole).toString(); + QMap<QString, QUrl> links = LocalHelpManager::helpEngine().indexModel()->linksForKeyword(keyword); + + if (links.size() == 1) { + emit linkActivated(links.first(), newPage); + } else if (links.size() > 1) { + emit linksActivated(links, keyword, newPage); + } } -QString IndexWindow::searchLineEditText() const +Qt::DropActions IndexFilterModel::supportedDragActions() const { - return m_searchLineEdit->text(); + return sourceModel()->supportedDragActions(); } -void IndexWindow::open(QHelpIndexWidget* indexWidget, const QModelIndex &index) +QModelIndex IndexFilterModel::index(int row, int column, const QModelIndex &parent) const { - QHelpIndexModel *model = qobject_cast<QHelpIndexModel*>(indexWidget->model()); - if (model) { - QString keyword = model->data(index, Qt::DisplayRole).toString(); - QMap<QString, QUrl> links = model->linksForKeyword(keyword); + Q_UNUSED(parent) + return createIndex(row, column); +} - QUrl url; - if (links.count() > 1) { - TopicChooser tc(this, keyword, links); - if (tc.exec() == QDialog::Accepted) - url = tc.link(); - } else if (links.count() == 1) { - url = links.constBegin().value(); - } else { - return; - } +QModelIndex IndexFilterModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child) + return QModelIndex(); +} + +int IndexFilterModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + return m_toSource.size(); +} + +int IndexFilterModel::columnCount(const QModelIndex &parent) const +{ + return sourceModel()->columnCount(mapToSource(parent)); +} - if (!HelpViewer::canOpenPage(url.path())) - CentralWidget::instance()->setSource(url); - else - OpenPagesManager::instance().createPage(url); +void IndexFilterModel::setSourceModel(QAbstractItemModel *sm) +{ + QAbstractItemModel *previousModel = sourceModel(); + if (previousModel) { + disconnect(previousModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(sourceDataChanged(QModelIndex,QModelIndex))); + disconnect(previousModel, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(sourceRowsInserted(QModelIndex,int,int))); + disconnect(previousModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(sourceRowsRemoved(QModelIndex,int,int))); + disconnect(previousModel, SIGNAL(modelReset()), + this, SLOT(sourceModelReset())); + } + QAbstractProxyModel::setSourceModel(sm); + if (sm) { + connect(sm, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(sourceDataChanged(QModelIndex,QModelIndex))); + connect(sm, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(sourceRowsInserted(QModelIndex,int,int))); + connect(sm, SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(sourceRowsRemoved(QModelIndex,int,int))); + connect(sm, SIGNAL(modelReset()), + this, SLOT(sourceModelReset())); } + filter(m_filter, m_wildcard); +} + +QModelIndex IndexFilterModel::sibling(int row, int column, const QModelIndex &idx) const +{ + return QAbstractItemModel::sibling(row, column, idx); +} + +Qt::ItemFlags IndexFilterModel::flags(const QModelIndex &index) const +{ + Q_UNUSED(index) + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; +} + +IndexFilterModel::IndexFilterModel(QObject *parent) + : QAbstractProxyModel(parent) +{ +} + +QModelIndex IndexFilterModel::filter(const QString &filter, const QString &wildcard) +{ + beginResetModel(); + + m_filter = filter; + m_wildcard = wildcard; + m_toSource.clear(); + + // adapted copy from QHelpIndexModel + + if (filter.isEmpty() && wildcard.isEmpty()) { + int count = sourceModel()->rowCount(); + m_toSource.reserve(count); + for (int i = 0; i < count; ++i) + m_toSource.append(i); + endResetModel(); + return index(0, 0); + } + + QHelpIndexModel *indexModel = qobject_cast<QHelpIndexModel *>(sourceModel()); + const QStringList indices = indexModel->stringList(); + int goodMatch = -1; + int perfectMatch = -1; + + if (!wildcard.isEmpty()) { + QRegExp regExp(wildcard, Qt::CaseInsensitive); + regExp.setPatternSyntax(QRegExp::Wildcard); + int i = 0; + foreach (const QString &index, indices) { + if (index.contains(regExp)) { + m_toSource.append(i); + if (perfectMatch == -1 && index.startsWith(filter, Qt::CaseInsensitive)) { + if (goodMatch == -1) + goodMatch = m_toSource.size() - 1; + if (filter.length() == index.length()){ + perfectMatch = m_toSource.size() - 1; + } + } else if (perfectMatch > -1 && index == filter) { + perfectMatch = m_toSource.size() - 1; + } + } + ++i; + } + } else { + int i = 0; + foreach (const QString &index, indices) { + if (index.contains(filter, Qt::CaseInsensitive)) { + m_toSource.append(i); + if (perfectMatch == -1 && index.startsWith(filter, Qt::CaseInsensitive)) { + if (goodMatch == -1) + goodMatch = m_toSource.size() - 1; + if (filter.length() == index.length()){ + perfectMatch = m_toSource.size() - 1; + } + } else if (perfectMatch > -1 && index == filter) { + perfectMatch = m_toSource.size() - 1; + } + } + ++i; + } + } + + if (perfectMatch == -1) + perfectMatch = qMax(0, goodMatch); + + endResetModel(); + return index(perfectMatch, 0, QModelIndex()); +} + +QModelIndex IndexFilterModel::mapToSource(const QModelIndex &proxyIndex) const +{ + if (!proxyIndex.isValid() || proxyIndex.parent().isValid() || proxyIndex.row() >= m_toSource.size()) + return QModelIndex(); + return index(m_toSource.at(proxyIndex.row()), proxyIndex.column()); +} + +QModelIndex IndexFilterModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + if (!sourceIndex.isValid() || sourceIndex.parent().isValid()) + return QModelIndex(); + int i = m_toSource.indexOf(sourceIndex.row()); + if (i < 0) + return QModelIndex(); + return index(i, sourceIndex.column()); +} + +void IndexFilterModel::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) +{ + QModelIndex topLeftIndex = mapFromSource(topLeft); + if (!topLeftIndex.isValid()) + topLeftIndex = index(0, topLeft.column()); + QModelIndex bottomRightIndex = mapFromSource(bottomRight); + if (!bottomRightIndex.isValid()) + bottomRightIndex = index(0, bottomRight.column()); + emit dataChanged(topLeftIndex, bottomRightIndex); +} + +void IndexFilterModel::sourceRowsRemoved(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(parent) + Q_UNUSED(start) + Q_UNUSED(end) + filter(m_filter, m_wildcard); +} + +void IndexFilterModel::sourceRowsInserted(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(parent) + Q_UNUSED(start) + Q_UNUSED(end) + filter(m_filter, m_wildcard); +} +void IndexFilterModel::sourceModelReset() +{ + filter(m_filter, m_wildcard); } diff --git a/src/shared/help/indexwindow.h b/src/shared/help/indexwindow.h index 1e5f17d53b..0be2a52d88 100644 --- a/src/shared/help/indexwindow.h +++ b/src/shared/help/indexwindow.h @@ -30,15 +30,57 @@ #ifndef INDEXWINDOW_H #define INDEXWINDOW_H +#include <QAbstractProxyModel> +#include <QList> #include <QUrl> #include <QWidget> QT_BEGIN_NAMESPACE -class QHelpIndexWidget; +class QHelpIndexModel; class QModelIndex; QT_END_NAMESPACE -namespace Utils { class FancyLineEdit; } +namespace Utils { +class FancyLineEdit; +class NavigationTreeView; +} + +namespace Help { +namespace Internal { + +class IndexFilterModel : public QAbstractProxyModel +{ + Q_OBJECT + +public: + IndexFilterModel(QObject *parent); + + QModelIndex filter(const QString &filter, const QString &wildcard); + QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + Qt::DropActions supportedDragActions() const; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &child) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + void setSourceModel(QAbstractItemModel *sm); + + // QAbstractProxyModel::sibling is broken in Qt 5 + QModelIndex sibling(int row, int column, const QModelIndex &idx) const; + + Qt::ItemFlags flags(const QModelIndex &index) const; +private slots: + void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void sourceRowsRemoved(const QModelIndex &parent, int start, int end); + void sourceRowsInserted(const QModelIndex &parent, int start, int end); + void sourceModelReset(); + +private: + QString m_filter; + QString m_wildcard; + QList<int> m_toSource; +}; class IndexWindow : public QWidget { @@ -48,25 +90,24 @@ public: IndexWindow(); ~IndexWindow(); - void setSearchLineEditText(const QString &text); - QString searchLineEditText() const; - signals: - void linkActivated(const QUrl &link); + void linkActivated(const QUrl &link, bool newPage); void linksActivated(const QMap<QString, QUrl> &links, - const QString &keyword); + const QString &keyword, bool newPage); -private slots: +private: void filterIndices(const QString &filter); void enableSearchLineEdit(); void disableSearchLineEdit(); - -private: bool eventFilter(QObject *obj, QEvent *e); - void open(QHelpIndexWidget* indexWidget, const QModelIndex &index); + void open(const QModelIndex &index, bool newPage = false); Utils::FancyLineEdit *m_searchLineEdit; - QHelpIndexWidget *m_indexWidget; + Utils::NavigationTreeView *m_indexWidget; + IndexFilterModel *m_filteredIndexModel; }; +} // Internal +} // Help + #endif // INDEXWINDOW_H |