File indexing completed on 2024-05-12 05:07:58
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 const QList<KMyMoneyMVCCombo*> comboList = widget->findChildren<KMyMoneyMVCCombo*>(); 0115 for (auto combo : comboList) { 0116 combo->setSubstringSearch(enabled); 0117 } 0118 } 0119 0120 void KMyMoneyMVCCombo::setPlaceholderText(const QString& hint) const 0121 { 0122 KLineEdit* le = qobject_cast<KLineEdit*>(lineEdit()); 0123 if (le) { 0124 le->setPlaceholderText(hint); 0125 } 0126 } 0127 0128 QString KMyMoneyMVCCombo::selectedItem() const 0129 { 0130 Q_D(const KMyMoneyMVCCombo); 0131 auto dataVariant = itemData(currentIndex()); 0132 if (dataVariant.isValid()) 0133 d->m_id = dataVariant.toString(); 0134 else 0135 d->m_id.clear(); 0136 return d->m_id; 0137 } 0138 0139 void KMyMoneyMVCCombo::setSelectedItem(const QString& id) 0140 { 0141 Q_D(KMyMoneyMVCCombo); 0142 d->m_id = id; 0143 setCurrentIndex(findData(QVariant(d->m_id))); 0144 } 0145 0146 void KMyMoneyMVCCombo::activated(int index) 0147 { 0148 Q_D(KMyMoneyMVCCombo); 0149 auto dataVariant = itemData(index); 0150 if (dataVariant.isValid()) { 0151 d->m_id = dataVariant.toString(); 0152 Q_EMIT itemSelected(d->m_id); 0153 } 0154 } 0155 0156 void KMyMoneyMVCCombo::connectNotify(const QMetaMethod & signal) 0157 { 0158 Q_D(KMyMoneyMVCCombo); 0159 if (signal != QMetaMethod::fromSignal(&KMyMoneyMVCCombo::createItem)) { 0160 d->m_canCreateObjects = true; 0161 } 0162 } 0163 0164 void KMyMoneyMVCCombo::disconnectNotify(const QMetaMethod & signal) 0165 { 0166 Q_D(KMyMoneyMVCCombo); 0167 if (signal != QMetaMethod::fromSignal(&KMyMoneyMVCCombo::createItem)) { 0168 d->m_canCreateObjects = false; 0169 } 0170 } 0171 0172 void KMyMoneyMVCCombo::setCurrentText(const QString& txt) 0173 { 0174 KComboBox::setItemText(KComboBox::currentIndex(), txt); 0175 } 0176 0177 void KMyMoneyMVCCombo::setCurrentText() 0178 { 0179 KComboBox::setItemText(KComboBox::currentIndex(), QString()); 0180 } 0181 0182 0183 void KMyMoneyMVCCombo::focusOutEvent(QFocusEvent* e) 0184 { 0185 Q_D(KMyMoneyMVCCombo); 0186 // when showing m_completion we'll receive a focus out event even if the focus 0187 // will still remain at this widget since this widget is the completion's focus proxy 0188 // so ignore the focus out event caused by showing a widget of type Qt::Popup 0189 if (e->reason() == Qt::PopupFocusReason) 0190 return; 0191 0192 if (d->m_inFocusOutEvent) { 0193 KComboBox::focusOutEvent(e); 0194 return; 0195 } 0196 0197 //check if the focus went to a widget in TransactionFrom, in the Register, or on a WizardPage 0198 if (e->reason() == Qt::MouseFocusReason) { 0199 QObject *w = this->parent(); 0200 QObject *q = qApp->focusWidget()->parent(); 0201 // KMyMoneyTagCombo is inside KTagContainer, KMyMoneyPayeeCombo isn't it 0202 if (w->inherits("KTagContainer")) 0203 w = w->parent(); 0204 while (q && q->objectName() != "qt_scrollarea_viewport") 0205 q = q->parent(); 0206 if (q != w && qApp->focusWidget()->parent() != w && qApp->focusWidget()->objectName() != "register") { 0207 KComboBox::focusOutEvent(e); 0208 return; 0209 } 0210 } 0211 0212 d->m_inFocusOutEvent = true; 0213 if (isEditable() && !currentText().isEmpty() && e->reason() != Qt::ActiveWindowFocusReason) { 0214 if (d->m_canCreateObjects) { 0215 // in case we tab out, we make sure that if the current completion 0216 // contains the current text that we set the current text to 0217 // the full completion text but only if the completion box is visible. 0218 // BUG 254984 is resolved with the visibility check 0219 if (e->reason() != Qt::MouseFocusReason) { 0220 if (d->m_completer->popup() && d->m_completer->popup()->isVisible() 0221 && d->m_completer->currentCompletion().contains(currentText(), Qt::CaseInsensitive)) { 0222 lineEdit()->setText(d->m_completer->currentCompletion()); 0223 } 0224 } 0225 0226 //check if the current text is contained in the internal list, if not ask the user if want to create a new item. 0227 checkCurrentText(); 0228 0229 // else if we cannot create objects, and the current text is not 0230 // in the list, then we clear the text and the selection. 0231 } else if (!contains(currentText())) { 0232 clearEditText(); 0233 } 0234 //this is to cover the case when you highlight an item but don't activate it with Enter 0235 if (currentText() != itemText(currentIndex())) { 0236 setCurrentIndex(findText(currentText(), Qt::MatchExactly)); 0237 Q_EMIT activated(currentIndex()); 0238 } 0239 } 0240 0241 KComboBox::focusOutEvent(e); 0242 0243 // force update of hint and id if there is no text in the widget 0244 if (isEditable() && currentText().isEmpty()) { 0245 QString id = d->m_id; 0246 d->m_id.clear(); 0247 setCurrentIndex(-1); 0248 if (!id.isEmpty()) 0249 Q_EMIT itemSelected(d->m_id); 0250 update(); 0251 } 0252 0253 d->m_inFocusOutEvent = false; 0254 // This is used only be KMyMoneyTagCombo at this time 0255 Q_EMIT lostFocus(); 0256 } 0257 0258 void KMyMoneyMVCCombo::checkCurrentText() 0259 { 0260 Q_D(KMyMoneyMVCCombo); 0261 const auto payeeName = currentText(); 0262 if (!contains(payeeName)) { 0263 QString id; 0264 // announce that we go into a possible dialog to create an object 0265 // This can be used by upstream widgets to disable filters etc. 0266 Q_EMIT objectCreation(true); 0267 0268 Q_EMIT createItem(payeeName, id); 0269 0270 // Announce that we return from object creation 0271 Q_EMIT objectCreation(false); 0272 0273 // update the field to a possibly created object 0274 d->m_id = id; 0275 addEntry(payeeName, id); 0276 setCurrentTextById(id); 0277 } 0278 } 0279 0280 void KMyMoneyMVCCombo::addEntry(const QString& newTxt, const QString& id) 0281 { 0282 // find the correct position in the list 0283 int idx; 0284 for(idx = 0; idx < model()->rowCount(); ++idx) { 0285 const QString txt = itemText(idx); 0286 if (txt.compare(newTxt) > 0) { 0287 break; 0288 } 0289 } 0290 // and insert the new item 0291 insertItem(idx - 1, QIcon(), newTxt, id); 0292 } 0293 0294 void KMyMoneyMVCCombo::setCurrentTextById(const QString& id) 0295 { 0296 clearEditText(); 0297 if (!id.isEmpty()) { 0298 int index = findData(QVariant(id), Qt::UserRole, Qt::MatchExactly); 0299 if (index > -1) { 0300 setCompletedText(itemText(index)); 0301 setEditText(itemText(index)); 0302 setCurrentIndex(index); 0303 } 0304 } 0305 } 0306 0307 void KMyMoneyMVCCombo::protectItem(int id, bool protect) 0308 { 0309 QStandardItemModel* standardModel = qobject_cast<QStandardItemModel*> (model()); 0310 QStandardItem* standardItem = standardModel->item(id); 0311 standardItem->setSelectable(!protect); 0312 }