File indexing completed on 2024-05-12 03:55:46

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