File indexing completed on 2024-05-12 16:44:02
0001 /* 0002 SPDX-FileCopyrightText: 2004-2011 Thomas Baumgart <tbaumgart@kde.org> 0003 SPDX-FileCopyrightText: 2017 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com> 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kmymoneycompletion.h" 0008 #include "kmymoneycompletion_p.h" 0009 0010 // ---------------------------------------------------------------------------- 0011 // QT Includes 0012 0013 #include <QApplication> 0014 #include <QKeyEvent> 0015 #include <QEvent> 0016 #include <QDesktopWidget> 0017 #include <QLineEdit> 0018 #include <QVBoxLayout> 0019 0020 // ---------------------------------------------------------------------------- 0021 // KDE Includes 0022 0023 // ---------------------------------------------------------------------------- 0024 // Project Includes 0025 0026 #include <kmymoneyselector.h> 0027 #include "kmymoneycombo.h" 0028 #include "widgetenums.h" 0029 0030 KMyMoneyCompletion::KMyMoneyCompletion(QWidget *parent) : 0031 QWidget(parent), 0032 d_ptr(new KMyMoneyCompletionPrivate) 0033 { 0034 Q_D(KMyMoneyCompletion); 0035 setWindowFlags(Qt::ToolTip); 0036 // make it look like the Qt completer 0037 QVBoxLayout *completionLayout = new QVBoxLayout(this); 0038 completionLayout->setContentsMargins(0, 0, 0, 0); 0039 completionLayout->setSpacing(0); 0040 0041 d->m_parent = parent; 0042 d->m_selector = new KMyMoneySelector(this); 0043 d->m_selector->listView()->setFocusProxy(parent); 0044 completionLayout->addWidget(d->m_selector); 0045 0046 // to handle the keyboard events received by this widget in the same way as 0047 // the keyboard events received by the other widgets 0048 installEventFilter(this); 0049 0050 connectSignals(d->m_selector, d->m_selector->listView()); 0051 } 0052 0053 void KMyMoneyCompletion::connectSignals(QWidget* widget, QTreeWidget* lv) 0054 { 0055 Q_D(KMyMoneyCompletion); 0056 d->m_widget = widget; 0057 d->m_lv = lv; 0058 connect(lv, &QTreeWidget::itemActivated, this, &KMyMoneyCompletion::slotItemSelected); 0059 connect(lv, &QTreeWidget::itemClicked, this, &KMyMoneyCompletion::slotItemSelected); 0060 } 0061 0062 KMyMoneyCompletion::KMyMoneyCompletion(KMyMoneyCompletionPrivate &dd, QWidget* parent) : 0063 QWidget(parent), 0064 d_ptr(&dd) 0065 { 0066 } 0067 0068 KMyMoneyCompletion::~KMyMoneyCompletion() 0069 { 0070 Q_D(KMyMoneyCompletion); 0071 delete d; 0072 } 0073 0074 void KMyMoneyCompletion::adjustSize() 0075 { 0076 Q_D(KMyMoneyCompletion); 0077 QTreeWidgetItemIterator it(d->m_lv, QTreeWidgetItemIterator::NotHidden); 0078 int count = 0; 0079 while (*it) { 0080 ++count; 0081 ++it; 0082 } 0083 adjustSize(count); 0084 } 0085 0086 void KMyMoneyCompletion::adjustSize(const int count) 0087 { 0088 Q_D(KMyMoneyCompletion); 0089 int w = d->m_widget->sizeHint().width(); 0090 if (d->m_parent && w < d->m_parent->width()) 0091 w = d->m_parent->width(); 0092 0093 const int minimumWidth = fontMetrics().width(QLatin1Char('W')) * 15; 0094 w = qMax(w, minimumWidth); 0095 0096 int h = 0; 0097 QTreeWidgetItemIterator it(d->m_lv, QTreeWidgetItemIterator::NotHidden); 0098 QTreeWidgetItem* item = *it; 0099 if (item) 0100 // the +1 in the next statement avoids the display of a scroll bar if count < MAX_ITEMS. 0101 h = item->treeWidget()->visualItemRect(item).height() * (count > KMyMoneyCompletionPrivate::MAX_ITEMS - 1 ? KMyMoneyCompletionPrivate::MAX_ITEMS : count + 1); 0102 0103 resize(w, h); 0104 0105 if (d->m_parent) { 0106 // the code of this basic block is taken from KCompletionBox::show() 0107 // and modified to our local needs 0108 0109 QRect screenSize = QApplication::desktop()->availableGeometry(parentWidget()); 0110 0111 QPoint orig = d->m_parent->mapToGlobal(QPoint(0, d->m_parent->height())); 0112 int x = orig.x(); 0113 int y = orig.y(); 0114 0115 if (x + width() > screenSize.right()) 0116 x = screenSize.right() - width(); 0117 0118 // check for the maximum height here to avoid flipping 0119 // of the completion box from top to bottom of the 0120 // edit widget. The offset (y) is certainly based 0121 // on the actual height. 0122 if (item) { 0123 if ((y + item->treeWidget()->visualItemRect(item).height() * KMyMoneyCompletionPrivate::MAX_ITEMS) > screenSize.bottom()) 0124 y = y - height() - d->m_parent->height(); 0125 } 0126 0127 move(x, y); 0128 } 0129 } 0130 0131 void KMyMoneyCompletion::showEvent(QShowEvent* e) 0132 { 0133 show(true); 0134 QWidget::showEvent(e); 0135 } 0136 0137 void KMyMoneyCompletion::show(bool presetSelected) 0138 { 0139 Q_D(KMyMoneyCompletion); 0140 if (!d->m_id.isEmpty() && presetSelected) 0141 d->m_selector->setSelected(d->m_id); 0142 0143 adjustSize(); 0144 0145 if (d->m_parent) { 0146 d->m_parent->installEventFilter(this); 0147 // make sure to install the filter for the combobox lineedit as well 0148 // We have do this here because QObject::installEventFilter() is not 0149 // declared virtual and we have no chance to override it in KMyMoneyCombo 0150 KMyMoneyCombo* c = dynamic_cast<KMyMoneyCombo*>(d->m_parent); 0151 if (c && c->lineEdit()) { 0152 c->lineEdit()->installEventFilter(this); 0153 } 0154 } 0155 QWidget::show(); 0156 0157 // make sure that the parent is the input context's focus widget instead of the selector's list 0158 //if (qApp->inputContext()->focusWidget() == m_selector->listView()) 0159 //qApp->inputContext()->setFocusWidget(m_parent); 0160 } 0161 0162 void KMyMoneyCompletion::hide() 0163 { 0164 Q_D(KMyMoneyCompletion); 0165 if (d->m_parent) { 0166 d->m_parent->removeEventFilter(this); 0167 // make sure to uninstall the filter for the combobox lineedit as well 0168 // We have do this here because QObject::installEventFilter() is not 0169 // declared virtual and we have no chance to override it in KMyMoneyCombo 0170 KMyMoneyCombo* c = dynamic_cast<KMyMoneyCombo*>(d->m_parent); 0171 if (c && c->lineEdit()) { 0172 c->lineEdit()->removeEventFilter(this); 0173 } 0174 } 0175 QWidget::hide(); 0176 } 0177 0178 bool KMyMoneyCompletion::eventFilter(QObject* o, QEvent* e) 0179 { 0180 Q_D(KMyMoneyCompletion); 0181 KMyMoneyCombo *c = dynamic_cast<KMyMoneyCombo*>(d->m_parent); 0182 if (o == d->m_parent || (c && o == c->lineEdit()) || o == this) { 0183 if (isVisible()) { 0184 #ifdef Q_OS_WIN32 //krazy:exclude=cpp 0185 // hide the completer only if the focus was not lost because of windows activation or the activated window is not an application window 0186 if (e->type() == QEvent::FocusOut && (static_cast<QFocusEvent*>(e)->reason() != Qt::ActiveWindowFocusReason || QApplication::activeWindow() == 0)) { 0187 #else 0188 if (e->type() == QEvent::FocusOut) { 0189 #endif 0190 hide(); 0191 } 0192 if (e->type() == QEvent::KeyPress) { 0193 QTreeWidgetItem* item = 0; 0194 QKeyEvent* ev = static_cast<QKeyEvent*>(e); 0195 switch (ev->key()) { 0196 case Qt::Key_Tab: 0197 case Qt::Key_Backtab: 0198 slotItemSelected(d->m_lv->currentItem(), 0); 0199 break; 0200 0201 case Qt::Key_Down: 0202 case Qt::Key_PageDown: 0203 item = d->m_lv->currentItem(); 0204 while (item) { 0205 item = d->m_lv->itemBelow(item); 0206 if (item && selector()->match(d->m_lastCompletion, item)) 0207 break; 0208 } 0209 if (item) { 0210 d->m_lv->setCurrentItem(item); 0211 d->m_lv->scrollToItem(item); 0212 } 0213 ev->accept(); 0214 return true; 0215 0216 case Qt::Key_Up: 0217 case Qt::Key_PageUp: 0218 item = d->m_lv->currentItem(); 0219 while (item) { 0220 item = d->m_lv->itemAbove(item); 0221 if (item && selector()->match(d->m_lastCompletion, item)) 0222 break; 0223 } 0224 if (item) { 0225 d->m_lv->setCurrentItem(item); 0226 // make sure, we always see a possible (non-selectable) group item 0227 if (d->m_lv->itemAbove(item)) 0228 item = d->m_lv->itemAbove(item); 0229 d->m_lv->scrollToItem(item); 0230 } 0231 ev->accept(); 0232 return true; 0233 0234 case Qt::Key_Escape: 0235 hide(); 0236 ev->accept(); 0237 return true; 0238 0239 case Qt::Key_Enter: 0240 case Qt::Key_Return: 0241 slotItemSelected(d->m_lv->currentItem(), 0); 0242 ev->accept(); 0243 return true; 0244 0245 case Qt::Key_Home: 0246 case Qt::Key_End: 0247 if (ev->modifiers() & Qt::ControlModifier) { 0248 item = d->m_lv->currentItem(); 0249 if (ev->key() == Qt::Key_Home) { 0250 while (item && d->m_lv->itemAbove(item)) { 0251 item = d->m_lv->itemAbove(item); 0252 } 0253 while (item && !selector()->match(d->m_lastCompletion, item)) { 0254 item = d->m_lv->itemBelow(item); 0255 } 0256 } else { 0257 while (item && d->m_lv->itemBelow(item)) { 0258 item = d->m_lv->itemBelow(item); 0259 } 0260 while (item && !selector()->match(d->m_lastCompletion, item)) { 0261 item = d->m_lv->itemAbove(item); 0262 } 0263 } 0264 if (item) { 0265 d->m_lv->setCurrentItem(item); 0266 // make sure, we always see a possible (non-selectable) group item 0267 if (d->m_lv->itemAbove(item)) 0268 item = d->m_lv->itemAbove(item); 0269 d->m_lv->scrollToItem(item); 0270 } 0271 ev->accept(); 0272 return true; 0273 } 0274 break; 0275 0276 default: 0277 break; 0278 } 0279 } 0280 } 0281 } 0282 return QWidget::eventFilter(o, e); 0283 } 0284 0285 void KMyMoneyCompletion::slotMakeCompletion(const QString& txt) 0286 { 0287 Q_D(KMyMoneyCompletion); 0288 auto cnt = selector()->slotMakeCompletion(txt.trimmed()); 0289 0290 if (d->m_parent && d->m_parent->isVisible() && !isVisible() && cnt) 0291 show(false); 0292 else { 0293 if (cnt != 0) { 0294 adjustSize(); 0295 } else { 0296 hide(); 0297 } 0298 } 0299 } 0300 0301 void KMyMoneyCompletion::slotItemSelected(QTreeWidgetItem *item, int) 0302 { 0303 Q_D(KMyMoneyCompletion); 0304 if (item && item->flags().testFlag(Qt::ItemIsSelectable)) { 0305 QString id = item->data(0, (int)eWidgets::Selector::Role::Id).toString(); 0306 // hide the widget, so we can debug the slots that are connect 0307 // to the signal we emit very soon 0308 hide(); 0309 d->m_id = id; 0310 emit itemSelected(id); 0311 } 0312 } 0313 0314 void KMyMoneyCompletion::setSelected(const QString& id) 0315 { 0316 Q_D(KMyMoneyCompletion); 0317 d->m_id = id; 0318 d->m_selector->setSelected(id, true); 0319 } 0320 0321 KMyMoneySelector* KMyMoneyCompletion::selector() const 0322 { 0323 Q_D(const KMyMoneyCompletion); 0324 return d->m_selector; 0325 }