1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qhelpfilterdata.h"
5#include "qhelpfiltersettings_p.h"
6#include "qhelpfiltersettingswidget.h"
7#include "ui_qhelpfiltersettingswidget.h"
8#include "qfilternamedialog_p.h"
9
10#include <QtWidgets/QMessageBox>
11#include <QtCore/QVersionNumber>
12
13QT_BEGIN_NAMESPACE
14
15static QStringList versionsToStringList(const QList<QVersionNumber> &versions)
16{
17 QStringList versionList;
18 for (const QVersionNumber &version : versions)
19 versionList.append(t: version.isNull() ? QString() : version.toString());
20 return versionList;
21}
22
23static QList<QVersionNumber> stringListToVersions(const QStringList &versionList)
24{
25 QList<QVersionNumber> versions;
26 for (const QString &versionString : versionList)
27 versions.append(t: QVersionNumber::fromString(string: versionString));
28 return versions;
29}
30
31class QHelpFilterSettingsWidgetPrivate
32{
33 QHelpFilterSettingsWidget *q_ptr;
34 Q_DECLARE_PUBLIC(QHelpFilterSettingsWidget)
35public:
36 QHelpFilterSettingsWidgetPrivate() = default;
37
38 QHelpFilterSettings filterSettings() const;
39 void setFilterSettings(const QHelpFilterSettings &settings);
40
41 void updateCurrentFilter();
42 void componentsChanged(const QStringList &components);
43 void versionsChanged(const QStringList &versions);
44 void addFilterClicked();
45 void renameFilterClicked();
46 void removeFilterClicked();
47 void addFilter(const QString &filterName,
48 const QHelpFilterData &filterData = QHelpFilterData());
49 void removeFilter(const QString &filterName);
50 QString getUniqueFilterName(const QString &windowTitle,
51 const QString &initialFilterName);
52 QString suggestedNewFilterName(const QString &initialFilterName) const;
53
54 QMap<QString, QListWidgetItem *> m_filterToItem;
55 QHash<QListWidgetItem *, QString> m_itemToFilter;
56
57 Ui::QHelpFilterSettingsWidget m_ui;
58 QStringList m_components;
59 QList<QVersionNumber> m_versions;
60 QHelpFilterSettings m_filterSettings;
61};
62
63void QHelpFilterSettingsWidgetPrivate::setFilterSettings(const QHelpFilterSettings &settings)
64{
65 QString currentFilter = m_itemToFilter.value(m_ui.filterWidget->currentItem());
66 if (currentFilter.isEmpty()) {
67 if (!m_filterSettings.currentFilter().isEmpty())
68 currentFilter = m_filterSettings.currentFilter();
69 else
70 currentFilter = settings.currentFilter();
71 }
72
73 m_filterSettings = settings;
74
75 m_ui.filterWidget->clear();
76 m_ui.componentWidget->clear();
77 m_ui.versionWidget->clear();
78 m_itemToFilter.clear();
79 m_filterToItem.clear();
80
81 for (const QString &filterName : m_filterSettings.filterNames()) {
82 QListWidgetItem *item = new QListWidgetItem(filterName);
83 m_ui.filterWidget->addItem(item);
84 m_itemToFilter.insert(item, filterName);
85 m_filterToItem.insert(filterName, item);
86 if (filterName == currentFilter)
87 m_ui.filterWidget->setCurrentItem(item);
88 }
89
90 if (!m_ui.filterWidget->currentItem() && !m_filterToItem.isEmpty())
91 m_ui.filterWidget->setCurrentItem(m_filterToItem.first());
92
93 updateCurrentFilter();
94}
95
96QHelpFilterSettings QHelpFilterSettingsWidgetPrivate::filterSettings() const
97{
98 return m_filterSettings;
99}
100
101void QHelpFilterSettingsWidgetPrivate::updateCurrentFilter()
102{
103 const QString &currentFilter = m_itemToFilter.value(m_ui.filterWidget->currentItem());
104
105 const bool filterSelected = !currentFilter.isEmpty();
106 m_ui.componentWidget->setEnabled(filterSelected);
107 m_ui.versionWidget->setEnabled(filterSelected);
108 m_ui.renameButton->setEnabled(filterSelected);
109 m_ui.removeButton->setEnabled(filterSelected);
110
111 m_ui.componentWidget->setOptions(m_components,
112 m_filterSettings.filterData(currentFilter).components());
113 m_ui.versionWidget->setOptions(versionsToStringList(m_versions),
114 versionsToStringList(m_filterSettings.filterData(currentFilter).versions()));
115}
116
117void QHelpFilterSettingsWidgetPrivate::componentsChanged(const QStringList &components)
118{
119 const QString &currentFilter = m_itemToFilter.value(m_ui.filterWidget->currentItem());
120 if (currentFilter.isEmpty())
121 return;
122
123 QHelpFilterData filterData = m_filterSettings.filterData(filterName: currentFilter);
124 filterData.setComponents(components);
125 m_filterSettings.setFilter(filterName: currentFilter, filterData);
126}
127
128void QHelpFilterSettingsWidgetPrivate::versionsChanged(const QStringList &versions)
129{
130 const QString &currentFilter = m_itemToFilter.value(m_ui.filterWidget->currentItem());
131 if (currentFilter.isEmpty())
132 return;
133
134 QHelpFilterData filterData = m_filterSettings.filterData(filterName: currentFilter);
135 filterData.setVersions(stringListToVersions(versionList: versions));
136 m_filterSettings.setFilter(filterName: currentFilter, filterData);
137}
138
139void QHelpFilterSettingsWidgetPrivate::addFilterClicked()
140{
141 const QString newFilterName = getUniqueFilterName(windowTitle: QHelpFilterSettingsWidget::tr(s: "Add Filter"),
142 initialFilterName: suggestedNewFilterName(initialFilterName: QHelpFilterSettingsWidget::tr(s: "New Filter")));
143 if (newFilterName.isEmpty())
144 return;
145
146 addFilter(filterName: newFilterName);
147}
148
149void QHelpFilterSettingsWidgetPrivate::renameFilterClicked()
150{
151 const QString &currentFilter = m_itemToFilter.value(m_ui.filterWidget->currentItem());
152 if (currentFilter.isEmpty())
153 return;
154
155 const QString newFilterName = getUniqueFilterName(windowTitle: QHelpFilterSettingsWidget::tr(s: "Rename Filter"), initialFilterName: currentFilter);
156 if (newFilterName.isEmpty())
157 return;
158
159 const QHelpFilterData oldFilterData = m_filterSettings.filterData(filterName: currentFilter);
160 removeFilter(filterName: currentFilter);
161 addFilter(filterName: newFilterName, filterData: oldFilterData);
162
163 if (m_filterSettings.currentFilter() == currentFilter)
164 m_filterSettings.setCurrentFilter(newFilterName);
165}
166
167void QHelpFilterSettingsWidgetPrivate::removeFilterClicked()
168{
169 Q_Q(QHelpFilterSettingsWidget);
170
171 const QString &currentFilter = m_itemToFilter.value(m_ui.filterWidget->currentItem());
172 if (currentFilter.isEmpty())
173 return;
174
175 if (QMessageBox::question(parent: q, title: QHelpFilterSettingsWidget::tr(s: "Remove Filter"),
176 text: QHelpFilterSettingsWidget::tr(s: "Are you sure you want to remove the \"%1\" filter?")
177 .arg(a: currentFilter),
178 buttons: QMessageBox::Yes | QMessageBox::No)
179 != QMessageBox::Yes) {
180 return;
181 }
182
183 removeFilter(filterName: currentFilter);
184
185 if (m_filterSettings.currentFilter() == currentFilter)
186 m_filterSettings.setCurrentFilter(QString());
187}
188
189void QHelpFilterSettingsWidgetPrivate::addFilter(const QString &filterName,
190 const QHelpFilterData &filterData)
191{
192 QListWidgetItem *item = new QListWidgetItem(filterName);
193 m_filterSettings.setFilter(filterName, filterData);
194 m_filterToItem.insert(filterName, item);
195 m_itemToFilter.insert(item, filterName);
196 m_ui.filterWidget->insertItem(m_filterToItem.keys().indexOf(filterName), item);
197
198 m_ui.filterWidget->setCurrentItem(item);
199 updateCurrentFilter();
200}
201
202void QHelpFilterSettingsWidgetPrivate::removeFilter(const QString &filterName)
203{
204 QListWidgetItem *item = m_filterToItem.value(filterName);
205 m_itemToFilter.remove(item);
206 m_filterToItem.remove(filterName);
207 delete item;
208
209 m_filterSettings.removeFilter(filterName);
210}
211
212QString QHelpFilterSettingsWidgetPrivate::getUniqueFilterName(const QString &windowTitle,
213 const QString &initialFilterName)
214{
215 Q_Q(QHelpFilterSettingsWidget);
216
217 QString newFilterName = initialFilterName;
218 while (1) {
219 QFilterNameDialog dialog(q);
220 dialog.setWindowTitle(windowTitle);
221 dialog.setFilterName(newFilterName);
222 if (dialog.exec() == QDialog::Rejected)
223 return QString();
224
225 newFilterName = dialog.filterName();
226 if (!m_filterToItem.contains(newFilterName))
227 break;
228
229 if (QMessageBox::warning(parent: q, title: QHelpFilterSettingsWidget::tr(s: "Filter Exists"),
230 text: QHelpFilterSettingsWidget::tr(s: "The filter \"%1\" already exists.")
231 .arg(a: newFilterName),
232 buttons: QMessageBox::Retry | QMessageBox::Cancel)
233 == QMessageBox::Cancel) {
234 return QString();
235 }
236 }
237
238 return newFilterName;
239}
240
241QString QHelpFilterSettingsWidgetPrivate::suggestedNewFilterName(const QString &initialFilterName) const
242{
243 QString newFilterName = initialFilterName;
244
245 int counter = 1;
246 while (m_filterToItem.contains(newFilterName)) {
247 newFilterName = initialFilterName + QLatin1Char(' ')
248 + QString::number(++counter);
249 }
250
251 return newFilterName;
252}
253
254/*!
255 \class QHelpFilterSettingsWidget
256 \inmodule QtHelp
257 \since 5.15
258 \brief The QHelpFilterSettingsWidget class provides a widget that allows
259 for creating, editing and removing filters.
260
261 The instance of QHelpFilterSettingsWidget may be a part of
262 a preferences dialog. Before showing the dialog, \l setAvailableComponents()
263 and \l setAvailableVersions() should be called, otherwise the filter
264 settings widget will only offer a creation of empty filters,
265 which wouldn't be useful. In addition, \l readSettings should also
266 be called to fill up the filter settings widget with the list of filters
267 already stored in the filter engine. The creation of new filters,
268 modifications to existing filters and removal of unneeded filters are
269 handled by the widget automatically. If you want to store the current
270 state of the widget and apply it to the filter engine e.g. after
271 the user clicked the apply button - call \l applySettings().
272*/
273
274/*!
275 Constructs a filter settings widget with \a parent as parent widget.
276*/
277QHelpFilterSettingsWidget::QHelpFilterSettingsWidget(QWidget *parent)
278 : QWidget(parent)
279 , d_ptr(new QHelpFilterSettingsWidgetPrivate())
280{
281 Q_D(QHelpFilterSettingsWidget);
282 d->q_ptr = this;
283 d->m_ui.setupUi(this);
284
285 // TODO: make resources configurable
286 QString resourcePath = QLatin1String(":/qt-project.org/assistant/images/");
287#ifdef Q_OS_MACOS
288 resourcePath.append(QLatin1String("mac"));
289#else
290 resourcePath.append(s: QLatin1String("win"));
291#endif
292 d->m_ui.addButton->setIcon(QIcon(resourcePath + QLatin1String("/plus.png")));
293 d->m_ui.removeButton->setIcon(QIcon(resourcePath + QLatin1String("/minus.png")));
294
295 connect(d->m_ui.componentWidget, &QOptionsWidget::optionSelectionChanged,
296 [this](const QStringList &options) {
297 Q_D(QHelpFilterSettingsWidget);
298 d->componentsChanged(options);
299 });
300 connect(d->m_ui.versionWidget, &QOptionsWidget::optionSelectionChanged,
301 [this](const QStringList &options) {
302 Q_D(QHelpFilterSettingsWidget);
303 d->versionsChanged(options);
304 });
305 connect(d->m_ui.filterWidget, &QListWidget::currentItemChanged,
306 this, [this](QListWidgetItem *) {
307 Q_D(QHelpFilterSettingsWidget);
308 d->updateCurrentFilter();
309 });
310 connect(d->m_ui.filterWidget, &QListWidget::itemDoubleClicked,
311 [this](QListWidgetItem *) {
312 Q_D(QHelpFilterSettingsWidget);
313 d->renameFilterClicked();
314 });
315
316 // TODO: repeat these actions on context menu
317 connect(d->m_ui.addButton, &QAbstractButton::clicked,
318 [this]() {
319 Q_D(QHelpFilterSettingsWidget);
320 d->addFilterClicked();
321 });
322 connect(d->m_ui.renameButton, &QAbstractButton::clicked,
323 [this]() {
324 Q_D(QHelpFilterSettingsWidget);
325 d->renameFilterClicked();
326 });
327 connect(d->m_ui.removeButton, &QAbstractButton::clicked,
328 [this]() {
329 Q_D(QHelpFilterSettingsWidget);
330 d->removeFilterClicked();
331 });
332
333 d->m_ui.componentWidget->setNoOptionText(tr(s: "No Component"));
334 d->m_ui.componentWidget->setInvalidOptionText(tr(s: "Invalid Component"));
335 d->m_ui.versionWidget->setNoOptionText(tr(s: "No Version"));
336 d->m_ui.versionWidget->setInvalidOptionText(tr(s: "Invalid Version"));
337}
338
339/*!
340 Destroys the filter settings widget.
341*/
342QHelpFilterSettingsWidget::~QHelpFilterSettingsWidget() = default;
343
344/*!
345 Sets the list of all available components to \a components.
346 \sa QHelpFilterEngine::availableComponents()
347*/
348void QHelpFilterSettingsWidget::setAvailableComponents(const QStringList &components)
349{
350 Q_D(QHelpFilterSettingsWidget);
351 d->m_components = components;
352 d->updateCurrentFilter();
353}
354
355/*!
356 Sets the list of all available version numbers to \a versions.
357 \sa QHelpFilterEngine::availableVersions()
358*/
359void QHelpFilterSettingsWidget::setAvailableVersions(const QList<QVersionNumber> &versions)
360{
361 Q_D(QHelpFilterSettingsWidget);
362 d->m_versions = versions;
363 d->updateCurrentFilter();
364}
365
366/*!
367 Reads the filter settings stored inside \a filterEngine and sets up
368 this filter settings widget accordingly.
369*/
370void QHelpFilterSettingsWidget::readSettings(const QHelpFilterEngine *filterEngine)
371{
372 Q_D(QHelpFilterSettingsWidget);
373 const QHelpFilterSettings settings = QHelpFilterSettings::readSettings(filterEngine);
374 d->setFilterSettings(settings);
375}
376
377/*!
378 Writes the filter settings, currently presented in this filter settings
379 widget, to the \a filterEngine. The old settings stored in the filter
380 engine will be overwritten. Returns \c true on success.
381*/
382bool QHelpFilterSettingsWidget::applySettings(QHelpFilterEngine *filterEngine) const
383{
384 Q_D(const QHelpFilterSettingsWidget);
385 return QHelpFilterSettings::applySettings(filterEngine, settings: d->filterSettings());
386}
387
388QT_END_NAMESPACE
389

source code of qttools/src/assistant/help/qhelpfiltersettingswidget.cpp