blob: 21fdfe07a0552055a2f1f8de4cd4d44c0148c271 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Assistant of the Qt Toolkit.
**
** $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 "qoptionswidget_p.h"
#include <QtWidgets/QComboBox>
#include <QtWidgets/QItemDelegate>
#include <QtWidgets/QListWidget>
#include <QtWidgets/QVBoxLayout>
QT_BEGIN_NAMESPACE
class ListWidgetDelegate : public QItemDelegate
{
public:
ListWidgetDelegate(QWidget *w) : QItemDelegate(w), m_widget(w) {}
static bool isSeparator(const QModelIndex &index) {
return index.data(Qt::AccessibleDescriptionRole).toString() == QLatin1String("separator");
}
static void setSeparator(QListWidgetItem *item) {
item->setData(Qt::AccessibleDescriptionRole, QString::fromLatin1("separator"));
item->setFlags(item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled));
}
protected:
void paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const override {
if (isSeparator(index)) {
QRect rect = option.rect;
if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView*>(option.widget))
rect.setWidth(view->viewport()->width());
QStyleOption opt;
opt.rect = rect;
m_widget->style()->drawPrimitive(QStyle::PE_IndicatorToolBarSeparator, &opt, painter, m_widget);
} else {
QItemDelegate::paint(painter, option, index);
}
}
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const override {
if (isSeparator(index)) {
int pm = m_widget->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, m_widget);
return QSize(pm, pm);
}
return QItemDelegate::sizeHint(option, index);
}
private:
QWidget *m_widget;
};
static QStringList subtract(const QStringList &minuend, const QStringList &subtrahend)
{
QStringList result = minuend;
for (const QString &str : subtrahend)
result.removeOne(str);
return result;
}
QOptionsWidget::QOptionsWidget(QWidget *parent)
: QWidget(parent)
, m_noOptionText(tr("No Option"))
, m_invalidOptionText(tr("Invalid Option"))
{
m_listWidget = new QListWidget(this);
m_listWidget->setItemDelegate(new ListWidgetDelegate(m_listWidget));
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(m_listWidget);
layout->setContentsMargins(QMargins());
connect(m_listWidget, &QListWidget::itemChanged, this, &QOptionsWidget::itemChanged);
}
void QOptionsWidget::clear()
{
setOptions(QStringList(), QStringList());
}
void QOptionsWidget::setOptions(const QStringList &validOptions,
const QStringList &selectedOptions)
{
m_listWidget->clear();
m_optionToItem.clear();
m_itemToOption.clear();
m_validOptions = validOptions;
m_validOptions.removeDuplicates();
std::sort(m_validOptions.begin(), m_validOptions.end());
m_selectedOptions = selectedOptions;
m_selectedOptions.removeDuplicates();
std::sort(m_selectedOptions.begin(), m_selectedOptions.end());
m_invalidOptions = subtract(m_selectedOptions, m_validOptions);
const QStringList validSelectedOptions = subtract(m_selectedOptions, m_invalidOptions);
const QStringList validUnselectedOptions = subtract(m_validOptions, m_selectedOptions);
for (const QString &option : validSelectedOptions)
appendItem(option, true, true);
for (const QString &option : m_invalidOptions)
appendItem(option, false, true);
if ((validSelectedOptions.count() + m_invalidOptions.count())
&& validUnselectedOptions.count()) {
appendSeparator();
}
for (const QString &option : validUnselectedOptions) {
appendItem(option, true, false);
if (option.isEmpty() && validUnselectedOptions.count() > 1) // special No Option item
appendSeparator();
}
}
QStringList QOptionsWidget::validOptions() const
{
return m_validOptions;
}
QStringList QOptionsWidget::selectedOptions() const
{
return m_selectedOptions;
}
void QOptionsWidget::setNoOptionText(const QString &text)
{
if (m_noOptionText == text)
return;
m_noOptionText = text;
// update GUI
const auto itEnd = m_optionToItem.constEnd();
for (auto it = m_optionToItem.constBegin(); it != itEnd; ++it) {
const QString optionName = it.key();
if (optionName.isEmpty())
it.value()->setText(optionText(optionName, m_validOptions.contains(optionName)));
}
}
void QOptionsWidget::setInvalidOptionText(const QString &text)
{
if (m_invalidOptionText == text)
return;
m_invalidOptionText = text;
// update GUI
for (const QString &option : m_invalidOptions)
m_optionToItem.value(option)->setText(optionText(option, false));
}
QString QOptionsWidget::optionText(const QString &optionName, bool valid) const
{
QString text = optionName;
if (optionName.isEmpty())
text = QLatin1Char('[') + m_noOptionText + QLatin1Char(']');
if (!valid)
text += QLatin1String("\t[") + m_invalidOptionText + QLatin1Char(']');
return text;
}
QListWidgetItem *QOptionsWidget::appendItem(const QString &optionName, bool valid, bool selected)
{
QListWidgetItem *optionItem = new QListWidgetItem(optionText(optionName, valid), m_listWidget);
optionItem->setCheckState(selected ? Qt::Checked : Qt::Unchecked);
m_listWidget->insertItem(m_listWidget->count(), optionItem);
m_optionToItem[optionName] = optionItem;
m_itemToOption[optionItem] = optionName;
return optionItem;
}
void QOptionsWidget::appendSeparator()
{
QListWidgetItem *separatorItem = new QListWidgetItem(m_listWidget);
ListWidgetDelegate::setSeparator(separatorItem);
m_listWidget->insertItem(m_listWidget->count(), separatorItem);
}
void QOptionsWidget::itemChanged(QListWidgetItem *item)
{
const auto it = m_itemToOption.constFind(item);
if (it == m_itemToOption.constEnd())
return;
const QString option = *it;
if (item->checkState() == Qt::Checked && !m_selectedOptions.contains(option)) {
m_selectedOptions.append(option);
std::sort(m_selectedOptions.begin(), m_selectedOptions.end());
} else if (item->checkState() == Qt::Unchecked && m_selectedOptions.contains(option)) {
m_selectedOptions.removeOne(option);
} else {
return;
}
emit optionSelectionChanged(m_selectedOptions);
}
QT_END_NAMESPACE