We now have a current svn snapshot of libqxt in our contrib dir, and
[quassel.git] / src / contrib / libqxt-2007-10-24 / src / gui / qxtcheckcombobox.cpp
diff --git a/src/contrib/libqxt-2007-10-24/src/gui/qxtcheckcombobox.cpp b/src/contrib/libqxt-2007-10-24/src/gui/qxtcheckcombobox.cpp
new file mode 100644 (file)
index 0000000..52b22e8
--- /dev/null
@@ -0,0 +1,380 @@
+/****************************************************************************
+**
+** 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();
+    }
+}