File indexing completed on 2025-04-27 03:58:21
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2008-03-14 0007 * Description : User interface for searches 0008 * 0009 * SPDX-FileCopyrightText: 2008-2012 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "comboboxutilities.h" 0016 0017 // Qt includes 0018 0019 #include <QAbstractItemView> 0020 #include <QAbstractListModel> 0021 #include <QMouseEvent> 0022 #include <QPointer> 0023 #include <QPainter> 0024 #include <QPen> 0025 #include <QStyle> 0026 #include <QStyleOption> 0027 #include <QTreeView> 0028 #include <QVBoxLayout> 0029 0030 // Local includes 0031 0032 #include "digikam_debug.h" 0033 0034 namespace Digikam 0035 { 0036 0037 ProxyLineEdit::ProxyLineEdit(QWidget* const parent) 0038 : QLineEdit(parent), 0039 m_widget (nullptr) 0040 { 0041 m_layout = new QVBoxLayout; 0042 m_layout->setSpacing(0); 0043 m_layout->setContentsMargins(QMargins()); 0044 setLayout(m_layout); 0045 0046 // unset text edit cursor 0047 0048 unsetCursor(); 0049 0050 // unset clear button per default 0051 0052 setClearButtonShown(false); 0053 0054 connect(this, SIGNAL(textChanged(QString)), 0055 this, SLOT(slotTextChanged(QString))); 0056 } 0057 0058 void ProxyLineEdit::setWidget(QWidget* widget) 0059 { 0060 if (m_widget) 0061 { 0062 delete m_widget; 0063 } 0064 0065 m_widget = widget; 0066 m_widget->setParent(this); 0067 m_layout->addWidget(m_widget); 0068 } 0069 0070 void ProxyLineEdit::setClearButtonShown(bool show) 0071 { 0072 setClearButtonEnabled(show); 0073 0074 int rightMargin = show ? height() : 0; 0075 m_layout->setContentsMargins(0, 0, rightMargin, 0); 0076 } 0077 0078 void ProxyLineEdit::slotTextChanged(const QString& text) 0079 { 0080 if (text.isEmpty() && isClearButtonEnabled()) 0081 { 0082 Q_EMIT signalClearButtonPressed(); 0083 } 0084 } 0085 0086 /** 0087 * NOTE: see bug #326718: We need to use QLineEdit parent class with these methods 0088 * to have clear button working fine. 0089 */ 0090 void ProxyLineEdit::mousePressEvent(QMouseEvent* event) 0091 { 0092 QLineEdit::mousePressEvent(event); 0093 } 0094 0095 void ProxyLineEdit::mouseReleaseEvent(QMouseEvent* event) 0096 { 0097 QLineEdit::mouseReleaseEvent(event); 0098 } 0099 0100 /** 0101 * We just re-implement all relevant QWidget event handlers and call 0102 * the QWidget implementation, not the QLineEdit one. 0103 */ 0104 void ProxyLineEdit::mouseMoveEvent(QMouseEvent* event) 0105 { 0106 QWidget::mouseMoveEvent(event); 0107 } 0108 0109 void ProxyLineEdit::mouseDoubleClickEvent(QMouseEvent* event) 0110 { 0111 QWidget::mouseDoubleClickEvent(event); 0112 } 0113 0114 void ProxyLineEdit::keyPressEvent(QKeyEvent* event) 0115 { 0116 QWidget::keyPressEvent(event); 0117 } 0118 0119 void ProxyLineEdit::focusInEvent(QFocusEvent* event) 0120 { 0121 QWidget::focusInEvent(event); 0122 } 0123 0124 void ProxyLineEdit::focusOutEvent(QFocusEvent* event) 0125 { 0126 QWidget::focusOutEvent(event); 0127 } 0128 0129 void ProxyLineEdit::paintEvent(QPaintEvent* event) 0130 { 0131 QWidget::paintEvent(event); 0132 } 0133 0134 void ProxyLineEdit::dragEnterEvent(QDragEnterEvent* event) 0135 { 0136 QWidget::dragEnterEvent(event); 0137 } 0138 0139 void ProxyLineEdit::dragMoveEvent(QDragMoveEvent* event) 0140 { 0141 QWidget::dragMoveEvent(event); 0142 } 0143 0144 void ProxyLineEdit::dragLeaveEvent(QDragLeaveEvent* event) 0145 { 0146 QWidget::dragLeaveEvent(event); 0147 } 0148 0149 void ProxyLineEdit::dropEvent(QDropEvent* event) 0150 { 0151 QWidget::dropEvent(event); 0152 } 0153 0154 void ProxyLineEdit::changeEvent(QEvent* event) 0155 { 0156 QWidget::changeEvent(event); 0157 } 0158 0159 void ProxyLineEdit::contextMenuEvent(QContextMenuEvent* event) 0160 { 0161 QWidget::contextMenuEvent(event); 0162 } 0163 0164 void ProxyLineEdit::inputMethodEvent(QInputMethodEvent* event) 0165 { 0166 QWidget::inputMethodEvent(event); 0167 } 0168 0169 QSize ProxyLineEdit::minimumSizeHint() const 0170 { 0171 return QWidget::minimumSizeHint(); 0172 } 0173 0174 QSize ProxyLineEdit::sizeHint() const 0175 { 0176 return QWidget::sizeHint(); 0177 } 0178 0179 // ------------------------------------------------------------------------- 0180 0181 ProxyClickLineEdit::ProxyClickLineEdit(QWidget* const parent) 0182 : ProxyLineEdit(parent) 0183 { 0184 } 0185 0186 void ProxyClickLineEdit::mouseReleaseEvent(QMouseEvent* event) 0187 { 0188 ProxyLineEdit::mouseReleaseEvent(event); 0189 0190 if (event->button() == Qt::LeftButton) 0191 { 0192 Q_EMIT leftClicked(); 0193 event->accept(); 0194 } 0195 } 0196 0197 // ------------------------------------------------------------------------- 0198 0199 ModelIndexBasedComboBox::ModelIndexBasedComboBox(QWidget* const parent) 0200 : QComboBox(parent) 0201 { 0202 } 0203 0204 void ModelIndexBasedComboBox::hidePopup() 0205 { 0206 m_currentIndex = view()->selectionModel()->currentIndex(); 0207 QComboBox::hidePopup(); 0208 } 0209 0210 void ModelIndexBasedComboBox::showPopup() 0211 { 0212 QComboBox::showPopup(); 0213 0214 if (m_currentIndex.isValid()) 0215 { 0216 view()->selectionModel()->setCurrentIndex(m_currentIndex, QItemSelectionModel::ClearAndSelect); 0217 } 0218 } 0219 0220 QModelIndex ModelIndexBasedComboBox::currentIndex() const 0221 { 0222 return m_currentIndex; 0223 } 0224 0225 void ModelIndexBasedComboBox::setCurrentIndex(const QModelIndex& index) 0226 { 0227 m_currentIndex = index; 0228 view()->selectionModel()->setCurrentIndex(m_currentIndex, QItemSelectionModel::ClearAndSelect); 0229 } 0230 0231 // ------------------------------------------------------------------------- 0232 0233 StayPoppedUpComboBox::StayPoppedUpComboBox(QWidget* const parent) 0234 : ModelIndexBasedComboBox(parent) 0235 { 0236 m_view = nullptr; 0237 } 0238 0239 void StayPoppedUpComboBox::installView(QAbstractItemView* view) 0240 { 0241 if (m_view) 0242 { 0243 return; 0244 } 0245 0246 // Create view 0247 0248 m_view = view; 0249 0250 // set on combo box 0251 0252 setView(m_view); 0253 0254 // Removing these event filters works just as the eventFilter() solution below, 0255 // but is much more dependent on Qt internals and not guaranteed to work in the future. 0256 /* 0257 m_view->removeEventFilter(m_view->parent()); 0258 m_view->viewport()->removeEventFilter(m_view->parent()); 0259 */ 0260 // Install event filters, _after_ setView() is called 0261 0262 m_view->installEventFilter(this); 0263 m_view->viewport()->installEventFilter(this); 0264 } 0265 0266 bool StayPoppedUpComboBox::eventFilter(QObject* o, QEvent* e) 0267 { 0268 // The combo box has installed an event filter on the view. 0269 // If it catches a valid mouse button release there, it will hide the popup. 0270 // Here we prevent this by eating the event ourselves, 0271 // and then dispatching it to its destination. 0272 0273 if ((o == m_view) || (o == m_view->viewport())) 0274 { 0275 switch (e->type()) 0276 { 0277 case QEvent::MouseButtonRelease: 0278 { 0279 QMouseEvent* m = static_cast<QMouseEvent*>(e); 0280 0281 if (m_view->isVisible() && m_view->rect().contains(m->pos())) 0282 { 0283 if (o == m_view) 0284 { 0285 o->event(e); 0286 } 0287 else 0288 { 0289 // Viewport: Calling event() does not work, viewportEvent() is needed. 0290 // This is the event that gets redirected to the QTreeView finally! 0291 0292 sendViewportEventToView(e); 0293 } 0294 0295 // we have dispatched the event privately; we filter it out from the main dispatching 0296 0297 return true; 0298 } 0299 0300 break; 0301 } 0302 0303 case QEvent::ContextMenu: 0304 { 0305 if (o != m_view) 0306 { 0307 // for whatever reason, the position of the event is slightly wrong 0308 0309 QContextMenuEvent* m = static_cast<QContextMenuEvent*>(e); 0310 QPoint correctPos = m_view->viewport()->mapFromGlobal(m->globalPos()); 0311 QContextMenuEvent corrected(m->reason(), correctPos, m->globalPos(), m->modifiers()); 0312 sendViewportEventToView(&corrected); 0313 0314 return true; 0315 } 0316 0317 break; 0318 } 0319 0320 default: 0321 break; 0322 } 0323 } 0324 0325 return QComboBox::eventFilter(o, e); 0326 } 0327 0328 // ------------------------------------------------------------------------- 0329 0330 class Q_DECL_HIDDEN TreeViewComboBoxTreeView : public QTreeView 0331 { 0332 Q_OBJECT 0333 0334 public: 0335 0336 // Needed to make viewportEvent() public 0337 0338 TreeViewComboBoxTreeView(QWidget* const parent = nullptr) 0339 : QTreeView(parent) 0340 { 0341 } 0342 0343 bool viewportEvent(QEvent* event) override 0344 { 0345 return QTreeView::viewportEvent(event); 0346 } 0347 }; 0348 0349 TreeViewComboBox::TreeViewComboBox(QWidget* const parent) 0350 : StayPoppedUpComboBox(parent) 0351 { 0352 } 0353 0354 void TreeViewComboBox::installView(QAbstractItemView* view) 0355 { 0356 // parent does the heavy work 0357 0358 if (view) 0359 { 0360 StayPoppedUpComboBox::installView(view); 0361 } 0362 else 0363 { 0364 QPointer<TreeViewComboBoxTreeView> tview = new TreeViewComboBoxTreeView; 0365 StayPoppedUpComboBox::installView(tview); 0366 } 0367 } 0368 0369 void TreeViewComboBox::sendViewportEventToView(QEvent* e) 0370 { 0371 static_cast<TreeViewComboBoxTreeView*>(m_view)->viewportEvent(e); 0372 } 0373 0374 QTreeView* TreeViewComboBox::view() const 0375 { 0376 return static_cast<QTreeView*>(m_view); 0377 } 0378 0379 // ------------------------------------------------------------------------- 0380 0381 class Q_DECL_HIDDEN ListViewComboBoxListView : public QListView 0382 { 0383 Q_OBJECT 0384 0385 public: 0386 0387 // Needed to make viewportEvent() public 0388 0389 ListViewComboBoxListView(QWidget* const parent = nullptr) 0390 : QListView(parent) 0391 { 0392 } 0393 0394 bool viewportEvent(QEvent* event) override 0395 { 0396 return QListView::viewportEvent(event); 0397 } 0398 0399 private: 0400 0401 // Disable 0402 explicit ListViewComboBoxListView(QObject*); 0403 }; 0404 0405 ListViewComboBox::ListViewComboBox(QWidget* const parent) 0406 : StayPoppedUpComboBox(parent) 0407 { 0408 } 0409 0410 void ListViewComboBox::installView(QAbstractItemView* view) 0411 { 0412 // parent does the heavy work 0413 0414 if (view) 0415 { 0416 StayPoppedUpComboBox::installView(view); 0417 } 0418 else 0419 { 0420 QPointer<ListViewComboBoxListView> lview = new ListViewComboBoxListView; 0421 StayPoppedUpComboBox::installView(lview); 0422 } 0423 } 0424 0425 void ListViewComboBox::sendViewportEventToView(QEvent* e) 0426 { 0427 static_cast<ListViewComboBoxListView*>(m_view)->viewportEvent(e); 0428 } 0429 0430 QListView* ListViewComboBox::view() const 0431 { 0432 return static_cast<QListView*>(m_view); 0433 } 0434 0435 // ------------------------------------------------------------------------- 0436 0437 class Q_DECL_HIDDEN TreeViewComboBoxLineEdit : public QLineEdit 0438 { 0439 Q_OBJECT 0440 0441 public: 0442 0443 // This line edit works like a weblink: 0444 // Readonly; A mouse press shows the popup; Cursor is the pointing hand. 0445 0446 explicit TreeViewComboBoxLineEdit(QComboBox* const box) 0447 : QLineEdit(box), 0448 m_box (box) 0449 { 0450 setReadOnly(true); 0451 setCursor(Qt::PointingHandCursor); 0452 } 0453 0454 void mouseReleaseEvent(QMouseEvent* event) override 0455 { 0456 QLineEdit::mouseReleaseEvent(event); 0457 m_box->showPopup(); 0458 } 0459 0460 void wheelEvent(QWheelEvent* /*event*/) override 0461 { 0462 m_box->showPopup(); 0463 } 0464 0465 public: 0466 0467 QComboBox* m_box; 0468 }; 0469 0470 // ------------------------------------------------------------------------- 0471 0472 TreeViewLineEditComboBox::TreeViewLineEditComboBox(QWidget* const parent) 0473 : TreeViewComboBox(parent), 0474 m_comboLineEdit (nullptr) 0475 { 0476 } 0477 0478 void TreeViewLineEditComboBox::setLineEditText(const QString& text) 0479 { 0480 if (m_comboLineEdit) 0481 { 0482 m_comboLineEdit->setText(text); 0483 } 0484 } 0485 0486 void TreeViewLineEditComboBox::installView(QAbstractItemView* view) 0487 { 0488 // parent does the heavy work 0489 0490 TreeViewComboBox::installView(view); 0491 0492 installLineEdit(); 0493 } 0494 0495 void TreeViewLineEditComboBox::installLineEdit() 0496 { 0497 if (!m_comboLineEdit) 0498 { 0499 setLineEdit(new TreeViewComboBoxLineEdit(this)); 0500 } 0501 } 0502 0503 void TreeViewLineEditComboBox::setLineEdit(QLineEdit* edit) 0504 { 0505 m_comboLineEdit = edit; 0506 TreeViewComboBox::setLineEdit(edit); 0507 } 0508 0509 } // namespace Digikam 0510 0511 #include "comboboxutilities.moc" 0512 0513 #include "moc_comboboxutilities.cpp"