--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) Qxt Foundation. Some rights reserved.
+**
+** This file is part of the QxtGui module of the Qt eXTension library
+**
+** This library is free software; you can redistribute it and/or modify it
+** under the terms of th Common Public License, version 1.0, as published by
+** IBM.
+**
+** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
+** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
+** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
+** FITNESS FOR A PARTICULAR PURPOSE.
+**
+** You should have received a copy of the CPL along with this file.
+** See the LICENSE file and the cpl1.0.txt file included with the source
+** distribution for more information. If you did not receive a copy of the
+** license, contact the Qxt Foundation.
+**
+** <http://libqxt.sourceforge.net> <foundation@libqxt.org>
+**
+****************************************************************************/
+#include "qxtcheckcombobox.h"
+#include "qxtcheckcombobox_p.h"
+#include <QStyleOptionButton>
+#include <QMouseEvent>
+#include <QLineEdit>
+#include <QTimer>
+
+QxtCheckComboBoxPrivate::QxtCheckComboBoxPrivate()
+{
+ separator = QLatin1String(",");
+}
+
+void QxtCheckComboBoxPrivate::hidePopup()
+{
+ qxt_p().hidePopup();
+}
+
+void QxtCheckComboBoxPrivate::updateCheckedItems()
+{
+ checkedItems.clear();
+ for (int i = 0; i < qxt_p().model()->rowCount(); ++i)
+ {
+ const QModelIndex& index = qxt_p().model()->index(i, 0);
+ const QVariant& data = index.data(Qt::CheckStateRole);
+ const Qt::CheckState state = static_cast<Qt::CheckState>(data.toInt());
+ if (state == Qt::Checked)
+ {
+ checkedItems += index.data().toString();
+ }
+ }
+
+ if (checkedItems.count() > 0)
+ qxt_p().lineEdit()->setText(checkedItems.join(separator));
+ else
+ qxt_p().lineEdit()->setText(defaultText);
+
+ // TODO: find a way to recalculate a meaningful size hint
+
+ emit qxt_p().checkedItemsChanged(checkedItems);
+}
+
+QxtCheckComboView::QxtCheckComboView(QWidget* parent)
+ : QListView(parent), mode(QxtCheckComboBox::CheckIndicator)
+{}
+
+QxtCheckComboView::~QxtCheckComboView()
+{}
+
+bool QxtCheckComboView::eventFilter(QObject* object, QEvent* event)
+{
+ Q_UNUSED(object);
+ if (event->type() == QEvent::MouseButtonRelease)
+ {
+ QMouseEvent* mouse = static_cast<QMouseEvent*>(event);
+ const QModelIndex& index = indexAt(mouse->pos());
+ if (index.isValid())
+ {
+ bool change = false;
+ switch (mode)
+ {
+ case QxtCheckComboBox::CheckIndicator:
+ change = handleIndicatorRelease(mouse, index);
+ break;
+
+ case QxtCheckComboBox::CheckWholeItem:
+ change = handleItemRelease(mouse, index);
+ break;
+
+ default:
+ qWarning("QxtCheckComboView::eventFilter(): unknown mode");
+ break;
+ }
+
+ if (change)
+ {
+ // the check state is about to change, bypass
+ // combobox and deliver the event just for the listview
+ QListView::mouseReleaseEvent(mouse);
+ }
+ else
+ {
+ // otherwise it's ok to close
+ emit hideRequested();
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool QxtCheckComboView::handleIndicatorRelease(QMouseEvent* event, const QModelIndex& index)
+{
+ // check if the mouse was released over the checkbox
+ QStyleOptionButton option;
+ option.QStyleOption::operator=(viewOptions());
+ option.rect = visualRect(index);
+ const QRect& rect = style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &option);
+ return rect.contains(event->pos());
+}
+
+bool QxtCheckComboView::handleItemRelease(QMouseEvent* event, const QModelIndex& index)
+{
+ // check if the mouse was released outside the checkbox
+ QStyleOptionButton option;
+ option.QStyleOption::operator=(viewOptions());
+ option.rect = visualRect(index);
+ const QRect& rect = style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &option);
+ if (!rect.contains(event->pos()))
+ {
+ Qt::CheckState state = (Qt::CheckState) index.data(Qt::CheckStateRole).toInt();
+ switch (state)
+ {
+ case Qt::Unchecked:
+ state = Qt::Checked;
+ break;
+
+ case Qt::Checked:
+ state = Qt::Unchecked;
+ break;
+
+ default:
+ qWarning("QxtCheckComboView::handleItemRelease(): partially checked item");
+ break;
+ }
+ model()->setData(index, state, Qt::CheckStateRole);
+ }
+ return true;
+}
+
+QxtCheckComboModel::QxtCheckComboModel(QObject* parent)
+ : QStandardItemModel(0, 1, parent) // rows,cols
+{
+}
+
+QxtCheckComboModel::~QxtCheckComboModel()
+{}
+
+Qt::ItemFlags QxtCheckComboModel::flags(const QModelIndex& index) const
+{
+ return QStandardItemModel::flags(index) | Qt::ItemIsUserCheckable;
+}
+
+QVariant QxtCheckComboModel::data(const QModelIndex& index, int role) const
+{
+ QVariant value = QStandardItemModel::data(index, role);
+ if (role == Qt::CheckStateRole && !value.isValid())
+ value = Qt::Unchecked;
+ return value;
+}
+
+bool QxtCheckComboModel::setData(const QModelIndex& index, const QVariant& value, int role)
+{
+ bool ok = QStandardItemModel::setData(index, value, role);
+ if (ok)
+ {
+ if (role == Qt::CheckStateRole)
+ {
+ emit checkStateChanged();
+ }
+ }
+ return ok;
+}
+
+/*!
+ \class QxtCheckComboBox QxtCheckComboBox
+ \ingroup QxtGui
+ \brief An extended QComboBox with checkable items.
+
+ QxtComboBox is a specialized combo box with checkable items.
+ Checked items are collected together in the line edit.
+
+ \image html qxtcheckcombobox.png "QxtCheckComboBox in Plastique style."
+ */
+
+/*!
+ \enum QxtCheckComboBox::CheckMode
+
+ This enum describes the check mode.
+
+ \sa QxtCheckComboBox::checkMode
+ */
+
+/*!
+ \var QxtCheckComboBox::CheckMode QxtCheckComboBox::CheckIndicator
+
+ The check state changes only via the check indicator (like in item views).
+ */
+
+/*!
+ \var QxtCheckComboBox::CheckMode QxtCheckComboBox::CheckWholeItem
+
+ The check state changes via the whole item (like with a combo box).
+ */
+
+/*!
+ \fn QxtCheckComboBox::checkedItemsChanged(const QStringList& items)
+
+ This signal is emitted whenever the checked items have been changed.
+ */
+
+/*!
+ Constructs a new QxtCheckComboBox with \a parent.
+ */
+QxtCheckComboBox::QxtCheckComboBox(QWidget* parent) : QComboBox(parent)
+{
+ QXT_INIT_PRIVATE(QxtCheckComboBox);
+ QxtCheckComboModel* model = new QxtCheckComboModel(this);
+ QxtCheckComboView* view = new QxtCheckComboView(this);
+ qxt_d().view = view;
+ setModel(model);
+ setView(view);
+
+ // these 2 lines below are important and must be
+ // applied AFTER QComboBox::setView() because
+ // QComboBox installs its own filter on the view
+ view->installEventFilter(view); // <--- !!!
+ view->viewport()->installEventFilter(view); // <--- !!!
+
+ // read-only contents
+ QLineEdit* lineEdit = new QLineEdit(this);
+ lineEdit->setReadOnly(true);
+ setLineEdit(lineEdit);
+
+ connect(view, SIGNAL(hideRequested()), &qxt_d(), SLOT(hidePopup()));
+ connect(model, SIGNAL(checkStateChanged()), &qxt_d(), SLOT(updateCheckedItems()));
+ QTimer::singleShot(0, &qxt_d(), SLOT(updateCheckedItems()));
+}
+
+/*!
+ Destructs the combo box.
+ */
+QxtCheckComboBox::~QxtCheckComboBox()
+{}
+
+/*!
+ Returns the check state of the item at \a index.
+ */
+Qt::CheckState QxtCheckComboBox::itemCheckState(int index) const
+{
+ return static_cast<Qt::CheckState>(itemData(index, Qt::CheckStateRole).toInt());
+}
+
+/*!
+ Sets the check state of the item at \a index to \a state.
+ */
+void QxtCheckComboBox::setItemCheckState(int index, Qt::CheckState state)
+{
+ setItemData(index, state, Qt::CheckStateRole);
+}
+
+/*!
+ \property QxtCheckComboBox::checkedItems
+ \brief This property holds the checked items.
+ */
+QStringList QxtCheckComboBox::checkedItems() const
+{
+ return qxt_d().checkedItems;
+}
+
+void QxtCheckComboBox::setCheckedItems(const QStringList& items)
+{
+ // not the most efficient solution but most likely nobody
+ // will put too many items into a combo box anyway so...
+ foreach (const QString& text, items)
+ {
+ const int index = findText(text);
+ setItemCheckState(index, index != -1 ? Qt::Checked : Qt::Unchecked);
+ }
+}
+
+/*!
+ \property QxtCheckComboBox::defaultText
+ \brief This property holds the default text.
+
+ The default text is shown when there are no checked items.
+ The default value is an empty string.
+ */
+QString QxtCheckComboBox::defaultText() const
+{
+ return qxt_d().defaultText;
+}
+
+void QxtCheckComboBox::setDefaultText(const QString& text)
+{
+ if (qxt_d().defaultText != text)
+ {
+ qxt_d().defaultText = text;
+ qxt_d().updateCheckedItems();
+ }
+}
+
+/*!
+ \property QxtCheckComboBox::separator
+ \brief This property holds the default separator.
+
+ The checked items are joined together with the separator string.
+ The default value is a comma (",").
+ */
+QString QxtCheckComboBox::separator() const
+{
+ return qxt_d().separator;
+}
+
+void QxtCheckComboBox::setSeparator(const QString& separator)
+{
+ if (qxt_d().separator != separator)
+ {
+ qxt_d().separator = separator;
+ qxt_d().updateCheckedItems();
+ }
+}
+
+/*!
+ \property QxtCheckComboBox::checkMode
+ \brief This property holds the check mode.
+
+ The check mode describes item checking behaviour.
+ The default value is \b QxtCheckComboBox::CheckIndicator.
+
+ \sa QxtCheckComboBox::CheckMode
+ */
+QxtCheckComboBox::CheckMode QxtCheckComboBox::checkMode() const
+{
+ return qxt_d().view->mode;
+}
+
+void QxtCheckComboBox::setCheckMode(QxtCheckComboBox::CheckMode mode)
+{
+ if (qxt_d().view->mode != mode)
+ {
+ qxt_d().view->mode = mode;
+ }
+}
+
+void QxtCheckComboBox::keyPressEvent(QKeyEvent* event)
+{
+ if (event->key() != Qt::Key_Up && event->key() != Qt::Key_Down)
+ {
+ QComboBox::keyPressEvent(event);
+ }
+ else
+ {
+ showPopup();
+ }
+}
+
+void QxtCheckComboBox::keyReleaseEvent(QKeyEvent* event)
+{
+ if (event->key() != Qt::Key_Up && event->key() != Qt::Key_Down)
+ {
+ QComboBox::keyReleaseEvent(event);
+ }
+ else
+ {
+ showPopup();
+ }
+}