forked from qt-creator/qt-creator
DocSettingsPage: Add a filter line edit for the registered documentation.
Change the item view to a QListView and use a model derived from QAbstractListModel keeping a QVector of entries. Use a QSortFilterProxyModel for filtering. This makes cleaning old documentation easier. Change-Id: I3a0781e0ddff3d0c00126a1b4effe1e1c93e354b Reviewed-by: Eike Ziller <eike.ziller@theqtcompany.com>
This commit is contained in:
@@ -28,13 +28,104 @@
|
|||||||
|
|
||||||
#include <coreplugin/helpmanager.h>
|
#include <coreplugin/helpmanager.h>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
using namespace Core;
|
using namespace Core;
|
||||||
|
|
||||||
|
namespace Help {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class DocEntry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString name;
|
||||||
|
QString fileName;
|
||||||
|
QString nameSpace;
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool operator<(const DocEntry &d1, const DocEntry &d2)
|
||||||
|
{ return d1.name < d2.name; }
|
||||||
|
|
||||||
|
static DocEntry createEntry(const QString &nameSpace, const QString &fileName, bool userManaged)
|
||||||
|
{
|
||||||
|
DocEntry result;
|
||||||
|
result.name = userManaged ? nameSpace : DocSettingsPage::tr("%1 (auto-detected)").arg(nameSpace);
|
||||||
|
result.fileName = fileName;
|
||||||
|
result.nameSpace = nameSpace;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DocModel : public QAbstractListModel {
|
||||||
|
public:
|
||||||
|
typedef QVector<DocEntry> DocEntries;
|
||||||
|
|
||||||
|
explicit DocModel(const DocEntries &e = DocEntries(), QObject *parent = nullptr)
|
||||||
|
: QAbstractListModel(parent), m_docEntries(e) {}
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex & = QModelIndex()) const override { return m_docEntries.size(); }
|
||||||
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
|
|
||||||
|
void insertEntry(const DocEntry &e);
|
||||||
|
void removeAt(int row);
|
||||||
|
|
||||||
|
const DocEntry &entryAt(int row) const { return m_docEntries.at(row); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
DocEntries m_docEntries;
|
||||||
|
};
|
||||||
|
|
||||||
|
QVariant DocModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
QVariant result;
|
||||||
|
const int row = index.row();
|
||||||
|
if (index.isValid() && row < m_docEntries.size()) {
|
||||||
|
switch (role) {
|
||||||
|
case Qt::DisplayRole:
|
||||||
|
result = QVariant(m_docEntries.at(row).name);
|
||||||
|
break;
|
||||||
|
case Qt::ToolTipRole:
|
||||||
|
result = QVariant(QDir::toNativeSeparators(m_docEntries.at(row).fileName));
|
||||||
|
break;
|
||||||
|
case Qt::UserRole:
|
||||||
|
result = QVariant(m_docEntries.at(row).nameSpace);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DocModel::insertEntry(const DocEntry &e)
|
||||||
|
{
|
||||||
|
const auto it = std::lower_bound(m_docEntries.begin(), m_docEntries.end(), e);
|
||||||
|
const int index = int(it - m_docEntries.begin());
|
||||||
|
beginInsertRows(QModelIndex(), index, index);
|
||||||
|
m_docEntries.insert(it, e);
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DocModel::removeAt(int row)
|
||||||
|
{
|
||||||
|
beginRemoveRows(QModelIndex(), row, row);
|
||||||
|
m_docEntries.removeAt(row);
|
||||||
|
endRemoveRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Help
|
||||||
|
|
||||||
using namespace Help::Internal;
|
using namespace Help::Internal;
|
||||||
|
|
||||||
DocSettingsPage::DocSettingsPage()
|
DocSettingsPage::DocSettingsPage()
|
||||||
@@ -49,25 +140,36 @@ DocSettingsPage::DocSettingsPage()
|
|||||||
QWidget *DocSettingsPage::widget()
|
QWidget *DocSettingsPage::widget()
|
||||||
{
|
{
|
||||||
if (!m_widget) {
|
if (!m_widget) {
|
||||||
m_widget = new QWidget;
|
|
||||||
m_ui.setupUi(m_widget);
|
|
||||||
|
|
||||||
connect(m_ui.addButton, SIGNAL(clicked()), this, SLOT(addDocumentation()));
|
|
||||||
connect(m_ui.removeButton, SIGNAL(clicked()), this, SLOT(removeDocumentation()));
|
|
||||||
|
|
||||||
m_ui.docsListWidget->installEventFilter(this);
|
|
||||||
|
|
||||||
const QStringList nameSpaces = HelpManager::registeredNamespaces();
|
const QStringList nameSpaces = HelpManager::registeredNamespaces();
|
||||||
const QSet<QString> userDocumentationPaths = HelpManager::userDocumentationPaths();
|
const QSet<QString> userDocumentationPaths = HelpManager::userDocumentationPaths();
|
||||||
|
DocModel::DocEntries entries;
|
||||||
|
entries.reserve(nameSpaces.size());
|
||||||
foreach (const QString &nameSpace, nameSpaces) {
|
foreach (const QString &nameSpace, nameSpaces) {
|
||||||
const QString filePath = HelpManager::fileFromNamespace(nameSpace);
|
const QString filePath = HelpManager::fileFromNamespace(nameSpace);
|
||||||
bool user = userDocumentationPaths.contains(filePath);
|
bool user = userDocumentationPaths.contains(filePath);
|
||||||
addItem(nameSpace, filePath, user);
|
entries.append(createEntry(nameSpace, filePath, user));
|
||||||
m_filesToRegister.insert(nameSpace, filePath);
|
m_filesToRegister.insert(nameSpace, filePath);
|
||||||
m_filesToRegisterUserManaged.insert(nameSpace, user);
|
m_filesToRegisterUserManaged.insert(nameSpace, user);
|
||||||
}
|
}
|
||||||
|
std::stable_sort(entries.begin(), entries.end());
|
||||||
|
|
||||||
m_filesToUnregister.clear();
|
m_filesToUnregister.clear();
|
||||||
|
|
||||||
|
m_widget = new QWidget;
|
||||||
|
m_ui.setupUi(m_widget);
|
||||||
|
m_model = new DocModel(entries, m_ui.docsListView);
|
||||||
|
m_proxyModel = new QSortFilterProxyModel(m_ui.docsListView);
|
||||||
|
m_proxyModel->setSourceModel(m_model);
|
||||||
|
m_ui.docsListView->setModel(m_proxyModel);
|
||||||
|
m_ui.filterLineEdit->setFiltering(true);
|
||||||
|
connect(m_ui.filterLineEdit, &QLineEdit::textChanged,
|
||||||
|
m_proxyModel, &QSortFilterProxyModel::setFilterFixedString);
|
||||||
|
|
||||||
|
connect(m_ui.addButton, &QAbstractButton::clicked, this, &DocSettingsPage::addDocumentation);
|
||||||
|
connect(m_ui.removeButton, &QAbstractButton::clicked, this,
|
||||||
|
[this] () { removeDocumentation(currentSelection()); });
|
||||||
|
|
||||||
|
m_ui.docsListView->installEventFilter(this);
|
||||||
}
|
}
|
||||||
return m_widget;
|
return m_widget;
|
||||||
}
|
}
|
||||||
@@ -97,7 +199,8 @@ void DocSettingsPage::addDocumentation()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
addItem(nameSpace, file, true/*user managed*/);
|
m_model->insertEntry(createEntry(nameSpace, file, true /* user managed */));
|
||||||
|
|
||||||
m_filesToRegister.insert(nameSpace, filePath);
|
m_filesToRegister.insert(nameSpace, filePath);
|
||||||
m_filesToRegisterUserManaged.insert(nameSpace, true/*user managed*/);
|
m_filesToRegisterUserManaged.insert(nameSpace, true/*user managed*/);
|
||||||
|
|
||||||
@@ -142,11 +245,6 @@ void DocSettingsPage::addDocumentation()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DocSettingsPage::removeDocumentation()
|
|
||||||
{
|
|
||||||
removeDocumentation(m_ui.docsListWidget->selectedItems());
|
|
||||||
}
|
|
||||||
|
|
||||||
void DocSettingsPage::apply()
|
void DocSettingsPage::apply()
|
||||||
{
|
{
|
||||||
HelpManager::unregisterDocumentation(m_filesToUnregister.keys());
|
HelpManager::unregisterDocumentation(m_filesToUnregister.keys());
|
||||||
@@ -169,14 +267,14 @@ void DocSettingsPage::finish()
|
|||||||
|
|
||||||
bool DocSettingsPage::eventFilter(QObject *object, QEvent *event)
|
bool DocSettingsPage::eventFilter(QObject *object, QEvent *event)
|
||||||
{
|
{
|
||||||
if (object != m_ui.docsListWidget)
|
if (object != m_ui.docsListView)
|
||||||
return IOptionsPage::eventFilter(object, event);
|
return IOptionsPage::eventFilter(object, event);
|
||||||
|
|
||||||
if (event->type() == QEvent::KeyPress) {
|
if (event->type() == QEvent::KeyPress) {
|
||||||
QKeyEvent *ke = static_cast<QKeyEvent*>(event);
|
QKeyEvent *ke = static_cast<QKeyEvent*>(event);
|
||||||
switch (ke->key()) {
|
switch (ke->key()) {
|
||||||
case Qt::Key_Delete:
|
case Qt::Key_Delete:
|
||||||
removeDocumentation(m_ui.docsListWidget->selectedItems());
|
removeDocumentation(currentSelection());
|
||||||
break;
|
break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
@@ -185,32 +283,32 @@ bool DocSettingsPage::eventFilter(QObject *object, QEvent *event)
|
|||||||
return IOptionsPage::eventFilter(object, event);
|
return IOptionsPage::eventFilter(object, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DocSettingsPage::removeDocumentation(const QList<QListWidgetItem*> &items)
|
void DocSettingsPage::removeDocumentation(const QList<QModelIndex> &items)
|
||||||
{
|
{
|
||||||
if (items.isEmpty())
|
if (items.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int row = 0;
|
for (int i = items.size() - 1; i >= 0; --i) {
|
||||||
foreach (QListWidgetItem* item, items) {
|
const int row = items.at(i).row();
|
||||||
const QString nameSpace = item->data(Qt::UserRole).toString();
|
const QString nameSpace = m_model->entryAt(row).nameSpace;
|
||||||
|
|
||||||
m_filesToRegister.remove(nameSpace);
|
m_filesToRegister.remove(nameSpace);
|
||||||
m_filesToRegisterUserManaged.remove(nameSpace);
|
m_filesToRegisterUserManaged.remove(nameSpace);
|
||||||
m_filesToUnregister.insertMulti(nameSpace, QDir::cleanPath(HelpManager::fileFromNamespace(nameSpace)));
|
m_filesToUnregister.insertMulti(nameSpace, QDir::cleanPath(HelpManager::fileFromNamespace(nameSpace)));
|
||||||
|
|
||||||
row = m_ui.docsListWidget->row(item);
|
m_model->removeAt(row);
|
||||||
delete m_ui.docsListWidget->takeItem(row);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_ui.docsListWidget->setCurrentRow(qMax(row - 1, 0),
|
const int newlySelectedRow = qMax(items.constFirst().row() - 1, 0);
|
||||||
QItemSelectionModel::ClearAndSelect);
|
const QModelIndex index = m_proxyModel->mapFromSource(m_model->index(newlySelectedRow));
|
||||||
|
m_ui.docsListView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DocSettingsPage::addItem(const QString &nameSpace, const QString &fileName, bool userManaged)
|
QList<QModelIndex> DocSettingsPage::currentSelection() const
|
||||||
{
|
{
|
||||||
const QString name = userManaged ? nameSpace : tr("%1 (auto-detected)").arg(nameSpace);
|
QModelIndexList result;
|
||||||
QListWidgetItem* item = new QListWidgetItem(name);
|
Q_ASSERT(!m_widget.isNull());
|
||||||
item->setToolTip(fileName);
|
foreach (const QModelIndex &index, m_ui.docsListView->selectionModel()->selectedRows())
|
||||||
item->setData(Qt::UserRole, nameSpace);
|
result.append(m_proxyModel->mapToSource(index));
|
||||||
m_ui.docsListWidget->addItem(item);
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,11 +29,17 @@
|
|||||||
#include "ui_docsettingspage.h"
|
#include "ui_docsettingspage.h"
|
||||||
#include <coreplugin/dialogs/ioptionspage.h>
|
#include <coreplugin/dialogs/ioptionspage.h>
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
|
|
||||||
|
QT_FORWARD_DECLARE_CLASS(QSortFilterProxyModel)
|
||||||
|
QT_FORWARD_DECLARE_CLASS(QModelIndex)
|
||||||
|
|
||||||
namespace Help {
|
namespace Help {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
class DocModel;
|
||||||
|
|
||||||
class DocSettingsPage : public Core::IOptionsPage
|
class DocSettingsPage : public Core::IOptionsPage
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -47,14 +53,14 @@ public:
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void addDocumentation();
|
void addDocumentation();
|
||||||
void removeDocumentation();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool eventFilter(QObject *object, QEvent *event);
|
bool eventFilter(QObject *object, QEvent *event);
|
||||||
void removeDocumentation(const QList<QListWidgetItem *> &items);
|
void removeDocumentation(const QList<QModelIndex> &items);
|
||||||
void addItem(const QString &nameSpace, const QString &fileName, bool userManaged);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QList<QModelIndex> currentSelection() const;
|
||||||
|
|
||||||
Ui::DocSettingsPage m_ui;
|
Ui::DocSettingsPage m_ui;
|
||||||
QPointer<QWidget> m_widget;
|
QPointer<QWidget> m_widget;
|
||||||
|
|
||||||
@@ -64,6 +70,9 @@ private:
|
|||||||
NameSpaceToPathHash m_filesToRegister;
|
NameSpaceToPathHash m_filesToRegister;
|
||||||
QHash<QString, bool> m_filesToRegisterUserManaged;
|
QHash<QString, bool> m_filesToRegisterUserManaged;
|
||||||
NameSpaceToPathHash m_filesToUnregister;
|
NameSpaceToPathHash m_filesToUnregister;
|
||||||
|
|
||||||
|
QSortFilterProxyModel *m_proxyModel = nullptr;
|
||||||
|
DocModel *m_model = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Help
|
} // namespace Help
|
||||||
|
|||||||
@@ -21,18 +21,22 @@
|
|||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QListWidget" name="docsListWidget">
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="Utils::FancyLineEdit" name="filterLineEdit"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QListView" name="docsListView">
|
||||||
<property name="selectionMode">
|
<property name="selectionMode">
|
||||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="uniformItemSizes">
|
<property name="uniformItemSizes">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="sortingEnabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="_4">
|
<layout class="QVBoxLayout" name="_4">
|
||||||
<item>
|
<item>
|
||||||
@@ -69,6 +73,13 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>Utils::FancyLineEdit</class>
|
||||||
|
<extends>QLineEdit</extends>
|
||||||
|
<header location="global">utils/fancylineedit.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
|||||||
Reference in New Issue
Block a user