diff --git a/src/kicondialog.cpp b/src/kicondialog.cpp index 1f4193e..12ccb4f 100644 --- a/src/kicondialog.cpp +++ b/src/kicondialog.cpp @@ -1,664 +1,664 @@ /* vi: ts=8 sts=4 sw=4 * * This file is part of the KDE project, module kfile. * Copyright (C) 2000 Geert Jansen * (C) 2000 Kurt Granroth * (C) 1997 Christoph Neerfeld * (C) 2002 Carsten Pfeiffer * * This is free software; it comes under the GNU Library General * Public License, version 2. See the file "COPYING.LIB" for the * exact licensing terms. */ #include "kicondialog.h" #include "kicondialog_p.h" #include #include #ifndef _WIN32_WCE #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * Qt allocates very little horizontal space for the icon name, * even if the gridSize width is large. This delegate allocates * the gridSize width (minus some padding) for the icon and icon name. */ class KIconCanvasDelegate : public QAbstractItemDelegate { public: KIconCanvasDelegate(KIconCanvas *parent, QAbstractItemDelegate *defaultDelegate); ~KIconCanvasDelegate() override {} void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; private: KIconCanvas *m_iconCanvas = nullptr; QAbstractItemDelegate *m_defaultDelegate = nullptr; static const int HORIZONTAL_EDGE_PAD = 3; }; KIconCanvasDelegate::KIconCanvasDelegate(KIconCanvas *parent, QAbstractItemDelegate *defaultDelegate) : QAbstractItemDelegate(parent) { m_iconCanvas = parent; m_defaultDelegate = defaultDelegate; } void KIconCanvasDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { const int GRID_WIDTH = m_iconCanvas->gridSize().width(); QStyleOptionViewItem newOption = option; // Manipulate the width available. newOption.rect.setX((option.rect.x() / GRID_WIDTH) * GRID_WIDTH + HORIZONTAL_EDGE_PAD); newOption.rect.setWidth(GRID_WIDTH - 2 * HORIZONTAL_EDGE_PAD); m_defaultDelegate->paint(painter, newOption, index); } QSize KIconCanvasDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { QSize size = m_defaultDelegate->sizeHint(option, index); const int GRID_WIDTH = m_iconCanvas->gridSize().width(); size.setWidth(GRID_WIDTH - 2 * HORIZONTAL_EDGE_PAD); return size; } /* * KIconCanvas: Iconview for the iconloader dialog. */ KIconCanvas::KIconCanvas(QWidget *parent) : QListWidget(parent), m_loading(false), m_timer(new QTimer(this)), m_delegate(new KIconCanvasDelegate(this, itemDelegate())) { setViewMode(IconMode); setUniformItemSizes(true); setMovement(Static); setIconSize(QSize(60, 60)); connect(m_timer, SIGNAL(timeout()), this, SLOT(loadFiles())); connect(this, &QListWidget::currentItemChanged, this, &KIconCanvas::currentListItemChanged); setGridSize(QSize(100, 80)); setItemDelegate(m_delegate); } KIconCanvas::~KIconCanvas() { delete m_timer; delete m_delegate; } void KIconCanvas::loadFiles(const QStringList &files) { clear(); m_files = files; emit startLoading(m_files.count()); m_timer->setSingleShot(true); m_timer->start(10); m_loading = false; } void KIconCanvas::loadFiles() { setResizeMode(QListWidget::Fixed); QApplication::setOverrideCursor(Qt::WaitCursor); // disable updates to not trigger paint events when adding child items, // but force an initial paint so that we do not get garbage repaint(); setUpdatesEnabled(false); // Cache these as we will call them frequently. const int canvasIconWidth = iconSize().width(); const int canvasIconHeight = iconSize().width(); const bool uniformIconSize = uniformItemSizes(); m_loading = true; int i; QStringList::ConstIterator it; uint emitProgress = 10; // so we will emit it once in the beginning QStringList::ConstIterator end(m_files.constEnd()); for (it = m_files.constBegin(), i = 0; it != end; ++it, ++i) { if (emitProgress >= 10) { emit progress(i); emitProgress = 0; } emitProgress++; if (!m_loading) { // user clicked on a button that will load another set of icons break; } QImage img; // Use the extension as the format. Works for XPM and PNG, but not for SVG QString path = *it; QString ext = path.right(3).toUpper(); if (ext != QLatin1String("SVG") && ext != QLatin1String("VGZ")) { img.load(*it); } else { #ifndef _WIN32_WCE // Special stuff for SVG icons img = QImage(canvasIconWidth, canvasIconHeight, QImage::Format_ARGB32_Premultiplied); img.fill(0); QSvgRenderer renderer(*it); if (renderer.isValid()) { QPainter p(&img); renderer.render(&p); } #endif } if (img.isNull()) { continue; } if (img.width() > canvasIconWidth || img.height() > canvasIconHeight) { if (img.width() / (float)canvasIconWidth > img.height() / (float)canvasIconHeight) { int height = (int)(((float)canvasIconWidth / img.width()) * img.height()); img = img.scaled(canvasIconWidth, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } else { int width = (int)(((float)canvasIconHeight / img.height()) * img.width()); img = img.scaled(width, canvasIconHeight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } } if (uniformIconSize && (img.width() != canvasIconWidth || img.height() != canvasIconHeight)) { // Image is smaller than desired. Draw onto a transparent QImage of the required dimensions. // (Unpleasant glitches occur if we break the uniformIconSizes() contract). QImage paddedImage = QImage(canvasIconWidth, canvasIconHeight, QImage::Format_ARGB32_Premultiplied); paddedImage.fill(0); QPainter painter(&paddedImage); painter.drawImage((canvasIconWidth - img.width()) / 2, (canvasIconHeight - img.height()) / 2, img); img = paddedImage; } QPixmap pm = QPixmap::fromImage(img); QFileInfo fi(*it); QListWidgetItem *item = new QListWidgetItem(pm, fi.completeBaseName(), this); item->setData(Qt::UserRole, *it); item->setToolTip(fi.completeBaseName()); } // enable updates since we have to draw the whole view now setUpdatesEnabled(true); QApplication::restoreOverrideCursor(); m_loading = false; emit finished(); setResizeMode(QListWidget::Adjust); } QString KIconCanvas::getCurrent() const { if (!currentItem()) { return QString(); } return currentItem()->data(Qt::UserRole).toString(); } void KIconCanvas::stopLoading() { m_loading = false; } void KIconCanvas::currentListItemChanged(QListWidgetItem *item) { emit nameChanged((item != nullptr) ? item->text() : QString()); } // TODO KF6 remove and override KIconDialog::showEvent() class ShowEventFilter : public QObject { public: explicit ShowEventFilter(QObject *parent) : QObject(parent) {} ~ShowEventFilter() override {} private: bool eventFilter(QObject *watched, QEvent *event) override { if (event->type() == QEvent::Show) { KIconDialog *q = static_cast(parent()); q->d->showIcons(); q->d->searchLine->setFocus(); } return QObject::eventFilter(watched, event); } }; /* * KIconDialog: Dialog for selecting icons. Both system and user * specified icons can be chosen. */ KIconDialog::KIconDialog(QWidget *parent) : QDialog(parent), d(new KIconDialogPrivate(this)) { setModal(true); setWindowTitle(i18n("Select Icon")); d->mpLoader = KIconLoader::global(); d->init(); installEventFilter(new ShowEventFilter(this)); } KIconDialog::KIconDialog(KIconLoader *loader, QWidget *parent) : QDialog(parent), d(new KIconDialogPrivate(this)) { setModal(true); setWindowTitle(i18n("Select Icon")); d->mpLoader = loader; d->init(); installEventFilter(new ShowEventFilter(this)); } void KIconDialog::KIconDialogPrivate::init() { mGroupOrSize = KIconLoader::Desktop; mContext = KIconLoader::Any; QVBoxLayout *top = new QVBoxLayout; q->setLayout(top); QGroupBox *bgroup = new QGroupBox(q); bgroup->setTitle(i18n("Icon Source")); QVBoxLayout *vbox = new QVBoxLayout; bgroup->setLayout(vbox); top->addWidget(bgroup); QGridLayout *grid = new QGridLayout(); vbox->addLayout(grid); mpSystemIcons = new QRadioButton(i18n("S&ystem icons:"), bgroup); connect(mpSystemIcons, SIGNAL(clicked()), q, SLOT(_k_slotSystemIconClicked())); grid->addWidget(mpSystemIcons, 1, 0); mpCombo = new QComboBox(bgroup); connect(mpCombo, SIGNAL(activated(int)), q, SLOT(_k_slotContext(int))); grid->addWidget(mpCombo, 1, 1); mpOtherIcons = new QRadioButton(i18n("O&ther icons:"), bgroup); connect(mpOtherIcons, SIGNAL(clicked()), q, SLOT(_k_slotOtherIconClicked())); grid->addWidget(mpOtherIcons, 2, 0); mpBrowseBut = new QPushButton(i18n("&Browse..."), bgroup); connect(mpBrowseBut, SIGNAL(clicked()), q, SLOT(_k_slotBrowse())); grid->addWidget(mpBrowseBut, 2, 1); // // ADD SEARCHLINE // QHBoxLayout *searchLayout = new QHBoxLayout(); searchLayout->setMargin(0); top->addLayout(searchLayout); QLabel *searchLabel = new QLabel(i18n("&Search:"), q); searchLayout->addWidget(searchLabel); searchLine = new KListWidgetSearchLine(q); searchLayout->addWidget(searchLine); searchLabel->setBuddy(searchLine); QString wtstr = i18n("Search interactively for icon names (e.g. folder)."); searchLabel->setWhatsThis(wtstr); searchLine->setWhatsThis(wtstr); mpCanvas = new KIconCanvas(q); connect(mpCanvas, SIGNAL(itemActivated(QListWidgetItem*)), q, SLOT(_k_slotAcceptIcons())); top->addWidget(mpCanvas); searchLine->setListWidget(mpCanvas); // Compute width of canvas with 4 icons displayed in a row QStyleOption opt; opt.initFrom(mpCanvas); int width = 4 * mpCanvas->gridSize().width() + 1; width += mpCanvas->verticalScrollBar()->sizeHint().width(); width += 2 * mpCanvas->frameWidth(); if (mpCanvas->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, &opt, mpCanvas)) { width += mpCanvas->style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing, &opt, mpCanvas); } mpCanvas->setMinimumSize(width, 125); mpProgress = new QProgressBar(q); top->addWidget(mpProgress); connect(mpCanvas, SIGNAL(startLoading(int)), q, SLOT(_k_slotStartLoading(int))); connect(mpCanvas, SIGNAL(progress(int)), q, SLOT(_k_slotProgress(int))); connect(mpCanvas, SIGNAL(finished()), q, SLOT(_k_slotFinished())); // When pressing Ok or Cancel, stop loading icons connect(q, &QDialog::finished, mpCanvas, &KIconCanvas::stopLoading); static const char *const context_text[] = { I18N_NOOP("Actions"), I18N_NOOP("Applications"), I18N_NOOP("Categories"), I18N_NOOP("Devices"), I18N_NOOP("Emblems"), I18N_NOOP("Emotes"), I18N_NOOP("Mimetypes"), I18N_NOOP("Places"), I18N_NOOP("Status"), I18N_NOOP("All") }; static const KIconLoader::Context context_id[] = { KIconLoader::Action, KIconLoader::Application, KIconLoader::Category, KIconLoader::Device, KIconLoader::Emblem, KIconLoader::Emote, KIconLoader::MimeType, KIconLoader::Place, KIconLoader::StatusIcon, KIconLoader::Any }; mNumContext = 0; int cnt = sizeof(context_text) / sizeof(context_text[ 0 ]); // check all 3 arrays have same sizes Q_ASSERT(cnt == sizeof(context_id) / sizeof(context_id[ 0 ]) && cnt == sizeof(mContextMap) / sizeof(mContextMap[ 0 ])); for (int i = 0; i < cnt; ++i) { if (mpLoader->hasContext(context_id[ i ])) { mpCombo->addItem(i18n(context_text[ i ])); mContextMap[ mNumContext++ ] = context_id[ i ]; } } mpCombo->setMaxVisibleItems(cnt); mpCombo->setFixedSize(mpCombo->sizeHint()); mpBrowseBut->setFixedWidth(mpCombo->width()); QDialogButtonBox *buttonBox = new QDialogButtonBox(q); buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox, &QDialogButtonBox::accepted, q, &KIconDialog::slotOk); connect(buttonBox, &QDialogButtonBox::rejected, q, &QDialog::reject); top->addWidget(buttonBox); // Make the dialog a little taller q->resize(q->sizeHint() + QSize(0, 100)); q->adjustSize(); } KIconDialog::~KIconDialog() { delete d; } void KIconDialog::KIconDialogPrivate::_k_slotAcceptIcons() { custom.clear(); q->slotOk(); } static bool sortByFileName(const QString &path1, const QString &path2) { const QString fileName1 = path1.mid(path1.lastIndexOf(QLatin1Char('/')) + 1); - const QString fileName2 = path2.mid(path1.lastIndexOf(QLatin1Char('/')) + 1); + const QString fileName2 = path2.mid(path2.lastIndexOf(QLatin1Char('/')) + 1); return QString::compare(fileName1, fileName2, Qt::CaseInsensitive) < 0; } void KIconDialog::KIconDialogPrivate::showIcons() { mpCanvas->clear(); QStringList filelist; if (mpSystemIcons->isChecked()) { if (m_bStrictIconSize) { filelist = mpLoader->queryIcons(mGroupOrSize, mContext); } else { filelist = mpLoader->queryIconsByContext(mGroupOrSize, mContext); } } else if (!customLocation.isEmpty()) { filelist = mpLoader->queryIconsByDir(customLocation); } else { // List PNG files found directly in the kiconload search paths. Q_FOREACH (const QString &relDir, KIconLoader::global()->searchPaths()) { const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, relDir, QStandardPaths::LocateDirectory); for (const QString &dir : dirs) { Q_FOREACH (const QString &fileName, QDir(dir).entryList(QStringList() << QLatin1String("*.png"))) { filelist << dir + QLatin1Char('/') + fileName; } } } } std::sort(filelist.begin(), filelist.end(), sortByFileName); // The KIconCanvas has uniformItemSizes set which really expects // all added icons to be the same size, otherwise weirdness ensues :) // Ensure all SVGs are scaled to the desired size and that as few icons // need to be padded as possible by specifying a sensible size. if (mGroupOrSize < -1) { // mGroupOrSize can be -1 if NoGroup is chosen. // Explicit size. mpCanvas->setIconSize(QSize(-mGroupOrSize, -mGroupOrSize)); } else { // Icon group. int groupSize = mpLoader->currentSize((KIconLoader::Group)mGroupOrSize); mpCanvas->setIconSize(QSize(groupSize, groupSize)); } mpCanvas->loadFiles(filelist); } void KIconDialog::setStrictIconSize(bool b) { d->m_bStrictIconSize = b; } bool KIconDialog::strictIconSize() const { return d->m_bStrictIconSize; } void KIconDialog::setIconSize(int size) { // see KIconLoader, if you think this is weird if (size == 0) { d->mGroupOrSize = KIconLoader::Desktop; // default Group } else { d->mGroupOrSize = -size; // yes, KIconLoader::queryIconsByContext is weird } } int KIconDialog::iconSize() const { // 0 or any other value ==> mGroupOrSize is a group, so we return 0 return (d->mGroupOrSize < 0) ? -d->mGroupOrSize : 0; } void KIconDialog::setup(KIconLoader::Group group, KIconLoader::Context context, bool strictIconSize, int iconSize, bool user, bool lockUser, bool lockCustomDir) { d->m_bStrictIconSize = strictIconSize; d->m_bLockUser = lockUser; d->m_bLockCustomDir = lockCustomDir; if (iconSize == 0) { if (group == KIconLoader::NoGroup) { // NoGroup has numeric value -1, which should // not really be used with KIconLoader::queryIcons*(...); // pick a proper group. d->mGroupOrSize = KIconLoader::Small; } else { d->mGroupOrSize = group; } } else { d->mGroupOrSize = -iconSize; } d->mpSystemIcons->setChecked(!user); d->mpSystemIcons->setEnabled(!lockUser || !user); d->mpOtherIcons->setChecked(user); d->mpOtherIcons->setEnabled(!lockUser || user); d->mpCombo->setEnabled(!user); d->mpBrowseBut->setEnabled(user && !lockCustomDir); d->setContext(context); } void KIconDialog::KIconDialogPrivate::setContext(KIconLoader::Context context) { mContext = context; for (int i = 0; i < mNumContext; ++i) { if (mContextMap[ i ] == context) { mpCombo->setCurrentIndex(i); return; } } } void KIconDialog::setCustomLocation(const QString &location) { d->customLocation = location; } QString KIconDialog::openDialog() { if (exec() == Accepted) { if (!d->custom.isEmpty()) { return d->custom; } const QString name = d->mpCanvas->getCurrent(); if (name.isEmpty() || d->mpOtherIcons->isChecked()) { return name; } QFileInfo fi(name); return fi.completeBaseName(); } return QString(); } void KIconDialog::showDialog() { setModal(false); show(); } void KIconDialog::slotOk() { QString name; if (!d->custom.isEmpty()) { name = d->custom; } else { name = d->mpCanvas->getCurrent(); if (!name.isEmpty() && d->mpSystemIcons->isChecked()) { const QFileInfo fi(name); name = fi.completeBaseName(); } } emit newIconName(name); QDialog::accept(); } QString KIconDialog::getIcon(KIconLoader::Group group, KIconLoader::Context context, bool strictIconSize, int iconSize, bool user, QWidget *parent, const QString &caption) { KIconDialog dlg(parent); dlg.setup(group, context, strictIconSize, iconSize, user); if (!caption.isEmpty()) { dlg.setWindowTitle(caption); } return dlg.openDialog(); } void KIconDialog::KIconDialogPrivate::_k_slotBrowse() { if (browseDialog) { browseDialog.data()->show(); browseDialog.data()->raise(); return; } // Create a file dialog to select an ICO, PNG, XPM or SVG file, // with the image previewer shown. QFileDialog *dlg = new QFileDialog(q, i18n("Select Icon"), QString(), i18n("*.ico *.png *.xpm *.svg *.svgz|Icon Files (*.ico *.png *.xpm *.svg *.svgz)")); dlg->setModal(false); dlg->setFileMode(QFileDialog::ExistingFile); connect(dlg, SIGNAL(fileSelected(QString)), q, SLOT(_k_customFileSelected(QString))); browseDialog = dlg; dlg->show(); } void KIconDialog::KIconDialogPrivate::_k_customFileSelected(const QString &path) { if (!path.isEmpty()) { custom = path; if (mpSystemIcons->isChecked()) { customLocation = QFileInfo(custom).absolutePath(); } q->slotOk(); } } void KIconDialog::KIconDialogPrivate::_k_slotSystemIconClicked() { mpBrowseBut->setEnabled(false); mpCombo->setEnabled(true); showIcons(); } void KIconDialog::KIconDialogPrivate::_k_slotOtherIconClicked() { mpBrowseBut->setEnabled(!m_bLockCustomDir); mpCombo->setEnabled(false); showIcons(); } void KIconDialog::KIconDialogPrivate::_k_slotContext(int id) { mContext = static_cast(mContextMap[ id ]); showIcons(); } void KIconDialog::KIconDialogPrivate::_k_slotStartLoading(int steps) { if (steps < 10) { mpProgress->hide(); } else { mNumOfSteps = steps; mpProgress->setValue(0); mpProgress->show(); } } void KIconDialog::KIconDialogPrivate::_k_slotProgress(int p) { mpProgress->setValue(static_cast(100.0 * (double)p / (double)mNumOfSteps)); } void KIconDialog::KIconDialogPrivate::_k_slotFinished() { mNumOfSteps = 1; mpProgress->hide(); } #include "moc_kicondialog.cpp" #include "moc_kicondialog_p.cpp"