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 }