File indexing completed on 2024-09-08 12:18:04
0001 /* vi: ts=8 sts=4 sw=4 0002 0003 This file is part of the KDE project, module kfile. 0004 SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org> 0005 SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org> 0006 SPDX-FileCopyrightText: 1997 Christoph Neerfeld <chris@kde.org> 0007 SPDX-FileCopyrightText: 2002 Carsten Pfeiffer <pfeiffer@kde.org> 0008 SPDX-FileCopyrightText: 2021 Kai Uwe Broulik <kde@broulik.de> 0009 0010 SPDX-License-Identifier: LGPL-2.0-only 0011 */ 0012 0013 #include "kicondialog.h" 0014 #include "kicondialog_p.h" 0015 #include "kicondialogmodel_p.h" 0016 0017 #include <KLazyLocalizedString> 0018 #include <KLocalizedString> 0019 #include <KStandardAction> 0020 0021 #include <QAbstractListModel> 0022 #include <QApplication> 0023 #include <QComboBox> 0024 #include <QDialogButtonBox> 0025 #include <QFileInfo> 0026 #include <QGraphicsOpacityEffect> 0027 #include <QLabel> 0028 #include <QPainter> 0029 #include <QScrollBar> 0030 #include <QStandardItemModel> // for manipulatig QComboBox 0031 #include <QStandardPaths> 0032 #include <QSvgRenderer> 0033 #include <QVector> 0034 0035 #include <algorithm> 0036 #include <math.h> 0037 0038 static const int s_edgePad = 3; 0039 0040 KIconDialogModel::KIconDialogModel(QObject *parent) 0041 : QAbstractListModel(parent) 0042 { 0043 } 0044 0045 KIconDialogModel::~KIconDialogModel() = default; 0046 0047 qreal KIconDialogModel::devicePixelRatio() const 0048 { 0049 return m_dpr; 0050 } 0051 0052 void KIconDialogModel::setDevicePixelRatio(qreal dpr) 0053 { 0054 m_dpr = dpr; 0055 } 0056 0057 QSize KIconDialogModel::iconSize() const 0058 { 0059 return m_iconSize; 0060 } 0061 0062 void KIconDialogModel::setIconSize(const QSize &iconSize) 0063 { 0064 m_iconSize = iconSize; 0065 } 0066 0067 void KIconDialogModel::load(const QStringList &paths) 0068 { 0069 beginResetModel(); 0070 0071 m_data.clear(); 0072 m_data.reserve(paths.count()); 0073 0074 for (const QString &path : paths) { 0075 const QFileInfo fi(path); 0076 0077 KIconDialogModelData item; 0078 item.name = fi.completeBaseName(); 0079 item.path = path; 0080 // pixmap is created on demand 0081 0082 m_data.append(item); 0083 } 0084 0085 endResetModel(); 0086 } 0087 0088 int KIconDialogModel::rowCount(const QModelIndex &parent) const 0089 { 0090 if (parent.isValid()) { 0091 return 0; 0092 } 0093 return m_data.count(); 0094 } 0095 0096 QVariant KIconDialogModel::data(const QModelIndex &index, int role) const 0097 { 0098 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)) { 0099 return QVariant(); 0100 } 0101 0102 const auto &item = m_data.at(index.row()); 0103 0104 switch (role) { 0105 case Qt::DisplayRole: 0106 return item.name; 0107 case Qt::DecorationRole: 0108 if (item.pixmap.isNull()) { 0109 const_cast<KIconDialogModel *>(this)->loadPixmap(index); 0110 } 0111 return item.pixmap; 0112 case Qt::ToolTipRole: 0113 return item.name; 0114 case PathRole: 0115 return item.path; 0116 } 0117 0118 return QVariant(); 0119 } 0120 0121 void KIconDialogModel::loadPixmap(const QModelIndex &index) 0122 { 0123 Q_ASSERT(index.isValid()); 0124 0125 auto &item = m_data[index.row()]; 0126 Q_ASSERT(item.pixmap.isNull()); 0127 0128 const auto dpr = devicePixelRatio(); 0129 const auto canvasIconWidth = iconSize().width(); 0130 const auto canvasIconHeight = iconSize().height(); 0131 0132 QImage img; 0133 0134 if (item.path.endsWith(QLatin1String(".svg"), Qt::CaseInsensitive) || item.path.endsWith(QLatin1String(".svgz"), Qt::CaseInsensitive)) { 0135 QSvgRenderer renderer(item.path); 0136 if (renderer.isValid()) { 0137 img = QImage(canvasIconWidth * dpr, canvasIconHeight * dpr, QImage::Format_ARGB32_Premultiplied); 0138 img.setDevicePixelRatio(dpr); 0139 img.fill(Qt::transparent); 0140 0141 QPainter p(&img); 0142 // FIXME why do I need to specify bounds for it to not crop the SVG on dpr > 1? 0143 renderer.render(&p, QRect(0, 0, canvasIconWidth, canvasIconHeight)); 0144 } 0145 } else { 0146 img.load(item.path); 0147 0148 if (!img.isNull()) { 0149 if (img.width() > canvasIconWidth || img.height() > canvasIconHeight) { 0150 if (img.width() / (float)canvasIconWidth > img.height() / (float)canvasIconHeight) { 0151 int height = (int)(((float)canvasIconWidth / img.width()) * img.height()); 0152 img = img.scaled(canvasIconWidth, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 0153 } else { 0154 int width = (int)(((float)canvasIconHeight / img.height()) * img.width()); 0155 img = img.scaled(width, canvasIconHeight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 0156 } 0157 } 0158 0159 if (/*uniformIconSize &&*/ (img.width() != canvasIconWidth || img.height() != canvasIconHeight)) { 0160 // Image is smaller than desired. Draw onto a transparent QImage of the required dimensions. 0161 // (Unpleasant glitches occur if we break the uniformIconSizes() contract). 0162 QImage paddedImage = 0163 QImage(canvasIconWidth * img.devicePixelRatioF(), canvasIconHeight * img.devicePixelRatioF(), QImage::Format_ARGB32_Premultiplied); 0164 paddedImage.setDevicePixelRatio(img.devicePixelRatioF()); 0165 paddedImage.fill(0); 0166 QPainter painter(&paddedImage); 0167 painter.drawImage((canvasIconWidth - img.width()) / 2, (canvasIconHeight - img.height()) / 2, img); 0168 img = paddedImage; 0169 } 0170 } 0171 } 0172 0173 if (!img.isNull()) { 0174 item.pixmap = QPixmap::fromImage(img); 0175 } 0176 } 0177 0178 /** 0179 * Qt allocates very little horizontal space for the icon name, 0180 * even if the gridSize width is large. This delegate allocates 0181 * the gridSize width (minus some padding) for the icon and icon name. 0182 */ 0183 class KIconCanvasDelegate : public QAbstractItemDelegate 0184 { 0185 Q_OBJECT 0186 public: 0187 KIconCanvasDelegate(QListView *parent, QAbstractItemDelegate *defaultDelegate); 0188 ~KIconCanvasDelegate() override 0189 { 0190 } 0191 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; 0192 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; 0193 0194 private: 0195 QAbstractItemDelegate *m_defaultDelegate = nullptr; 0196 }; 0197 0198 KIconCanvasDelegate::KIconCanvasDelegate(QListView *parent, QAbstractItemDelegate *defaultDelegate) 0199 : QAbstractItemDelegate(parent) 0200 { 0201 m_defaultDelegate = defaultDelegate; 0202 } 0203 0204 void KIconCanvasDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const 0205 { 0206 auto *canvas = static_cast<QListView *>(parent()); 0207 const int gridWidth = canvas->gridSize().width(); 0208 QStyleOptionViewItem newOption = option; 0209 newOption.displayAlignment = Qt::AlignHCenter | Qt::AlignTop; 0210 newOption.features.setFlag(QStyleOptionViewItem::WrapText); 0211 // Manipulate the width available. 0212 newOption.rect.setX((option.rect.x() / gridWidth) * gridWidth + s_edgePad); 0213 newOption.rect.setY(option.rect.y() + s_edgePad); 0214 newOption.rect.setWidth(gridWidth - 2 * s_edgePad); 0215 newOption.rect.setHeight(option.rect.height() - 2 * s_edgePad); 0216 m_defaultDelegate->paint(painter, newOption, index); 0217 } 0218 0219 QSize KIconCanvasDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const 0220 { 0221 auto *canvas = static_cast<QListView *>(parent()); 0222 0223 // TODO can we set wrap text and display alignment somewhere globally? 0224 QStyleOptionViewItem newOption = option; 0225 newOption.displayAlignment = Qt::AlignHCenter | Qt::AlignTop; 0226 newOption.features.setFlag(QStyleOptionViewItem::WrapText); 0227 0228 QSize size = m_defaultDelegate->sizeHint(newOption, index); 0229 const int gridWidth = canvas->gridSize().width(); 0230 const int gridHeight = canvas->gridSize().height(); 0231 size.setWidth(gridWidth - 2 * s_edgePad); 0232 size.setHeight(gridHeight - 2 * s_edgePad); 0233 QFontMetrics metrics(option.font); 0234 size.setHeight(gridHeight + metrics.height() * 3); 0235 return size; 0236 } 0237 0238 // TODO KF6 remove and override KIconDialog::showEvent() 0239 class ShowEventFilter : public QObject 0240 { 0241 public: 0242 explicit ShowEventFilter(QObject *parent) 0243 : QObject(parent) 0244 { 0245 } 0246 ~ShowEventFilter() override 0247 { 0248 } 0249 0250 private: 0251 bool eventFilter(QObject *watched, QEvent *event) override 0252 { 0253 if (event->type() == QEvent::Show) { 0254 KIconDialog *q = static_cast<KIconDialog *>(parent()); 0255 q->d->showIcons(); 0256 q->d->ui.searchLine->setFocus(); 0257 } 0258 0259 return QObject::eventFilter(watched, event); 0260 } 0261 }; 0262 0263 /* 0264 * KIconDialog: Dialog for selecting icons. Both system and user 0265 * specified icons can be chosen. 0266 */ 0267 0268 KIconDialog::KIconDialog(QWidget *parent) 0269 : QDialog(parent) 0270 , d(new KIconDialogPrivate(this)) 0271 { 0272 setModal(true); 0273 0274 d->mpLoader = KIconLoader::global(); 0275 d->init(); 0276 0277 installEventFilter(new ShowEventFilter(this)); 0278 } 0279 0280 #if KICONTHEMES_BUILD_DEPRECATED_SINCE(5, 104) 0281 KIconDialog::KIconDialog(KIconLoader *loader, QWidget *parent) 0282 : QDialog(parent) 0283 , d(new KIconDialogPrivate(this)) 0284 { 0285 setModal(true); 0286 0287 d->mpLoader = loader; 0288 d->init(); 0289 0290 installEventFilter(new ShowEventFilter(this)); 0291 } 0292 #endif 0293 0294 void KIconDialogPrivate::init() 0295 { 0296 mGroupOrSize = KIconLoader::Desktop; 0297 mContext = KIconLoader::Any; 0298 0299 ui.setupUi(q); 0300 0301 auto updatePlaceholder = [this] { 0302 updatePlaceholderLabel(); 0303 }; 0304 QObject::connect(proxyModel, &QSortFilterProxyModel::modelReset, q, updatePlaceholder); 0305 QObject::connect(proxyModel, &QSortFilterProxyModel::rowsInserted, q, updatePlaceholder); 0306 QObject::connect(proxyModel, &QSortFilterProxyModel::rowsRemoved, q, updatePlaceholder); 0307 0308 QAction *findAction = KStandardAction::find(ui.searchLine, qOverload<>(&QWidget::setFocus), q); 0309 q->addAction(findAction); 0310 0311 QObject::connect(ui.searchLine, &QLineEdit::textChanged, proxyModel, &QSortFilterProxyModel::setFilterFixedString); 0312 0313 static const KLazyLocalizedString context_text[] = { 0314 kli18n("All"), 0315 kli18n("Actions"), 0316 kli18n("Applications"), 0317 kli18n("Categories"), 0318 kli18n("Devices"), 0319 kli18n("Emblems"), 0320 kli18n("Emotes"), 0321 kli18n("Mimetypes"), 0322 kli18n("Places"), 0323 kli18n("Status"), 0324 }; 0325 static const KIconLoader::Context context_id[] = { 0326 KIconLoader::Any, 0327 KIconLoader::Action, 0328 KIconLoader::Application, 0329 KIconLoader::Category, 0330 KIconLoader::Device, 0331 KIconLoader::Emblem, 0332 KIconLoader::Emote, 0333 KIconLoader::MimeType, 0334 KIconLoader::Place, 0335 KIconLoader::StatusIcon, 0336 }; 0337 const int cnt = sizeof(context_text) / sizeof(context_text[0]); 0338 for (int i = 0; i < cnt; ++i) { 0339 if (mpLoader->hasContext(context_id[i])) { 0340 ui.contextCombo->addItem(context_text[i].toString(), context_id[i]); 0341 if (i == 0) { 0342 ui.contextCombo->insertSeparator(i + 1); 0343 } 0344 } 0345 } 0346 ui.contextCombo->insertSeparator(ui.contextCombo->count()); 0347 ui.contextCombo->addItem(i18nc("Other icons", "Other")); 0348 ui.contextCombo->setMaxVisibleItems(ui.contextCombo->count()); 0349 ui.contextCombo->setFixedSize(ui.contextCombo->sizeHint()); 0350 0351 QObject::connect(ui.contextCombo, qOverload<int>(&QComboBox::activated), q, [this]() { 0352 const auto currentData = ui.contextCombo->currentData(); 0353 if (currentData.isValid()) { 0354 mContext = static_cast<KIconLoader::Context>(ui.contextCombo->currentData().toInt()); 0355 } else { 0356 mContext = static_cast<KIconLoader::Context>(-1); 0357 } 0358 showIcons(); 0359 }); 0360 0361 auto *delegate = new KIconCanvasDelegate(ui.canvas, ui.canvas->itemDelegate()); 0362 ui.canvas->setItemDelegate(delegate); 0363 0364 ui.canvas->setModel(proxyModel); 0365 0366 QObject::connect(ui.canvas, &QAbstractItemView::activated, q, [this]() { 0367 custom.clear(); 0368 q->slotOk(); 0369 }); 0370 0371 // You can't just stack widgets on top of each other in Qt Designer 0372 auto *placeholderLayout = new QVBoxLayout(ui.canvas); 0373 0374 placeholderLabel = new QLabel(); 0375 QFont placeholderLabelFont; 0376 // To match the size of a level 2 Heading/KTitleWidget 0377 placeholderLabelFont.setPointSize(qRound(placeholderLabelFont.pointSize() * 1.3)); 0378 placeholderLabel->setFont(placeholderLabelFont); 0379 placeholderLabel->setTextInteractionFlags(Qt::NoTextInteraction); 0380 placeholderLabel->setWordWrap(true); 0381 placeholderLabel->setAlignment(Qt::AlignCenter); 0382 0383 // Match opacity of QML placeholder label component 0384 auto *effect = new QGraphicsOpacityEffect(placeholderLabel); 0385 effect->setOpacity(0.5); 0386 placeholderLabel->setGraphicsEffect(effect); 0387 0388 updatePlaceholderLabel(); 0389 0390 placeholderLayout->addWidget(placeholderLabel); 0391 placeholderLayout->setAlignment(placeholderLabel, Qt::AlignCenter); 0392 0393 // TODO I bet there is a KStandardAction for that? 0394 browseButton = new QPushButton(QIcon::fromTheme(QStringLiteral("folder-open")), i18n("Browse…")); 0395 // TODO does this have implicatons? I just want the "Browse" button on the left side :) 0396 ui.buttonBox->addButton(browseButton, QDialogButtonBox::HelpRole); 0397 QObject::connect(browseButton, &QPushButton::clicked, q, [this] { 0398 browse(); 0399 }); 0400 0401 QObject::connect(ui.buttonBox, &QDialogButtonBox::accepted, q, &KIconDialog::slotOk); 0402 QObject::connect(ui.buttonBox, &QDialogButtonBox::rejected, q, &QDialog::reject); 0403 0404 q->adjustSize(); 0405 } 0406 0407 KIconDialog::~KIconDialog() = default; 0408 0409 static bool sortByFileName(const QString &path1, const QString &path2) 0410 { 0411 const QString fileName1 = path1.mid(path1.lastIndexOf(QLatin1Char('/')) + 1); 0412 const QString fileName2 = path2.mid(path2.lastIndexOf(QLatin1Char('/')) + 1); 0413 return QString::compare(fileName1, fileName2, Qt::CaseInsensitive) < 0; 0414 } 0415 0416 void KIconDialogPrivate::showIcons() 0417 { 0418 QStringList filelist; 0419 if (isSystemIconsContext()) { 0420 if (m_bStrictIconSize) { 0421 filelist = mpLoader->queryIcons(mGroupOrSize, mContext); 0422 } else { 0423 filelist = mpLoader->queryIconsByContext(mGroupOrSize, mContext); 0424 } 0425 } else if (!customLocation.isEmpty()) { 0426 filelist = mpLoader->queryIconsByDir(customLocation); 0427 } else { 0428 // List PNG files found directly in the kiconload search paths. 0429 const QStringList pngNameFilter(QStringLiteral("*.png")); 0430 for (const QString &relDir : KIconLoader::global()->searchPaths()) { 0431 const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, relDir, QStandardPaths::LocateDirectory); 0432 for (const QString &dir : dirs) { 0433 const auto files = QDir(dir).entryList(pngNameFilter); 0434 for (const QString &fileName : files) { 0435 filelist << dir + QLatin1Char('/') + fileName; 0436 } 0437 } 0438 } 0439 } 0440 0441 std::sort(filelist.begin(), filelist.end(), sortByFileName); 0442 0443 // The KIconCanvas has uniformItemSizes set which really expects 0444 // all added icons to be the same size, otherwise weirdness ensues :) 0445 // Ensure all SVGs are scaled to the desired size and that as few icons 0446 // need to be padded as possible by specifying a sensible size. 0447 if (mGroupOrSize < -1) { 0448 // mGroupOrSize can be -1 if NoGroup is chosen. 0449 // Explicit size. 0450 ui.canvas->setIconSize(QSize(-mGroupOrSize, -mGroupOrSize)); 0451 } else { 0452 // Icon group. 0453 int groupSize = mpLoader->currentSize(static_cast<KIconLoader::Group>(mGroupOrSize)); 0454 ui.canvas->setIconSize(QSize(groupSize, groupSize)); 0455 } 0456 0457 // Try to make room for three lines of text... 0458 QFontMetrics metrics(ui.canvas->font()); 0459 const int frameHMargin = ui.canvas->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, ui.canvas) + 1; 0460 const int lineCount = 3; 0461 ui.canvas->setGridSize(QSize(100, ui.canvas->iconSize().height() + lineCount * metrics.height() + 3 * frameHMargin)); 0462 0463 // Set a minimum size of 6x3 icons 0464 const int columnCount = 6; 0465 const int rowCount = 3; 0466 QStyleOption opt; 0467 opt.initFrom(ui.canvas); 0468 int width = columnCount * ui.canvas->gridSize().width(); 0469 width += ui.canvas->verticalScrollBar()->sizeHint().width() + 1; 0470 width += 2 * ui.canvas->frameWidth(); 0471 if (ui.canvas->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, &opt, ui.canvas)) { 0472 width += ui.canvas->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing, &opt, ui.canvas); 0473 } 0474 int height = rowCount * ui.canvas->gridSize().height() + 1; 0475 height += 2 * ui.canvas->frameWidth(); 0476 0477 ui.canvas->setMinimumSize(QSize(width, height)); 0478 0479 model->setIconSize(ui.canvas->iconSize()); 0480 model->setDevicePixelRatio(q->devicePixelRatioF()); 0481 model->load(filelist); 0482 0483 if (!pendingSelectedIcon.isEmpty()) { 0484 selectIcon(pendingSelectedIcon); 0485 pendingSelectedIcon.clear(); 0486 } 0487 } 0488 0489 bool KIconDialogPrivate::selectIcon(const QString &iconName) 0490 { 0491 for (int i = 0; i < proxyModel->rowCount(); ++i) { 0492 const QModelIndex idx = proxyModel->index(i, 0); 0493 0494 QString name = idx.data(KIconDialogModel::PathRole).toString(); 0495 if (!name.isEmpty() && isSystemIconsContext()) { 0496 const QFileInfo fi(name); 0497 name = fi.completeBaseName(); 0498 } 0499 0500 if (iconName == name) { 0501 ui.canvas->setCurrentIndex(idx); 0502 return true; 0503 } 0504 } 0505 0506 return false; 0507 } 0508 0509 void KIconDialog::setStrictIconSize(bool b) 0510 { 0511 d->m_bStrictIconSize = b; 0512 } 0513 0514 bool KIconDialog::strictIconSize() const 0515 { 0516 return d->m_bStrictIconSize; 0517 } 0518 0519 void KIconDialog::setIconSize(int size) 0520 { 0521 // see KIconLoader, if you think this is weird 0522 if (size == 0) { 0523 d->mGroupOrSize = KIconLoader::Desktop; // default Group 0524 } else { 0525 d->mGroupOrSize = -size; // yes, KIconLoader::queryIconsByContext is weird 0526 } 0527 } 0528 0529 int KIconDialog::iconSize() const 0530 { 0531 // 0 or any other value ==> mGroupOrSize is a group, so we return 0 0532 return (d->mGroupOrSize < 0) ? -d->mGroupOrSize : 0; 0533 } 0534 0535 void KIconDialog::setSelectedIcon(const QString &iconName) 0536 { 0537 // TODO Update live when dialog is already open 0538 d->pendingSelectedIcon = iconName; 0539 } 0540 0541 void KIconDialog::setup(KIconLoader::Group group, KIconLoader::Context context, bool strictIconSize, int iconSize, bool user, bool lockUser, bool lockCustomDir) 0542 { 0543 d->m_bStrictIconSize = strictIconSize; 0544 d->m_bLockUser = lockUser; 0545 d->m_bLockCustomDir = lockCustomDir; 0546 if (iconSize == 0) { 0547 if (group == KIconLoader::NoGroup) { 0548 // NoGroup has numeric value -1, which should 0549 // not really be used with KIconLoader::queryIcons*(...); 0550 // pick a proper group. 0551 d->mGroupOrSize = KIconLoader::Small; 0552 } else { 0553 d->mGroupOrSize = group; 0554 } 0555 } else { 0556 d->mGroupOrSize = -iconSize; 0557 } 0558 0559 if (user) { 0560 d->ui.contextCombo->setCurrentIndex(d->ui.contextCombo->count() - 1); 0561 } else { 0562 d->setContext(context); 0563 } 0564 0565 d->ui.contextCombo->setEnabled(!user || !lockUser); 0566 0567 // Disable "Other" entry when user is locked 0568 auto *model = qobject_cast<QStandardItemModel *>(d->ui.contextCombo->model()); 0569 auto *otherItem = model->item(model->rowCount() - 1); 0570 auto flags = otherItem->flags(); 0571 flags.setFlag(Qt::ItemIsEnabled, !lockUser); 0572 otherItem->setFlags(flags); 0573 0574 // Only allow browsing when explicitly allowed and user icons are allowed 0575 // An app may not expect a path when asking only about system icons 0576 d->browseButton->setVisible(!lockCustomDir && (!user || !lockUser)); 0577 } 0578 0579 void KIconDialogPrivate::setContext(KIconLoader::Context context) 0580 { 0581 mContext = context; 0582 const int index = ui.contextCombo->findData(context); 0583 if (index > -1) { 0584 ui.contextCombo->setCurrentIndex(index); 0585 } 0586 } 0587 0588 void KIconDialogPrivate::updatePlaceholderLabel() 0589 { 0590 if (proxyModel->rowCount() > 0) { 0591 placeholderLabel->hide(); 0592 return; 0593 } 0594 0595 if (!ui.searchLine->text().isEmpty()) { 0596 placeholderLabel->setText(i18n("No icons matching the search")); 0597 } else { 0598 placeholderLabel->setText(i18n("No icons in this category")); 0599 } 0600 0601 placeholderLabel->show(); 0602 } 0603 0604 void KIconDialog::setCustomLocation(const QString &location) 0605 { 0606 d->customLocation = location; 0607 } 0608 0609 QString KIconDialog::openDialog() 0610 { 0611 if (exec() == Accepted) { 0612 if (!d->custom.isEmpty()) { 0613 return d->custom; 0614 } 0615 0616 const QString name = d->ui.canvas->currentIndex().data(KIconDialogModel::PathRole).toString(); 0617 if (name.isEmpty() || !d->ui.contextCombo->currentData().isValid()) { 0618 return name; 0619 } 0620 0621 QFileInfo fi(name); 0622 return fi.completeBaseName(); 0623 } 0624 0625 return QString(); 0626 } 0627 0628 void KIconDialog::showDialog() 0629 { 0630 setModal(false); 0631 show(); 0632 } 0633 0634 void KIconDialog::slotOk() 0635 { 0636 QString name; 0637 if (!d->custom.isEmpty()) { 0638 name = d->custom; 0639 } else { 0640 name = d->ui.canvas->currentIndex().data(KIconDialogModel::PathRole).toString(); 0641 if (!name.isEmpty() && d->isSystemIconsContext()) { 0642 const QFileInfo fi(name); 0643 name = fi.completeBaseName(); 0644 } 0645 } 0646 0647 Q_EMIT newIconName(name); 0648 QDialog::accept(); 0649 } 0650 0651 QString KIconDialog::getIcon(KIconLoader::Group group, 0652 KIconLoader::Context context, 0653 bool strictIconSize, 0654 int iconSize, 0655 bool user, 0656 QWidget *parent, 0657 const QString &title) 0658 { 0659 KIconDialog dlg(parent); 0660 dlg.setup(group, context, strictIconSize, iconSize, user); 0661 if (!title.isEmpty()) { 0662 dlg.setWindowTitle(title); 0663 } 0664 0665 return dlg.openDialog(); 0666 } 0667 0668 void KIconDialogPrivate::browse() 0669 { 0670 if (browseDialog) { 0671 browseDialog.data()->show(); 0672 browseDialog.data()->raise(); 0673 return; 0674 } 0675 0676 // Create a file dialog to select an ICO, PNG, XPM or SVG file, 0677 // with the image previewer shown. 0678 QFileDialog *dlg = new QFileDialog(q, i18n("Select Icon"), QString(), i18n("*.ico *.png *.xpm *.svg *.svgz|Icon Files (*.ico *.png *.xpm *.svg *.svgz)")); 0679 // TODO This was deliberately modal before, why? Or just because "window modal" wasn't a thing? 0680 dlg->setWindowModality(Qt::WindowModal); 0681 dlg->setFileMode(QFileDialog::ExistingFile); 0682 QObject::connect(dlg, &QFileDialog::fileSelected, q, [this](const QString &path) { 0683 if (!path.isEmpty()) { 0684 custom = path; 0685 if (isSystemIconsContext()) { 0686 customLocation = QFileInfo(custom).absolutePath(); 0687 } 0688 q->slotOk(); 0689 } 0690 }); 0691 browseDialog = dlg; 0692 dlg->show(); 0693 } 0694 0695 bool KIconDialogPrivate::isSystemIconsContext() const 0696 { 0697 return ui.contextCombo->currentData().isValid(); 0698 } 0699 0700 #include "kicondialog.moc" 0701 #include "moc_kicondialog.cpp" 0702 #include "moc_kicondialogmodel_p.cpp"