diff --git a/krusader/GUI/mediabutton.cpp b/krusader/GUI/mediabutton.cpp index 8c789315..f234e869 100644 --- a/krusader/GUI/mediabutton.cpp +++ b/krusader/GUI/mediabutton.cpp @@ -1,634 +1,633 @@ /***************************************************************************** * Copyright (C) 2006 Csaba Karai * * Copyright (C) 2006-2018 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 2 of the License, or * * (at your option) any later version. * * * * Krusader is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "mediabutton.h" #include "../krglobal.h" #include "../icon.h" #include "../MountMan/kmountman.h" // QtCore #include // QtGui #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include QString MediaButton::remotePrefix = QLatin1String("remote:"); MediaButton::MediaButton(QWidget *parent) : QToolButton(parent), popupMenu(0), rightMenu(0), openInNewTab(false) { setAutoRaise(true); setIcon(Icon("system-file-manager")); setText(i18n("Open the available media list")); setToolTip(i18n("Open the available media list")); setPopupMode(QToolButton::InstantPopup); setAcceptDrops(false); popupMenu = new QMenu(this); popupMenu->installEventFilter(this); Q_CHECK_PTR(popupMenu); setMenu(popupMenu); connect(popupMenu, SIGNAL(aboutToShow()), this, SLOT(slotAboutToShow())); connect(popupMenu, SIGNAL(aboutToHide()), this, SLOT(slotAboutToHide())); connect(popupMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotPopupActivated(QAction*))); Solid::DeviceNotifier *notifier = Solid::DeviceNotifier::instance(); connect(notifier, SIGNAL(deviceAdded(QString)), this, SLOT(slotDeviceAdded(QString))); connect(notifier, SIGNAL(deviceRemoved(QString)), this, SLOT(slotDeviceRemoved(QString))); connect(&mountCheckerTimer, SIGNAL(timeout()), this, SLOT(slotCheckMounts())); } MediaButton::~MediaButton() { } void MediaButton::updateIcon(const QString &mountPoint) { if(!mountPoint.isEmpty() && mountPoint == currentMountPoint) return; currentMountPoint = mountPoint; QString icon("system-file-manager"); QStringList overlays; if(!mountPoint.isEmpty()) { Solid::Device device(krMtMan.findUdiForPath(mountPoint, Solid::DeviceInterface::StorageAccess));; Solid::StorageVolume *vol = device.as (); if(device.isValid()) icon = device.icon(); if (vol && vol->usage() == Solid::StorageVolume::Encrypted) overlays << "security-high"; } - setIcon(KDE::icon(icon, overlays)); + setIcon(Icon(icon, overlays)); } void MediaButton::slotAboutToShow() { emit aboutToShow(); popupMenu->clear(); udiNameMap.clear(); createMediaList(); } void MediaButton::slotAboutToHide() { if (rightMenu) rightMenu->close(); mountCheckerTimer.stop(); } void MediaButton::createMediaList() { // devices detected by solid storageDevices = Solid::Device::listFromType(Solid::DeviceInterface::StorageAccess); for (int p = storageDevices.count() - 1 ; p >= 0; p--) { Solid::Device device = storageDevices[ p ]; QString udi = device.udi(); QString name; QIcon kdeIcon; if (!getNameAndIcon(device, name, kdeIcon)) continue; QAction * act = popupMenu->addAction(kdeIcon, name); act->setData(QVariant(udi)); udiNameMap[ udi ] = name; connect(device.as(), SIGNAL(accessibilityChanged(bool,QString)), this, SLOT(slotAccessibilityChanged(bool,QString))); } KMountPoint::List possibleMountList = KMountPoint::possibleMountPoints(); KMountPoint::List currentMountList = KMountPoint::currentMountPoints(); for (KMountPoint::List::iterator it = possibleMountList.begin(); it != possibleMountList.end(); ++it) { if (krMtMan.networkFilesystem((*it)->mountType())) { QString path = (*it)->mountPoint(); bool mounted = false; for (KMountPoint::List::iterator it2 = currentMountList.begin(); it2 != currentMountList.end(); ++it2) { if (krMtMan.networkFilesystem((*it2)->mountType()) && (*it)->mountPoint() == (*it2)->mountPoint()) { mounted = true; break; } } QString name = i18nc("%1 is the mount point of the remote share", "Remote Share [%1]", (*it)->mountPoint()); QStringList overlays; if (mounted) overlays << "emblem-mounted"; - QAction * act = popupMenu->addAction(KDE::icon("network-wired", overlays), name); + QAction * act = popupMenu->addAction(Icon("network-wired", overlays), name); QString udi = remotePrefix + (*it)->mountPoint(); act->setData(QVariant(udi)); } } mountCheckerTimer.setSingleShot(true); mountCheckerTimer.start(1000); } bool MediaButton::getNameAndIcon(Solid::Device & device, QString &name, QIcon &iconOut) { Solid::StorageAccess *access = device.as(); if (access == 0) return false; QString udi = device.udi(); QString label = i18nc("Unknown label", "Unknown"); bool mounted = access->isAccessible(); QString path = access->filePath(); QString type = i18nc("Unknown media type", "Unknown"); QString icon = device.icon(); QString fstype; QString size; Solid::StorageVolume * vol = device.as (); if (vol) { label = vol->label(); fstype = vol->fsType(); size = KIO::convertSize(vol->size()); } bool printSize = false; if (icon == "media-floppy") type = i18n("Floppy"); else if (icon == "drive-optical") type = i18n("CD/DVD-ROM"); else if (icon == "drive-removable-media-usb-pendrive") type = i18n("USB pen drive"), printSize = true; else if (icon == "drive-removable-media-usb") type = i18n("USB device"), printSize = true; else if (icon == "drive-removable-media") type = i18n("Removable media"), printSize = true; else if (icon == "drive-harddisk") type = i18n("Hard Disk"), printSize = true; else if (icon == "camera-photo") type = i18n("Camera"); else if (icon == "media-optical-video") type = i18n("Video CD/DVD-ROM"); else if (icon == "media-optical-audio") type = i18n("Audio CD/DVD-ROM"); else if (icon == "media-optical") type = i18n("Recordable CD/DVD-ROM"); KConfigGroup cfg(KSharedConfig::openConfig(), QStringLiteral("MediaMenu")); if (printSize) { QString showSizeSetting = cfg.readEntry("ShowSize", "Always"); if (showSizeSetting == "WhenNoLabel") printSize = label.isEmpty(); else if (showSizeSetting == "Never") printSize = false; } if (printSize && !size.isEmpty()) name += size + ' '; if (!label.isEmpty()) name += label + ' '; else name += type + ' '; if (!fstype.isEmpty() && cfg.readEntry("ShowFSType", true)) name += '(' + fstype + ") "; if (!path.isEmpty() && cfg.readEntry("ShowPath", true)) name += '[' + path + "] "; name = name.trimmed(); QStringList overlays; if (mounted) { overlays << "emblem-mounted"; } else { overlays << QString(); // We have to guarantee the placement of the next emblem } if (vol && vol->usage() == Solid::StorageVolume::Encrypted) { overlays << "security-high"; } - iconOut = KDE::icon(icon, overlays); + iconOut = Icon(icon, overlays); return true; } void MediaButton::slotPopupActivated(QAction *action) { if (action && action->data().canConvert()) { QString udi = action->data().toString(); if (!udi.isEmpty()) { bool mounted = false; QString mountPoint; getStatus(udi, mounted, &mountPoint); if (mounted) emit openUrl(QUrl::fromLocalFile(mountPoint)); else mount(udi, true); } } } void MediaButton::showMenu() { QMenu * pP = menu(); if (pP) { menu() ->exec(mapToGlobal(QPoint(0, height()))); } } bool MediaButton::eventFilter(QObject *o, QEvent *e) { if (o == popupMenu) { if (e->type() == QEvent::ContextMenu) { QAction *act = popupMenu->activeAction(); if (act && act->data().canConvert()) { QString id = act->data().toString(); if (!id.isEmpty()) { QPoint globalPos = popupMenu->mapToGlobal(popupMenu->actionGeometry(act).topRight()); rightClickMenu(id, globalPos); return true; } } } else if (e->type() == QEvent::KeyPress) { QKeyEvent *ke = static_cast(e); if (ke->key() == Qt::Key_Return && ke->modifiers() == Qt::ControlModifier) { if (QAction *act = popupMenu->activeAction()) { QString id = act->data().toString(); if (!id.isEmpty()) { toggleMount(id); return true; } } } } else if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonRelease) { QMouseEvent *m = static_cast(e); if (m->button() == Qt::RightButton) { if (e->type() == QEvent::MouseButtonPress) { QAction * act = popupMenu->actionAt(m->pos()); if (act && act->data().canConvert()) { QString id = act->data().toString(); if (!id.isEmpty()) rightClickMenu(id, m->globalPos()); } } m->accept(); return true; } } } return false; } void MediaButton::rightClickMenu(QString udi, QPoint pos) { if (rightMenu) rightMenu->close(); bool ejectable = false; bool mounted = false; QString mountPoint; getStatus(udi, mounted, &mountPoint, &ejectable); QUrl openURL = QUrl::fromLocalFile(mountPoint); QMenu * myMenu = rightMenu = new QMenu(popupMenu); QAction * actOpen = myMenu->addAction(i18n("Open")); actOpen->setData(QVariant(1)); QAction * actOpenNewTab = myMenu->addAction(i18n("Open in a new tab")); actOpenNewTab->setData(QVariant(2)); myMenu->addSeparator(); if (!mounted) { QAction * actMount = myMenu->addAction(i18n("Mount")); actMount->setData(QVariant(3)); } else { QAction * actUnmount = myMenu->addAction(i18n("Unmount")); actUnmount->setData(QVariant(4)); } if (ejectable) { QAction * actEject = myMenu->addAction(i18n("Eject")); actEject->setData(QVariant(5)); } QAction *act = myMenu->exec(pos); int result = -1; if (act != 0 && act->data().canConvert()) result = act->data().toInt(); delete myMenu; if (rightMenu == myMenu) rightMenu = 0; else return; switch (result) { case 1: case 2: popupMenu->close(); if (mounted) { if (result == 1) emit openUrl(openURL); else emit newTab(openURL); } else { mount(udi, true, result == 2); // mount first, when mounted open the tab } break; case 3: mount(udi); break; case 4: umount(udi); break; case 5: eject(udi); break; default: break; } } void MediaButton::toggleMount(QString udi) { bool mounted = false; getStatus(udi, mounted); if (mounted) umount(udi); else mount(udi); } void MediaButton::getStatus(QString udi, bool &mounted, QString *mountPointOut, bool *ejectableOut) { mounted = false; bool network = udi.startsWith(remotePrefix); bool ejectable = false; QString mountPoint; if (network) { mountPoint = udi.mid(remotePrefix.length()); KMountPoint::List currentMountList = KMountPoint::currentMountPoints(); for (KMountPoint::List::iterator it = currentMountList.begin(); it != currentMountList.end(); ++it) { if (krMtMan.networkFilesystem((*it)->mountType()) && (*it)->mountPoint() == mountPoint) { mounted = true; break; } } } else { Solid::Device device(udi); Solid::StorageAccess *access = device.as(); Solid::OpticalDisc *optdisc = device.as(); if (access) mountPoint = access->filePath(); if (access && access->isAccessible()) mounted = true; if (optdisc) ejectable = true; } if (mountPointOut) *mountPointOut = mountPoint; if (ejectableOut) *ejectableOut = ejectable; } void MediaButton::mount(QString udi, bool open, bool newtab) { if (udi.startsWith(remotePrefix)) { QString mp = udi.mid(remotePrefix.length()); krMtMan.mount(mp, true); if (newtab) emit newTab(QUrl::fromLocalFile(mp)); else emit openUrl(QUrl::fromLocalFile(mp)); return; } Solid::Device device(udi); Solid::StorageAccess *access = device.as(); if (access && !access->isAccessible()) { if (open) udiToOpen = device.udi(), openInNewTab = newtab; connect(access, SIGNAL(setupDone(Solid::ErrorType,QVariant,QString)), this, SLOT(slotSetupDone(Solid::ErrorType,QVariant,QString))); access->setup(); } } void MediaButton::slotSetupDone(Solid::ErrorType error, QVariant errorData, const QString &udi) { if (error == Solid::NoError) { if (udi == udiToOpen) { Solid::StorageAccess *access = Solid::Device(udi).as(); if (access && access->isAccessible()) { if (openInNewTab) emit newTab(QUrl::fromLocalFile(access->filePath())); else emit openUrl(QUrl::fromLocalFile(access->filePath())); } udiToOpen = QString(), openInNewTab = false; } } else { if (udi == udiToOpen) udiToOpen = QString(), openInNewTab = false; QString name; if (udiNameMap.contains(udi)) name = udiNameMap[ udi ]; if (errorData.isValid()) { KMessageBox::sorry(this, i18n("An error occurred while accessing '%1', the system responded: %2", name, errorData.toString())); } else { KMessageBox::sorry(this, i18n("An error occurred while accessing '%1'", name)); } } } void MediaButton::umount(QString udi) { if (udi.startsWith(remotePrefix)) { krMtMan.unmount(udi.mid(remotePrefix.length()), false); return; } krMtMan.unmount(krMtMan.pathForUdi(udi), false); } void MediaButton::eject(QString udi) { krMtMan.eject(krMtMan.pathForUdi(udi)); } void MediaButton::slotAccessibilityChanged(bool /*accessible*/, const QString & udi) { QList actionList = popupMenu->actions(); foreach(QAction * act, actionList) { if (act && act->data().canConvert() && act->data().toString() == udi) { Solid::Device device(udi); QString name; QIcon kdeIcon; if (getNameAndIcon(device, name, kdeIcon)) { act->setText(name); act->setIcon(kdeIcon); } break; } } } void MediaButton::slotDeviceAdded(const QString& udi) { if (popupMenu->isHidden()) return; Solid::Device device(udi); Solid::StorageAccess *access = device.as(); if (access == 0) return; QString name; QIcon kdeIcon; if (!getNameAndIcon(device, name, kdeIcon)) return; QAction * act = popupMenu->addAction(kdeIcon, name); act->setData(QVariant(udi)); udiNameMap[ udi ] = name; connect(device.as(), SIGNAL(accessibilityChanged(bool,QString)), this, SLOT(slotAccessibilityChanged(bool,QString))); } void MediaButton::slotDeviceRemoved(const QString& udi) { if (popupMenu->isHidden()) return; QList actionList = popupMenu->actions(); foreach(QAction * act, actionList) { if (act && act->data().canConvert() && act->data().toString() == udi) { popupMenu->removeAction(act); delete act; break; } } } void MediaButton::slotCheckMounts() { if (isHidden()) return; KMountPoint::List possibleMountList = KMountPoint::possibleMountPoints(); KMountPoint::List currentMountList = KMountPoint::currentMountPoints(); QList actionList = popupMenu->actions(); foreach(QAction * act, actionList) { if (act && act->data().canConvert() && act->data().toString().startsWith(remotePrefix)) { QString mountPoint = act->data().toString().mid(remotePrefix.length()); bool available = false; for (KMountPoint::List::iterator it = possibleMountList.begin(); it != possibleMountList.end(); ++it) { if (krMtMan.networkFilesystem((*it)->mountType()) && (*it)->mountPoint() == mountPoint) { available = true; break; } } if (!available) { popupMenu->removeAction(act); delete act; } } } for (KMountPoint::List::iterator it = possibleMountList.begin(); it != possibleMountList.end(); ++it) { if (krMtMan.networkFilesystem((*it)->mountType())) { QString path = (*it)->mountPoint(); bool mounted = false; QString udi = remotePrefix + path; QAction * correspondingAct = 0; foreach(QAction * act, actionList) { if (act && act->data().canConvert() && act->data().toString() == udi) { correspondingAct = act; break; } } for (KMountPoint::List::iterator it2 = currentMountList.begin(); it2 != currentMountList.end(); ++it2) { if (krMtMan.networkFilesystem((*it2)->mountType()) && path == (*it2)->mountPoint()) { mounted = true; break; } } QString name = i18nc("%1 is the mount point of the remote share", "Remote Share [%1]", (*it)->mountPoint()); QStringList overlays; if (mounted) overlays << "emblem-mounted"; - QIcon kdeIcon = KDE::icon("network-wired", overlays); + QIcon kdeIcon = Icon("network-wired", overlays); if (!correspondingAct) { QAction * act = popupMenu->addAction(kdeIcon, name); act->setData(QVariant(udi)); } else { correspondingAct->setText(name); correspondingAct->setIcon(kdeIcon); } } } mountCheckerTimer.setSingleShot(true); mountCheckerTimer.start(1000); } diff --git a/krusader/MountMan/kmountmangui.cpp b/krusader/MountMan/kmountmangui.cpp index a3ae8dbf..17ccf149 100644 --- a/krusader/MountMan/kmountmangui.cpp +++ b/krusader/MountMan/kmountmangui.cpp @@ -1,531 +1,530 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 2 of the License, or * * (at your option) any later version. * * * * Krusader is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "kmountmangui.h" #include "../krglobal.h" #include "../icon.h" #include "../Dialogs/krspecialwidgets.h" #include "../defaults.h" #include "../FileSystem/filesystem.h" // QtCore #include #include #include // QtGui #include #include #include // QtWidgets #include #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #ifndef BSD #define MTAB "/etc/mtab" #endif KMountManGUI::KMountManGUI(KMountMan *mntMan) : QDialog(mntMan->parentWindow), mountMan(mntMan), info(0), mountList(0), cbShowOnlyRemovable(0), watcher(0), sizeX(-1), sizeY(-1) { setWindowTitle(i18n("MountMan - Your Mount-Manager")); setWindowModality(Qt::WindowModal); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); watcher = new QTimer(this); connect(watcher, SIGNAL(timeout()), this, SLOT(checkMountChange())); mainLayout->addLayout(createMainPage()); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); mainLayout->addWidget(buttonBox); ejectButton = new QPushButton(i18n("&Eject")); ejectButton->setIcon(Icon(QStringLiteral("media-eject"))); ejectButton->setEnabled(false); buttonBox->addButton(ejectButton, QDialogButtonBox::ActionRole); mountButton = new QPushButton(i18n("&Unmount")); mountButton->setEnabled(false); buttonBox->addButton(mountButton, QDialogButtonBox::ActionRole); // connections connect(buttonBox, SIGNAL(rejected()), SLOT(reject())); connect(ejectButton, SIGNAL(clicked()), SLOT(slotEject())); connect(mountButton, SIGNAL(clicked()), SLOT(slotToggleMount())); connect(mountList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(doubleClicked(QTreeWidgetItem*))); connect(mountList, SIGNAL(itemRightClicked(QTreeWidgetItem*,QPoint,int)), this, SLOT(clicked(QTreeWidgetItem*,QPoint))); connect(mountList, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(changeActive(QTreeWidgetItem*))); connect(mountList, SIGNAL(itemSelectionChanged()), this, SLOT(changeActive())); KConfigGroup group(krConfig, "MountMan"); int sx = group.readEntry("Window Width", -1); int sy = group.readEntry("Window Height", -1); if (sx != -1 && sy != -1) resize(sx, sy); else resize(600, 300); if (group.readEntry("Window Maximized", false)) showMaximized(); else show(); getSpaceData(); exec(); } KMountManGUI::~KMountManGUI() { watcher->stop(); delete watcher; KConfigGroup group(krConfig, "MountMan"); group.writeEntry("Window Width", sizeX); group.writeEntry("Window Height", sizeY); group.writeEntry("Window Maximized", isMaximized()); group.writeEntry("Last State", mountList->header()->saveState()); group.writeEntry("ShowOnlyRemovable", cbShowOnlyRemovable->isChecked()); } void KMountManGUI::resizeEvent(QResizeEvent *e) { if (!isMaximized()) { sizeX = e->size().width(); sizeY = e->size().height(); } QDialog::resizeEvent(e); } QLayout *KMountManGUI::createMainPage() { QGridLayout *layout = new QGridLayout(); layout->setSpacing(10); mountList = new KrTreeWidget(this); // create the main container KConfigGroup grp(krConfig, "Look&Feel"); mountList->setFont(grp.readEntry("Filelist Font", _FilelistFont)); mountList->setSelectionMode(QAbstractItemView::SingleSelection); mountList->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); mountList->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); QStringList labels; labels << i18n("Name"); labels << i18n("Type"); labels << i18n("Mnt.Point"); labels << i18n("Total Size"); labels << i18n("Free Size"); labels << i18n("Free %"); mountList->setHeaderLabels(labels); mountList->header()->setSectionResizeMode(QHeaderView::Interactive); grp = KConfigGroup(krConfig, "MountMan"); if (grp.hasKey("Last State")) mountList->header()->restoreState(grp.readEntry("Last State", QByteArray())); else { int i = QFontMetrics(mountList->font()).width("W"); int j = QFontMetrics(mountList->font()).width("0"); j = (i > j ? i : j); mountList->setColumnWidth(0, j*8); mountList->setColumnWidth(1, j*4); mountList->setColumnWidth(2, j*8); mountList->setColumnWidth(3, j*6); mountList->setColumnWidth(4, j*6); mountList->setColumnWidth(5, j*5); } mountList->setAllColumnsShowFocus(true); mountList->header()->setSortIndicatorShown(true); mountList->sortItems(0, Qt::AscendingOrder); // now the list is created, time to fill it with data. //=>mountMan->forceUpdate(); QGroupBox *box = new QGroupBox(i18n("MountMan.Info"), this); box->setAlignment(Qt::AlignHCenter); QVBoxLayout *vboxl = new QVBoxLayout(box); info = new KRFSDisplay(box); vboxl->addWidget(info); info->resize(info->width(), height()); cbShowOnlyRemovable = new QCheckBox(i18n("Show only removable devices"), this); cbShowOnlyRemovable->setChecked(grp.readEntry("ShowOnlyRemovable", false)); connect(cbShowOnlyRemovable , SIGNAL(stateChanged(int)), SLOT(updateList())); layout->addWidget(box, 0, 0); layout->addWidget(cbShowOnlyRemovable, 1, 0); layout->addWidget(mountList, 0, 1, 2, 1); return layout; } void KMountManGUI::getSpaceData() { fileSystems.clear(); KrMountDetector::getInstance()->hasMountsChanged(); mounted = KMountPoint::currentMountPoints(); possible = KMountPoint::possibleMountPoints(); if (mounted.size() == 0) { // nothing is mounted addNonMounted(); updateList(); // let's continue return; } for (KMountPoint::List::iterator it = mounted.begin(); it != mounted.end(); ++it) { // don't bother with invalid file systems if (mountMan->invalidFilesystem((*it)->mountType())) { continue; } KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo((*it) ->mountPoint()); if(!info.isValid()) { continue; } fsData data; data.setMntPoint((*it) ->mountPoint()); data.setMounted(true); data.setTotalBlks(info.size() / 1024); data.setFreeBlks(info.available() / 1024); data.setName((*it)->mountedFrom()); data.setType((*it)->mountType()); fileSystems.append(data); } addNonMounted(); updateList(); } void KMountManGUI::addNonMounted() { // handle the non-mounted ones for (KMountPoint::List::iterator it = possible.begin(); it != possible.end(); ++it) { // make sure we don't add things we've already added if (KMountMan::findInListByMntPoint(mounted, (*it)->mountPoint())) { continue; } else { fsData data; data.setMntPoint((*it)->mountPoint()); data.setMounted(false); data.setType((*it)->mountType()); data.setName((*it)->mountedFrom()); if (mountMan->invalidFilesystem(data.type())) continue; fileSystems.append(data); } } } void KMountManGUI::addItemToMountList(KrTreeWidget *lst, fsData &fs) { Solid::Device device(mountMan->findUdiForPath(fs.mntPoint(), Solid::DeviceInterface::StorageAccess)); if (cbShowOnlyRemovable->isChecked() && !mountMan->removable(device)) return; bool mtd = fs.mounted(); QString tSize = QString("%1").arg(KIO::convertSizeFromKiB(fs.totalBlks())); QString fSize = QString("%1").arg(KIO::convertSizeFromKiB(fs.freeBlks())); QString sPrct = QString("%1%").arg(100 - (fs.usedPerct())); QTreeWidgetItem *item = new QTreeWidgetItem(lst); item->setText(0, fs.name()); item->setText(1, fs.type()); item->setText(2, fs.mntPoint()); item->setText(3, (mtd ? tSize : QString("N/A"))); item->setText(4, (mtd ? fSize : QString("N/A"))); item->setText(5, (mtd ? sPrct : QString("N/A"))); Solid::StorageVolume *vol = device.as (); QString icon; if(device.isValid()) icon = device.icon(); else if(mountMan->networkFilesystem(fs.type())) icon = "folder-remote"; QStringList overlays; if (mtd) { overlays << "emblem-mounted"; } else { overlays << QString(); // We have to guarantee the placement of the next emblem } if (vol && vol->usage() == Solid::StorageVolume::Encrypted) { overlays << "security-high"; } - item->setIcon(0, KDE::icon(icon, overlays)); + item->setIcon(0, Icon(icon, overlays)); } void KMountManGUI::updateList() { QString currentMP; int currentIdx = 0; QTreeWidgetItem *currentItem = mountList->currentItem(); if(currentItem) { currentMP = getMntPoint(currentItem); currentIdx = mountList->indexOfTopLevelItem(currentItem); } mountList->clearSelection(); mountList->clear(); for (QList::iterator it = fileSystems.begin(); it != fileSystems.end() ; ++it) addItemToMountList(mountList, *it); currentItem = mountList->topLevelItem(currentIdx); for(int i = 0; i < mountList->topLevelItemCount(); i++) { QTreeWidgetItem *item = mountList->topLevelItem(i); if(getMntPoint(item) == currentMP) currentItem = item; } if(!currentItem) currentItem = mountList->topLevelItem(0); mountList->setCurrentItem(currentItem); changeActive(currentItem); mountList->setFocus(); watcher->setSingleShot(true); watcher->start(WATCHER_DELAY); // starting the watch timer ( single shot ) } void KMountManGUI::checkMountChange() { if (KrMountDetector::getInstance()->hasMountsChanged()) getSpaceData(); watcher->setSingleShot(true); watcher->start(WATCHER_DELAY); // starting the watch timer ( single shot ) } void KMountManGUI::doubleClicked(QTreeWidgetItem *i) { if (!i) return; // we don't want to refresh to swap, do we ? // change the active panel to this mountpoint mountMan->emitRefreshPanel(QUrl::fromLocalFile(getMntPoint(i))); close(); } void KMountManGUI::changeActive() { QList seld = mountList->selectedItems(); if (seld.count() > 0) changeActive(seld[ 0 ]); } // when user clicks on a filesystem, change information void KMountManGUI::changeActive(QTreeWidgetItem *i) { if (!i) { if (info) { info->setEmpty(true); info->update(); } mountButton->setEnabled(false); ejectButton->setEnabled(false); return; } fsData *system = getFsData(i); info->setAlias(system->mntPoint()); info->setRealName(system->name()); info->setMounted(system->mounted()); info->setEmpty(false); info->setTotalSpace(system->totalBlks()); info->setFreeSpace(system->freeBlks()); info->repaint(); if(system->mounted()) mountButton->setText(i18n("&Unmount")); else mountButton->setText(i18n("&Mount")); ejectButton->setEnabled(mountMan->ejectable(system->mntPoint())); mountButton->setEnabled(true); } // called when right-clicked on a filesystem void KMountManGUI::clicked(QTreeWidgetItem *item, const QPoint & pos) { // these are the values that will exist in the menu #define MOUNT_ID 90 #define UNMOUNT_ID 91 #define FORMAT_ID 93 #define EJECT_ID 94 ////////////////////////////////////////////////////////// if (!item) return; fsData *system = getFsData(item); // create the menu QMenu popup; popup.setTitle(i18n("MountMan")); if (!system->mounted()) { QAction *mountAct = popup.addAction(i18n("Mount")); mountAct->setData(QVariant(MOUNT_ID)); bool enable = !(mountMan->nonmountFilesystem(system->type(), system->mntPoint())); mountAct->setEnabled(enable); } else { QAction * umountAct = popup.addAction(i18n("Unmount")); umountAct->setData(QVariant(UNMOUNT_ID)); bool enable = !(mountMan->nonmountFilesystem(system->type(), system->mntPoint())); umountAct->setEnabled(enable); } if (mountMan->ejectable(system->mntPoint())) // if (system->type()=="iso9660" || mountMan->followLink(system->name()).left(2)=="cd") popup.addAction(i18n("Eject"))->setData(QVariant(EJECT_ID)); else { QAction *formatAct = popup.addAction(i18n("Format")); formatAct->setData(QVariant(FORMAT_ID)); formatAct->setEnabled(false); } QString mountPoint = system->mntPoint(); QAction * res = popup.exec(pos); int result = -1; if (res && res->data().canConvert()) result = res->data().toInt(); // check out the user's option switch (result) { case - 1 : return ; // the user clicked outside of the menu case MOUNT_ID : case UNMOUNT_ID : mountMan->toggleMount(mountPoint); break; case FORMAT_ID : break; case EJECT_ID : mountMan->eject(mountPoint); break; } } void KMountManGUI::slotToggleMount() { QTreeWidgetItem *item = mountList->currentItem(); if(item) { mountMan->toggleMount(getFsData(item)->mntPoint()); } } void KMountManGUI::slotEject() { QTreeWidgetItem *item = mountList->currentItem(); if(item) { mountMan->eject(getFsData(item)->mntPoint()); } } fsData* KMountManGUI::getFsData(QTreeWidgetItem *item) { for (QList::Iterator it = fileSystems.begin(); it != fileSystems.end(); ++it) { // the only thing which is unique is the mount point if ((*it).mntPoint() == getMntPoint(item)) { return & (*it); } } //this point shouldn't be reached abort(); return 0; } QString KMountManGUI::getMntPoint(QTreeWidgetItem *item) { return item->text(2); // text(2) ? ugly ugly ugly } KrMountDetector::KrMountDetector() { hasMountsChanged(); } bool KrMountDetector::hasMountsChanged() { bool result = false; #ifndef BSD QFileInfo mtabInfo(MTAB); if (!mtabInfo.exists() || mtabInfo.isSymLink()) { // if mtab is a symlimk to /proc/mounts the mtime is unusable #endif KMountPoint::List mountPoints = KMountPoint::currentMountPoints(KMountPoint::NeedRealDeviceName); QCryptographicHash md5(QCryptographicHash::Md5); for (KMountPoint::List::iterator i = mountPoints.begin(); i != mountPoints.end(); ++i) { md5.addData((*i)->mountedFrom().toUtf8()); md5.addData((*i)->realDeviceName().toUtf8()); md5.addData((*i)->mountPoint().toUtf8()); md5.addData((*i)->mountType().toUtf8()); } QString s = md5.result(); result = s != checksum; checksum = s; #ifndef BSD } else { result = mtabInfo.lastModified() != lastMtab; lastMtab = mtabInfo.lastModified(); } #endif return result; } KrMountDetector krMountDetector; KrMountDetector * KrMountDetector::getInstance() { return & krMountDetector; } diff --git a/krusader/Panel/PanelView/krview.cpp b/krusader/Panel/PanelView/krview.cpp index 714f76d0..50fb110e 100644 --- a/krusader/Panel/PanelView/krview.cpp +++ b/krusader/Panel/PanelView/krview.cpp @@ -1,1223 +1,1223 @@ /***************************************************************************** * Copyright (C) 2000-2002 Shie Erlich * * Copyright (C) 2000-2002 Rafi Yanai * * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 2 of the License, or * * (at your option) any later version. * * * * Krusader is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "krview.h" #include "krselectionmode.h" #include "krviewfactory.h" #include "krviewitem.h" #include "../FileSystem/dirlisterinterface.h" #include "../FileSystem/fileitem.h" #include "../FileSystem/krpermhandler.h" #include "../Filter/filterdialog.h" #include "../defaults.h" #include "../filelisticon.h" #include "../krcolorcache.h" #include "../krglobal.h" #include "../krpreviews.h" #include "../viewactions.h" // QtCore #include #include // QtGui #include #include #include #include // QtWidgets #include #include #include #include #include #include #include -#include + #define FILEITEM getFileItem() KrView *KrViewOperator::_changedView = 0; KrViewProperties::PropertyType KrViewOperator::_changedProperties = KrViewProperties::NoProperty; // ----------------------------- operator KrViewOperator::KrViewOperator(KrView *view, QWidget *widget) : _view(view), _widget(widget), _massSelectionUpdate(false) { _saveDefaultSettingsTimer.setSingleShot(true); connect(&_saveDefaultSettingsTimer, SIGNAL(timeout()), SLOT(saveDefaultSettings())); } KrViewOperator::~KrViewOperator() { if(_changedView == _view) saveDefaultSettings(); } void KrViewOperator::startUpdate() { _view->refresh(); } void KrViewOperator::cleared() { _view->clear(); } void KrViewOperator::fileAdded(FileItem *fileitem) { _view->addItem(fileitem); } void KrViewOperator::fileUpdated(FileItem *newFileitem) { _view->updateItem(newFileitem); } void KrViewOperator::startDrag() { QStringList items; _view->getSelectedItems(&items); if (items.empty()) return ; // don't drag an empty thing QPixmap px; if (items.count() > 1 || _view->getCurrentKrViewItem() == 0) px = FileListIcon("document-multiple").pixmap(); // we are dragging multiple items else px = _view->getCurrentKrViewItem()->icon(); emit letsDrag(items, px); } bool KrViewOperator::searchItem(const QString &text, bool caseSensitive, int direction) { KrViewItem * item = _view->getCurrentKrViewItem(); if (!item) { return false; } const QRegExp regeEx(text, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::Wildcard); if (!direction) { if (regeEx.indexIn(item->name()) == 0) { return true; } direction = 1; } KrViewItem * startItem = item; while (true) { item = (direction > 0) ? _view->getNext(item) : _view->getPrev(item); if (!item) item = (direction > 0) ? _view->getFirst() : _view->getLast(); if (regeEx.indexIn(item->name()) == 0) { _view->setCurrentKrViewItem(item); _view->makeItemVisible(item); return true; } if (item == startItem) { return false; } } } bool KrViewOperator::filterSearch(const QString &text, bool caseSensitive) { _view->_quickFilterMask = QRegExp(text, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::Wildcard); _view->refresh(); return _view->_count || !_view->_files->numFileItems(); } void KrViewOperator::setMassSelectionUpdate(bool upd) { _massSelectionUpdate = upd; if (!upd) { emit selectionChanged(); _view->redraw(); } } void KrViewOperator::settingsChanged(KrViewProperties::PropertyType properties) { if(!_view->_updateDefaultSettings || _view->_ignoreSettingsChange) return; if(_changedView != _view) saveDefaultSettings(); _changedView = _view; _changedProperties = static_cast(_changedProperties | properties); _saveDefaultSettingsTimer.start(100); } void KrViewOperator::saveDefaultSettings() { _saveDefaultSettingsTimer.stop(); if(_changedView) _changedView->saveDefaultSettings(_changedProperties); _changedProperties = KrViewProperties::NoProperty; _changedView = 0; } // ----------------------------- krview const KrView::IconSizes KrView::iconSizes; KrView::KrView(KrViewInstance &instance, KConfig *cfg) : _config(cfg), _properties(0), _focused(false), _fileIconSize(0), _instance(instance), _files(0), _mainWindow(0), _widget(0), _nameToMakeCurrent(QString()), _previews(0), _updateDefaultSettings(false), _ignoreSettingsChange(false), _count(0), _numDirs(0), _dummyFileItem(0) { } KrView::~KrView() { _instance.m_objects.removeOne(this); delete _previews; _previews = 0; delete _dummyFileItem; _dummyFileItem = 0; if (_properties) qFatal("A class inheriting KrView didn't delete _properties!"); if (_operator) qFatal("A class inheriting KrView didn't delete _operator!"); } void KrView::init(bool enableUpdateDefaultSettings) { // sanity checks: if (!_widget) qFatal("_widget must be set during construction of KrView inheritors"); // ok, continue initProperties(); _operator = createOperator(); setup(); restoreDefaultSettings(); _updateDefaultSettings = enableUpdateDefaultSettings && KConfigGroup(_config, "Startup").readEntry("Update Default Panel Settings", _RememberPos); _instance.m_objects.append(this); } void KrView::initProperties() { const KConfigGroup grpInstance(_config, _instance.name()); const bool displayIcons = grpInstance.readEntry("With Icons", _WithIcons); const KConfigGroup grpSvr(_config, "Look&Feel"); const bool numericPermissions = grpSvr.readEntry("Numeric permissions", _NumericPermissions); int sortOps = 0; if (grpSvr.readEntry("Show Directories First", true)) sortOps |= KrViewProperties::DirsFirst; if(grpSvr.readEntry("Always sort dirs by name", false)) sortOps |= KrViewProperties::AlwaysSortDirsByName; if (!grpSvr.readEntry("Case Sensative Sort", _CaseSensativeSort)) sortOps |= KrViewProperties::IgnoreCase; if (grpSvr.readEntry("Locale Aware Sort", true)) sortOps |= KrViewProperties::LocaleAwareSort; KrViewProperties::SortOptions sortOptions = static_cast(sortOps); KrViewProperties::SortMethod sortMethod = static_cast( grpSvr.readEntry("Sort method", (int)_DefaultSortMethod)); const bool humanReadableSize = grpSvr.readEntry("Human Readable Size", _HumanReadableSize); // see KDE bug #40131 const bool localeAwareCompareIsCaseSensitive = QString("a").localeAwareCompare("B") > 0; QStringList defaultAtomicExtensions; defaultAtomicExtensions += ".tar.gz"; defaultAtomicExtensions += ".tar.bz2"; defaultAtomicExtensions += ".tar.lzma"; defaultAtomicExtensions += ".tar.xz"; defaultAtomicExtensions += ".moc.cpp"; QStringList atomicExtensions = grpSvr.readEntry("Atomic Extensions", defaultAtomicExtensions); for (QStringList::iterator i = atomicExtensions.begin(); i != atomicExtensions.end();) { QString & ext = *i; ext = ext.trimmed(); if (!ext.length()) { i = atomicExtensions.erase(i); continue; } if (!ext.startsWith('.')) ext.insert(0, '.'); ++i; } _properties = new KrViewProperties(displayIcons, numericPermissions, sortOptions, sortMethod, humanReadableSize, localeAwareCompareIsCaseSensitive, atomicExtensions); } void KrView::showPreviews(bool show) { if(show) { if(!_previews) { _previews = new KrPreviews(this); _previews->update(); } } else { delete _previews; _previews = 0; } redraw(); // op()->settingsChanged(KrViewProperties::PropShowPreviews); op()->emitRefreshActions(); } void KrView::updatePreviews() { if(_previews) _previews->update(); } QPixmap KrView::processIcon(const QPixmap &icon, bool dim, const QColor & dimColor, int dimFactor, bool symlink) { QPixmap pixmap = icon; if (symlink) { const QStringList overlays = QStringList() << QString() << "emblem-symbolic-link"; - KIconLoader::global()->drawOverlays(overlays, pixmap, KIconLoader::Desktop); + Icon::applyOverlays(&pixmap, overlays); } if(!dim) return pixmap; QImage dimmed = pixmap.toImage(); QPainter p(&dimmed); p.setCompositionMode(QPainter::CompositionMode_SourceIn); p.fillRect(0, 0, icon.width(), icon.height(), dimColor); p.setCompositionMode(QPainter::CompositionMode_SourceOver); p.setOpacity((qreal)dimFactor / (qreal)100); p.drawPixmap(0, 0, icon.width(), icon.height(), pixmap); return QPixmap::fromImage(dimmed, Qt::ColorOnly | Qt::ThresholdDither | Qt::ThresholdAlphaDither | Qt::NoOpaqueDetection ); } QPixmap KrView::getIcon(FileItem *fileitem, bool active, int size/*, KRListItem::cmpColor color*/) { // KConfigGroup ag( krConfig, "Advanced"); ////////////////////////////// QPixmap icon; QString icon_name = fileitem->getIcon(); QString cacheName; if(!size) size = _FilelistIconSize.toInt(); QColor dimColor; int dimFactor; bool dim = !active && KrColorCache::getColorCache().getDimSettings(dimColor, dimFactor); if (icon_name.isNull()) icon_name = ""; cacheName.append(QString::number(size)); if(fileitem->isSymLink()) cacheName.append("LINK_"); if(dim) cacheName.append("DIM_"); cacheName.append(icon_name); //QPixmapCache::setCacheLimit( ag.readEntry("Icon Cache Size",_IconCacheSize) ); // first try the cache if (!QPixmapCache::find(cacheName, icon)) { icon = processIcon(Icon(icon_name).pixmap(size), dim, dimColor, dimFactor, fileitem->isSymLink()); // insert it into the cache QPixmapCache::insert(cacheName, icon); } return icon; } QPixmap KrView::getIcon(FileItem *fileitem) { if(_previews) { QPixmap icon; if(_previews->getPreview(fileitem, icon, _focused)) return icon; } return getIcon(fileitem, _focused, _fileIconSize); } /** * this function ADDs a list of selected item names into 'names'. * it assumes the list is ready and doesn't initialize it, or clears it */ void KrView::getItemsByMask(QString mask, QStringList* names, bool dirs, bool files) { for (KrViewItem * it = getFirst(); it != 0; it = getNext(it)) { if ((it->name() == "..") || !QDir::match(mask, it->name())) continue; // if we got here, than the item fits the mask if (it->getFileItem()->isDir() && !dirs) continue; // do we need to skip folders? if (!it->getFileItem()->isDir() && !files) continue; // do we need to skip files names->append(it->name()); } } /** * this function ADDs a list of selected item names into 'names'. * it assumes the list is ready and doesn't initialize it, or clears it */ void KrView::getSelectedItems(QStringList *names, bool fallbackToFocused) { for (KrViewItem *it = getFirst(); it != 0; it = getNext(it)) if (it->isSelected() && (it->name() != "..")) names->append(it->name()); if (fallbackToFocused) { // if all else fails, take the current item const QString item = getCurrentItem(); if (names->empty() && !item.isEmpty() && item != "..") { names->append(item); } } } void KrView::getSelectedKrViewItems(KrViewItemList *items) { for (KrViewItem * it = getFirst(); it != 0; it = getNext(it)) if (it->isSelected() && (it->name() != "..")) items->append(it); // if all else fails, take the current item QString item = getCurrentItem(); if (items->empty() && !item.isEmpty() && item != ".." && getCurrentKrViewItem() != 0) { items->append(getCurrentKrViewItem()); } } QString KrView::statistics() { KIO::filesize_t size = calcSize(); KIO::filesize_t selectedSize = calcSelectedSize(); QString tmp; KConfigGroup grp(_config, "Look&Feel"); if(grp.readEntry("Show Size In Bytes", false)) { tmp = i18nc("%1=number of selected items,%2=total number of items, \ %3=filesize of selected items,%4=filesize in Bytes, \ %5=filesize of all items in folder,%6=filesize in Bytes", "%1 out of %2, %3 (%4) out of %5 (%6)", numSelected(), _count, KIO::convertSize(selectedSize), KRpermHandler::parseSize(selectedSize), KIO::convertSize(size), KRpermHandler::parseSize(size)); } else { tmp = i18nc("%1=number of selected items,%2=total number of items, \ %3=filesize of selected items,%4=filesize of all items in folder", "%1 out of %2, %3 out of %4", numSelected(), _count, KIO::convertSize(selectedSize), KIO::convertSize(size)); } // notify if we're running a filtered view if (filter() != KrViewProperties::All) tmp = ">> [ " + filterMask().nameFilter() + " ] " + tmp; return tmp; } bool KrView::changeSelection(const KRQuery& filter, bool select) { KConfigGroup grpSvr(_config, "Look&Feel"); return changeSelection(filter, select, grpSvr.readEntry("Mark Dirs", _MarkDirs), true); } bool KrView::changeSelection(const KRQuery& filter, bool select, bool includeDirs, bool makeVisible) { if (op()) op()->setMassSelectionUpdate(true); KrViewItem *temp = getCurrentKrViewItem(); KrViewItem *firstMatch = 0; for (KrViewItem * it = getFirst(); it != 0; it = getNext(it)) { if (it->name() == "..") continue; if (it->getFileItem()->isDir() && !includeDirs) continue; FileItem * file = it->getMutableFileItem(); // filter::match calls getMimetype which isn't const if (file == 0) continue; if (filter.match(file)) { it->setSelected(select); if (!firstMatch) firstMatch = it; } } if (op()) op()->setMassSelectionUpdate(false); updateView(); if (ensureVisibilityAfterSelect() && temp != 0) { makeItemVisible(temp); } else if (makeVisible && firstMatch != 0) { // if no selected item is visible... KrViewItemList selectedItems; getSelectedKrViewItems(&selectedItems); bool anyVisible = false; for (KrViewItem *item : selectedItems) { if (isItemVisible(item)) { anyVisible = true; break; } } if (!anyVisible) { // ...scroll to fist selected item makeItemVisible(firstMatch); } } redraw(); return firstMatch != 0; // return if any file was selected } void KrView::invertSelection() { if (op()) op()->setMassSelectionUpdate(true); KConfigGroup grpSvr(_config, "Look&Feel"); bool markDirs = grpSvr.readEntry("Mark Dirs", _MarkDirs); KrViewItem *temp = getCurrentKrViewItem(); for (KrViewItem * it = getFirst(); it != 0; it = getNext(it)) { if (it->name() == "..") continue; if (it->getFileItem()->isDir() && !markDirs && !it->isSelected()) continue; it->setSelected(!it->isSelected()); } if (op()) op()->setMassSelectionUpdate(false); updateView(); if (ensureVisibilityAfterSelect() && temp != 0) makeItemVisible(temp); } QString KrView::firstUnmarkedBelowCurrent(const bool skipCurrent) { if (getCurrentKrViewItem() == 0) return QString(); KrViewItem *iterator = getCurrentKrViewItem(); if (skipCurrent) iterator = getNext(iterator); while (iterator && iterator->isSelected()) iterator = getNext(iterator); if (!iterator) { iterator = getPrev(getCurrentKrViewItem()); while (iterator && iterator->isSelected()) iterator = getPrev(iterator); } if (!iterator) return QString(); return iterator->name(); } void KrView::deleteItem(const QString &name, bool onUpdate) { KrViewItem *viewItem = findItemByName(name); if (!viewItem) return; if (_previews && !onUpdate) _previews->deletePreview(viewItem); preDeleteItem(viewItem); if (viewItem->FILEITEM->isDir()) { --_numDirs; } --_count; delete viewItem; if (!onUpdate) op()->emitSelectionChanged(); } void KrView::addItem(FileItem *fileItem, bool onUpdate) { if (isFiltered(fileItem)) return; KrViewItem *viewItem = preAddItem(fileItem); if (!viewItem) return; // not added if (_previews) _previews->updatePreview(viewItem); if (fileItem->isDir()) ++_numDirs; ++_count; if (!onUpdate) { op()->emitSelectionChanged(); } } void KrView::updateItem(FileItem *newFileItem) { // file name did not change const QString name = newFileItem->getName(); // preserve 'current' and 'selection' const bool isCurrent = getCurrentItem() == name; QStringList selectedNames; getSelectedItems(&selectedNames, false); const bool isSelected = selectedNames.contains(name); // delete old file item deleteItem(name, true); if (!isFiltered(newFileItem)) { addItem(newFileItem, true); } if (isCurrent) setCurrentItem(name, false); if (isSelected) setSelected(newFileItem, true); op()->emitSelectionChanged(); } void KrView::clear() { if(_previews) _previews->clear(); _count = _numDirs = 0; delete _dummyFileItem; _dummyFileItem = 0; redraw(); } bool KrView::handleKeyEvent(QKeyEvent *e) { qDebug() << "key event=" << e; switch (e->key()) { case Qt::Key_Enter : case Qt::Key_Return : { if (e->modifiers() & Qt::ControlModifier) // let the panel handle it e->ignore(); else { KrViewItem * i = getCurrentKrViewItem(); if (i == 0) return true; QString tmp = i->name(); op()->emitExecuted(tmp); } return true; } case Qt::Key_QuoteLeft : // Terminal Emulator bugfix if (e->modifiers() == Qt::ControlModifier) { // let the panel handle it e->ignore(); } else { // a normal click - do a lynx-like moving thing // ask krusader to move to the home directory op()->emitGoHome(); } return true; case Qt::Key_Delete : // delete/trash the file (delete with alternative mode is a panel action) // allow only no modifier or KeypadModifier (i.e. Del on a Numeric Keypad) if ((e->modifiers() & ~Qt::KeypadModifier) == Qt::NoModifier) { op()->emitDefaultDeleteFiles(); } return true; case Qt::Key_Insert: { KrViewItem * i = getCurrentKrViewItem(); if (!i) return true; i->setSelected(!i->isSelected()); if (KrSelectionMode::getSelectionHandler()->insertMovesDown()) { KrViewItem * next = getNext(i); if (next) { setCurrentKrViewItem(next); makeItemVisible(next); } } op()->emitSelectionChanged(); return true; } case Qt::Key_Space: { KrViewItem * viewItem = getCurrentKrViewItem(); if (viewItem != 0) { viewItem->setSelected(!viewItem->isSelected()); if (viewItem->getFileItem()->isDir() && KrSelectionMode::getSelectionHandler()->spaceCalculatesDiskSpace()) { op()->emitQuickCalcSpace(viewItem); } if (KrSelectionMode::getSelectionHandler()->spaceMovesDown()) { KrViewItem * next = getNext(viewItem); if (next) { setCurrentKrViewItem(next); makeItemVisible(next); } } op()->emitSelectionChanged(); } return true; } case Qt::Key_Backspace : // Terminal Emulator bugfix case Qt::Key_Left : if (e->modifiers() == Qt::ControlModifier || e->modifiers() == Qt::ShiftModifier || e->modifiers() == Qt::AltModifier) { // let the panel handle it e->ignore(); } else { // a normal click - do a lynx-like moving thing // ask krusader to move up a directory op()->emitDirUp(); } return true; // safety case Qt::Key_Right : if (e->modifiers() == Qt::ControlModifier || e->modifiers() == Qt::ShiftModifier || e->modifiers() == Qt::AltModifier) { // let the panel handle it e->ignore(); } else { // just a normal click - do a lynx-like moving thing KrViewItem *i = getCurrentKrViewItem(); if (i) op()->emitGoInside(i->name()); } return true; case Qt::Key_Up : if (e->modifiers() == Qt::ControlModifier) { // let the panel handle it - jump to the Location Bar e->ignore(); } else { KrViewItem *item = getCurrentKrViewItem(); if (item) { if (e->modifiers() == Qt::ShiftModifier) { item->setSelected(!item->isSelected()); op()->emitSelectionChanged(); } item = getPrev(item); if (item) { setCurrentKrViewItem(item); makeItemVisible(item); } } } return true; case Qt::Key_Down : if (e->modifiers() == Qt::ControlModifier || e->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) { // let the panel handle it - jump to command line e->ignore(); } else { KrViewItem *item = getCurrentKrViewItem(); if (item) { if (e->modifiers() == Qt::ShiftModifier) { item->setSelected(!item->isSelected()); op()->emitSelectionChanged(); } item = getNext(item); if (item) { setCurrentKrViewItem(item); makeItemVisible(item); } } } return true; case Qt::Key_Home: { if (e->modifiers() & Qt::ShiftModifier) { bool select = true; KrViewItem *pos = getCurrentKrViewItem(); if (pos == 0) pos = getLast(); KrViewItem *item = getFirst(); op()->setMassSelectionUpdate(true); while (item) { item->setSelected(select); if (item == pos) select = false; item = getNext(item); } op()->setMassSelectionUpdate(false); } KrViewItem * first = getFirst(); if (first) { setCurrentKrViewItem(first); makeItemVisible(first); } } return true; case Qt::Key_End: if (e->modifiers() & Qt::ShiftModifier) { bool select = false; KrViewItem *pos = getCurrentKrViewItem(); if (pos == 0) pos = getFirst(); op()->setMassSelectionUpdate(true); KrViewItem *item = getFirst(); while (item) { if (item == pos) select = true; item->setSelected(select); item = getNext(item); } op()->setMassSelectionUpdate(false); } else { KrViewItem *last = getLast(); if (last) { setCurrentKrViewItem(last); makeItemVisible(last); } } return true; case Qt::Key_PageDown: { KrViewItem * current = getCurrentKrViewItem(); int downStep = itemsPerPage(); while (downStep != 0 && current) { KrViewItem * newCurrent = getNext(current); if (newCurrent == 0) break; current = newCurrent; downStep--; } if (current) { setCurrentKrViewItem(current); makeItemVisible(current); } return true; } case Qt::Key_PageUp: { KrViewItem * current = getCurrentKrViewItem(); int upStep = itemsPerPage(); while (upStep != 0 && current) { KrViewItem * newCurrent = getPrev(current); if (newCurrent == 0) break; current = newCurrent; upStep--; } if (current) { setCurrentKrViewItem(current); makeItemVisible(current); } return true; } case Qt::Key_Escape: e->ignore(); return true; // otherwise the selection gets lost??!?? // also it is needed by the panel case Qt::Key_A : // mark all if (e->modifiers() == Qt::ControlModifier) { //FIXME: shouldn't there also be a shortcut for unselecting everything ? selectAllIncludingDirs(); return true; } #if __GNUC__ >= 7 [[gnu::fallthrough]]; #endif default: return false; } return false; } void KrView::zoomIn() { int idx = iconSizes.indexOf(_fileIconSize); if(idx >= 0 && (idx+1) < iconSizes.count()) setFileIconSize(iconSizes[idx+1]); } void KrView::zoomOut() { int idx = iconSizes.indexOf(_fileIconSize); if(idx > 0) setFileIconSize(iconSizes[idx-1]); } void KrView::setFileIconSize(int size) { if(iconSizes.indexOf(size) < 0) return; _fileIconSize = size; if(_previews) { _previews->clear(); _previews->update(); } redraw(); op()->emitRefreshActions(); } int KrView::defaultFileIconSize() { KConfigGroup grpSvr(_config, _instance.name()); return grpSvr.readEntry("IconSize", _FilelistIconSize).toInt(); } void KrView::saveDefaultSettings(KrViewProperties::PropertyType properties) { saveSettings(KConfigGroup(_config, _instance.name()), properties); op()->emitRefreshActions(); } void KrView::restoreDefaultSettings() { restoreSettings(KConfigGroup(_config, _instance.name())); } void KrView::saveSettings(KConfigGroup group, KrViewProperties::PropertyType properties) { if(properties & KrViewProperties::PropIconSize) group.writeEntry("IconSize", fileIconSize()); if(properties & KrViewProperties::PropShowPreviews) group.writeEntry("ShowPreviews", previewsShown()); if(properties & KrViewProperties::PropSortMode) saveSortMode(group); if(properties & KrViewProperties::PropFilter) { group.writeEntry("Filter", static_cast(_properties->filter)); group.writeEntry("FilterApplysToDirs", _properties->filterApplysToDirs); if(_properties->filterSettings.isValid()) _properties->filterSettings.save(KConfigGroup(&group, "FilterSettings")); } } void KrView::restoreSettings(KConfigGroup group) { _ignoreSettingsChange = true; doRestoreSettings(group); _ignoreSettingsChange = false; refresh(); } void KrView::doRestoreSettings(KConfigGroup group) { restoreSortMode(group); setFileIconSize(group.readEntry("IconSize", defaultFileIconSize())); showPreviews(group.readEntry("ShowPreviews", false)); _properties->filter = static_cast(group.readEntry("Filter", static_cast(KrViewProperties::All))); _properties->filterApplysToDirs = group.readEntry("FilterApplysToDirs", false); _properties->filterSettings.load(KConfigGroup(&group, "FilterSettings")); _properties->filterMask = _properties->filterSettings.toQuery(); } void KrView::applySettingsToOthers() { for(int i = 0; i < _instance.m_objects.length(); i++) { KrView *view = _instance.m_objects[i]; if(this != view) { view->_ignoreSettingsChange = true; view->copySettingsFrom(this); view->_ignoreSettingsChange = false; } } } void KrView::sortModeUpdated(KrViewProperties::ColumnType sortColumn, bool descending) { if(sortColumn == _properties->sortColumn && descending == (bool) (_properties->sortOptions & KrViewProperties::Descending)) return; int options = _properties->sortOptions; if(descending) options |= KrViewProperties::Descending; else options &= ~KrViewProperties::Descending; _properties->sortColumn = sortColumn; _properties->sortOptions = static_cast(options); // op()->settingsChanged(KrViewProperties::PropSortMode); } bool KrView::drawCurrent() const { return isFocused() || KConfigGroup(_config, "Look&Feel") .readEntry("Always Show Current Item", _AlwaysShowCurrentItem); } void KrView::saveSortMode(KConfigGroup &group) { group.writeEntry("Sort Column", static_cast(_properties->sortColumn)); group.writeEntry("Descending Sort Order", _properties->sortOptions & KrViewProperties::Descending); } void KrView::restoreSortMode(KConfigGroup &group) { int column = group.readEntry("Sort Column", static_cast(KrViewProperties::Name)); bool isDescending = group.readEntry("Descending Sort Order", false); setSortMode(static_cast(column), isDescending); } QString KrView::krPermissionText(const FileItem * fileitem) { QString tmp; switch (fileitem->isReadable()) { case ALLOWED_PERM: tmp+='r'; break; case UNKNOWN_PERM: tmp+='?'; break; case NO_PERM: tmp+='-'; break; } switch (fileitem->isWriteable()) { case ALLOWED_PERM: tmp+='w'; break; case UNKNOWN_PERM: tmp+='?'; break; case NO_PERM: tmp+='-'; break; } switch (fileitem->isExecutable()) { case ALLOWED_PERM: tmp+='x'; break; case UNKNOWN_PERM: tmp+='?'; break; case NO_PERM: tmp+='-'; break; } return tmp; } QString KrView::permissionsText(const KrViewProperties *properties, const FileItem *fileItem) { return properties->numericPermissions ? QString().asprintf("%.4o", fileItem->getMode() & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)) : fileItem->getPerm(); } QString KrView::sizeText(const KrViewProperties *properties, KIO::filesize_t size) { return properties->humanReadableSize ? KIO::convertSize(size) : KRpermHandler::parseSize(size); } QString KrView::mimeTypeText(FileItem *fileItem) { QMimeType mt = QMimeDatabase().mimeTypeForName(fileItem->getMime()); return mt.isValid() ? mt.comment() : QString(); } bool KrView::isFiltered(FileItem *fileitem) { if (_quickFilterMask.isValid() && _quickFilterMask.indexIn(fileitem->getName()) == -1) return true; bool filteredOut = false; bool isDir = fileitem->isDir(); if (!isDir || (isDir && properties()->filterApplysToDirs)) { switch (properties()->filter) { case KrViewProperties::All : break; case KrViewProperties::Custom : if (!properties()->filterMask.match(fileitem)) filteredOut = true; break; case KrViewProperties::Dirs: if (!isDir) filteredOut = true; break; case KrViewProperties::Files: if (isDir) filteredOut = true; break; default: break; } } return filteredOut; } void KrView::setFiles(DirListerInterface *files) { if(files != _files) { clear(); if(_files) QObject::disconnect(_files, 0, op(), 0); _files = files; } if(!_files) return; QObject::disconnect(_files, 0, op(), 0); QObject::connect(_files, &DirListerInterface::scanDone, op(), &KrViewOperator::startUpdate); QObject::connect(_files, &DirListerInterface::cleared, op(), &KrViewOperator::cleared); QObject::connect(_files, &DirListerInterface::addedFileItem, op(), &KrViewOperator::fileAdded); QObject::connect(_files, &DirListerInterface::updatedFileItem, op(), &KrViewOperator::fileUpdated); } void KrView::setFilter(KrViewProperties::FilterSpec filter, FilterSettings customFilter, bool applyToDirs) { _properties->filter = filter; _properties->filterSettings = customFilter; _properties->filterMask = customFilter.toQuery(); _properties->filterApplysToDirs = applyToDirs; refresh(); } void KrView::setFilter(KrViewProperties::FilterSpec filter) { KConfigGroup cfg(_config, "Look&Feel"); bool rememberSettings = cfg.readEntry("FilterDialogRemembersSettings", _FilterDialogRemembersSettings); bool applyToDirs = rememberSettings ? _properties->filterApplysToDirs : false; switch (filter) { case KrViewProperties::All : break; case KrViewProperties::Custom : { FilterDialog dialog(_widget, i18n("Filter Files"), QStringList(i18n("Apply filter to folders")), false); dialog.checkExtraOption(i18n("Apply filter to folders"), applyToDirs); if(rememberSettings) dialog.applySettings(_properties->filterSettings); dialog.exec(); FilterSettings s(dialog.getSettings()); if(!s.isValid()) // if the user canceled - quit return; _properties->filterSettings = s; _properties->filterMask = s.toQuery(); applyToDirs = dialog.isExtraOptionChecked(i18n("Apply filter to folders")); } break; default: return; } _properties->filterApplysToDirs = applyToDirs; _properties->filter = filter; refresh(); } void KrView::customSelection(bool select) { KConfigGroup grpSvr(_config, "Look&Feel"); bool includeDirs = grpSvr.readEntry("Mark Dirs", _MarkDirs); FilterDialog dialog(0, i18n("Select Files"), QStringList(i18n("Apply selection to folders")), false); dialog.checkExtraOption(i18n("Apply selection to folders"), includeDirs); dialog.exec(); KRQuery query = dialog.getQuery(); // if the user canceled - quit if (query.isNull()) return; includeDirs = dialog.isExtraOptionChecked(i18n("Apply selection to folders")); changeSelection(query, select, includeDirs); } void KrView::refresh() { const QString currentItem = !nameToMakeCurrent().isEmpty() ? // nameToMakeCurrent() : getCurrentItem(); bool scrollToCurrent = !nameToMakeCurrent().isEmpty() || isItemVisible(getCurrentKrViewItem()); setNameToMakeCurrent(QString()); const QModelIndex currentIndex = getCurrentIndex(); const QList selection = selectedUrls(); clear(); if(!_files) return; QList fileItems; // if we are not at the root add the ".." entry if(!_files->isRoot()) { _dummyFileItem = FileItem::createDummy(); fileItems << _dummyFileItem; } foreach(FileItem *fileitem, _files->fileItems()) { if(!fileitem || isFiltered(fileitem)) continue; if(fileitem->isDir()) _numDirs++; _count++; fileItems << fileitem; } populate(fileItems, _dummyFileItem); if(!selection.isEmpty()) setSelectionUrls(selection); if (!currentItem.isEmpty()) { if (currentItem == ".." && _count > 0 && // !_quickFilterMask.isEmpty() && _quickFilterMask.isValid()) { // In a filtered view we should never select the dummy entry if // there are real matches. setCurrentKrViewItem(getNext(getFirst())); } else { setCurrentItem(currentItem, scrollToCurrent, currentIndex); } } else { setCurrentKrViewItem(getFirst()); } updatePreviews(); redraw(); op()->emitSelectionChanged(); } void KrView::setSelected(const FileItem* fileitem, bool select) { if (fileitem == _dummyFileItem) return; if (select) clearSavedSelection(); intSetSelected(fileitem, select); } void KrView::saveSelection() { _savedSelection = selectedUrls(); op()->emitRefreshActions(); } void KrView::restoreSelection() { if(canRestoreSelection()) setSelectionUrls(_savedSelection); } void KrView::clearSavedSelection() { _savedSelection.clear(); op()->emitRefreshActions(); } void KrView::markSameBaseName() { KrViewItem* item = getCurrentKrViewItem(); if (!item) return; KRQuery query(QString("%1.*").arg(item->name(false))); changeSelection(query, true, false); } void KrView::markSameExtension() { KrViewItem* item = getCurrentKrViewItem(); if (!item) return; KRQuery query(QString("*.%1").arg(item->extension())); changeSelection(query, true, false); } diff --git a/krusader/icon.cpp b/krusader/icon.cpp index d6c9d127..84a15468 100644 --- a/krusader/icon.cpp +++ b/krusader/icon.cpp @@ -1,244 +1,282 @@ /***************************************************************************** * Copyright (C) 2018 Nikita Melnichenko * * Copyright (C) 2018 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 2 of the License, or * * (at your option) any later version. * * * * Krusader is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "icon.h" #include "krglobal.h" // QtCore #include #include #include #include // QtGui #include #include #include #include static const int cacheSize = 500; static const char *missingIconPath = ":/icons/icon-missing.svgz"; static inline QStringList getThemeFallbackList() { QStringList themes; // add user fallback theme if set if (krConfig) { const KConfigGroup group(krConfig, QStringLiteral("Startup")); QString userFallbackTheme = group.readEntry("Fallback Icon Theme", QString()); if (!userFallbackTheme.isEmpty()) { themes << userFallbackTheme; } } // Breeze and Oxygen are weak dependencies of Krusader, // i.e. each of the themes provide a complete set of icons used in the interface themes << "breeze" << "oxygen"; return themes; } class IconEngine : public QIconEngine { public: - IconEngine(QString iconName, QIcon fallbackIcon) : _iconName(iconName), _fallbackIcon(fallbackIcon) + IconEngine(QString iconName, QIcon fallbackIcon, QStringList overlays = QStringList()) : + _iconName(iconName), _fallbackIcon(fallbackIcon), _overlays(overlays) { _themeFallbackList = getThemeFallbackList(); } virtual void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override; virtual QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override; virtual IconEngine *clone() const override { return new IconEngine(*this); } private: QString _iconName; QStringList _themeFallbackList; QIcon _fallbackIcon; + QStringList _overlays; }; Icon::Icon() : QIcon() { } -Icon::Icon(QString name) : QIcon(new IconEngine(name, QIcon(missingIconPath))) +Icon::Icon(QString name, QStringList overlays) : + QIcon(new IconEngine(name, QIcon(missingIconPath), overlays)) { } struct IconSearchResult { QIcon icon; ///< icon returned by search; null icon if not found QString originalThemeName; ///< original theme name if theme is modified by search IconSearchResult(QIcon icon, QString originalThemeName) : icon(icon), originalThemeName(originalThemeName) {} }; // Search icon in the configured themes. // If this call modifies active theme, the original theme name will be specified in the result. static inline IconSearchResult searchIcon(QString iconName, QStringList themeFallbackList) { if (QDir::isAbsolutePath(iconName)) { // a path is used - directly load the icon return IconSearchResult(QIcon(iconName), QString()); } else if (QIcon::hasThemeIcon(iconName)) { // current theme has the icon - load seamlessly return IconSearchResult(QIcon::fromTheme(iconName), QString()); } else if (KIconLoader::global()->hasIcon(iconName)) { // KF icon loader does a wider search and helps with mime icons return IconSearchResult(KDE::icon(iconName), QString()); } else { // search the icon in fallback themes auto currentTheme = QIcon::themeName(); for (auto fallbackThemeName : themeFallbackList) { QIcon::setThemeName(fallbackThemeName); if (QIcon::hasThemeIcon(iconName)) { return IconSearchResult(QIcon::fromTheme(iconName), currentTheme); } } QIcon::setThemeName(currentTheme); // not found return IconSearchResult(QIcon(), QString()); } } bool Icon::exists(QString iconName) { static QCache cache(cacheSize); static QString cachedTheme; // invalidate cache if system theme is changed if (cachedTheme != QIcon::themeName()) { cache.clear(); cachedTheme = QIcon::themeName(); } // return cached result when possible if (cache.contains(iconName)) { return *cache.object(iconName); } auto searchResult = searchIcon(iconName, getThemeFallbackList()); if (!searchResult.originalThemeName.isNull()) { QIcon::setThemeName(searchResult.originalThemeName); } bool *result = new bool(!searchResult.icon.isNull()); // update the cache; the cache takes ownership over the result cache.insert(iconName, result); return *result; } +void Icon::applyOverlays(QPixmap *pixmap, QStringList overlays) +{ + auto iconLoader = KIconLoader::global(); + + // Since KIconLoader loadIcon is not virtual method, we can't redefine loadIcon + // that is called by drawOverlays. The best we can do is to go over the overlays + // and ensure they exist from the icon loader point of view. + // If not, we replace the overlay with "emblem-unreadable" which should be available + // per freedesktop icon name specification: + // https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html + QStringList fixedOverlays; + for (auto overlay : overlays) { + if (overlay.isEmpty() || iconLoader->hasIcon(overlay)) { + fixedOverlays << overlay; + } else { + fixedOverlays << "emblem-unreadable"; + } + } + + iconLoader->drawOverlays(fixedOverlays, *pixmap, KIconLoader::Desktop); +} + class IconCacheKey { public: - IconCacheKey(const QString &name, const QSize &size, QIcon::Mode mode, QIcon::State state) : - name(name), size(size), mode(mode), state(state) + IconCacheKey(const QString &name, QStringList overlays, + const QSize &size, QIcon::Mode mode, QIcon::State state) : + name(name), overlays(overlays), size(size), mode(mode), state(state) { + + auto repr = QString("%1 [%2] %3x%4 %5 %6").arg(name).arg(overlays.join(';')) + .arg(size.width()).arg(size.height()) + .arg((int)mode).arg((int)state); + _hash = qHash(repr); } bool operator ==(const IconCacheKey &x) const { - return name == x.name && size == x.size && mode == x.mode && state == x.state; + return name == x.name && overlays == x.overlays + && size == x.size && mode == x.mode && state == x.state; } uint hash() const { - return qHash(QString("%1 %2x%3 %4 %5").arg(name).arg(size.width()).arg(size.height()) - .arg((int)mode).arg((int)state)); + return _hash; } QString name; + QStringList overlays; QSize size; QIcon::Mode mode; QIcon::State state; + +private: + uint _hash; }; uint qHash(const IconCacheKey &key) { return key.hash(); } QPixmap IconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) { static QCache cache(cacheSize); static QString cachedTheme; // invalidate cache if system theme is changed if (cachedTheme != QIcon::themeName()) { if (!cachedTheme.isEmpty()) { qDebug() << "System icon theme changed:" << cachedTheme << "->" << QIcon::themeName(); } cache.clear(); cachedTheme = QIcon::themeName(); } // an empty icon name is a special case - we don't apply any fallback if (_iconName.isEmpty()) { return QPixmap(); } - auto key = IconCacheKey(_iconName, size, mode, state); + auto key = IconCacheKey(_iconName, _overlays, size, mode, state); // return cached icon when possible if (cache.contains(key)) { return *cache.object(key); } // search icon and extract pixmap auto pixmap = new QPixmap; auto searchResult = searchIcon(_iconName, _themeFallbackList); if (!searchResult.icon.isNull()) { *pixmap = searchResult.icon.pixmap(size, mode, state); } if (!searchResult.originalThemeName.isNull()) { QIcon::setThemeName(searchResult.originalThemeName); } // can't find the icon neither in system theme nor in fallback themes - load fallback icon if (pixmap->isNull()) { qWarning() << "Unable to find icon" << _iconName << "of size" << size << "in any configured theme"; *pixmap = _fallbackIcon.pixmap(size, mode, state); } + // apply overlays in a safe manner + Icon::applyOverlays(pixmap, _overlays); + // update the cache; the cache takes ownership over the pixmap cache.insert(key, pixmap); return *pixmap; } void IconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) { QSize pixmapSize = rect.size(); QPixmap px = pixmap(pixmapSize, mode, state); painter->drawPixmap(rect, px); } diff --git a/krusader/icon.h b/krusader/icon.h index 53de7819..25212f21 100644 --- a/krusader/icon.h +++ b/krusader/icon.h @@ -1,48 +1,51 @@ /***************************************************************************** * Copyright (C) 2018 Nikita Melnichenko * * Copyright (C) 2018 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 2 of the License, or * * (at your option) any later version. * * * * Krusader is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef ICON_H #define ICON_H // QtGui #include #include /** * @class Icon * * Universal icon class for Krusader. * * There is a list of configured themes that Icon searches in. * The order of themes is the following: active theme, theme specified by user, * fallback themes that provide complete icon set for Krusader. * If icon is not found in any configured theme, the fallback icon is used. */ class Icon : public QIcon { public: Icon(); - explicit Icon(QString name); + explicit Icon(QString name, QStringList overlays = QStringList()); /// Check if icon exists in any configured theme static bool exists(QString iconName); + + /// Apply overlays to the pixmap with fallbacks to standard emblems + static void applyOverlays(QPixmap *pixmap, QStringList overlays); }; #endif // ICON_H