summaryrefslogtreecommitdiffstats
path: root/src/libs/installer
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/installer')
-rw-r--r--src/libs/installer/aspectratiolabel.cpp8
-rw-r--r--src/libs/installer/componentmodel.cpp31
-rw-r--r--src/libs/installer/componentmodel.h4
-rw-r--r--src/libs/installer/componentselectionpage_p.cpp91
-rw-r--r--src/libs/installer/componentselectionpage_p.h6
-rw-r--r--src/libs/installer/componentsortfilterproxymodel.cpp152
-rw-r--r--src/libs/installer/componentsortfilterproxymodel.h63
-rw-r--r--src/libs/installer/fileutils.cpp32
-rw-r--r--src/libs/installer/installer.pro2
9 files changed, 332 insertions, 57 deletions
diff --git a/src/libs/installer/aspectratiolabel.cpp b/src/libs/installer/aspectratiolabel.cpp
index a9af93a55..c8c3c1693 100644
--- a/src/libs/installer/aspectratiolabel.cpp
+++ b/src/libs/installer/aspectratiolabel.cpp
@@ -81,9 +81,11 @@ QSize AspectRatioLabel::sizeHint() const
*/
QPixmap AspectRatioLabel::scaledPixmap() const
{
- return m_pixmap.isNull()
- ? QPixmap()
- : m_pixmap.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
+ if (m_pixmap.isNull())
+ return QPixmap();
+
+ return m_pixmap.scaled(size() * m_pixmap.devicePixelRatio(),
+ Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
/*!
diff --git a/src/libs/installer/componentmodel.cpp b/src/libs/installer/componentmodel.cpp
index 642828ad7..56ee57fc6 100644
--- a/src/libs/installer/componentmodel.cpp
+++ b/src/libs/installer/componentmodel.cpp
@@ -101,7 +101,6 @@ ComponentModel::ComponentModel(int columns, PackageManagerCore *core)
, m_modelState(DefaultChecked)
{
m_headerData.insert(0, columns, QVariant());
- connect(this, &QAbstractItemModel::modelReset, this, &ComponentModel::slotModelReset);
}
/*!
@@ -417,6 +416,7 @@ void ComponentModel::setRootComponents(QList<QInstaller::Component*> rootCompone
m_rootComponentList.append(component);
}
endResetModel();
+ postModelReset();
}
/*!
@@ -459,7 +459,16 @@ void ComponentModel::setCheckedState(QInstaller::ComponentModel::ModelStateFlag
// -- private slots
-void ComponentModel::slotModelReset()
+void ComponentModel::onVirtualStateChanged()
+{
+ // If the virtual state of a component changes, force a reset of the component model.
+ setRootComponents(m_core->components(PackageManagerCore::ComponentType::Root));
+}
+
+
+// -- private
+
+void ComponentModel::postModelReset()
{
ComponentList components = m_rootComponentList;
if (!m_core->isUpdater()) {
@@ -485,15 +494,6 @@ void ComponentModel::slotModelReset()
updateAndEmitModelState(); // update the internal state
}
-void ComponentModel::onVirtualStateChanged()
-{
- // If the virtual state of a component changes, force a reset of the component model.
- setRootComponents(m_core->components(PackageManagerCore::ComponentType::Root));
-}
-
-
-// -- private
-
void ComponentModel::updateAndEmitModelState()
{
m_modelState = ComponentModel::DefaultChecked;
@@ -511,15 +511,6 @@ void ComponentModel::updateAndEmitModelState()
}
emit checkStateChanged(m_modelState);
-
- foreach (const Component *component, m_rootComponentList) {
- emit dataChanged(indexFromComponentName(component->treeName()),
- indexFromComponentName(component->treeName()));
- QList<Component *> children = component->childItems();
- foreach (const Component *child, children)
- emit dataChanged(indexFromComponentName(child->treeName()),
- indexFromComponentName(child->treeName()));
- }
}
void ComponentModel::collectComponents(Component *const component, const QModelIndex &parent) const
diff --git a/src/libs/installer/componentmodel.h b/src/libs/installer/componentmodel.h
index 8a9fbf884..e5cd2c57d 100644
--- a/src/libs/installer/componentmodel.h
+++ b/src/libs/installer/componentmodel.h
@@ -1,6 +1,6 @@
/**************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
@@ -94,10 +94,10 @@ Q_SIGNALS:
void checkStateChanged(QInstaller::ComponentModel::ModelState state);
private Q_SLOTS:
- void slotModelReset();
void onVirtualStateChanged();
private:
+ void postModelReset();
void updateAndEmitModelState();
void collectComponents(Component *const component, const QModelIndex &parent) const;
QSet<QModelIndex> updateCheckedState(const ComponentSet &components, Qt::CheckState state);
diff --git a/src/libs/installer/componentselectionpage_p.cpp b/src/libs/installer/componentselectionpage_p.cpp
index a180888d1..7717f4cf3 100644
--- a/src/libs/installer/componentselectionpage_p.cpp
+++ b/src/libs/installer/componentselectionpage_p.cpp
@@ -50,6 +50,7 @@
#include <QStackedLayout>
#include <QStackedWidget>
#include <QToolBox>
+#include <QLineEdit>
namespace QInstaller {
@@ -71,8 +72,11 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
, m_descriptionBaseWidget(nullptr)
, m_categoryWidget(Q_NULLPTR)
, m_categoryLayoutVisible(false)
+ , m_proxyModel(new ComponentSortFilterProxyModel(q))
{
m_treeView->setObjectName(QLatin1String("ComponentsTreeView"));
+ m_proxyModel->setRecursiveFilteringEnabled(true);
+ m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
m_descriptionBaseWidget = new QWidget(q);
m_descriptionBaseWidget->setObjectName(QLatin1String("DescriptionBaseWidget"));
@@ -155,9 +159,18 @@ ComponentSelectionPagePrivate::ComponentSelectionPagePrivate(ComponentSelectionP
metaLayout->addWidget(m_progressBar);
metaLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding));
+ m_searchLineEdit = new QLineEdit(q);
+ m_searchLineEdit->setObjectName(QLatin1String("SearchLineEdit"));
+ m_searchLineEdit->setPlaceholderText(ComponentSelectionPage::tr("Search"));
+ m_searchLineEdit->setClearButtonEnabled(true);
+ connect(m_searchLineEdit, &QLineEdit::textChanged,
+ this, &ComponentSelectionPagePrivate::setSearchPattern);
+ connect(q, &ComponentSelectionPage::entered, m_searchLineEdit, &QLineEdit::clear);
+
QVBoxLayout *treeViewVLayout = new QVBoxLayout;
treeViewVLayout->setObjectName(QLatin1String("TreeviewLayout"));
treeViewVLayout->addWidget(m_treeView, 3);
+ treeViewVLayout->addWidget(m_searchLineEdit);
QWidget *mainStackedWidget = new QWidget();
m_mainGLayout = new QGridLayout(mainStackedWidget);
@@ -292,15 +305,11 @@ void ComponentSelectionPagePrivate::updateTreeView()
this, &ComponentSelectionPagePrivate::currentSelectedChanged);
}
+ m_searchLineEdit->setVisible(!m_core->isUpdater());
m_currentModel = m_core->isUpdater() ? m_updaterModel : m_allModel;
- m_treeView->setModel(m_currentModel);
- m_treeView->setExpanded(m_currentModel->index(0, 0), true);
- foreach (Component *component, m_core->components(PackageManagerCore::ComponentType::All)) {
- if (component->isExpandedByDefault()) {
- const QModelIndex index = m_currentModel->indexFromComponentName(component->treeName());
- m_treeView->setExpanded(index, true);
- }
- }
+ m_proxyModel->setSourceModel(m_currentModel);
+ m_treeView->setModel(m_proxyModel);
+ expandDefault();
const bool installActionColumnVisible = m_core->settings().installActionColumnVisible();
if (!installActionColumnVisible)
@@ -341,7 +350,44 @@ void ComponentSelectionPagePrivate::updateTreeView()
connect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged,
this, &ComponentSelectionPagePrivate::currentSelectedChanged);
- m_treeView->setCurrentIndex(m_currentModel->index(0, 0));
+ m_treeView->setCurrentIndex(m_proxyModel->index(0, 0));
+}
+
+/*!
+ Expands components that should be expanded by default.
+*/
+void ComponentSelectionPagePrivate::expandDefault()
+{
+ m_treeView->setExpanded(m_proxyModel->index(0, 0), true);
+ foreach (auto *component, m_core->components(PackageManagerCore::ComponentType::All)) {
+ if (component->isExpandedByDefault()) {
+ const QModelIndex index = m_proxyModel->mapFromSource(
+ m_currentModel->indexFromComponentName(component->treeName()));
+ m_treeView->setExpanded(index, true);
+ }
+ }
+}
+
+/*!
+ Expands components that were accepted by proxy models filter.
+*/
+void ComponentSelectionPagePrivate::expandSearchResults()
+{
+ // Expand parents of root indexes accepted by filter
+ const QVector<QModelIndex> acceptedIndexes = m_proxyModel->directlyAcceptedIndexes();
+ for (auto proxyModelIndex : acceptedIndexes) {
+ if (!proxyModelIndex.isValid())
+ continue;
+
+ QModelIndex index = proxyModelIndex.parent();
+ while (index.isValid()) {
+ if (m_treeView->isExpanded(index))
+ break; // Multiple direct matches in a branch, can be skipped
+
+ m_treeView->expand(index);
+ index = index.parent();
+ }
+ }
}
void ComponentSelectionPagePrivate::currentSelectedChanged(const QModelIndex &current)
@@ -351,12 +397,12 @@ void ComponentSelectionPagePrivate::currentSelectedChanged(const QModelIndex &cu
m_sizeLabel->setText(QString());
- QString description = m_currentModel->data(m_currentModel->index(current.row(),
+ QString description = m_proxyModel->data(m_proxyModel->index(current.row(),
ComponentModelHelper::NameColumn, current.parent()), Qt::ToolTipRole).toString();
m_descriptionLabel->setText(description);
- Component *component = m_currentModel->componentFromIndex(current);
+ Component *component = m_currentModel->componentFromIndex(m_proxyModel->mapToSource(current));
if ((m_core->isUninstaller()) || (!component))
return;
@@ -429,6 +475,7 @@ void ComponentSelectionPagePrivate::fetchRepositoryCategories()
QLatin1String("FailToFetchPackages"), tr("Error"), m_core->error());
}
updateWidgetVisibility(false);
+ m_searchLineEdit->text().isEmpty() ? expandDefault() : expandSearchResults();
}
void ComponentSelectionPagePrivate::customButtonClicked(int which)
@@ -507,4 +554,26 @@ void ComponentSelectionPagePrivate::onModelStateChanged(QInstaller::ComponentMod
currentSelectedChanged(m_treeView->selectionModel()->currentIndex());
}
+/*!
+ Sets the new filter pattern to \a text and expands the tree nodes.
+*/
+void ComponentSelectionPagePrivate::setSearchPattern(const QString &text)
+{
+ m_proxyModel->setFilterWildcard(text);
+
+ m_treeView->collapseAll();
+ if (text.isEmpty()) {
+ // Expand user selection and default expanded, ensure selected is visible
+ QModelIndex index = m_treeView->selectionModel()->currentIndex();
+ while (index.isValid()) {
+ m_treeView->expand(index);
+ index = index.parent();
+ }
+ expandDefault();
+ m_treeView->scrollTo(m_treeView->selectionModel()->currentIndex());
+ } else {
+ expandSearchResults();
+ }
+}
+
} // namespace QInstaller
diff --git a/src/libs/installer/componentselectionpage_p.h b/src/libs/installer/componentselectionpage_p.h
index d2d30e3f0..fc37ebdaa 100644
--- a/src/libs/installer/componentselectionpage_p.h
+++ b/src/libs/installer/componentselectionpage_p.h
@@ -34,6 +34,7 @@
#include "componentmodel.h"
#include "packagemanagergui.h"
+#include "componentsortfilterproxymodel.h"
class QTreeView;
class QLabel;
@@ -70,6 +71,8 @@ public:
void setupCategoryLayout();
void showCategoryLayout(bool show);
void updateTreeView();
+ void expandDefault();
+ void expandSearchResults();
public slots:
void currentSelectedChanged(const QModelIndex &current);
@@ -84,6 +87,7 @@ public slots:
void setTotalProgress(int totalProgress);
void selectDefault();
void onModelStateChanged(QInstaller::ComponentModel::ModelState state);
+ void setSearchPattern(const QString &text);
private:
ComponentSelectionPage *q;
@@ -107,6 +111,8 @@ private:
ComponentModel *m_updaterModel;
ComponentModel *m_currentModel;
QStackedLayout *m_stackedLayout;
+ ComponentSortFilterProxyModel *m_proxyModel;
+ QLineEdit *m_searchLineEdit;
};
} // namespace QInstaller
diff --git a/src/libs/installer/componentsortfilterproxymodel.cpp b/src/libs/installer/componentsortfilterproxymodel.cpp
new file mode 100644
index 000000000..ab736a7c9
--- /dev/null
+++ b/src/libs/installer/componentsortfilterproxymodel.cpp
@@ -0,0 +1,152 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#include "componentsortfilterproxymodel.h"
+
+namespace QInstaller {
+
+/*!
+ \class QInstaller::ComponentSortFilterProxyModel
+ \inmodule QtInstallerFramework
+ \brief The ComponentSortFilterProxyModel provides support for sorting and
+ filtering data passed between another model and a view.
+
+ The class subclasses QSortFilterProxyModel. Compared to the base class,
+ filters affect also child indexes in the base model, meaning if a
+ certain row has a parent that is accepted by filter, it is also accepted.
+ A distinction is made betweed directly and indirectly accepted indexes.
+*/
+
+/*!
+ \enum ComponentSortFilterProxyModel::AcceptType
+
+ This enum holds the possible values for filter acception type for model indexes.
+
+ \value Direct
+ Index was accepted directly by filter.
+ \value Descendant
+ Index is a descendant of an accepted index.
+ \value Reject
+ Index was not accepted by filter.
+*/
+
+/*!
+ Constructs object with \a parent.
+*/
+ComponentSortFilterProxyModel::ComponentSortFilterProxyModel(QObject *parent)
+ : QSortFilterProxyModel(parent)
+{
+}
+
+/*!
+ Returns a list of source model indexes that were accepted directly by the filter.
+*/
+QVector<QModelIndex> ComponentSortFilterProxyModel::directlyAcceptedIndexes() const
+{
+ QVector<QModelIndex> indexes;
+ for (int i = 0; i < rowCount(); i++) {
+ QModelIndex childIndex = index(i, 0, QModelIndex());
+ findDirectlyAcceptedIndexes(childIndex, indexes);
+ }
+ return indexes;
+}
+
+/*!
+ Returns \c true if the item in the row indicated by the given \a sourceRow and
+ \a sourceParent should be included in the model; otherwise returns \c false.
+*/
+bool ComponentSortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
+{
+ return acceptsRow(sourceRow, sourceParent);
+}
+
+/*!
+ Returns \c true if the item in the row indicated by the given \a sourceRow and
+ \a sourceParent should be included in the model; otherwise returns \c false. The
+ acception type can be retrieved with \a type.
+*/
+bool ComponentSortFilterProxyModel::acceptsRow(int sourceRow, const QModelIndex &sourceParent,
+ AcceptType *type) const
+{
+ if (type)
+ *type = AcceptType::Rejected;
+
+ if (QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent)) {
+ if (type)
+ *type = AcceptType::Direct;
+ return true;
+ }
+
+ if (!isRecursiveFilteringEnabled()) // filter not applied for children
+ return false;
+
+ if (!sourceParent.isValid()) // already root
+ return false;
+
+ int currentRow = sourceParent.row();
+ QModelIndex currentParent = sourceParent.parent();
+ // Ascend and check if any parent is accepted
+ forever {
+ if (QSortFilterProxyModel::filterAcceptsRow(currentRow, currentParent)) {
+ if (type)
+ *type = AcceptType::Descendant;
+ return true;
+ }
+ if (!currentParent.isValid()) // we hit a root node
+ break;
+
+ currentRow = currentParent.row();
+ currentParent = currentParent.parent();
+ }
+ return false;
+}
+
+/*!
+ Finds directly accepted child \a indexes for parent index \a in. Returns \c true
+ if at least one accepted index was found, \c false otherwise.
+*/
+bool ComponentSortFilterProxyModel::findDirectlyAcceptedIndexes(const QModelIndex &in, QVector<QModelIndex> &indexes) const
+{
+ bool found = false;
+ for (int i = 0; i < rowCount(in); i++) {
+ if (findDirectlyAcceptedIndexes(index(i, 0, in), indexes))
+ found = true;
+ }
+ if (!hasChildren(in) || !found) { // No need to check current if any child matched
+ AcceptType acceptType;
+ const QModelIndex sourceIndex = mapToSource(in);
+ acceptsRow(sourceIndex.row(), sourceIndex.parent(), &acceptType);
+ if (acceptType == AcceptType::Direct) {
+ indexes.append(in);
+ found = true;
+ }
+ }
+ return found;
+}
+
+} // namespace QInstaller
diff --git a/src/libs/installer/componentsortfilterproxymodel.h b/src/libs/installer/componentsortfilterproxymodel.h
new file mode 100644
index 000000000..a6167d17b
--- /dev/null
+++ b/src/libs/installer/componentsortfilterproxymodel.h
@@ -0,0 +1,63 @@
+/**************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt Installer Framework.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+**************************************************************************/
+
+#ifndef COMPONENTSORTFILTERPROXYMODEL_H
+#define COMPONENTSORTFILTERPROXYMODEL_H
+
+#include "installer_global.h"
+
+#include <QSortFilterProxyModel>
+
+namespace QInstaller {
+
+class INSTALLER_EXPORT ComponentSortFilterProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+
+public:
+ enum AcceptType {
+ Direct,
+ Descendant,
+ Rejected
+ };
+
+ explicit ComponentSortFilterProxyModel(QObject *parent = nullptr);
+
+ QVector<QModelIndex> directlyAcceptedIndexes() const;
+
+protected:
+ bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const Q_DECL_OVERRIDE;
+
+private:
+ bool acceptsRow(int sourceRow, const QModelIndex &sourceParent, AcceptType *type = nullptr) const;
+ bool findDirectlyAcceptedIndexes(const QModelIndex &in, QVector<QModelIndex> &indexes) const;
+};
+
+} // namespace QInstaller
+
+#endif // COMPONENTSORTFILTERPROXYMODEL_H
diff --git a/src/libs/installer/fileutils.cpp b/src/libs/installer/fileutils.cpp
index 3c9f2ad1c..8c015a1c2 100644
--- a/src/libs/installer/fileutils.cpp
+++ b/src/libs/installer/fileutils.cpp
@@ -454,31 +454,21 @@ void QInstaller::mkpath(const QString &path)
*/
QString QInstaller::generateTemporaryFileName(const QString &templ)
{
- if (templ.isEmpty()) {
- QTemporaryFile f;
- if (!f.open()) {
+ static const QLatin1String staticPart("%1.tmp.XXXXXX");
+
+ QTemporaryFile f;
+ if (!templ.isEmpty())
+ f.setFileTemplate(QString(staticPart).arg(templ));
+
+ if (!f.open()) {
+ if (!templ.isEmpty()) {
+ throw Error(QCoreApplication::translate("QInstaller",
+ "Cannot open temporary file for template %1: %2").arg(templ, f.errorString()));
+ } else {
throw Error(QCoreApplication::translate("QInstaller",
"Cannot open temporary file: %1").arg(f.errorString()));
}
- return f.fileName();
- }
-
- static const QString characters = QLatin1String("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890");
- QString suffix;
- for (int i = 0; i < 5; ++i)
- suffix += characters[QRandomGenerator::global()->generate() % characters.length()];
-
- const QString tmp = QLatin1String("%1.tmp.%2.%3");
- int count = 1;
- while (QFile::exists(tmp.arg(templ, suffix).arg(count)))
- ++count;
-
- QFile f(tmp.arg(templ, suffix).arg(count));
- if (!f.open(QIODevice::WriteOnly)) {
- throw Error(QCoreApplication::translate("QInstaller",
- "Cannot open temporary file for template %1: %2").arg(templ, f.errorString()));
}
- f.remove();
return f.fileName();
}
diff --git a/src/libs/installer/installer.pro b/src/libs/installer/installer.pro
index 4721bb089..ed7d739ed 100644
--- a/src/libs/installer/installer.pro
+++ b/src/libs/installer/installer.pro
@@ -44,6 +44,7 @@ win32:QT += winextras
HEADERS += packagemanagercore.h \
aspectratiolabel.h \
+ componentsortfilterproxymodel.h \
loggingutils.h \
packagemanagercore_p.h \
packagemanagergui.h \
@@ -150,6 +151,7 @@ SOURCES += packagemanagercore.cpp \
aspectratiolabel.cpp \
directoryguard.cpp \
lib7zarchive.cpp \
+ componentsortfilterproxymodel.cpp \
loggingutils.cpp \
packagemanagercore_p.cpp \
packagemanagergui.cpp \