1/****************************************************************************
2**
3** Copyright (C) 2020 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Assistant of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "qoptionswidget_p.h"
30
31#include <QtWidgets/QComboBox>
32#include <QtWidgets/QItemDelegate>
33#include <QtWidgets/QListWidget>
34#include <QtWidgets/QVBoxLayout>
35
36QT_BEGIN_NAMESPACE
37
38class ListWidgetDelegate : public QItemDelegate
39{
40public:
41 ListWidgetDelegate(QWidget *w) : QItemDelegate(w), m_widget(w) {}
42
43 static bool isSeparator(const QModelIndex &index) {
44 return index.data(arole: Qt::AccessibleDescriptionRole).toString() == QLatin1String("separator");
45 }
46 static void setSeparator(QListWidgetItem *item) {
47 item->setData(role: Qt::AccessibleDescriptionRole, value: QString::fromLatin1(str: "separator"));
48 item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled));
49 }
50
51protected:
52 void paint(QPainter *painter,
53 const QStyleOptionViewItem &option,
54 const QModelIndex &index) const override {
55 if (isSeparator(index)) {
56 QRect rect = option.rect;
57 if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView*>(object: option.widget))
58 rect.setWidth(view->viewport()->width());
59 QStyleOption opt;
60 opt.rect = rect;
61 m_widget->style()->drawPrimitive(pe: QStyle::PE_IndicatorToolBarSeparator, opt: &opt, p: painter, w: m_widget);
62 } else {
63 QItemDelegate::paint(painter, option, index);
64 }
65 }
66
67 QSize sizeHint(const QStyleOptionViewItem &option,
68 const QModelIndex &index) const override {
69 if (isSeparator(index)) {
70 int pm = m_widget->style()->pixelMetric(metric: QStyle::PM_DefaultFrameWidth, option: nullptr, widget: m_widget);
71 return QSize(pm, pm);
72 }
73 return QItemDelegate::sizeHint(option, index);
74 }
75private:
76 QWidget *m_widget;
77};
78
79static QStringList subtract(const QStringList &minuend, const QStringList &subtrahend)
80{
81 QStringList result = minuend;
82 for (const QString &str : subtrahend)
83 result.removeOne(t: str);
84 return result;
85}
86
87QOptionsWidget::QOptionsWidget(QWidget *parent)
88 : QWidget(parent)
89 , m_noOptionText(tr(s: "No Option"))
90 , m_invalidOptionText(tr(s: "Invalid Option"))
91{
92 m_listWidget = new QListWidget(this);
93 m_listWidget->setItemDelegate(new ListWidgetDelegate(m_listWidget));
94 QVBoxLayout *layout = new QVBoxLayout(this);
95 layout->addWidget(m_listWidget);
96 layout->setContentsMargins(QMargins());
97
98 connect(sender: m_listWidget, signal: &QListWidget::itemChanged, receiver: this, slot: &QOptionsWidget::itemChanged);
99}
100
101void QOptionsWidget::clear()
102{
103 setOptions(validOptions: QStringList(), selectedOptions: QStringList());
104}
105
106void QOptionsWidget::setOptions(const QStringList &validOptions,
107 const QStringList &selectedOptions)
108{
109 m_listWidget->clear();
110 m_optionToItem.clear();
111 m_itemToOption.clear();
112
113 m_validOptions = validOptions;
114 m_validOptions.removeDuplicates();
115 std::sort(first: m_validOptions.begin(), last: m_validOptions.end());
116
117 m_selectedOptions = selectedOptions;
118 m_selectedOptions.removeDuplicates();
119 std::sort(first: m_selectedOptions.begin(), last: m_selectedOptions.end());
120
121 m_invalidOptions = subtract(minuend: m_selectedOptions, subtrahend: m_validOptions);
122 const QStringList validSelectedOptions = subtract(minuend: m_selectedOptions, subtrahend: m_invalidOptions);
123 const QStringList validUnselectedOptions = subtract(minuend: m_validOptions, subtrahend: m_selectedOptions);
124
125 for (const QString &option : validSelectedOptions)
126 appendItem(optionName: option, valid: true, selected: true);
127
128 for (const QString &option : m_invalidOptions)
129 appendItem(optionName: option, valid: false, selected: true);
130
131 if ((validSelectedOptions.count() + m_invalidOptions.count())
132 && validUnselectedOptions.count()) {
133 appendSeparator();
134 }
135
136 for (const QString &option : validUnselectedOptions) {
137 appendItem(optionName: option, valid: true, selected: false);
138 if (option.isEmpty() && validUnselectedOptions.count() > 1) // special No Option item
139 appendSeparator();
140 }
141}
142
143QStringList QOptionsWidget::validOptions() const
144{
145 return m_validOptions;
146}
147
148QStringList QOptionsWidget::selectedOptions() const
149{
150 return m_selectedOptions;
151}
152
153void QOptionsWidget::setNoOptionText(const QString &text)
154{
155 if (m_noOptionText == text)
156 return;
157
158 m_noOptionText = text;
159
160 // update GUI
161 const auto itEnd = m_optionToItem.constEnd();
162 for (auto it = m_optionToItem.constBegin(); it != itEnd; ++it) {
163 const QString optionName = it.key();
164 if (optionName.isEmpty())
165 it.value()->setText(optionText(optionName, valid: m_validOptions.contains(str: optionName)));
166 }
167}
168
169void QOptionsWidget::setInvalidOptionText(const QString &text)
170{
171 if (m_invalidOptionText == text)
172 return;
173
174 m_invalidOptionText = text;
175
176 // update GUI
177 for (const QString &option : m_invalidOptions)
178 m_optionToItem.value(akey: option)->setText(optionText(optionName: option, valid: false));
179}
180
181QString QOptionsWidget::optionText(const QString &optionName, bool valid) const
182{
183 QString text = optionName;
184 if (optionName.isEmpty())
185 text = QLatin1Char('[') + m_noOptionText + QLatin1Char(']');
186 if (!valid)
187 text += QLatin1String("\t[") + m_invalidOptionText + QLatin1Char(']');
188 return text;
189}
190
191QListWidgetItem *QOptionsWidget::appendItem(const QString &optionName, bool valid, bool selected)
192{
193 QListWidgetItem *optionItem = new QListWidgetItem(optionText(optionName, valid), m_listWidget);
194 optionItem->setCheckState(selected ? Qt::Checked : Qt::Unchecked);
195 m_listWidget->insertItem(row: m_listWidget->count(), item: optionItem);
196 m_optionToItem[optionName] = optionItem;
197 m_itemToOption[optionItem] = optionName;
198 return optionItem;
199}
200
201void QOptionsWidget::appendSeparator()
202{
203 QListWidgetItem *separatorItem = new QListWidgetItem(m_listWidget);
204 ListWidgetDelegate::setSeparator(separatorItem);
205 m_listWidget->insertItem(row: m_listWidget->count(), item: separatorItem);
206}
207
208void QOptionsWidget::itemChanged(QListWidgetItem *item)
209{
210 const auto it = m_itemToOption.constFind(akey: item);
211 if (it == m_itemToOption.constEnd())
212 return;
213
214 const QString option = *it;
215
216 if (item->checkState() == Qt::Checked && !m_selectedOptions.contains(str: option)) {
217 m_selectedOptions.append(t: option);
218 std::sort(first: m_selectedOptions.begin(), last: m_selectedOptions.end());
219 } else if (item->checkState() == Qt::Unchecked && m_selectedOptions.contains(str: option)) {
220 m_selectedOptions.removeOne(t: option);
221 } else {
222 return;
223 }
224
225 emit optionSelectionChanged(options: m_selectedOptions);
226}
227
228
229QT_END_NAMESPACE
230

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