File indexing completed on 2024-05-12 16:44:03
0001 /* 0002 SPDX-FileCopyrightText: 2010-2018 Thomas Baumgart <tbaumgart@kde.org> 0003 SPDX-FileCopyrightText: 2010-2016 Cristian Oneț <onet.cristian@gmail.com> 0004 SPDX-FileCopyrightText: 2010 Alvaro Soliverez <asoliverez@gmail.com> 0005 SPDX-FileCopyrightText: 2017-2018 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com> 0006 SPDX-FileCopyrightText: 2020 Robert Szczesiak <dev.rszczesiak@gmail.com> 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "kmymoneymvccombo.h" 0011 #include "kmymoneymvccombo_p.h" 0012 0013 // ---------------------------------------------------------------------------- 0014 // QT Includes 0015 0016 #include <QStandardItemModel> 0017 #include <QAbstractItemView> 0018 #include <QCompleter> 0019 #include <QFocusEvent> 0020 #include <QApplication> 0021 #include <QMetaMethod> 0022 0023 // ---------------------------------------------------------------------------- 0024 // KDE Includes 0025 0026 #include <KLineEdit> 0027 0028 // ---------------------------------------------------------------------------- 0029 // Project Includes 0030 0031 0032 KMyMoneyMVCCombo::KMyMoneyMVCCombo(QWidget* parent) : 0033 KComboBox(parent), 0034 d_ptr(new KMyMoneyMVCComboPrivate) 0035 { 0036 view()->setAlternatingRowColors(true); 0037 connect(this, static_cast<void (KComboBox::*)(int)>(&KMyMoneyMVCCombo::KComboBox::activated), this, &KMyMoneyMVCCombo::activated); 0038 } 0039 0040 KMyMoneyMVCCombo::KMyMoneyMVCCombo(bool editable, QWidget* parent) : 0041 KComboBox(editable, parent), 0042 d_ptr(new KMyMoneyMVCComboPrivate) 0043 { 0044 Q_D(KMyMoneyMVCCombo); 0045 d->m_completer = new QCompleter(this); 0046 d->m_completer->setCaseSensitivity(Qt::CaseInsensitive); 0047 d->m_completer->setModel(model()); 0048 setCompleter(d->m_completer); 0049 0050 // setSubstringSearch(!KMyMoneySettings::stringMatchFromStart()); 0051 0052 view()->setAlternatingRowColors(true); 0053 setInsertPolicy(QComboBox::NoInsert); // don't insert new objects due to object creation 0054 connect(this, static_cast<void (KComboBox::*)(int)>(&KMyMoneyMVCCombo::KComboBox::activated), this, &KMyMoneyMVCCombo::activated); 0055 } 0056 0057 KMyMoneyMVCCombo::KMyMoneyMVCCombo(KMyMoneyMVCComboPrivate &dd, QWidget* parent) : 0058 KComboBox(parent), 0059 d_ptr(&dd) 0060 { 0061 view()->setAlternatingRowColors(true); 0062 connect(this, static_cast<void (KComboBox::*)(int)>(&KMyMoneyMVCCombo::KComboBox::activated), this, &KMyMoneyMVCCombo::activated); 0063 } 0064 0065 KMyMoneyMVCCombo::KMyMoneyMVCCombo(KMyMoneyMVCComboPrivate &dd, bool editable, QWidget* parent) : 0066 KComboBox(editable, parent), 0067 d_ptr(&dd) 0068 { 0069 Q_D(KMyMoneyMVCCombo); 0070 d->m_completer = new QCompleter(this); 0071 d->m_completer->setCaseSensitivity(Qt::CaseInsensitive); 0072 d->m_completer->setModel(model()); 0073 setCompleter(d->m_completer); 0074 0075 // setSubstringSearch(!KMyMoneySettings::stringMatchFromStart()); 0076 0077 view()->setAlternatingRowColors(true); 0078 setInsertPolicy(QComboBox::NoInsert); // don't insert new objects due to object creation 0079 connect(this, static_cast<void (KComboBox::*)(int)>(&KMyMoneyMVCCombo::KComboBox::activated), this, &KMyMoneyMVCCombo::activated); 0080 } 0081 0082 KMyMoneyMVCCombo::~KMyMoneyMVCCombo() 0083 { 0084 Q_D(KMyMoneyMVCCombo); 0085 delete d; 0086 } 0087 0088 void KMyMoneyMVCCombo::setEditable(bool editable) 0089 { 0090 Q_D(KMyMoneyMVCCombo); 0091 KComboBox::setEditable(editable); 0092 0093 if(editable) { 0094 if(!d->m_completer) { 0095 d->m_completer = new QCompleter(this); 0096 d->m_completer->setCaseSensitivity(Qt::CaseInsensitive); 0097 d->m_completer->setModel(model()); 0098 } 0099 setCompleter(d->m_completer); 0100 } 0101 } 0102 0103 void KMyMoneyMVCCombo::setSubstringSearch(bool enabled) 0104 { 0105 Q_D(KMyMoneyMVCCombo); 0106 d->m_completer->setCompletionMode(QCompleter::PopupCompletion); 0107 d->m_completer->setModel(model()); 0108 d->m_completer->setFilterMode(enabled ? Qt::MatchContains : Qt::MatchStartsWith); 0109 } 0110 0111 void KMyMoneyMVCCombo::setSubstringSearchForChildren(QWidget*const widget, bool enabled) 0112 { 0113 Q_CHECK_PTR(widget); 0114 QList<KMyMoneyMVCCombo *> comboList; 0115 comboList = widget->findChildren<KMyMoneyMVCCombo *>(); 0116 foreach (KMyMoneyMVCCombo *combo, comboList) { 0117 combo->setSubstringSearch(enabled); 0118 } 0119 } 0120 0121 void KMyMoneyMVCCombo::setPlaceholderText(const QString& hint) const 0122 { 0123 KLineEdit* le = qobject_cast<KLineEdit*>(lineEdit()); 0124 if (le) { 0125 le->setPlaceholderText(hint); 0126 } 0127 } 0128 0129 QString KMyMoneyMVCCombo::selectedItem() const 0130 { 0131 Q_D(const KMyMoneyMVCCombo); 0132 auto dataVariant = itemData(currentIndex()); 0133 if (dataVariant.isValid()) 0134 d->m_id = dataVariant.toString(); 0135 else 0136 d->m_id.clear(); 0137 return d->m_id; 0138 } 0139 0140 void KMyMoneyMVCCombo::setSelectedItem(const QString& id) 0141 { 0142 Q_D(KMyMoneyMVCCombo); 0143 d->m_id = id; 0144 setCurrentIndex(findData(QVariant(d->m_id))); 0145 } 0146 0147 void KMyMoneyMVCCombo::activated(int index) 0148 { 0149 Q_D(KMyMoneyMVCCombo); 0150 auto dataVariant = itemData(index); 0151 if (dataVariant.isValid()) { 0152 d->m_id = dataVariant.toString(); 0153 emit itemSelected(d->m_id); 0154 } 0155 } 0156 0157 void KMyMoneyMVCCombo::connectNotify(const QMetaMethod & signal) 0158 { 0159 Q_D(KMyMoneyMVCCombo); 0160 if (signal != QMetaMethod::fromSignal(&KMyMoneyMVCCombo::createItem)) { 0161 d->m_canCreateObjects = true; 0162 } 0163 } 0164 0165 void KMyMoneyMVCCombo::disconnectNotify(const QMetaMethod & signal) 0166 { 0167 Q_D(KMyMoneyMVCCombo); 0168 if (signal != QMetaMethod::fromSignal(&KMyMoneyMVCCombo::createItem)) { 0169 d->m_canCreateObjects = false; 0170 } 0171 } 0172 0173 void KMyMoneyMVCCombo::setCurrentText(const QString& txt) 0174 { 0175 KComboBox::setItemText(KComboBox::currentIndex(), txt); 0176 } 0177 0178 void KMyMoneyMVCCombo::setCurrentText() 0179 { 0180 KComboBox::setItemText(KComboBox::currentIndex(), QString()); 0181 } 0182 0183 0184 void KMyMoneyMVCCombo::focusOutEvent(QFocusEvent* e) 0185 { 0186 Q_D(KMyMoneyMVCCombo); 0187 // when showing m_completion we'll receive a focus out event even if the focus 0188 // will still remain at this widget since this widget is the completion's focus proxy 0189 // so ignore the focus out event caused by showing a widget of type Qt::Popup 0190 if (e->reason() == Qt::PopupFocusReason) 0191 return; 0192 0193 if (d->m_inFocusOutEvent) { 0194 KComboBox::focusOutEvent(e); 0195 return; 0196 } 0197 0198 //check if the focus went to a widget in TransactionFrom, in the Register, or on a WizardPage 0199 if (e->reason() == Qt::MouseFocusReason) { 0200 QObject *w = this->parent(); 0201 QObject *q = qApp->focusWidget()->parent(); 0202 // KMyMoneyTagCombo is inside KTagContainer, KMyMoneyPayeeCombo isn't it 0203 if (w->inherits("KTagContainer")) 0204 w = w->parent(); 0205 while (q && q->objectName() != "qt_scrollarea_viewport") 0206 q = q->parent(); 0207 if (q != w && qApp->focusWidget()->parent() != w && qApp->focusWidget()->objectName() != "register") { 0208 KComboBox::focusOutEvent(e); 0209 return; 0210 } 0211 } 0212 0213 d->m_inFocusOutEvent = true; 0214 if (isEditable() && !currentText().isEmpty() && e->reason() != Qt::ActiveWindowFocusReason) { 0215 if (d->m_canCreateObjects) { 0216 // in case we tab out, we make sure that if the current completion 0217 // contains the current text that we set the current text to 0218 // the full completion text but only if the completion box is visible. 0219 // BUG 254984 is resolved with the visibility check 0220 if (e->reason() != Qt::MouseFocusReason) { 0221 if (d->m_completer->popup() && d->m_completer->popup()->isVisible() 0222 && d->m_completer->currentCompletion().contains(currentText(), Qt::CaseInsensitive)) { 0223 lineEdit()->setText(d->m_completer->currentCompletion()); 0224 } 0225 } 0226 0227 //check if the current text is contained in the internal list, if not ask the user if want to create a new item. 0228 checkCurrentText(); 0229 0230 // else if we cannot create objects, and the current text is not 0231 // in the list, then we clear the text and the selection. 0232 } else if (!contains(currentText())) { 0233 clearEditText(); 0234 } 0235 //this is to cover the case when you highlight an item but don't activate it with Enter 0236 if (currentText() != itemText(currentIndex())) { 0237 setCurrentIndex(findText(currentText(), Qt::MatchExactly)); 0238 emit activated(currentIndex()); 0239 } 0240 } 0241 0242 KComboBox::focusOutEvent(e); 0243 0244 // force update of hint and id if there is no text in the widget 0245 if (isEditable() && currentText().isEmpty()) { 0246 QString id = d->m_id; 0247 d->m_id.clear(); 0248 setCurrentIndex(-1); 0249 if (!id.isEmpty()) 0250 emit itemSelected(d->m_id); 0251 update(); 0252 } 0253 0254 d->m_inFocusOutEvent = false; 0255 // This is used only be KMyMoneyTagCombo at this time 0256 emit lostFocus(); 0257 } 0258 0259 void KMyMoneyMVCCombo::checkCurrentText() 0260 { 0261 Q_D(KMyMoneyMVCCombo); 0262 const auto payeeName = currentText(); 0263 if (!contains(payeeName)) { 0264 QString id; 0265 // announce that we go into a possible dialog to create an object 0266 // This can be used by upstream widgets to disable filters etc. 0267 emit objectCreation(true); 0268 0269 emit createItem(payeeName, id); 0270 0271 // Announce that we return from object creation 0272 emit objectCreation(false); 0273 0274 // update the field to a possibly created object 0275 d->m_id = id; 0276 addEntry(payeeName, id); 0277 setCurrentTextById(id); 0278 } 0279 } 0280 0281 void KMyMoneyMVCCombo::addEntry(const QString& newTxt, const QString& id) 0282 { 0283 // find the correct position in the list 0284 int idx; 0285 for(idx = 0; idx < model()->rowCount(); ++idx) { 0286 const QString txt = itemText(idx); 0287 if (txt.compare(newTxt) > 0) { 0288 break; 0289 } 0290 } 0291 // and insert the new item 0292 insertItem(idx - 1, QIcon(), newTxt, id); 0293 } 0294 0295 void KMyMoneyMVCCombo::setCurrentTextById(const QString& id) 0296 { 0297 clearEditText(); 0298 if (!id.isEmpty()) { 0299 int index = findData(QVariant(id), Qt::UserRole, Qt::MatchExactly); 0300 if (index > -1) { 0301 setCompletedText(itemText(index)); 0302 setEditText(itemText(index)); 0303 setCurrentIndex(index); 0304 } 0305 } 0306 } 0307 0308 void KMyMoneyMVCCombo::protectItem(int id, bool protect) 0309 { 0310 QStandardItemModel* standardModel = qobject_cast<QStandardItemModel*> (model()); 0311 QStandardItem* standardItem = standardModel->item(id); 0312 standardItem->setSelectable(!protect); 0313 }