KIconThemes

kicondialog.cpp
1/* vi: ts=8 sts=4 sw=4
2
3 This file is part of the KDE project, module kfile.
4 SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org>
5 SPDX-FileCopyrightText: 2000 Kurt Granroth <granroth@kde.org>
6 SPDX-FileCopyrightText: 1997 Christoph Neerfeld <chris@kde.org>
7 SPDX-FileCopyrightText: 2002 Carsten Pfeiffer <pfeiffer@kde.org>
8 SPDX-FileCopyrightText: 2021 Kai Uwe Broulik <kde@broulik.de>
9
10 SPDX-License-Identifier: LGPL-2.0-only
11*/
12
13#include "kicondialog.h"
14#include "kicondialog_p.h"
15#include "kicondialogmodel_p.h"
16
17#include <KLazyLocalizedString>
18#include <KLocalizedString>
19#include <KStandardAction>
20
21#include <QAbstractListModel>
22#include <QActionGroup>
23#include <QApplication>
24#include <QComboBox>
25#include <QDialogButtonBox>
26#include <QFileInfo>
27#include <QGraphicsOpacityEffect>
28#include <QLabel>
29#include <QList>
30#include <QMenu>
31#include <QPainter>
32#include <QScrollBar>
33#include <QSortFilterProxyModel>
34#include <QStandardItemModel> // for manipulatig QComboBox
35#include <QStandardPaths>
36#include <QSvgRenderer>
37
38#include <algorithm>
39#include <math.h>
40
41static const int s_edgePad = 3;
42
43class KIconDialogSortFilterProxyModel : public QSortFilterProxyModel
44{
46
47public:
48 explicit KIconDialogSortFilterProxyModel(QObject *parent);
49
50 enum SymbolicIcons { AllSymbolicIcons, OnlySymbolicIcons, NoSymbolicIcons };
51
52 void setSymbolicIcons(SymbolicIcons symbolicIcons);
53 void setHasSymbolicIcon(bool hasSymbolicIcon);
54
55protected:
56 bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
57
58private:
59 SymbolicIcons m_symbolicIcons = AllSymbolicIcons;
60 bool m_hasSymbolicIcon = false;
61};
62
63KIconDialogSortFilterProxyModel::KIconDialogSortFilterProxyModel(QObject *parent)
64 : QSortFilterProxyModel(parent)
65{
66}
67
68void KIconDialogSortFilterProxyModel::setSymbolicIcons(SymbolicIcons symbolicIcons)
69{
70 if (m_symbolicIcons == symbolicIcons) {
71 return;
72 }
73
74 m_symbolicIcons = symbolicIcons;
76}
77
78void KIconDialogSortFilterProxyModel::setHasSymbolicIcon(bool hasSymbolicIcon)
79{
80 if (m_hasSymbolicIcon == hasSymbolicIcon) {
81 return;
82 }
83
84 m_hasSymbolicIcon = hasSymbolicIcon;
86}
87
88bool KIconDialogSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
89{
90 if (m_hasSymbolicIcon) {
91 if (m_symbolicIcons == OnlySymbolicIcons || m_symbolicIcons == NoSymbolicIcons) {
92 const QString display = sourceModel()->index(source_row, 0, source_parent).data(Qt::DisplayRole).toString();
93 const bool isSymbolic = display.endsWith(KIconDialogModel::symbolicSuffix());
94 if ((m_symbolicIcons == OnlySymbolicIcons && !isSymbolic) || (m_symbolicIcons == NoSymbolicIcons && isSymbolic)) {
95 return false;
96 }
97 }
98 }
99
100 return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
101}
102
103KIconDialogModel::KIconDialogModel(KIconLoader *loader, QObject *parent)
104 : QAbstractListModel(parent)
105 , m_loader(loader)
106{
107}
108
109KIconDialogModel::~KIconDialogModel() = default;
110
111qreal KIconDialogModel::devicePixelRatio() const
112{
113 return m_dpr;
114}
115
116void KIconDialogModel::setDevicePixelRatio(qreal dpr)
117{
118 m_dpr = dpr;
119}
120
121QSize KIconDialogModel::iconSize() const
122{
123 return m_iconSize;
124}
125
126void KIconDialogModel::setIconSize(const QSize &iconSize)
127{
128 m_iconSize = iconSize;
129}
130
131QLatin1String KIconDialogModel::symbolicSuffix()
132{
133 return QLatin1String("-symbolic");
134}
135
136bool KIconDialogModel::hasSymbolicIcon() const
137{
138 return m_hasSymbolicIcon;
139}
140
141void KIconDialogModel::load(const QStringList &paths)
142{
143 beginResetModel();
144
145 const bool oldSymbolic = m_hasSymbolicIcon;
146 m_hasSymbolicIcon = false;
147
148 m_data.clear();
149 m_data.reserve(paths.count());
150
151 for (const QString &path : paths) {
152 const QFileInfo fi(path);
153
154 KIconDialogModelData item;
155 item.name = fi.completeBaseName();
156 item.path = path;
157 // pixmap is created on demand
158
159 if (!m_hasSymbolicIcon && item.name.endsWith(symbolicSuffix())) {
160 m_hasSymbolicIcon = true;
161 }
162
163 m_data.append(item);
164 }
165
166 endResetModel();
167
168 if (oldSymbolic != m_hasSymbolicIcon) {
169 Q_EMIT hasSymbolicIconChanged(m_hasSymbolicIcon);
170 }
171}
172
173int KIconDialogModel::rowCount(const QModelIndex &parent) const
174{
175 if (parent.isValid()) {
176 return 0;
177 }
178 return m_data.count();
179}
180
181QVariant KIconDialogModel::data(const QModelIndex &index, int role) const
182{
183 if (!checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)) {
184 return QVariant();
185 }
186
187 const auto &item = m_data.at(index.row());
188
189 switch (role) {
190 case Qt::DisplayRole:
191 return item.name;
193 if (item.pixmap.isNull()) {
194 const_cast<KIconDialogModel *>(this)->loadPixmap(index);
195 }
196 return item.pixmap;
197 case Qt::ToolTipRole:
198 return item.name;
199 case PathRole:
200 return item.path;
201 }
202
203 return QVariant();
204}
205
206void KIconDialogModel::loadPixmap(const QModelIndex &index)
207{
208 Q_ASSERT(index.isValid());
209
210 auto &item = m_data[index.row()];
211 Q_ASSERT(item.pixmap.isNull());
212
213 const auto dpr = devicePixelRatio();
214
215 item.pixmap = m_loader->loadScaledIcon(item.path, KIconLoader::Desktop, dpr, iconSize(), KIconLoader::DefaultState, {}, nullptr, true);
216 item.pixmap.setDevicePixelRatio(dpr);
217}
218
219/**
220 * Qt allocates very little horizontal space for the icon name,
221 * even if the gridSize width is large. This delegate allocates
222 * the gridSize width (minus some padding) for the icon and icon name.
223 */
224class KIconCanvasDelegate : public QAbstractItemDelegate
225{
227public:
228 KIconCanvasDelegate(QListView *parent, QAbstractItemDelegate *defaultDelegate);
229 ~KIconCanvasDelegate() override
230 {
231 }
232 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
233 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
234
235private:
236 QAbstractItemDelegate *m_defaultDelegate = nullptr;
237};
238
239KIconCanvasDelegate::KIconCanvasDelegate(QListView *parent, QAbstractItemDelegate *defaultDelegate)
240 : QAbstractItemDelegate(parent)
241{
242 m_defaultDelegate = defaultDelegate;
243}
244
245void KIconCanvasDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
246{
247 auto *canvas = static_cast<QListView *>(parent());
248 const int gridWidth = canvas->gridSize().width();
249 QStyleOptionViewItem newOption = option;
250 newOption.displayAlignment = Qt::AlignHCenter | Qt::AlignTop;
251 newOption.features.setFlag(QStyleOptionViewItem::WrapText);
252 // Manipulate the width available.
253 newOption.rect.setX((option.rect.x() / gridWidth) * gridWidth + s_edgePad);
254 newOption.rect.setY(option.rect.y() + s_edgePad);
255 newOption.rect.setWidth(gridWidth - 2 * s_edgePad);
256 newOption.rect.setHeight(option.rect.height() - 2 * s_edgePad);
257 m_defaultDelegate->paint(painter, newOption, index);
258}
259
260QSize KIconCanvasDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
261{
262 auto *canvas = static_cast<QListView *>(parent());
263
264 // TODO can we set wrap text and display alignment somewhere globally?
265 QStyleOptionViewItem newOption = option;
266 newOption.displayAlignment = Qt::AlignHCenter | Qt::AlignTop;
267 newOption.features.setFlag(QStyleOptionViewItem::WrapText);
268
269 QSize size = m_defaultDelegate->sizeHint(newOption, index);
270 const int gridWidth = canvas->gridSize().width();
271 const int gridHeight = canvas->gridSize().height();
272 size.setWidth(gridWidth - 2 * s_edgePad);
273 size.setHeight(gridHeight - 2 * s_edgePad);
274 QFontMetrics metrics(option.font);
275 size.setHeight(gridHeight + metrics.height() * 3);
276 return size;
277}
278
279KIconDialogPrivate::KIconDialogPrivate(KIconDialog *qq)
280 : q(qq)
281 , mpLoader(KIconLoader::global())
282 , model(new KIconDialogModel(mpLoader, qq))
283 , proxyModel(new KIconDialogSortFilterProxyModel(qq))
284 , filterSymbolicAction(new QAction(qq))
285 , filterSymbolicGroup(new QActionGroup(qq))
286{
287 proxyModel->setSourceModel(model);
288 proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
289
290 filterSymbolicGroup->setExclusive(true);
291
292 QObject::connect(model, &KIconDialogModel::hasSymbolicIconChanged, filterSymbolicAction, &QAction::setVisible);
293 QObject::connect(model, &KIconDialogModel::hasSymbolicIconChanged, proxyModel, &KIconDialogSortFilterProxyModel::setHasSymbolicIcon);
294}
295
296/*
297 * KIconDialog: Dialog for selecting icons. Both system and user
298 * specified icons can be chosen.
299 */
300
302 : QDialog(parent)
303 , d(new KIconDialogPrivate(this))
304{
305 setModal(true);
306
307 d->init();
308}
309
310void KIconDialogPrivate::init()
311{
312 mGroupOrSize = KIconLoader::Desktop;
313 mContext = KIconLoader::Any;
314
315 ui.setupUi(q);
316
317 auto updatePlaceholder = [this] {
318 updatePlaceholderLabel();
319 };
320 QObject::connect(proxyModel, &QSortFilterProxyModel::modelReset, q, updatePlaceholder);
321 QObject::connect(proxyModel, &QSortFilterProxyModel::rowsInserted, q, updatePlaceholder);
322 QObject::connect(proxyModel, &QSortFilterProxyModel::rowsRemoved, q, updatePlaceholder);
323
324 QAction *findAction = KStandardAction::find(ui.searchLine, qOverload<>(&QWidget::setFocus), q);
325 q->addAction(findAction);
326
327 QMenu *filterSymbolicMenu = new QMenu(q);
328
329 QAction *filterSymbolicAll = filterSymbolicMenu->addAction(i18nc("@item:inmenu All icons", "All"));
330 filterSymbolicAll->setData(KIconDialogSortFilterProxyModel::AllSymbolicIcons);
331 filterSymbolicAll->setChecked(true); // Start with "All" icons.
332 filterSymbolicAll->setCheckable(true);
333
334 QAction *filterSymbolicOnly = filterSymbolicMenu->addAction(i18nc("@item:inmenu Show only symbolic icons", "Only Symbolic"));
335 filterSymbolicOnly->setData(KIconDialogSortFilterProxyModel::OnlySymbolicIcons);
336 filterSymbolicOnly->setCheckable(true);
337
338 QAction *filterSymbolicNone = filterSymbolicMenu->addAction(i18nc("@item:inmenu Hide symbolic icons", "No Symbolic"));
339 filterSymbolicNone->setData(KIconDialogSortFilterProxyModel::NoSymbolicIcons);
340 filterSymbolicNone->setCheckable(true);
341
342 filterSymbolicAction->setIcon(QIcon::fromTheme(QStringLiteral("view-filter")));
343 filterSymbolicAction->setCheckable(true);
344 filterSymbolicAction->setChecked(true);
345 filterSymbolicAction->setMenu(filterSymbolicMenu);
346
347 filterSymbolicGroup->addAction(filterSymbolicAll);
348 filterSymbolicGroup->addAction(filterSymbolicOnly);
349 filterSymbolicGroup->addAction(filterSymbolicNone);
350 QObject::connect(filterSymbolicGroup, &QActionGroup::triggered, q, [this](QAction *action) {
351 proxyModel->setSymbolicIcons(static_cast<KIconDialogSortFilterProxyModel::SymbolicIcons>(action->data().toInt()));
352 });
353
354 ui.searchLine->addAction(filterSymbolicAction, QLineEdit::TrailingPosition);
355
357
358 static const KLazyLocalizedString context_text[] = {
359 kli18n("All"),
360 kli18n("Actions"),
361 kli18n("Applications"),
362 kli18n("Categories"),
363 kli18n("Devices"),
364 kli18n("Emblems"),
365 kli18n("Emotes"),
366 kli18n("Mimetypes"),
367 kli18n("Places"),
368 kli18n("Status"),
369 };
370 static const KIconLoader::Context context_id[] = {
381 };
382 const int cnt = sizeof(context_text) / sizeof(context_text[0]);
383 for (int i = 0; i < cnt; ++i) {
384 if (mpLoader->hasContext(context_id[i])) {
385 ui.contextCombo->addItem(context_text[i].toString(), context_id[i]);
386 if (i == 0) {
387 ui.contextCombo->insertSeparator(i + 1);
388 }
389 }
390 }
391 ui.contextCombo->insertSeparator(ui.contextCombo->count());
392 ui.contextCombo->addItem(i18nc("Other icons", "Other"));
393 ui.contextCombo->setMaxVisibleItems(ui.contextCombo->count());
394 ui.contextCombo->setFixedSize(ui.contextCombo->sizeHint());
395
396 QObject::connect(ui.contextCombo, qOverload<int>(&QComboBox::activated), q, [this]() {
397 const auto currentData = ui.contextCombo->currentData();
398 if (currentData.isValid()) {
399 mContext = static_cast<KIconLoader::Context>(ui.contextCombo->currentData().toInt());
400 } else {
401 mContext = static_cast<KIconLoader::Context>(-1);
402 }
403 showIcons();
404 });
405
406 auto *delegate = new KIconCanvasDelegate(ui.canvas, ui.canvas->itemDelegate());
407 ui.canvas->setItemDelegate(delegate);
408
409 ui.canvas->setModel(proxyModel);
410
411 QObject::connect(ui.canvas, &QAbstractItemView::activated, q, [this]() {
412 custom.clear();
413 q->slotOk();
414 });
415
416 // You can't just stack widgets on top of each other in Qt Designer
417 auto *placeholderLayout = new QVBoxLayout(ui.canvas);
418
419 placeholderLabel = new QLabel();
420 QFont placeholderLabelFont;
421 // To match the size of a level 2 Heading/KTitleWidget
422 placeholderLabelFont.setPointSize(qRound(placeholderLabelFont.pointSize() * 1.3));
423 placeholderLabel->setFont(placeholderLabelFont);
424 placeholderLabel->setTextInteractionFlags(Qt::NoTextInteraction);
425 placeholderLabel->setWordWrap(true);
426 placeholderLabel->setAlignment(Qt::AlignCenter);
427
428 // Match opacity of QML placeholder label component
429 auto *effect = new QGraphicsOpacityEffect(placeholderLabel);
430 effect->setOpacity(0.5);
431 placeholderLabel->setGraphicsEffect(effect);
432
433 updatePlaceholderLabel();
434
435 placeholderLayout->addWidget(placeholderLabel);
436 placeholderLayout->setAlignment(placeholderLabel, Qt::AlignCenter);
437
438 // TODO I bet there is a KStandardAction for that?
439 browseButton = new QPushButton(QIcon::fromTheme(QStringLiteral("folder-open")), i18n("Browse…"));
440 // TODO does this have implicatons? I just want the "Browse" button on the left side :)
441 ui.buttonBox->addButton(browseButton, QDialogButtonBox::HelpRole);
442 QObject::connect(browseButton, &QPushButton::clicked, q, [this] {
443 browse();
444 });
445
446 QObject::connect(ui.buttonBox, &QDialogButtonBox::accepted, q, &KIconDialog::slotOk);
448
449 q->adjustSize();
450}
451
452KIconDialog::~KIconDialog() = default;
453
454static bool sortByFileName(const QString &path1, const QString &path2)
455{
456 const QString fileName1 = path1.mid(path1.lastIndexOf(QLatin1Char('/')) + 1);
457 const QString fileName2 = path2.mid(path2.lastIndexOf(QLatin1Char('/')) + 1);
458 return QString::compare(fileName1, fileName2, Qt::CaseInsensitive) < 0;
459}
460
461void KIconDialogPrivate::showIcons()
462{
463 QStringList filelist;
464 if (isSystemIconsContext()) {
465 if (m_bStrictIconSize) {
466 filelist = mpLoader->queryIcons(mGroupOrSize, mContext);
467 } else {
468 filelist = mpLoader->queryIconsByContext(mGroupOrSize, mContext);
469 }
470 } else if (!customLocation.isEmpty()) {
471 filelist = mpLoader->queryIconsByDir(customLocation);
472 } else {
473 // List PNG files found directly in the kiconload search paths.
474 const QStringList pngNameFilter(QStringLiteral("*.png"));
475 for (const QString &relDir : KIconLoader::global()->searchPaths()) {
477 for (const QString &dir : dirs) {
478 const auto files = QDir(dir).entryList(pngNameFilter);
479 for (const QString &fileName : files) {
480 filelist << dir + QLatin1Char('/') + fileName;
481 }
482 }
483 }
484 }
485
486 std::sort(filelist.begin(), filelist.end(), sortByFileName);
487
488 // The KIconCanvas has uniformItemSizes set which really expects
489 // all added icons to be the same size, otherwise weirdness ensues :)
490 // Ensure all SVGs are scaled to the desired size and that as few icons
491 // need to be padded as possible by specifying a sensible size.
492 if (mGroupOrSize < -1) {
493 // mGroupOrSize can be -1 if NoGroup is chosen.
494 // Explicit size.
495 ui.canvas->setIconSize(QSize(-mGroupOrSize, -mGroupOrSize));
496 } else {
497 // Icon group.
498 int groupSize = mpLoader->currentSize(static_cast<KIconLoader::Group>(mGroupOrSize));
499 ui.canvas->setIconSize(QSize(groupSize, groupSize));
500 }
501
502 // Try to make room for three lines of text...
503 QFontMetrics metrics(ui.canvas->font());
504 const int frameHMargin = ui.canvas->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, ui.canvas) + 1;
505 const int lineCount = 3;
506 ui.canvas->setGridSize(QSize(100, ui.canvas->iconSize().height() + lineCount * metrics.height() + 3 * frameHMargin));
507
508 // Set a minimum size of 6x3 icons
509 const int columnCount = 6;
510 const int rowCount = 3;
511 QStyleOption opt;
512 opt.initFrom(ui.canvas);
513 int width = columnCount * ui.canvas->gridSize().width();
514 width += ui.canvas->verticalScrollBar()->sizeHint().width() + 1;
515 width += 2 * ui.canvas->frameWidth();
516 if (ui.canvas->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, &opt, ui.canvas)) {
517 width += ui.canvas->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing, &opt, ui.canvas);
518 }
519 int height = rowCount * ui.canvas->gridSize().height() + 1;
520 height += 2 * ui.canvas->frameWidth();
521
522 ui.canvas->setMinimumSize(QSize(width, height));
523
524 model->setIconSize(ui.canvas->iconSize());
525 model->setDevicePixelRatio(q->devicePixelRatioF());
526 model->load(filelist);
527
528 if (!pendingSelectedIcon.isEmpty()) {
529 selectIcon(pendingSelectedIcon);
530 pendingSelectedIcon.clear();
531 }
532}
533
534bool KIconDialogPrivate::selectIcon(const QString &iconName)
535{
536 for (int i = 0; i < proxyModel->rowCount(); ++i) {
537 const QModelIndex idx = proxyModel->index(i, 0);
538
539 QString name = idx.data(KIconDialogModel::PathRole).toString();
540 if (!name.isEmpty() && isSystemIconsContext()) {
541 const QFileInfo fi(name);
542 name = fi.completeBaseName();
543 }
544
545 if (iconName == name) {
546 ui.canvas->setCurrentIndex(idx);
547 return true;
548 }
549 }
550
551 return false;
552}
553
555{
556 d->m_bStrictIconSize = b;
557}
558
560{
561 return d->m_bStrictIconSize;
562}
563
565{
566 // see KIconLoader, if you think this is weird
567 if (size == 0) {
568 d->mGroupOrSize = KIconLoader::Desktop; // default Group
569 } else {
570 d->mGroupOrSize = -size; // yes, KIconLoader::queryIconsByContext is weird
571 }
572}
573
575{
576 // 0 or any other value ==> mGroupOrSize is a group, so we return 0
577 return (d->mGroupOrSize < 0) ? -d->mGroupOrSize : 0;
578}
579
581{
582 // TODO Update live when dialog is already open
583 d->pendingSelectedIcon = iconName;
584}
585
586void KIconDialog::setup(KIconLoader::Group group, KIconLoader::Context context, bool strictIconSize, int iconSize, bool user, bool lockUser, bool lockCustomDir)
587{
588 d->m_bStrictIconSize = strictIconSize;
589 d->m_bLockUser = lockUser;
590 d->m_bLockCustomDir = lockCustomDir;
591 if (iconSize == 0) {
592 if (group == KIconLoader::NoGroup) {
593 // NoGroup has numeric value -1, which should
594 // not really be used with KIconLoader::queryIcons*(...);
595 // pick a proper group.
596 d->mGroupOrSize = KIconLoader::Small;
597 } else {
598 d->mGroupOrSize = group;
599 }
600 } else {
601 d->mGroupOrSize = -iconSize;
602 }
603
604 if (user) {
605 d->ui.contextCombo->setCurrentIndex(d->ui.contextCombo->count() - 1);
606 } else {
607 d->setContext(context);
608 }
609
610 d->ui.contextCombo->setEnabled(!user || !lockUser);
611
612 // Disable "Other" entry when user is locked
613 auto *model = qobject_cast<QStandardItemModel *>(d->ui.contextCombo->model());
614 auto *otherItem = model->item(model->rowCount() - 1);
615 auto flags = otherItem->flags();
616 flags.setFlag(Qt::ItemIsEnabled, !lockUser);
617 otherItem->setFlags(flags);
618
619 // Only allow browsing when explicitly allowed and user icons are allowed
620 // An app may not expect a path when asking only about system icons
621 d->browseButton->setVisible(!lockCustomDir && (!user || !lockUser));
622}
623
624void KIconDialogPrivate::setContext(KIconLoader::Context context)
625{
626 mContext = context;
627 const int index = ui.contextCombo->findData(context);
628 if (index > -1) {
629 ui.contextCombo->setCurrentIndex(index);
630 }
631}
632
633void KIconDialogPrivate::updatePlaceholderLabel()
634{
635 if (proxyModel->rowCount() > 0) {
636 placeholderLabel->hide();
637 return;
638 }
639
640 if (!ui.searchLine->text().isEmpty()) {
641 placeholderLabel->setText(i18n("No icons matching the search"));
642 } else {
643 placeholderLabel->setText(i18n("No icons in this category"));
644 }
645
646 placeholderLabel->show();
647}
648
650{
651 d->customLocation = location;
652}
653
655{
656 if (exec() == Accepted) {
657 if (!d->custom.isEmpty()) {
658 return d->custom;
659 }
660
661 const QString name = d->ui.canvas->currentIndex().data(KIconDialogModel::PathRole).toString();
662 if (name.isEmpty() || !d->ui.contextCombo->currentData().isValid()) {
663 return name;
664 }
665
666 QFileInfo fi(name);
667 return fi.completeBaseName();
668 }
669
670 return QString();
671}
672
674{
675 setModal(false);
676 show();
677}
678
679void KIconDialog::slotOk()
680{
681 QString name;
682 if (!d->custom.isEmpty()) {
683 name = d->custom;
684 } else {
685 name = d->ui.canvas->currentIndex().data(KIconDialogModel::PathRole).toString();
686 if (!name.isEmpty() && d->isSystemIconsContext()) {
687 const QFileInfo fi(name);
688 name = fi.completeBaseName();
689 }
690 }
691
692 Q_EMIT newIconName(name);
694}
695
696void KIconDialog::showEvent(QShowEvent *event)
697{
699 d->showIcons();
700 d->ui.searchLine->setFocus();
701}
702
704 KIconLoader::Context context,
705 bool strictIconSize,
706 int iconSize,
707 bool user,
708 QWidget *parent,
709 const QString &title)
710{
711 KIconDialog dlg(parent);
712 dlg.setup(group, context, strictIconSize, iconSize, user);
713 if (!title.isEmpty()) {
714 dlg.setWindowTitle(title);
715 }
716
717 return dlg.openDialog();
718}
719
720void KIconDialogPrivate::browse()
721{
722 if (browseDialog) {
723 browseDialog.data()->show();
724 browseDialog.data()->raise();
725 return;
726 }
727
728 // Create a file dialog to select an ICO, PNG, XPM or SVG file,
729 // with the image previewer shown.
730 QFileDialog *dlg = new QFileDialog(q, i18n("Select Icon"), QString(), i18n("*.ico *.png *.xpm *.svg *.svgz|Icon Files (*.ico *.png *.xpm *.svg *.svgz)"));
731 // TODO This was deliberately modal before, why? Or just because "window modal" wasn't a thing?
734 QObject::connect(dlg, &QFileDialog::fileSelected, q, [this](const QString &path) {
735 if (!path.isEmpty()) {
736 custom = path;
737 if (isSystemIconsContext()) {
738 customLocation = QFileInfo(custom).absolutePath();
739 }
740 q->slotOk();
741 }
742 });
743 browseDialog = dlg;
744 dlg->show();
745}
746
747bool KIconDialogPrivate::isSystemIconsContext() const
748{
749 return ui.contextCombo->currentData().isValid();
750}
751
752#include "kicondialog.moc"
753#include "moc_kicondialog.cpp"
754#include "moc_kicondialogmodel_p.cpp"
Dialog for interactive selection of icons.
Definition kicondialog.h:33
bool strictIconSize() const
Returns true if a strict icon size policy is set.
KIconDialog(QWidget *parent=nullptr)
Constructs an icon selection dialog using the global icon loader.
void setStrictIconSize(bool policy)
Sets a strict icon size policy for allowed icons.
void setup(KIconLoader::Group group, KIconLoader::Context context=KIconLoader::Application, bool strictIconSize=false, int iconSize=0, bool user=false, bool lockUser=false, bool lockCustomDir=false)
Allows you to set the same parameters as in the class method getIcon(), as well as two additional par...
~KIconDialog() override
Destructs the dialog.
int iconSize() const
Returns the icon size set via setIconSize() or 0, if the default icon size will be used.
void setSelectedIcon(const QString &iconName)
Sets the icon that is initially selected in the dialog.
void showDialog()
show()s this dialog and emits a newIconName(const QString&) signal when successful.
static QString getIcon(KIconLoader::Group group=KIconLoader::Desktop, KIconLoader::Context context=KIconLoader::Application, bool strictIconSize=false, int iconSize=0, bool user=false, QWidget *parent=nullptr, const QString &title=QString())
Pops up the dialog an lets the user select an icon.
QString openDialog()
exec()utes this modal dialog and returns the name of the selected icon, or QString() if the dialog wa...
void setIconSize(int size)
Sets the size of the icons to be shown / selected.
void setCustomLocation(const QString &location)
Sets the location of the custom icon directory.
Iconloader for KDE.
Definition kiconloader.h:71
Group
The group of the icon.
@ Small
Small icons, e.g. for buttons.
@ Desktop
Desktop icons.
@ NoGroup
No group.
@ DefaultState
The default state.
Context
Defines the context of the icon.
Definition kiconloader.h:78
@ Category
An icon that represents a category.
Definition kiconloader.h:85
@ Emblem
An icon that adds information to an existing icon.
Definition kiconloader.h:86
@ StatusIcon
An icon that represents an event.
Definition kiconloader.h:90
@ Application
An icon that represents an application.
Definition kiconloader.h:81
@ Emote
An icon that expresses an emotion.
Definition kiconloader.h:87
@ Any
Some icon with unknown purpose.
Definition kiconloader.h:79
@ Place
An icon that represents a location (e.g. 'home', 'trash').
Definition kiconloader.h:89
@ MimeType
An icon that represents a mime type (or file type).
Definition kiconloader.h:83
@ Action
An action icon (e.g. 'save', 'print').
Definition kiconloader.h:80
@ Device
An icon that represents a device.
Definition kiconloader.h:82
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
QString path(const QString &relativePath)
KIOCORE_EXPORT QString dir(const QString &fileClass)
QAction * find(const QObject *recvr, const char *slot, QObject *parent)
QString name(StandardShortcut id)
void clicked(bool checked)
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const const=0
virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const const=0
void rowsInserted(const QModelIndex &parent, int first, int last)
void rowsRemoved(const QModelIndex &parent, int first, int last)
void activated(const QModelIndex &index)
void setCheckable(bool)
void setChecked(bool)
QVariant data() const const
void setData(const QVariant &data)
void setVisible(bool)
void activated(int index)
virtual void accept()
virtual int exec()
void setModal(bool modal)
virtual void reject()
virtual void showEvent(QShowEvent *event) override
QStringList entryList(Filters filters, SortFlags sort) const const
void setFileMode(FileMode mode)
void fileSelected(const QString &file)
QString completeBaseName() const const
int pointSize() const const
void setPointSize(int pointSize)
QIcon fromTheme(const QString &name)
void textChanged(const QString &text)
iterator begin()
qsizetype count() const const
iterator end()
QAction * addAction(const QIcon &icon, const QString &text, Functor functor, const QKeySequence &shortcut)
QVariant data(int role) const const
bool isValid() const const
int row() const const
Q_EMITQ_EMIT
Q_OBJECTQ_OBJECT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
void setHeight(int height)
void setWidth(int width)
virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const const
void setFilterFixedString(const QString &pattern)
QStringList locateAll(StandardLocation type, const QString &fileName, LocateOptions options)
QString & append(QChar ch)
int compare(QLatin1StringView s1, const QString &s2, Qt::CaseSensitivity cs)
QChar * data()
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype lastIndexOf(QChar ch, Qt::CaseSensitivity cs) const const
QString mid(qsizetype position, qsizetype n) const const
PM_FocusFrameHMargin
SH_ScrollView_FrameOnlyAroundContents
void initFrom(const QWidget *widget)
AlignHCenter
CaseInsensitive
DisplayRole
ItemIsEnabled
NoTextInteraction
WindowModal
int toInt(bool *ok) const const
QString toString() const const
virtual bool event(QEvent *event) override
void setFocus()
void show()
void setWindowModality(Qt::WindowModality windowModality)
void setWindowTitle(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:15 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.