File indexing completed on 2024-04-21 04:41:00

0001 /* This file is part of the KDE project
0002    Copyright (C) 2004 Alexander Dymo <cloudtemple@mskat.net>
0003    Copyright (C) 2006-2018 Jarosław Staniek <staniek@kde.org>
0004 
0005    This library is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU Library General Public
0007    License as published by the Free Software Foundation; either
0008    version 2 of the License, or (at your option) any later version.
0009 
0010    This library is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013    Library General Public License for more details.
0014 
0015    You should have received a copy of the GNU Library General Public License
0016    along with this library; see the file COPYING.LIB.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "booledit.h"
0022 #include "KPropertyListData.h"
0023 #include "KPropertyUtils.h"
0024 #include "KPropertyUtils_p.h"
0025 #include "kproperty_debug.h"
0026 
0027 #include <QIcon>
0028 #include <QVariant>
0029 
0030 namespace {
0031 
0032 
0033 /**
0034  * 3-state Index
0035  */
0036 enum ThreeStateIndex {
0037     TrueIndex,
0038     FalseIndex,
0039     NoneIndex
0040 };
0041 
0042 //! @return name for state with index @a index
0043 QString stateName(ThreeStateIndex index, const QLocale &locale, const KProperty* prop = nullptr)
0044 {
0045     QString stateNameString;
0046     switch (index) {
0047     case TrueIndex:
0048         stateNameString = prop ? prop->option("yesName", QString()).toString() : QString();
0049         if (stateNameString.isEmpty()) {
0050             stateNameString = locale.language() == QLocale::C
0051                 ? QString::fromLatin1("true")
0052                 : QObject::tr("Yes", "Property value: Boolean state Yes");
0053         }
0054         break;
0055     case FalseIndex:
0056         stateNameString = prop ? prop->option("noName", QString()).toString() : QString();
0057         if (stateNameString.isEmpty()) {
0058             stateNameString = locale.language() == QLocale::C
0059                 ? QString::fromLatin1("false")
0060                 : QObject::tr("No", "Property value: Boolean state No");
0061         }
0062         break;
0063     case NoneIndex:
0064         stateNameString = prop ? prop->option("3rdStateName", QString()).toString() : QString();
0065         if (stateNameString.isEmpty()) {
0066             stateNameString = locale.language() == QLocale::C
0067                 ? QString::fromLatin1("null")
0068                 : QObject::tr("None", "Property value: Boolean (3rd) undefined state None");
0069         }
0070         break;
0071     }
0072     return stateNameString;
0073 }
0074 
0075 //! Sets up @a data list data with keys and names for true, false, none values, respectively
0076 void setupThreeStateListData(KPropertyListData *data, const KProperty* prop)
0077 {
0078     data->setKeys({ true, false, QVariant() });
0079     data->setNamesAsStringList({ stateName(TrueIndex, QLocale(), prop), stateName(FalseIndex, QLocale(), prop),
0080                                  stateName(NoneIndex, QLocale(), prop) });
0081 }
0082 
0083 /**
0084  * Returns index for given value
0085  *
0086  * Assumes that support for 3-state is enabled.
0087  */
0088 ThreeStateIndex valueToIndex(const QVariant& value)
0089 {
0090     if (value.isNull() || !value.isValid())
0091         return NoneIndex;
0092     else
0093         return value.toBool() ? TrueIndex : FalseIndex;
0094 }
0095 } // namespace
0096 
0097 //-------------------------
0098 
0099 class BoolEditGlobal
0100 {
0101 public:
0102     BoolEditGlobal()
0103         : yesIcon(QIcon::fromTheme(QLatin1String("dialog-ok")))
0104         , noIcon(QIcon::fromTheme(QLatin1String("kproperty-value-false")))
0105     {
0106         QPixmap none(16, 16);
0107         none.fill(Qt::transparent);
0108         noneIcon.addPixmap(none);
0109         none = QPixmap(22, 22);
0110         none.fill(Qt::transparent);
0111         noneIcon.addPixmap(none);
0112     }
0113     QIcon yesIcon;
0114     QIcon noIcon;
0115     QIcon noneIcon;
0116 };
0117 
0118 Q_GLOBAL_STATIC(BoolEditGlobal, g_boolEdit)
0119 
0120 class Q_DECL_HIDDEN KPropertyBoolEditor::Private
0121 {
0122 public:
0123     Private(const KProperty *prop)
0124      : yesText( stateName(TrueIndex, QLocale(), prop) )
0125      , noText( stateName(FalseIndex, QLocale(), prop) )
0126     {
0127     }
0128     QVariant value;
0129     QString yesText;
0130     QString noText;
0131 };
0132 
0133 KPropertyBoolEditor::KPropertyBoolEditor(const KProperty *prop, QWidget *parent)
0134     : QToolButton(parent), d(new Private(prop))
0135 {
0136     setFocusPolicy(Qt::WheelFocus);
0137     setCheckable(true);
0138     setAutoFillBackground(true);
0139     connect(this, SIGNAL(toggled(bool)), this, SLOT(slotValueChanged(bool)));
0140 }
0141 
0142 KPropertyBoolEditor::~KPropertyBoolEditor()
0143 {
0144     delete d;
0145 }
0146 
0147 QVariant KPropertyBoolEditor::value() const
0148 {
0149     return d->value;
0150 }
0151 
0152 void KPropertyBoolEditor::setValue(const QVariant &value)
0153 {
0154     d->value = value;
0155     if (value.type() == QVariant::Bool) {
0156         setChecked(value.toBool());
0157     }
0158 }
0159 
0160 void
0161 KPropertyBoolEditor::slotValueChanged(bool state)
0162 {
0163     d->value = state;
0164     emit commitData(this);
0165 }
0166 
0167 void KPropertyBoolEditor::draw(QPainter *p, const QRect &r, const QVariant &value,
0168                     const QString& text, bool threeState)
0169 {
0170     QIcon icon;
0171     QSize actualIconSize;
0172     QPoint textOffset;
0173     if (threeState && valueToIndex(value) == NoneIndex) {
0174         // draw icon for the 3rd state for Three-State editor
0175         icon = g_boolEdit->noneIcon;
0176         actualIconSize = g_boolEdit->yesIcon.actualSize(r.size());
0177         textOffset = QPoint(actualIconSize.width() + 6, 0);
0178     } else {
0179         // draw true or false icon regardless of the 2 or 3 state version
0180         icon = value.toBool() ? g_boolEdit->yesIcon : g_boolEdit->noIcon;
0181         actualIconSize = icon.actualSize(r.size());
0182         textOffset = QPoint(actualIconSize.width() + 6, 0);
0183     }
0184     QRect r2(r);
0185     r2.moveTop(r2.top() + 2);
0186     r2.setLeft(r2.left() + 3);
0187     //r2.setTop(r2.top() + (r.height() - actualIconSize.height()) / 2);
0188 
0189     icon.paint(p, r2, Qt::AlignVCenter | Qt::AlignLeft);
0190     p->drawText(r2.translated(textOffset), Qt::AlignVCenter | Qt::AlignLeft, text);
0191 }
0192 
0193 void KPropertyBoolEditor::paintEvent( QPaintEvent * event )
0194 {
0195     QToolButton::paintEvent(event);
0196     QPainter p(this);
0197     const QVariant v( value() );
0198     KPropertyBoolEditor::draw(&p, rect(), v,
0199         v.toBool() ? d->yesText : d->noText, false /*2state*/);
0200 }
0201 
0202 bool KPropertyBoolEditor::eventFilter(QObject* watched, QEvent* e)
0203 {
0204     if (e->type() == QEvent::KeyPress) {
0205         QKeyEvent* ev = static_cast<QKeyEvent*>(e);
0206         const int k = ev->key();
0207         if (k == Qt::Key_Space || k == Qt::Key_Enter || k == Qt::Key_Return) {
0208             toggle();
0209             return true;
0210         }
0211     }
0212     return QToolButton::eventFilter(watched, e);
0213 }
0214 
0215 //--------------------------------------------------
0216 
0217 class ThreeStateBoolIconProvider : public KPropertyComboBoxEditorIconProviderInterface
0218 {
0219 public:
0220     ThreeStateBoolIconProvider() {}
0221     QIcon icon(int index) const override
0222     {
0223         switch (index) {
0224         case TrueIndex:
0225             return g_boolEdit->yesIcon;
0226         case FalseIndex:
0227             return g_boolEdit->noIcon;
0228         default:
0229             return g_boolEdit->noneIcon;
0230         }
0231     }
0232     KPropertyComboBoxEditorIconProviderInterface* clone() const override
0233     {
0234         return new ThreeStateBoolIconProvider();
0235     }
0236 };
0237 
0238 static KPropertyComboBoxEditorOptions initThreeStateBoolOptions()
0239 {
0240     KPropertyComboBoxEditorOptions options;
0241     options.iconProvider = new ThreeStateBoolIconProvider();
0242     return options;
0243 }
0244 
0245 class Q_DECL_HIDDEN KPropertyThreeStateBoolEditor::Private
0246 {
0247 public:
0248     Private() {
0249     }
0250 };
0251 
0252 KPropertyThreeStateBoolEditor::KPropertyThreeStateBoolEditor(const KPropertyListData &listData,
0253                                                              QWidget *parent)
0254     : KPropertyComboBoxEditor(listData, initThreeStateBoolOptions(), parent), d(new Private)
0255 {
0256 //    QPixmap nullIcon(m_yesIcon.size());   //transparent pixmap of appropriate size
0257 //    nullIcon.fill(Qt::transparent);
0258 //    m_edit->addItem(nullIcon, thirdState.toString().isEmpty() ? tr("None") : thirdState.toString());
0259     setCurrentIndex(NoneIndex);
0260 }
0261 
0262 KPropertyThreeStateBoolEditor::~KPropertyThreeStateBoolEditor()
0263 {
0264     delete d;
0265 }
0266 
0267 QVariant KPropertyThreeStateBoolEditor::value() const
0268 {
0269     // list items: true, false, NULL
0270     const int i = currentIndex();
0271     const ThreeStateIndex index
0272         = (i >= TrueIndex && i <= NoneIndex) ? static_cast<ThreeStateIndex>(i) : NoneIndex;
0273     switch (index) {
0274     case TrueIndex:
0275         return true;
0276     case FalseIndex:
0277         return false;
0278     default:
0279         return QVariant();
0280     }
0281 }
0282 
0283 /*void ThreeStateBoolEdit::setProperty(Property *prop)
0284 {
0285     m_setValueEnabled = false; //setValue() couldn't be called before fillBox()
0286     Widget::setProperty(prop);
0287     m_setValueEnabled = true;
0288     if (prop)
0289         setValue(prop->value(), KProperty::ValueOption::IgnoreOld); //now the value can be set
0290 }*/
0291 
0292 void KPropertyThreeStateBoolEditor::setValue(const QVariant &value)
0293 {
0294     setCurrentIndex( valueToIndex(value) );
0295 }
0296 
0297 //---------------
0298 
0299 KPropertyBoolDelegate::KPropertyBoolDelegate()
0300 {
0301     options()->setBordersVisible(true);
0302 }
0303 
0304 QWidget * KPropertyBoolDelegate::createEditor( int type, QWidget *parent,
0305     const QStyleOptionViewItem & option, const QModelIndex & index ) const
0306 {
0307     Q_UNUSED(type);
0308     Q_UNUSED(option);
0309     KProperty *prop = KPropertyUtils::propertyForIndex(index);
0310 
0311     // boolean editors can optionally accept 3rd state:
0312     if (prop && prop->option("3State", false).toBool()) {
0313         KPropertyListData threeStateListData;
0314         setupThreeStateListData(&threeStateListData, prop);
0315         return new KPropertyThreeStateBoolEditor(threeStateListData, parent);
0316     }
0317     else {
0318         return new KPropertyBoolEditor(prop, parent);
0319     }
0320 }
0321 
0322 void KPropertyBoolDelegate::paint( QPainter * painter,
0323     const QStyleOptionViewItem & option, const QModelIndex & index ) const
0324 {
0325     const KPropertyUtilsPrivate::PainterSaver saver(painter);
0326     KProperty *prop = KPropertyUtils::propertyForIndex(index);
0327     if (!prop) {
0328         return;
0329     }
0330     const QVariant value( index.data(Qt::EditRole) );
0331     QRect rect(option.rect);
0332     const bool threeState = prop->option("3State", false).toBool();
0333     KPropertyBoolEditor::draw(painter, rect.translated(0, -2), value, propertyValueToString(prop, QLocale()), threeState);
0334 }
0335 
0336 QString KPropertyBoolDelegate::propertyValueToString(const KProperty* prop, const QLocale &locale) const
0337 {
0338     if (prop->option("3State", false).toBool()) {
0339         const ThreeStateIndex listIndex = valueToIndex(prop->value());
0340         return stateName(listIndex, locale, prop);
0341     }
0342     if (prop->value().isNull() && !prop->option("nullName", QString()).toString().isEmpty()) {
0343         return prop->option("nullName", QString()).toString();
0344     }
0345     return valueToString(prop->value(), locale);
0346 }
0347 
0348 QString KPropertyBoolDelegate::valueToString(const QVariant& value, const QLocale &locale) const
0349 {
0350     // assume 2-state
0351     return stateName(value.toBool() ? TrueIndex : FalseIndex, locale);
0352 }