forked from qt-creator/qt-creator
Help: Make it possible to create multiple index views
This unfortunately means doing the filtering by hand, because filtering on the QHelpIndexModel would be shared between multiple views. Change-Id: Iae38952a92dbb1b4a9685aea6f057d96f0d0784f Reviewed-by: Eike Ziller <eike.ziller@digia.com>
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
||||
QString IndexWindow::searchLineEditText() const
|
||||
{
|
||||
return m_searchLineEdit->text();
|
||||
}
|
||||
|
||||
void IndexWindow::open(QHelpIndexWidget* indexWidget, const QModelIndex &index)
|
||||
{
|
||||
QHelpIndexModel *model = qobject_cast<QHelpIndexModel*>(indexWidget->model());
|
||||
if (model) {
|
||||
QString keyword = model->data(index, Qt::DisplayRole).toString();
|
||||
QMap<QString, QUrl> links = model->linksForKeyword(keyword);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!HelpViewer::canOpenPage(url.path()))
|
||||
CentralWidget::instance()->setSource(url);
|
||||
else
|
||||
OpenPagesManager::instance().createPage(url);
|
||||
if (links.size() == 1) {
|
||||
emit linkActivated(links.first(), newPage);
|
||||
} else if (links.size() > 1) {
|
||||
emit linksActivated(links, keyword, newPage);
|
||||
}
|
||||
}
|
||||
|
||||
Qt::DropActions IndexFilterModel::supportedDragActions() const
|
||||
{
|
||||
return sourceModel()->supportedDragActions();
|
||||
}
|
||||
|
||||
QModelIndex IndexFilterModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return createIndex(row, column);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user