File indexing completed on 2025-01-19 03:53:50
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2009-11-03 0007 * Description : A dialog base class which can handle multiple pages. 0008 * 0009 * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * SPDX-FileCopyrightText: 2007 by Rafael Fernández López <ereslibre at kde dot org> 0011 * SPDX-FileCopyrightText: 2006 by Tobias Koenig <tokoe at kde dot org> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 #include "dconfigdlgview_p.h" 0018 0019 // Qt includes 0020 0021 #include <QApplication> 0022 #include <QHeaderView> 0023 #include <QPainter> 0024 #include <QTextLayout> 0025 #include <QVBoxLayout> 0026 0027 // Local includes 0028 0029 #include "dconfigdlgmodels.h" 0030 0031 namespace Digikam 0032 { 0033 0034 using namespace DConfigDlgInternal; 0035 0036 DConfigDlgPlainView::DConfigDlgPlainView(QWidget* const parent) 0037 : QAbstractItemView(parent) 0038 { 0039 hide(); 0040 } 0041 0042 QModelIndex DConfigDlgPlainView::indexAt(const QPoint&) const 0043 { 0044 return QModelIndex(); 0045 } 0046 0047 void DConfigDlgPlainView::scrollTo(const QModelIndex&, ScrollHint) 0048 { 0049 } 0050 0051 QRect DConfigDlgPlainView::visualRect(const QModelIndex&) const 0052 { 0053 return QRect(); 0054 } 0055 0056 QModelIndex DConfigDlgPlainView::moveCursor(QAbstractItemView::CursorAction, Qt::KeyboardModifiers) 0057 { 0058 return QModelIndex(); 0059 } 0060 0061 int DConfigDlgPlainView::horizontalOffset() const 0062 { 0063 return 0; 0064 } 0065 0066 int DConfigDlgPlainView::verticalOffset() const 0067 { 0068 return 0; 0069 } 0070 0071 bool DConfigDlgPlainView::isIndexHidden(const QModelIndex&) const 0072 { 0073 return false; 0074 } 0075 0076 void DConfigDlgPlainView::setSelection(const QRect&, QFlags<QItemSelectionModel::SelectionFlag>) 0077 { 0078 } 0079 0080 QRegion DConfigDlgPlainView::visualRegionForSelection(const QItemSelection&) const 0081 { 0082 return QRegion(); 0083 } 0084 0085 // ------------------------------------------------------------------------------------------------------ 0086 0087 DConfigDlgListView::DConfigDlgListView(QWidget* const parent) 0088 : QListView(parent) 0089 { 0090 setViewMode(QListView::ListMode); 0091 setMovement(QListView::Static); 0092 setVerticalScrollMode(QListView::ScrollPerPixel); 0093 0094 QFont boldFont(font()); 0095 boldFont.setBold(true); 0096 setFont(boldFont); 0097 0098 setItemDelegate(new DConfigDlgListViewDelegate(this)); 0099 } 0100 0101 DConfigDlgListView::~DConfigDlgListView() 0102 { 0103 } 0104 0105 void DConfigDlgListView::setModel(QAbstractItemModel* model) 0106 { 0107 /* 0108 DConfigDlgListViewProxy* const proxy = new DConfigDlgListViewProxy( this ); 0109 proxy->setSourceModel( model ); 0110 proxy->rebuildMap(); 0111 0112 connect(model, SIGNAL(layoutChanged()), 0113 proxy, SLOT(rebuildMap()) ); 0114 */ 0115 0116 connect(model, &QAbstractItemModel::layoutChanged, 0117 this, &DConfigDlgListView::updateWidth); 0118 0119 /* 0120 QListView::setModel(proxy); 0121 */ 0122 QListView::setModel(model); 0123 0124 // Set our own selection model, which won't allow our current selection to be cleared 0125 0126 setSelectionModel(new DConfigDlgInternal::SelectionModel(model, this)); 0127 0128 updateWidth(); 0129 } 0130 0131 void DConfigDlgListView::updateWidth() 0132 { 0133 if (!model()) 0134 { 0135 return; 0136 } 0137 0138 int rows = model()->rowCount(); 0139 0140 int width = 0; 0141 0142 for (int i = 0 ; i < rows ; ++i) 0143 { 0144 width = qMax(width, sizeHintForIndex(model()->index(i, 0)).width()); 0145 } 0146 0147 setFixedWidth(width + 25); 0148 } 0149 0150 // ------------------------------------------------------------------------------------------- 0151 0152 DConfigDlgTreeView::DConfigDlgTreeView(QWidget* const parent) 0153 : QTreeView(parent) 0154 { 0155 setUniformRowHeights(true); 0156 header()->hide(); 0157 } 0158 0159 void DConfigDlgTreeView::setModel(QAbstractItemModel* model) 0160 { 0161 connect(model, &QAbstractItemModel::layoutChanged, 0162 this, &DConfigDlgTreeView::updateWidth); 0163 0164 QTreeView::setModel(model); 0165 0166 // Set our own selection model, which won't allow our current selection to be cleared 0167 0168 setSelectionModel(new DConfigDlgInternal::SelectionModel(model, this)); 0169 0170 updateWidth(); 0171 } 0172 0173 void DConfigDlgTreeView::updateWidth() 0174 { 0175 if (!model()) 0176 { 0177 return; 0178 } 0179 0180 int columns = model()->columnCount(); 0181 0182 expandItems(); 0183 0184 int width = 0; 0185 0186 for (int i = 0 ; i < columns ; ++i) 0187 { 0188 resizeColumnToContents(i); 0189 width = qMax(width, sizeHintForColumn(i)); 0190 } 0191 0192 setFixedWidth(width + 25); 0193 } 0194 0195 void DConfigDlgTreeView::expandItems(const QModelIndex& index) 0196 { 0197 setExpanded(index, true); 0198 0199 const int count = model()->rowCount(index); 0200 0201 for (int i = 0 ; i < count ; ++i) 0202 { 0203 expandItems(model()->index(i, 0, index)); 0204 } 0205 } 0206 0207 // --------------------------------------------------------------------------- 0208 0209 DConfigDlgTabbedView::DConfigDlgTabbedView(QWidget* const parent) 0210 : QAbstractItemView(parent) 0211 { 0212 // hide the viewport of the QAbstractScrollArea 0213 0214 const QList<QWidget *> list = findChildren<QWidget *>(); 0215 0216 for (int i = 0 ; i < list.count() ; ++i) 0217 { 0218 list[i]->hide(); 0219 } 0220 0221 setFrameShape(NoFrame); 0222 0223 QVBoxLayout* const layout = new QVBoxLayout(this); 0224 layout->setContentsMargins(QMargins()); 0225 0226 mTabWidget = new QTabWidget(this); 0227 0228 connect(mTabWidget, &QTabWidget::currentChanged, this, 0229 &DConfigDlgTabbedView::currentPageChanged); 0230 0231 layout->addWidget(mTabWidget); 0232 } 0233 0234 DConfigDlgTabbedView::~DConfigDlgTabbedView() 0235 { 0236 if (model()) 0237 { 0238 for (int i = 0 ; i < mTabWidget->count() ; ++i) 0239 { 0240 QWidget* const page = qvariant_cast<QWidget *>(model()->data(model()->index(i, 0), DConfigDlgModel::WidgetRole)); 0241 0242 if (page) 0243 { 0244 page->setVisible(false); 0245 page->setParent(nullptr); // reparent our children before they are deleted 0246 } 0247 } 0248 } 0249 } 0250 0251 void DConfigDlgTabbedView::setModel(QAbstractItemModel* model) 0252 { 0253 QAbstractItemView::setModel(model); 0254 0255 connect(model, &QAbstractItemModel::layoutChanged, this, 0256 &DConfigDlgTabbedView::layoutChanged); 0257 0258 layoutChanged(); 0259 } 0260 0261 QModelIndex DConfigDlgTabbedView::indexAt(const QPoint&) const 0262 { 0263 if (model()) 0264 { 0265 return model()->index(0, 0); 0266 } 0267 else 0268 { 0269 return QModelIndex(); 0270 } 0271 } 0272 0273 void DConfigDlgTabbedView::scrollTo(const QModelIndex& index, ScrollHint) 0274 { 0275 if (!index.isValid()) 0276 { 0277 return; 0278 } 0279 0280 mTabWidget->setCurrentIndex(index.row()); 0281 } 0282 0283 QRect DConfigDlgTabbedView::visualRect(const QModelIndex&) const 0284 { 0285 return QRect(); 0286 } 0287 0288 QSize DConfigDlgTabbedView::minimumSizeHint() const 0289 { 0290 return mTabWidget->minimumSizeHint(); 0291 } 0292 0293 QModelIndex DConfigDlgTabbedView::moveCursor(QAbstractItemView::CursorAction, Qt::KeyboardModifiers) 0294 { 0295 return QModelIndex(); 0296 } 0297 0298 int DConfigDlgTabbedView::horizontalOffset() const 0299 { 0300 return 0; 0301 } 0302 0303 int DConfigDlgTabbedView::verticalOffset() const 0304 { 0305 return 0; 0306 } 0307 0308 bool DConfigDlgTabbedView::isIndexHidden(const QModelIndex& index) const 0309 { 0310 return (mTabWidget->currentIndex() != index.row()); 0311 } 0312 0313 void DConfigDlgTabbedView::setSelection(const QRect& , QFlags<QItemSelectionModel::SelectionFlag>) 0314 { 0315 } 0316 0317 QRegion DConfigDlgTabbedView::visualRegionForSelection(const QItemSelection&) const 0318 { 0319 return QRegion(); 0320 } 0321 0322 void DConfigDlgTabbedView::currentPageChanged(int index) 0323 { 0324 if (!model()) 0325 { 0326 return; 0327 } 0328 0329 QModelIndex modelIndex = model()->index(index, 0); 0330 0331 selectionModel()->setCurrentIndex(modelIndex, QItemSelectionModel::ClearAndSelect); 0332 } 0333 0334 void DConfigDlgTabbedView::layoutChanged() 0335 { 0336 // save old position 0337 0338 int pos = mTabWidget->currentIndex(); 0339 0340 // clear tab bar 0341 0342 int count = mTabWidget->count(); 0343 0344 for (int i = 0 ; i < count ; ++i) 0345 { 0346 mTabWidget->removeTab(0); 0347 } 0348 0349 if (!model()) 0350 { 0351 return; 0352 } 0353 0354 // add new tabs 0355 0356 for (int i = 0 ; i < model()->rowCount() ; ++i) 0357 { 0358 const QString title = model()->data(model()->index(i, 0)).toString(); 0359 const QIcon icon = model()->data(model()->index(i, 0), Qt::DecorationRole).value<QIcon>(); 0360 QWidget* const page = qvariant_cast<QWidget *>(model()->data(model()->index(i, 0), DConfigDlgModel::WidgetRole)); 0361 0362 if (page) 0363 { 0364 QWidget* const widget = new QWidget(this); 0365 QVBoxLayout* const layout = new QVBoxLayout(widget); 0366 widget->setLayout(layout); 0367 layout->addWidget(page); 0368 page->setVisible(true); 0369 mTabWidget->addTab(widget, icon, title); 0370 } 0371 } 0372 0373 mTabWidget->setCurrentIndex(pos); 0374 } 0375 0376 void DConfigDlgTabbedView::dataChanged(const QModelIndex& index, const QModelIndex&, const QVector<int>& roles) 0377 { 0378 if (!index.isValid()) 0379 { 0380 return; 0381 } 0382 0383 if ((index.row() < 0) || (index.row() >= mTabWidget->count())) 0384 { 0385 return; 0386 } 0387 0388 if (roles.isEmpty() || roles.contains(Qt::DisplayRole) || roles.contains(Qt::DecorationRole)) 0389 { 0390 const QString title = model()->data(index).toString(); 0391 const QIcon icon = model()->data(index, Qt::DecorationRole).value<QIcon>(); 0392 0393 mTabWidget->setTabText(index.row(), title); 0394 mTabWidget->setTabIcon(index.row(), icon); 0395 } 0396 } 0397 0398 // ----------------------------------------------------------------------------------------------- 0399 0400 DConfigDlgListViewDelegate::DConfigDlgListViewDelegate(QObject* const parent) 0401 : QAbstractItemDelegate(parent) 0402 { 0403 } 0404 0405 static int layoutText(QTextLayout* layout, int maxWidth) 0406 { 0407 qreal height = 0; 0408 int textWidth = 0; 0409 layout->beginLayout(); 0410 0411 while (true) 0412 { 0413 QTextLine line = layout->createLine(); 0414 0415 if (!line.isValid()) 0416 { 0417 break; 0418 } 0419 0420 line.setLineWidth(maxWidth); 0421 line.setPosition(QPointF(0, height)); 0422 height += line.height(); 0423 textWidth = qMax(textWidth, qRound(line.naturalTextWidth() + 0.5)); 0424 } 0425 0426 layout->endLayout(); 0427 return textWidth; 0428 } 0429 0430 void DConfigDlgListViewDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const 0431 { 0432 if (!index.isValid()) 0433 { 0434 return; 0435 } 0436 0437 QStyleOptionViewItem opt(option); 0438 opt.showDecorationSelected = true; 0439 QStyle* const style = opt.widget ? opt.widget->style() : QApplication::style(); 0440 0441 int iconSize = style->pixelMetric(QStyle::PM_IconViewIconSize); 0442 const QString text = index.model()->data(index, Qt::DisplayRole).toString(); 0443 const QIcon icon = index.model()->data(index, Qt::DecorationRole).value<QIcon>(); 0444 const QPixmap pixmap = icon.pixmap(iconSize, iconSize); 0445 0446 QFontMetrics fm = painter->fontMetrics(); 0447 int wp = pixmap.width() / pixmap.devicePixelRatio(); 0448 int hp = pixmap.height() / pixmap.devicePixelRatio(); 0449 0450 QTextLayout iconTextLayout(text, option.font); 0451 QTextOption textOption(Qt::AlignHCenter); 0452 iconTextLayout.setTextOption(textOption); 0453 int maxWidth = qMax(3 * wp, 8 * fm.height()); 0454 layoutText(&iconTextLayout, maxWidth); 0455 0456 QPen pen = painter->pen(); 0457 QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? QPalette::Normal 0458 : QPalette::Disabled; 0459 0460 if ((cg == QPalette::Normal) && !(option.state & QStyle::State_Active)) 0461 { 0462 cg = QPalette::Inactive; 0463 } 0464 0465 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget); 0466 0467 if (option.state & QStyle::State_Selected) 0468 { 0469 painter->setPen(option.palette.color(cg, QPalette::HighlightedText)); 0470 } 0471 else 0472 { 0473 painter->setPen(option.palette.color(cg, QPalette::Text)); 0474 } 0475 0476 painter->drawPixmap(option.rect.x() + (option.rect.width() / 2) - (wp / 2), option.rect.y() + 5, pixmap); 0477 0478 if (!text.isEmpty()) 0479 { 0480 iconTextLayout.draw(painter, QPoint(option.rect.x() + (option.rect.width() / 2) - (maxWidth / 2), option.rect.y() + hp + 7)); 0481 } 0482 0483 painter->setPen(pen); 0484 0485 drawFocus(painter, option, option.rect); 0486 } 0487 0488 QSize DConfigDlgListViewDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const 0489 { 0490 if (!index.isValid()) 0491 { 0492 return QSize(0, 0); 0493 } 0494 0495 QStyleOptionViewItem opt(option); 0496 opt.showDecorationSelected = true; 0497 QStyle* const style = opt.widget ? opt.widget->style() : QApplication::style(); 0498 0499 int iconSize = style->pixelMetric(QStyle::PM_IconViewIconSize); 0500 const QString text = index.model()->data(index, Qt::DisplayRole).toString(); 0501 const QIcon icon = index.model()->data(index, Qt::DecorationRole).value<QIcon>(); 0502 const QPixmap pixmap = icon.pixmap(iconSize, iconSize); 0503 0504 QFontMetrics fm = option.fontMetrics; 0505 int gap = fm.height(); 0506 int wp = pixmap.width() / pixmap.devicePixelRatio(); 0507 int hp = pixmap.height() / pixmap.devicePixelRatio(); 0508 0509 if (hp == 0) 0510 { 0511 /** 0512 * No pixmap loaded yet, we'll use the default icon size in this case. 0513 */ 0514 hp = iconSize; 0515 wp = iconSize; 0516 } 0517 0518 QTextLayout iconTextLayout(text, option.font); 0519 int wt = layoutText(&iconTextLayout, qMax(3 * wp, 8 * fm.height())); 0520 int ht = iconTextLayout.boundingRect().height(); 0521 0522 int width, height; 0523 0524 if (text.isEmpty()) 0525 { 0526 height = hp; 0527 } 0528 else 0529 { 0530 height = hp + ht + 10; 0531 } 0532 0533 width = qMax(wt, wp) + gap; 0534 0535 return QSize(width, height); 0536 } 0537 0538 void DConfigDlgListViewDelegate::drawFocus(QPainter* painter, const QStyleOptionViewItem& option, const QRect& rect) const 0539 { 0540 if (option.state & QStyle::State_HasFocus) 0541 { 0542 QStyleOptionFocusRect o; 0543 0544 o.QStyleOption::operator = (option); 0545 o.rect = rect; 0546 o.state |= QStyle::State_KeyboardFocusChange; 0547 QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) 0548 ? QPalette::Normal : QPalette::Disabled; 0549 o.backgroundColor = option.palette.color(cg, (option.state & QStyle::State_Selected) 0550 ? QPalette::Highlight : QPalette::Window); 0551 0552 QApplication::style()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter); 0553 } 0554 } 0555 0556 // ------------------------------------------------------------------------------------------------------ 0557 0558 DConfigDlgListViewProxy::DConfigDlgListViewProxy(QObject* const parent) 0559 : QAbstractProxyModel(parent) 0560 { 0561 } 0562 0563 DConfigDlgListViewProxy::~DConfigDlgListViewProxy() 0564 { 0565 } 0566 0567 int DConfigDlgListViewProxy::rowCount(const QModelIndex&) const 0568 { 0569 return mList.count(); 0570 } 0571 0572 int DConfigDlgListViewProxy::columnCount(const QModelIndex&) const 0573 { 0574 return 1; 0575 } 0576 0577 QModelIndex DConfigDlgListViewProxy::index(int row, int column, const QModelIndex&) const 0578 { 0579 if ((column > 1) || (row >= mList.count())) 0580 { 0581 return QModelIndex(); 0582 } 0583 else 0584 { 0585 return createIndex(row, column, mList[ row ].internalPointer()); 0586 } 0587 } 0588 0589 QModelIndex DConfigDlgListViewProxy::parent(const QModelIndex&) const 0590 { 0591 return QModelIndex(); 0592 } 0593 0594 QVariant DConfigDlgListViewProxy::data(const QModelIndex& index, int role) const 0595 { 0596 if (!index.isValid()) 0597 { 0598 return QVariant(); 0599 } 0600 0601 if (index.row() >= mList.count()) 0602 { 0603 return QVariant(); 0604 } 0605 0606 return sourceModel()->data(mList[ index.row() ], role); 0607 } 0608 0609 QModelIndex DConfigDlgListViewProxy::mapFromSource(const QModelIndex& index) const 0610 { 0611 if (!index.isValid()) 0612 { 0613 return QModelIndex(); 0614 } 0615 0616 for (int i = 0 ; i < mList.count() ; ++i) 0617 { 0618 if (mList[i] == index) 0619 { 0620 return createIndex(i, 0, index.internalPointer()); 0621 } 0622 } 0623 0624 return QModelIndex(); 0625 } 0626 0627 QModelIndex DConfigDlgListViewProxy::mapToSource(const QModelIndex& index) const 0628 { 0629 if (!index.isValid()) 0630 { 0631 return QModelIndex(); 0632 } 0633 0634 return mList[index.row()]; 0635 } 0636 0637 void DConfigDlgListViewProxy::rebuildMap() 0638 { 0639 mList.clear(); 0640 0641 const QAbstractItemModel* model = sourceModel(); 0642 0643 if (!model) 0644 { 0645 return; 0646 } 0647 0648 for (int i = 0 ; i < model->rowCount() ; ++i) 0649 { 0650 addMapEntry(model->index(i, 0)); 0651 } 0652 0653 for (int i = 0 ; i < mList.count() ; ++i) 0654 { 0655 qDebug("%d:0 -> %d:%d", i, mList[ i ].row(), mList[ i ].column()); 0656 } 0657 0658 Q_EMIT layoutChanged(); 0659 } 0660 0661 void DConfigDlgListViewProxy::addMapEntry(const QModelIndex& index) 0662 { 0663 if (sourceModel()->rowCount(index) == 0) 0664 { 0665 mList.append(index); 0666 } 0667 else 0668 { 0669 const int count = sourceModel()->rowCount(index); 0670 0671 for (int i = 0 ; i < count ; ++i) 0672 { 0673 addMapEntry(sourceModel()->index(i, 0, index)); 0674 } 0675 } 0676 } 0677 0678 // --------------------------------------------------------------------------------------- 0679 0680 SelectionModel::SelectionModel(QAbstractItemModel* const model, QObject* const parent) 0681 : QItemSelectionModel(model, parent) 0682 { 0683 } 0684 0685 void SelectionModel::clear() 0686 { 0687 // Don't allow the current selection to be cleared 0688 } 0689 0690 void SelectionModel::select(const QModelIndex& index, QItemSelectionModel::SelectionFlags command) 0691 { 0692 // Don't allow the current selection to be cleared 0693 0694 if (!index.isValid() && (command & QItemSelectionModel::Clear)) 0695 { 0696 return; 0697 } 0698 0699 QItemSelectionModel::select(index, command); 0700 } 0701 0702 void SelectionModel::select(const QItemSelection& selection, QItemSelectionModel::SelectionFlags command) 0703 { 0704 // Don't allow the current selection to be cleared 0705 0706 if (!selection.count() && (command & QItemSelectionModel::Clear)) 0707 { 0708 return; 0709 } 0710 0711 QItemSelectionModel::select(selection, command); 0712 } 0713 0714 } // namespace Digikam 0715 0716 #include "moc_dconfigdlgview_p.cpp"