File indexing completed on 2025-01-19 03:55:41

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-07-05
0007  * Description : A combobox delegate to display in Web service image lists.
0008  *
0009  * SPDX-FileCopyrightText: 2009 by Pieter Edelman <pieter dot edelman at gmx dot net>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "wscomboboxdelegate.h"
0016 
0017 // Qt includes
0018 
0019 #include <QApplication>
0020 #include <QComboBox>
0021 #include <QPaintEvent>
0022 #include <QStyleOption>
0023 
0024 // Local includes
0025 
0026 #include "digikam_debug.h"
0027 
0028 namespace Digikam
0029 {
0030 
0031 class Q_DECL_HIDDEN ComboBoxDelegate::Private
0032 {
0033 public:
0034 
0035     explicit Private()
0036       : parent   (nullptr),
0037         rowEdited(-1)
0038     {
0039     }
0040 
0041     DItemsList*       parent;
0042     QMap<int, QString> items;
0043 
0044     /*
0045      * The row in the view that is currently being edited. Should be -1 to
0046      * indicate that no row is edited.
0047      */
0048     int                rowEdited;
0049 
0050     QSize              size;
0051 };
0052 
0053 ComboBoxDelegate::ComboBoxDelegate(DItemsList* const parent, const QMap<int, QString>& items)
0054     : QAbstractItemDelegate(parent),
0055       d                    (new Private)
0056 {
0057     d->parent = parent;
0058     d->items  = items;
0059 
0060     // Figure out the maximum width of a displayed item from the items list and
0061     // save it in the d->size parameter.
0062 
0063     QFontMetrics listFont = parent->fontMetrics();
0064     d->size               = QSize(0, listFont.height());
0065     int tmpWidth          = 0;
0066     QMapIterator<int, QString> i(d->items);
0067 
0068     while (i.hasNext())
0069     {
0070         i.next();
0071 
0072         tmpWidth = listFont.horizontalAdvance(i.value());
0073 
0074         if (tmpWidth > d->size.width())
0075         {
0076             d->size.setWidth(tmpWidth);
0077         }
0078     }
0079 }
0080 
0081 ComboBoxDelegate::~ComboBoxDelegate()
0082 {
0083     delete d;
0084 }
0085 
0086 void ComboBoxDelegate::startEditing(QTreeWidgetItem* item, int column)
0087 {
0088     // Start editing the item. This is part of a hack to make sure the item text
0089     // doesn't get painted whenever a combobox is drawn (otherwise the text can
0090     // be seen around the edges of the combobox. This method breaks the OO
0091     // paradigm.
0092 
0093     d->rowEdited = d->parent->listView()->currentIndex().row();
0094     item->setFlags(item->flags() | Qt::ItemIsEditable);
0095     d->parent->listView()->editItem(item, column);
0096     item->setFlags(item->flags() & ~Qt::ItemIsEditable);
0097 }
0098 
0099 void ComboBoxDelegate::paint(QPainter* painter,
0100                              const QStyleOptionViewItem& option,
0101                              const QModelIndex& index) const
0102 {
0103     // Draw a panel item primitive element as background.
0104 
0105     QStyle* const style = QApplication::style();
0106     style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter);
0107 
0108     // If the element that gets painted is not currently edited, the item text
0109     // should be displayed.
0110     // Note that this method to detect which item is edited is a horrible hack
0111     // to work around the fact that there's no reliable way to detect if an item
0112     // is being edited from the parameters (although the documentation suggests
0113     // QStyle::State_Editing should be set in the option.flags parameter).
0114 
0115     if (d->rowEdited != index.row())
0116     {
0117         // Get the currently selected index in the items list.
0118 
0119         int currIndex = (index.data()).value<int>();
0120 
0121         // PE: These values are found by trial and error. I don't have any idea
0122         // if it's actually correct, but it seems to work across all themes.
0123 
0124         QPalette::ColorRole textColor = QPalette::Text;
0125 
0126         if (option.state & QStyle::State_Selected)
0127         {
0128             textColor = QPalette::HighlightedText;
0129         }
0130 
0131         // Draw the text.
0132 
0133         style->drawItemText(painter, option.rect, option.displayAlignment,
0134                             option.palette, true, d->items[currIndex],
0135                             textColor);
0136     }
0137 }
0138 
0139 QSize ComboBoxDelegate::sizeHint(const QStyleOptionViewItem&, const QModelIndex&) const
0140 {
0141     // Return the size based on the widest item in the items list.
0142 
0143     return d->size;
0144 }
0145 
0146 QWidget* ComboBoxDelegate::createEditor(QWidget* parent,
0147                                         const QStyleOptionViewItem& option,
0148                                         const QModelIndex&) const
0149 {
0150     // This method returns the widget that should be used to edit the current
0151     // element, which is in this case a QComboBox with the items supplied by
0152     // the user items list on construction.
0153 
0154     QComboBox* const cb = new QComboBox(parent);
0155     QMapIterator<int, QString> i(d->items);
0156 
0157     while (i.hasNext())
0158     {
0159         i.next();
0160         cb->addItem(i.value(), QVariant(i.key()));
0161     }
0162 
0163     // Set the geometry
0164 
0165     cb->setGeometry(option.rect);
0166 
0167     // If the index is changed, the editing should be finished and the editor
0168     // destroyed.
0169 
0170     connect(cb, SIGNAL(activated(int)),
0171             this, SLOT(slotCommitAndCloseEditor(int)));
0172 
0173     // To keep track of the item being edited, the d->rowEdited parameter should
0174     // be reset when the editor is destroyed.
0175 
0176     connect(cb, SIGNAL(destroyed(QObject*)),
0177             this, SLOT(slotResetEditedState(QObject*)));
0178 
0179     return cb;
0180 }
0181 
0182 void ComboBoxDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const
0183 {
0184     // Scroll the combobox to the current selected state on initialization.
0185 
0186     QComboBox* const cb = qobject_cast<QComboBox*>(editor);
0187 
0188     for (int i = 0 ; i < cb->count() ; ++i)
0189     {
0190         if (cb->itemData(i).toInt() == index.data().toInt())
0191         {
0192             cb->setCurrentIndex(i);
0193         }
0194     }
0195 }
0196 
0197 void ComboBoxDelegate::setModelData(QWidget* editor,
0198                                     QAbstractItemModel* model,
0199                                     const QModelIndex& index) const
0200 {
0201     // Write the data to the model when finishing has completed.
0202 
0203     QComboBox* const cb = qobject_cast<QComboBox*>(editor);
0204     int selected        = cb->itemData(cb->currentIndex()).toInt();
0205     model->setData(index, selected);
0206 }
0207 
0208 void ComboBoxDelegate::slotCommitAndCloseEditor(int)
0209 {
0210     // Emit the proper signals when editing has finished.
0211 
0212     QComboBox* const editor = qobject_cast<QComboBox*>(sender());
0213     Q_EMIT commitData(editor);
0214     Q_EMIT closeEditor(editor);
0215 }
0216 
0217 void ComboBoxDelegate::slotResetEditedState(QObject*)
0218 {
0219     d->rowEdited = -1;
0220 }
0221 
0222 } // namespace Digikam
0223 
0224 #include "moc_wscomboboxdelegate.cpp"