project(Dolphin VERSION ${RELEASE_SERVICE_VERSION})
set(QT_MIN_VERSION "5.15.0")
-set(KF5_MIN_VERSION "5.90.0")
+set(KF5_MIN_VERSION "5.91.0")
# ECM setup
find_package(ECM ${KF5_MIN_VERSION} CONFIG REQUIRED)
trash/dolphintrash.cpp
filterbar/filterbar.cpp
panels/places/placespanel.cpp
- panels/places/placesitem.cpp
- panels/places/placesitemlistgroupheader.cpp
- panels/places/placesitemlistwidget.cpp
- panels/places/placesitemmodel.cpp
- panels/places/placesitemsignalhandler.cpp
- panels/places/placesview.cpp
panels/panel.cpp
panels/folders/foldersitemlistwidget.cpp
panels/folders/treeviewcontextmenu.cpp
#include "dolphinplacesmodelsingleton.h"
#include "dolphinremoveaction.h"
#include "dolphinviewcontainer.h"
-#include "panels/places/placesitem.h"
-#include "panels/places/placesitemmodel.h"
#include "trash/dolphintrash.h"
#include "views/dolphinview.h"
#include "views/viewmodecontroller.h"
#include "dolphinnavigatorswidgetaction.h"
#include "dolphinnewfilemenu.h"
#include "dolphinrecenttabsmenu.h"
+#include "dolphinplacesmodelsingleton.h"
#include "dolphinurlnavigatorscontroller.h"
#include "dolphinviewcontainer.h"
#include "dolphintabpage.h"
#include "middleclickactioneventfilter.h"
#include "panels/folders/folderspanel.h"
-#include "panels/places/placesitemmodel.h"
#include "panels/places/placespanel.h"
#include "panels/terminal/terminalpanel.h"
#include "settings/dolphinsettingsdialog.h"
name = dirToAdd.name();
}
if (url.isValid()) {
- PlacesItemModel model;
QString icon;
if (m_activeViewContainer->isSearchModeEnabled()) {
icon = QStringLiteral("folder-saved-search-symbolic");
} else {
icon = KIO::iconNameForUrl(url);
}
- model.createPlacesItem(name, url, icon);
+ DolphinPlacesModelSingleton::instance().placesModel()->addPlace(name, url, icon);
}
}
addDockWidget(Qt::LeftDockWidgetArea, placesDock);
connect(m_placesPanel, &PlacesPanel::placeActivated,
this, &DolphinMainWindow::slotPlaceActivated);
- connect(m_placesPanel, &PlacesPanel::placeActivatedInNewTab,
+ connect(m_placesPanel, &PlacesPanel::tabRequested,
this, &DolphinMainWindow::openNewTab);
- connect(m_placesPanel, &PlacesPanel::placeActivatedInNewActiveTab,
+ connect(m_placesPanel, &PlacesPanel::activeTabRequested,
this, &DolphinMainWindow::openNewTabAndActivate);
+ connect(m_placesPanel, &PlacesPanel::newWindowRequested, this, [this](const QUrl &url) {
+ Dolphin::openNewWindow({url}, this);
+ });
connect(m_placesPanel, &PlacesPanel::errorMessage,
this, &DolphinMainWindow::showErrorMessage);
connect(this, &DolphinMainWindow::urlChanged,
"appear semi-transparent unless you uncheck their hide property."));
connect(actionShowAllPlaces, &QAction::triggered, this, [actionShowAllPlaces, this](bool checked){
- actionShowAllPlaces->setIcon(QIcon::fromTheme(checked ? QStringLiteral("view-visible") : QStringLiteral("view-hidden")));
- m_placesPanel->showHiddenEntries(checked);
+ m_placesPanel->setShowAll(checked);
});
-
- connect(m_placesPanel, &PlacesPanel::showHiddenEntriesChanged, this, [actionShowAllPlaces] (bool checked){
- actionShowAllPlaces->setChecked(checked);
- actionShowAllPlaces->setIcon(QIcon::fromTheme(checked ? QStringLiteral("view-visible") : QStringLiteral("view-hidden")));
- });
+ connect(m_placesPanel, &PlacesPanel::allPlacesShownChanged, actionShowAllPlaces, &QAction::setChecked);
actionCollection()->action(QStringLiteral("show_places_panel"))
->setWhatsThis(xi18nc("@info:whatsthis", "<para>This toggles the "
panelsMenu->addAction(lockLayoutAction);
connect(panelsMenu->menu(), &QMenu::aboutToShow, this, [actionShowAllPlaces, this]{
- actionShowAllPlaces->setEnabled(m_placesPanel->hiddenListCount());
+ actionShowAllPlaces->setEnabled(DolphinPlacesModelSingleton::instance().placesModel()->hiddenCount());
});
}
*/
#include "dolphinplacesmodelsingleton.h"
+#include "trash/dolphintrash.h"
#include <KAboutData>
#include <KFilePlacesModel>
+#include <QIcon>
+
+DolphinPlacesModel::DolphinPlacesModel(const QString &alternativeApplicationName, QObject *parent)
+ : KFilePlacesModel(alternativeApplicationName, parent)
+{
+ connect(&Trash::instance(), &Trash::emptinessChanged, this, &DolphinPlacesModel::slotTrashEmptinessChanged);
+}
+
+DolphinPlacesModel::~DolphinPlacesModel() = default;
+
+QVariant DolphinPlacesModel::data(const QModelIndex &index, int role) const
+{
+ if (role == Qt::DecorationRole) {
+ if (isTrash(index)) {
+ if (m_isEmpty) {
+ return QIcon::fromTheme(QStringLiteral("user-trash"));
+ } else {
+ return QIcon::fromTheme(QStringLiteral("user-trash-full"));
+ }
+ }
+ }
+
+ return KFilePlacesModel::data(index, role);
+}
+
+void DolphinPlacesModel::slotTrashEmptinessChanged(bool isEmpty)
+{
+ if (m_isEmpty == isEmpty) {
+ return;
+ }
+
+ // NOTE Trash::isEmpty() reads the config file whereas emptinessChanged is
+ // hooked up to whether a dirlister in trash:/ has any files and they disagree...
+ m_isEmpty = isEmpty;
+
+ for (int i = 0; i < rowCount(); ++i) {
+ const QModelIndex index = this->index(i, 0);
+ if (isTrash(index)) {
+ Q_EMIT dataChanged(index, index, {Qt::DecorationRole});
+ }
+ }
+}
+
+bool DolphinPlacesModel::isTrash(const QModelIndex &index) const
+{
+ return url(index) == QUrl(QStringLiteral("trash:/"));
+}
+
DolphinPlacesModelSingleton::DolphinPlacesModelSingleton()
- : m_placesModel(new KFilePlacesModel(KAboutData::applicationData().componentName() + applicationNameSuffix()))
+ : m_placesModel(new DolphinPlacesModel(KAboutData::applicationData().componentName() + applicationNameSuffix()))
{
}
#include <QString>
#include <QScopedPointer>
-class KFilePlacesModel;
+#include <KFilePlacesModel>
+
+/**
+ * @brief Dolphin's special-cased KFilePlacesModel
+ *
+ * It returns the trash's icon based on whether
+ * it is full or not.
+ */
+class DolphinPlacesModel : public KFilePlacesModel
+{
+ Q_OBJECT
+
+public:
+ explicit DolphinPlacesModel(const QString &alternativeApplicationName, QObject *parent = nullptr);
+ ~DolphinPlacesModel() override;
+
+protected:
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+
+private Q_SLOTS:
+ void slotTrashEmptinessChanged(bool isEmpty);
+
+private:
+ bool isTrash(const QModelIndex &index) const;
+
+ bool m_isEmpty = false;
+};
/**
* @brief Provides a global KFilePlacesModel instance.
<kcfgfile name="dolphinrc"/>
<group name="PlacesPanel">
<entry name="IconSize" type="Int">
- <label>Size of icons in the Places Panel (-1 means "use the style's small size")</label>
+ <label>Size of icons in the Places Panel (-1 means "automatic")</label>
<default code="true">KIconLoader::SizeSmallMedium</default>
</entry>
</group>
+++ /dev/null
-/*
- * SPDX-FileCopyrightText: 2012 Peter Penz <peter.penz19@gmail.com>
- * SPDX-FileCopyrightText: 2018 Elvis Angelaccio <elvis.angelaccio@kde.org>
- *
- * Based on KFilePlacesItem from kdelibs:
- * SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#include "placesitem.h"
-#include "trash/dolphintrash.h"
-
-#include "dolphindebug.h"
-#include "placesitemsignalhandler.h"
-
-#include <KDirLister>
-#include <KLocalizedString>
-#include <Solid/Block>
-
-PlacesItem::PlacesItem(const KBookmark& bookmark, PlacesItem* parent) :
- KStandardItem(parent),
- m_device(),
- m_access(),
- m_volume(),
- m_disc(),
- m_player(),
- m_signalHandler(nullptr),
- m_bookmark()
-{
- m_signalHandler = new PlacesItemSignalHandler(this);
- setBookmark(bookmark);
-}
-
-PlacesItem::~PlacesItem()
-{
- delete m_signalHandler;
-}
-
-void PlacesItem::setUrl(const QUrl &url)
-{
- // The default check in KStandardItem::setDataValue()
- // for equal values does not work with a custom value
- // like QUrl. Hence do a manual check to prevent that
- // setting an equal URL results in an itemsChanged()
- // signal.
- if (dataValue("url").toUrl() != url) {
- if (url.scheme() == QLatin1String("trash")) {
- QObject::connect(&Trash::instance(), &Trash::emptinessChanged, m_signalHandler.data(), &PlacesItemSignalHandler::onTrashEmptinessChanged);
- }
-
- setDataValue("url", url);
- }
-}
-
-QUrl PlacesItem::url() const
-{
- return dataValue("url").toUrl();
-}
-
-void PlacesItem::setUdi(const QString& udi)
-{
- setDataValue("udi", udi);
-}
-
-QString PlacesItem::udi() const
-{
- return dataValue("udi").toString();
-}
-
-void PlacesItem::setApplicationName(const QString &applicationName)
-{
- setDataValue("applicationName", applicationName);
-}
-
-QString PlacesItem::applicationName() const
-{
- return dataValue("applicationName").toString();
-}
-
-void PlacesItem::setHidden(bool hidden)
-{
- setDataValue("isHidden", hidden);
-}
-
-bool PlacesItem::isHidden() const
-{
- return dataValue("isHidden").toBool();
-}
-
-bool PlacesItem::isGroupHidden() const
-{
- return dataValue("isGroupHidden").toBool();
-}
-
-void PlacesItem::setGroupHidden(bool hidden)
-{
- setDataValue("isGroupHidden", hidden);
-}
-
-void PlacesItem::setSystemItem(bool isSystemItem)
-{
- setDataValue("isSystemItem", isSystemItem);
-}
-
-bool PlacesItem::isSystemItem() const
-{
- return dataValue("isSystemItem").toBool();
-}
-
-Solid::Device PlacesItem::device() const
-{
- return m_device;
-}
-
-void PlacesItem::setBookmark(const KBookmark& bookmark)
-{
- const bool bookmarkDataChanged = !(bookmark == m_bookmark);
-
- // bookmark object must be updated to keep in sync with source model
- m_bookmark = bookmark;
-
- if (!bookmarkDataChanged) {
- return;
- }
-
- delete m_access;
- delete m_volume;
- delete m_disc;
- delete m_player;
-
- const QString udi = bookmark.metaDataItem(QStringLiteral("UDI"));
- if (udi.isEmpty()) {
- setIcon(bookmark.icon());
- setText(i18ndc("kio5", "KFile System Bookmarks", bookmark.text().toUtf8().constData()));
- setUrl(bookmark.url());
- setSystemItem(bookmark.metaDataItem(QStringLiteral("isSystemItem")) == QLatin1String("true"));
- } else {
- initializeDevice(udi);
- }
-
- setHidden(bookmark.metaDataItem(QStringLiteral("IsHidden")) == QLatin1String("true"));
-}
-
-KBookmark PlacesItem::bookmark() const
-{
- return m_bookmark;
-}
-
-bool PlacesItem::storageSetupNeeded() const
-{
- return m_access ? !m_access->isAccessible() : false;
-}
-
-bool PlacesItem::isSearchOrTimelineUrl() const
-{
- const QString urlScheme = url().scheme();
- return (urlScheme.contains("search") || urlScheme.contains("timeline"));
-}
-
-void PlacesItem::onDataValueChanged(const QByteArray& role,
- const QVariant& current,
- const QVariant& previous)
-{
- Q_UNUSED(current)
- Q_UNUSED(previous)
-
- if (!m_bookmark.isNull()) {
- updateBookmarkForRole(role);
- }
-}
-
-void PlacesItem::onDataChanged(const QHash<QByteArray, QVariant>& current,
- const QHash<QByteArray, QVariant>& previous)
-{
- Q_UNUSED(previous)
-
- if (!m_bookmark.isNull()) {
- QHashIterator<QByteArray, QVariant> it(current);
- while (it.hasNext()) {
- it.next();
- updateBookmarkForRole(it.key());
- }
- }
-}
-
-void PlacesItem::initializeDevice(const QString& udi)
-{
- m_device = Solid::Device(udi);
- if (!m_device.isValid()) {
- return;
- }
-
- m_access = m_device.as<Solid::StorageAccess>();
- m_volume = m_device.as<Solid::StorageVolume>();
- m_disc = m_device.as<Solid::OpticalDisc>();
- m_player = m_device.as<Solid::PortableMediaPlayer>();
-
- setText(m_device.displayName());
- setIcon(m_device.icon());
- setIconOverlays(m_device.emblems());
- setUdi(udi);
-
- if (m_access) {
- setUrl(QUrl::fromLocalFile(m_access->filePath()));
- QObject::connect(m_access.data(), &Solid::StorageAccess::accessibilityChanged,
- m_signalHandler.data(), &PlacesItemSignalHandler::onAccessibilityChanged);
- QObject::connect(m_access.data(), &Solid::StorageAccess::teardownRequested,
- m_signalHandler.data(), &PlacesItemSignalHandler::onTearDownRequested);
- } else if (m_disc && (m_disc->availableContent() & Solid::OpticalDisc::Audio) != 0) {
- Solid::Block *block = m_device.as<Solid::Block>();
- if (block) {
- const QString device = block->device();
- setUrl(QUrl(QStringLiteral("audiocd:/?device=%1").arg(device)));
- } else {
- setUrl(QUrl(QStringLiteral("audiocd:/")));
- }
- } else if (m_player) {
- const QStringList protocols = m_player->supportedProtocols();
- if (!protocols.isEmpty()) {
- setUrl(QUrl(QStringLiteral("%1:udi=%2").arg(protocols.first(), m_device.udi())));
- }
- }
-}
-
-void PlacesItem::onAccessibilityChanged()
-{
- setIconOverlays(m_device.emblems());
- setUrl(QUrl::fromLocalFile(m_access->filePath()));
-}
-
-void PlacesItem::updateBookmarkForRole(const QByteArray& role)
-{
- Q_ASSERT(!m_bookmark.isNull());
- if (role == "iconName") {
- m_bookmark.setIcon(icon());
- } else if (role == "text") {
- // Only store the text in the KBookmark if it is not the translation of
- // the current text. This makes sure that the text is re-translated if
- // the user chooses another language, or the translation itself changes.
- //
- // NOTE: It is important to use "KFile System Bookmarks" as context
- // (see PlacesItemModel::createSystemBookmarks()).
- if (text() != i18ndc("kio5", "KFile System Bookmarks", m_bookmark.text().toUtf8().data())) {
- m_bookmark.setFullText(text());
- }
- } else if (role == "url") {
- m_bookmark.setUrl(url());
- } else if (role == "udi") {
- m_bookmark.setMetaDataItem(QStringLiteral("UDI"), udi());
- } else if (role == "applicationName") {
- m_bookmark.setMetaDataItem(QStringLiteral("OnlyInApp"), applicationName());
- } else if (role == "isSystemItem") {
- m_bookmark.setMetaDataItem(QStringLiteral("isSystemItem"), isSystemItem() ? QStringLiteral("true") : QStringLiteral("false"));
- } else if (role == "isHidden") {
- m_bookmark.setMetaDataItem(QStringLiteral("IsHidden"), isHidden() ? QStringLiteral("true") : QStringLiteral("false"));
- }
-}
-
-QString PlacesItem::generateNewId()
-{
- // The ID-generation must be different as done in KFilePlacesItem from kdelibs
- // to prevent identical IDs, because 'count' is of course not shared. We append a
- // " (V2)" to indicate that the ID has been generated by
- // a new version of the places view.
- static int count = 0;
- return QString::number(QDateTime::currentDateTimeUtc().toSecsSinceEpoch()) +
- '/' + QString::number(count++) + " (V2)";
-}
-
-PlacesItemSignalHandler *PlacesItem::signalHandler() const
-{
- return m_signalHandler.data();
-}
+++ /dev/null
-/*
- * SPDX-FileCopyrightText: 2012 Peter Penz <peter.penz19@gmail.com>
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#ifndef PLACESITEM_H
-#define PLACESITEM_H
-
-#include "kitemviews/kstandarditem.h"
-
-#include <KBookmark>
-#include <Solid/Device>
-#include <Solid/OpticalDisc>
-#include <Solid/PortableMediaPlayer>
-#include <Solid/StorageAccess>
-#include <Solid/StorageVolume>
-
-#include <QPointer>
-#include <QUrl>
-
-
-class KDirLister;
-class PlacesItemSignalHandler;
-
-/**
- * @brief Extends KStandardItem by places-specific properties.
- */
-class PlacesItem : public KStandardItem
-{
-
-public:
- explicit PlacesItem(const KBookmark& bookmark, PlacesItem* parent = nullptr);
- ~PlacesItem() override;
-
- void setUrl(const QUrl& url);
- QUrl url() const;
-
- void setUdi(const QString& udi);
- QString udi() const;
-
- void setApplicationName(const QString& applicationName);
- QString applicationName() const;
-
- void setHidden(bool hidden);
- bool isHidden() const;
-
- void setGroupHidden(bool hidden);
- bool isGroupHidden() const;
-
- void setSystemItem(bool isSystemItem);
- bool isSystemItem() const;
-
- Solid::Device device() const;
-
- void setBookmark(const KBookmark& bookmark);
- KBookmark bookmark() const;
-
- bool storageSetupNeeded() const;
-
- bool isSearchOrTimelineUrl() const;
-
- PlacesItemSignalHandler* signalHandler() const;
-
-protected:
- void onDataValueChanged(const QByteArray& role,
- const QVariant& current,
- const QVariant& previous) override;
-
- void onDataChanged(const QHash<QByteArray, QVariant>& current,
- const QHash<QByteArray, QVariant>& previous) override;
-
-private:
- PlacesItem(const PlacesItem& item);
-
- void initializeDevice(const QString& udi);
-
- /**
- * Is invoked if the accessibility of the storage access
- * m_access has been changed and updates the emblem.
- */
- void onAccessibilityChanged();
-
- /**
- * Applies the data-value from the role to m_bookmark.
- */
- void updateBookmarkForRole(const QByteArray& role);
-
- static QString generateNewId();
-
-private:
- Solid::Device m_device;
- QPointer<Solid::StorageAccess> m_access;
- QPointer<Solid::StorageVolume> m_volume;
- QPointer<Solid::OpticalDisc> m_disc;
- QPointer<Solid::PortableMediaPlayer> m_player;
- QPointer<PlacesItemSignalHandler> m_signalHandler;
- KBookmark m_bookmark;
-
- friend class PlacesItemSignalHandler; // Calls onAccessibilityChanged()
-};
-
-#endif
-
-
+++ /dev/null
-/*
- * SPDX-FileCopyrightText: 2012 Peter Penz <peter.penz19@gmail.com>
- *
- * Based on the Itemviews NG project from Trolltech Labs
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#include "placesitemlistgroupheader.h"
-
-PlacesItemListGroupHeader::PlacesItemListGroupHeader(QGraphicsWidget* parent) :
- KStandardItemListGroupHeader(parent)
-{
-}
-
-PlacesItemListGroupHeader::~PlacesItemListGroupHeader()
-{
-}
-
-void PlacesItemListGroupHeader::paintSeparator(QPainter* painter, const QColor& color)
-{
- Q_UNUSED(painter)
- Q_UNUSED(color)
-}
-
-QPalette::ColorRole PlacesItemListGroupHeader::normalTextColorRole() const
-{
- return QPalette::WindowText;
-}
-
+++ /dev/null
-/*
- * SPDX-FileCopyrightText: 2012 Peter Penz <peter.penz19@gmail.com>
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#ifndef PLACESITEMLISTGROUPHEADER_H
-#define PLACESITEMLISTGROUPHEADER_H
-
-#include "kitemviews/kstandarditemlistgroupheader.h"
-
-class PlacesItemListGroupHeader : public KStandardItemListGroupHeader
-{
- Q_OBJECT
-
-public:
- explicit PlacesItemListGroupHeader(QGraphicsWidget* parent = nullptr);
- ~PlacesItemListGroupHeader() override;
-
-protected:
- void paintSeparator(QPainter* painter, const QColor& color) override;
-
- QPalette::ColorRole normalTextColorRole() const override;
-};
-#endif
-
-
+++ /dev/null
-/*
- * SPDX-FileCopyrightText: 2012 Peter Penz <peter.penz19@gmail.com>
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#include "placesitemlistwidget.h"
-
-#include <QStyleOption>
-
-#include <KColorScheme>
-
-#include <Solid/Device>
-#include <Solid/NetworkShare>
-
-#define CAPACITYBAR_HEIGHT 2
-#define CAPACITYBAR_MARGIN 2
-#define CAPACITYBAR_CACHE_TTL 60000
-
-
-PlacesItemListWidget::PlacesItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent) :
- KStandardItemListWidget(informant, parent)
- , m_drawCapacityBar(false)
-{
-}
-
-PlacesItemListWidget::~PlacesItemListWidget()
-{
-}
-
-bool PlacesItemListWidget::isHidden() const
-{
- return data().value("isHidden").toBool() ||
- data().value("isGroupHidden").toBool();
-}
-
-QPalette::ColorRole PlacesItemListWidget::normalTextColorRole() const
-{
- return QPalette::WindowText;
-}
-
-void PlacesItemListWidget::updateCapacityBar()
-{
- const QString udi = data().value("udi").toString();
- if (udi.isEmpty()) {
- resetCapacityBar();
- return;
- }
- const Solid::Device device = Solid::Device(udi);
- if (device.isDeviceInterface(Solid::DeviceInterface::NetworkShare)
- || device.isDeviceInterface(Solid::DeviceInterface::OpticalDrive)
- || device.isDeviceInterface(Solid::DeviceInterface::OpticalDisc)) {
- resetCapacityBar();
- return;
- }
- const QUrl url = data().value("url").toUrl();
-
- if (url.isEmpty() || m_freeSpaceInfo.job || !m_freeSpaceInfo.lastUpdated.hasExpired()) {
- // No url, job running or cache is still valid.
- return;
- }
-
- m_freeSpaceInfo.job = KIO::fileSystemFreeSpace(url);
- connect(
- m_freeSpaceInfo.job,
- &KIO::FileSystemFreeSpaceJob::result,
- this,
- [this](KIO::Job *job, KIO::filesize_t size, KIO::filesize_t available) {
- // even if we receive an error we want to refresh lastUpdated to avoid repeatedly querying in this case
- m_freeSpaceInfo.lastUpdated.setRemainingTime(CAPACITYBAR_CACHE_TTL);
-
- if (job->error()) {
- return;
- }
-
- m_freeSpaceInfo.size = size;
- m_freeSpaceInfo.used = size - available;
- m_freeSpaceInfo.usedRatio = (qreal)m_freeSpaceInfo.used / (qreal)m_freeSpaceInfo.size;
- m_drawCapacityBar = size > 0;
-
- update();
- }
- );
-}
-
-void PlacesItemListWidget::resetCapacityBar()
-{
- m_drawCapacityBar = false;
- delete m_freeSpaceInfo.job;
- m_freeSpaceInfo.lastUpdated.setRemainingTime(0);
- m_freeSpaceInfo.size = 0;
- m_freeSpaceInfo.used = 0;
- m_freeSpaceInfo.usedRatio = 0;
-}
-
-void PlacesItemListWidget::polishEvent()
-{
- updateCapacityBar();
-
- QGraphicsWidget::polishEvent();
-}
-
-void PlacesItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
-{
- KStandardItemListWidget::paint(painter, option, widget);
-
- // We check if option=nullptr since it is null when the place is dragged (Bug #430441)
- if (m_drawCapacityBar && option) {
- const TextInfo* textInfo = m_textInfo.value("text");
- if (textInfo) { // See KStandarItemListWidget::paint() for info on why we check textInfo.
- painter->save();
-
- const QRect capacityRect(
- textInfo->pos.x(),
- option->rect.top() + option->rect.height() - CAPACITYBAR_HEIGHT - CAPACITYBAR_MARGIN,
- qMin((qreal)option->rect.width(), selectionRect().width()) - (textInfo->pos.x() - option->rect.left()),
- CAPACITYBAR_HEIGHT
- );
-
- const QPalette pal = palette();
- const QPalette::ColorGroup group = isActiveWindow() ? QPalette::Active : QPalette::Inactive;
-
- // Background
- const QColor bgColor = isSelected()
- ? pal.color(group, QPalette::Highlight).darker(180)
- : pal.color(group, QPalette::Window).darker(120);
-
- painter->fillRect(capacityRect, bgColor);
-
- // Fill
- const QRect fillRect(capacityRect.x(), capacityRect.y(), capacityRect.width() * m_freeSpaceInfo.usedRatio, capacityRect.height());
- if (m_freeSpaceInfo.usedRatio >= 0.95) { // More than 95% full!
- const QColor dangerUsedColor = KColorScheme(group, KColorScheme::View).foreground(KColorScheme::NegativeText).color();
- painter->fillRect(fillRect, dangerUsedColor);
- } else {
- const QPalette::ColorRole role = isSelected() ? QPalette::HighlightedText : QPalette::Highlight;
- const QColor normalUsedColor = styleOption().palette.color(group, role);
- painter->fillRect(fillRect, normalUsedColor);
- }
-
- painter->restore();
- }
- }
-
- updateCapacityBar();
-}
+++ /dev/null
-/*
- * SPDX-FileCopyrightText: 2012 Peter Penz <peter.penz19@gmail.com>
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#ifndef PLACESITEMLISTWIDGET_H
-#define PLACESITEMLISTWIDGET_H
-
-#include "kitemviews/kstandarditemlistwidget.h"
-
-#include <QDeadlineTimer>
-#include <QPainter>
-#include <QPointer>
-#include <QStyleOptionGraphicsItem>
-#include <QWidget>
-
-#include <KIO/FileSystemFreeSpaceJob>
-
-
-// The free space / capacity bar is based on KFilePlacesView.
-// https://invent.kde.org/frameworks/kio/-/commit/933887dc334f3498505af7a86d25db7faae91019
-struct PlaceFreeSpaceInfo
-{
- QDeadlineTimer lastUpdated;
- KIO::filesize_t used = 0;
- KIO::filesize_t size = 0;
- qreal usedRatio = 0;
- QPointer<KIO::FileSystemFreeSpaceJob> job;
-};
-
-
-/**
- * @brief Extends KStandardItemListWidget to interpret the hidden
- * property of the PlacesModel and use the right text color.
-*/
-class PlacesItemListWidget : public KStandardItemListWidget
-{
- Q_OBJECT
-
-public:
- PlacesItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent);
- ~PlacesItemListWidget() override;
-
- void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override;
- void polishEvent() override;
-
-protected:
- bool isHidden() const override;
- QPalette::ColorRole normalTextColorRole() const override;
- void updateCapacityBar();
- void resetCapacityBar();
-
-private:
- bool m_drawCapacityBar;
- PlaceFreeSpaceInfo m_freeSpaceInfo;
-};
-
-#endif
-
-
+++ /dev/null
-/*
- * SPDX-FileCopyrightText: 2012 Peter Penz <peter.penz19@gmail.com>
- *
- * Based on KFilePlacesModel from kdelibs:
- * SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
- * SPDX-FileCopyrightText: 2007 David Faure <faure@kde.org>
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#include "placesitemmodel.h"
-
-#include "dolphin_generalsettings.h"
-#include "dolphindebug.h"
-#include "dolphinplacesmodelsingleton.h"
-#include "placesitem.h"
-#include "placesitemsignalhandler.h"
-#include "views/dolphinview.h"
-#include "views/viewproperties.h"
-
-#include <KAboutData>
-#include <KLocalizedString>
-#include <KUrlMimeData>
-#include <Solid/DeviceNotifier>
-#include <Solid/OpticalDrive>
-#include <KCoreAddons/KProcessList>
-#include <KCoreAddons/KListOpenFilesJob>
-
-#include <QAction>
-#include <QIcon>
-#include <QMimeData>
-#include <QTimer>
-
-PlacesItemModel::PlacesItemModel(QObject* parent) :
- KStandardItemModel(parent),
- m_hiddenItemsShown(false),
- m_deviceToTearDown(nullptr),
- m_storageSetupInProgress(),
- m_sourceModel(DolphinPlacesModelSingleton::instance().placesModel())
-{
- cleanupBookmarks();
- loadBookmarks();
- initializeDefaultViewProperties();
-
- connect(m_sourceModel, &KFilePlacesModel::rowsInserted, this, &PlacesItemModel::onSourceModelRowsInserted);
- connect(m_sourceModel, &KFilePlacesModel::rowsAboutToBeRemoved, this, &PlacesItemModel::onSourceModelRowsAboutToBeRemoved);
- connect(m_sourceModel, &KFilePlacesModel::dataChanged, this, &PlacesItemModel::onSourceModelDataChanged);
- connect(m_sourceModel, &KFilePlacesModel::rowsAboutToBeMoved, this, &PlacesItemModel::onSourceModelRowsAboutToBeMoved);
- connect(m_sourceModel, &KFilePlacesModel::rowsMoved, this, &PlacesItemModel::onSourceModelRowsMoved);
- connect(m_sourceModel, &KFilePlacesModel::groupHiddenChanged, this, &PlacesItemModel::onSourceModelGroupHiddenChanged);
-}
-
-PlacesItemModel::~PlacesItemModel()
-{
-}
-
-void PlacesItemModel::createPlacesItem(const QString &text, const QUrl &url, const QString &iconName, const QString &appName)
-{
- createPlacesItem(text, url, iconName, appName, -1);
-}
-
-void PlacesItemModel::createPlacesItem(const QString &text, const QUrl &url, const QString &iconName, const QString &appName, int after)
-{
- m_sourceModel->addPlace(text, url, iconName, appName, mapToSource(after));
-}
-
-PlacesItem* PlacesItemModel::placesItem(int index) const
-{
- return dynamic_cast<PlacesItem*>(item(index));
-}
-
-int PlacesItemModel::hiddenCount() const
-{
- return m_sourceModel->hiddenCount();
-}
-
-void PlacesItemModel::setHiddenItemsShown(bool show)
-{
- if (m_hiddenItemsShown == show) {
- return;
- }
-
- m_hiddenItemsShown = show;
-
- if (show) {
- for (int r = 0, rMax = m_sourceModel->rowCount(); r < rMax; r++) {
- const QModelIndex index = m_sourceModel->index(r, 0);
- if (!m_sourceModel->isHidden(index)) {
- continue;
- }
- addItemFromSourceModel(index);
- }
- } else {
- for (int r = 0, rMax = m_sourceModel->rowCount(); r < rMax; r++) {
- const QModelIndex index = m_sourceModel->index(r, 0);
- if (m_sourceModel->isHidden(index)) {
- removeItemByIndex(index);
- }
- }
- }
-}
-
-bool PlacesItemModel::hiddenItemsShown() const
-{
- return m_hiddenItemsShown;
-}
-
-int PlacesItemModel::closestItem(const QUrl& url) const
-{
- return mapFromSource(m_sourceModel->closestItem(url));
-}
-
-// look for the correct position for the item based on source model
-void PlacesItemModel::insertSortedItem(PlacesItem* item)
-{
- if (!item) {
- return;
- }
-
- const KBookmark iBookmark = item->bookmark();
- const QString iBookmarkId = bookmarkId(iBookmark);
- QModelIndex sourceIndex;
- int pos = 0;
-
- for(int r = 0, rMax = m_sourceModel->rowCount(); r < rMax; r++) {
- sourceIndex = m_sourceModel->index(r, 0);
- const KBookmark sourceBookmark = m_sourceModel->bookmarkForIndex(sourceIndex);
-
- if (bookmarkId(sourceBookmark) == iBookmarkId) {
- break;
- }
-
- if (m_hiddenItemsShown || !m_sourceModel->isHidden(sourceIndex)) {
- pos++;
- }
- }
-
- m_indexMap.insert(pos, sourceIndex);
- insertItem(pos, item);
-}
-
-void PlacesItemModel::onItemInserted(int index)
-{
- KStandardItemModel::onItemInserted(index);
-}
-
-void PlacesItemModel::onItemRemoved(int index, KStandardItem* removedItem)
-{
- m_indexMap.removeAt(index);
-
- KStandardItemModel::onItemRemoved(index, removedItem);
-}
-
-void PlacesItemModel::onItemChanged(int index, const QSet<QByteArray>& changedRoles)
-{
- const QModelIndex sourceIndex = mapToSource(index);
- const PlacesItem *changedItem = placesItem(mapFromSource(sourceIndex));
-
- if (!changedItem || !sourceIndex.isValid()) {
- qWarning() << "invalid item changed signal";
- return;
- }
- if (changedRoles.contains("isHidden")) {
- if (m_sourceModel->isHidden(sourceIndex) != changedItem->isHidden()) {
- m_sourceModel->setPlaceHidden(sourceIndex, changedItem->isHidden());
- } else {
- m_sourceModel->refresh();
- }
- }
- KStandardItemModel::onItemChanged(index, changedRoles);
-}
-
-QAction* PlacesItemModel::ejectAction(int index) const
-{
- const PlacesItem* item = placesItem(index);
- if (item && item->device().is<Solid::OpticalDisc>()) {
- return new QAction(QIcon::fromTheme(QStringLiteral("media-eject")), i18nc("@item", "Eject"), nullptr);
- }
-
- return nullptr;
-}
-
-QAction* PlacesItemModel::teardownAction(int index) const
-{
- const PlacesItem* item = placesItem(index);
- if (!item) {
- return nullptr;
- }
-
- Solid::Device device = item->device();
- const bool providesTearDown = device.is<Solid::StorageAccess>() &&
- device.as<Solid::StorageAccess>()->isAccessible();
- if (!providesTearDown) {
- return nullptr;
- }
-
- Solid::StorageDrive* drive = device.as<Solid::StorageDrive>();
- if (!drive) {
- drive = device.parent().as<Solid::StorageDrive>();
- }
-
- bool hotPluggable = false;
- bool removable = false;
- if (drive) {
- hotPluggable = drive->isHotpluggable();
- removable = drive->isRemovable();
- }
-
- QString iconName;
- QString text;
- if (device.is<Solid::OpticalDisc>()) {
- text = i18nc("@item", "Release");
- } else if (removable || hotPluggable) {
- text = i18nc("@item", "Safely Remove");
- iconName = QStringLiteral("media-eject");
- } else {
- text = i18nc("@item", "Unmount");
- iconName = QStringLiteral("media-eject");
- }
-
- if (iconName.isEmpty()) {
- return new QAction(text, nullptr);
- }
-
- return new QAction(QIcon::fromTheme(iconName), text, nullptr);
-}
-
-void PlacesItemModel::requestEject(int index)
-{
- const PlacesItem* item = placesItem(index);
- if (item) {
- Solid::OpticalDrive* drive = item->device().parent().as<Solid::OpticalDrive>();
- if (drive) {
- connect(drive, &Solid::OpticalDrive::ejectDone,
- this, &PlacesItemModel::slotStorageTearDownDone);
- drive->eject();
- } else {
- const QString label = item->text();
- const QString message = i18nc("@info", "The device '%1' is not a disk and cannot be ejected.", label);
- Q_EMIT errorMessage(message);
- }
- }
-}
-
-void PlacesItemModel::requestTearDown(int index)
-{
- const PlacesItem* item = placesItem(index);
- if (item) {
- Solid::StorageAccess *tmp = item->device().as<Solid::StorageAccess>();
- if (tmp) {
- m_deviceToTearDown = tmp;
- // disconnect the Solid::StorageAccess::teardownRequested
- // to prevent emitting PlacesItemModel::storageTearDownExternallyRequested
- // after we have emitted PlacesItemModel::storageTearDownRequested
- disconnect(tmp, &Solid::StorageAccess::teardownRequested,
- item->signalHandler(), &PlacesItemSignalHandler::onTearDownRequested);
- Q_EMIT storageTearDownRequested(tmp->filePath());
- }
- }
-}
-
-bool PlacesItemModel::storageSetupNeeded(int index) const
-{
- const PlacesItem* item = placesItem(index);
- return item ? item->storageSetupNeeded() : false;
-}
-
-void PlacesItemModel::requestStorageSetup(int index)
-{
- const PlacesItem* item = placesItem(index);
- if (!item) {
- return;
- }
-
- Solid::Device device = item->device();
- const bool setup = device.is<Solid::StorageAccess>()
- && !m_storageSetupInProgress.contains(device.as<Solid::StorageAccess>())
- && !device.as<Solid::StorageAccess>()->isAccessible();
- if (setup) {
- Solid::StorageAccess* access = device.as<Solid::StorageAccess>();
-
- m_storageSetupInProgress[access] = index;
-
- connect(access, &Solid::StorageAccess::setupDone,
- this, &PlacesItemModel::slotStorageSetupDone);
-
- access->setup();
- }
-}
-
-QMimeData* PlacesItemModel::createMimeData(const KItemSet& indexes) const
-{
- QList<QUrl> urls;
- QByteArray itemData;
-
- QDataStream stream(&itemData, QIODevice::WriteOnly);
-
- for (int index : indexes) {
- const QUrl itemUrl = placesItem(index)->url();
- if (itemUrl.isValid()) {
- urls << itemUrl;
- }
- stream << index;
- }
-
- QMimeData* mimeData = new QMimeData();
- if (!urls.isEmpty()) {
- mimeData->setUrls(urls);
- } else {
- // #378954: prevent itemDropEvent() drops if there isn't a source url.
- mimeData->setData(blacklistItemDropEventMimeType(), QByteArrayLiteral("true"));
- }
- mimeData->setData(internalMimeType(), itemData);
-
- return mimeData;
-}
-
-bool PlacesItemModel::supportsDropping(int index) const
-{
- return index >= 0 && index < count();
-}
-
-void PlacesItemModel::dropMimeDataBefore(int index, const QMimeData* mimeData)
-{
- if (mimeData->hasFormat(internalMimeType())) {
- // The item has been moved inside the view
- QByteArray itemData = mimeData->data(internalMimeType());
- QDataStream stream(&itemData, QIODevice::ReadOnly);
- int oldIndex;
- stream >> oldIndex;
-
- QModelIndex sourceIndex = mapToSource(index);
- QModelIndex oldSourceIndex = mapToSource(oldIndex);
-
- m_sourceModel->movePlace(oldSourceIndex.row(), sourceIndex.row());
- } else if (mimeData->hasFormat(QStringLiteral("text/uri-list"))) {
- // One or more items must be added to the model
- const QList<QUrl> urls = KUrlMimeData::urlsFromMimeData(mimeData);
- for (int i = urls.count() - 1; i >= 0; --i) {
- const QUrl& url = urls[i];
-
- QString text = url.fileName();
- if (text.isEmpty()) {
- text = url.host();
- }
-
- if ((url.isLocalFile() && !QFileInfo(url.toLocalFile()).isDir())
- || url.scheme() == QLatin1String("trash")) {
- // Only directories outside the trash are allowed
- continue;
- }
-
- createPlacesItem(text, url, KIO::iconNameForUrl(url), {}, qMax(0, index - 1));
- }
- }
- // will save bookmark alteration and fix sort if that is broken by the drag/drop operation
- refresh();
-}
-
-void PlacesItemModel::addItemFromSourceModel(const QModelIndex &index)
-{
- if (!m_hiddenItemsShown && m_sourceModel->isHidden(index)) {
- return;
- }
-
- const KBookmark bookmark = m_sourceModel->bookmarkForIndex(index);
- Q_ASSERT(!bookmark.isNull());
- PlacesItem *item = new PlacesItem(bookmark);
- updateItem(item, index);
- insertSortedItem(item);
-
- if (m_sourceModel->isDevice(index)) {
- connect(item->signalHandler(), &PlacesItemSignalHandler::tearDownExternallyRequested,
- this, &PlacesItemModel::storageTearDownExternallyRequested);
- }
-}
-
-void PlacesItemModel::removeItemByIndex(const QModelIndex &sourceIndex)
-{
- QString id = bookmarkId(m_sourceModel->bookmarkForIndex(sourceIndex));
-
- for (int i = 0, iMax = count(); i < iMax; ++i) {
- if (bookmarkId(placesItem(i)->bookmark()) == id) {
- removeItem(i);
- return;
- }
- }
-}
-
-QString PlacesItemModel::bookmarkId(const KBookmark &bookmark) const
-{
- QString id = bookmark.metaDataItem(QStringLiteral("UDI"));
- if (id.isEmpty()) {
- id = bookmark.metaDataItem(QStringLiteral("ID"));
- }
- return id;
-}
-
-void PlacesItemModel::initializeDefaultViewProperties() const
-{
- for(int i = 0, iMax = m_sourceModel->rowCount(); i < iMax; i++) {
- const QModelIndex index = m_sourceModel->index(i, 0);
- const PlacesItem *item = placesItem(mapFromSource(index));
- if (!item) {
- continue;
- }
-
- // Create default view-properties for all "Search For" and "Recently Saved" bookmarks
- // in case the user has not already created custom view-properties for a corresponding
- // query yet.
- const bool createDefaultViewProperties = item->isSearchOrTimelineUrl() && !GeneralSettings::self()->globalViewProps();
- if (createDefaultViewProperties) {
- const QUrl itemUrl = item->url();
- ViewProperties props(KFilePlacesModel::convertedUrl(itemUrl));
- if (!props.exist()) {
- const QString path = itemUrl.path();
- if (path == QLatin1String("/documents")) {
- props.setViewMode(DolphinView::DetailsView);
- props.setPreviewsShown(false);
- props.setVisibleRoles({"text", "path"});
- } else if (path == QLatin1String("/images")) {
- props.setViewMode(DolphinView::IconsView);
- props.setPreviewsShown(true);
- props.setVisibleRoles({"text", "height", "width"});
- } else if (path == QLatin1String("/audio")) {
- props.setViewMode(DolphinView::DetailsView);
- props.setPreviewsShown(false);
- props.setVisibleRoles({"text", "artist", "album"});
- } else if (path == QLatin1String("/videos")) {
- props.setViewMode(DolphinView::IconsView);
- props.setPreviewsShown(true);
- props.setVisibleRoles({"text"});
- } else if (itemUrl.scheme() == QLatin1String("timeline")) {
- props.setViewMode(DolphinView::DetailsView);
- props.setVisibleRoles({"text", "modificationtime"});
- }
- props.save();
- }
- }
- }
-}
-
-void PlacesItemModel::updateItem(PlacesItem *item, const QModelIndex &index)
-{
- item->setGroup(index.data(KFilePlacesModel::GroupRole).toString());
- item->setIcon(index.data(KFilePlacesModel::IconNameRole).toString());
- item->setGroupHidden(index.data(KFilePlacesModel::GroupHiddenRole).toBool());
-}
-
-void PlacesItemModel::slotStorageTearDownDone(Solid::ErrorType error, const QVariant& errorData)
-{
- if (error && errorData.isValid()) {
- if (error == Solid::ErrorType::DeviceBusy) {
- KListOpenFilesJob* listOpenFilesJob = new KListOpenFilesJob(m_deviceToTearDown->filePath());
- connect(listOpenFilesJob, &KIO::Job::result, this, [this, listOpenFilesJob](KJob*) {
- const KProcessList::KProcessInfoList blockingProcesses = listOpenFilesJob->processInfoList();
- QString errorString;
- if (blockingProcesses.isEmpty()) {
- errorString = i18n("One or more files on this device are open within an application.");
- } else {
- QStringList blockingApps;
- for (const auto& process : blockingProcesses) {
- blockingApps << process.name();
- }
- blockingApps.removeDuplicates();
- errorString = xi18np("One or more files on this device are opened in application <application>\"%2\"</application>.",
- "One or more files on this device are opened in following applications: <application>%2</application>.",
- blockingApps.count(), blockingApps.join(i18nc("separator in list of apps blocking device unmount", ", ")));
- }
- Q_EMIT errorMessage(errorString);
- });
- listOpenFilesJob->start();
- } else {
- Q_EMIT errorMessage(errorData.toString());
- }
- } else {
- // No error; it must have been unmounted successfully
- Q_EMIT storageTearDownSuccessful();
- }
- disconnect(m_deviceToTearDown, &Solid::StorageAccess::teardownDone,
- this, &PlacesItemModel::slotStorageTearDownDone);
- m_deviceToTearDown = nullptr;
-}
-
-void PlacesItemModel::slotStorageSetupDone(Solid::ErrorType error,
- const QVariant& errorData,
- const QString& udi)
-{
- Q_UNUSED(udi)
-
- const int index = m_storageSetupInProgress.take(sender());
- const PlacesItem* item = placesItem(index);
- if (!item) {
- return;
- }
-
- if (error != Solid::NoError) {
- if (errorData.isValid()) {
- Q_EMIT errorMessage(i18nc("@info", "An error occurred while accessing '%1', the system responded: %2",
- item->text(),
- errorData.toString()));
- } else {
- Q_EMIT errorMessage(i18nc("@info", "An error occurred while accessing '%1'",
- item->text()));
- }
- Q_EMIT storageSetupDone(index, false);
- } else {
- Q_EMIT storageSetupDone(index, true);
- }
-}
-
-void PlacesItemModel::onSourceModelRowsInserted(const QModelIndex &parent, int first, int last)
-{
- for (int i = first; i <= last; i++) {
- const QModelIndex index = m_sourceModel->index(i, 0, parent);
- addItemFromSourceModel(index);
- }
-}
-
-void PlacesItemModel::onSourceModelRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
-{
- for(int r = first; r <= last; r++) {
- const QModelIndex index = m_sourceModel->index(r, 0, parent);
- int oldIndex = mapFromSource(index);
- if (oldIndex != -1) {
- removeItem(oldIndex);
- }
- }
-}
-
-void PlacesItemModel::onSourceModelRowsAboutToBeMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row)
-{
- Q_UNUSED(destination)
- Q_UNUSED(row)
-
- for(int r = start; r <= end; r++) {
- const QModelIndex sourceIndex = m_sourceModel->index(r, 0, parent);
- // remove moved item
- removeItem(mapFromSource(sourceIndex));
- }
-}
-
-void PlacesItemModel::onSourceModelRowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row)
-{
- Q_UNUSED(destination)
- Q_UNUSED(parent)
-
- const int blockSize = (end - start) + 1;
-
- for (int r = start; r <= end; r++) {
- // insert the moved item in the new position
- const int targetRow = row + (start - r) - (r < row ? blockSize : 0);
- const QModelIndex targetIndex = m_sourceModel->index(targetRow, 0, destination);
-
- addItemFromSourceModel(targetIndex);
- }
-}
-
-void PlacesItemModel::onSourceModelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
-{
- Q_UNUSED(roles)
-
- for (int r = topLeft.row(); r <= bottomRight.row(); r++) {
- const QModelIndex sourceIndex = m_sourceModel->index(r, 0);
- const KBookmark bookmark = m_sourceModel->bookmarkForIndex(sourceIndex);
- PlacesItem *placeItem = itemFromBookmark(bookmark);
-
- if (placeItem && (!m_hiddenItemsShown && m_sourceModel->isHidden(sourceIndex))) {
- //hide item if it became invisible
- removeItem(index(placeItem));
- return;
- }
-
- if (!placeItem && (m_hiddenItemsShown || !m_sourceModel->isHidden(sourceIndex))) {
- //show item if it became visible
- addItemFromSourceModel(sourceIndex);
- return;
- }
-
- if (placeItem && !m_sourceModel->isDevice(sourceIndex)) {
- // must update the bookmark object
- placeItem->setBookmark(bookmark);
- }
- }
-}
-
-void PlacesItemModel::onSourceModelGroupHiddenChanged(KFilePlacesModel::GroupType group, bool hidden)
-{
- const auto groupIndexes = m_sourceModel->groupIndexes(group);
- for (const QModelIndex &sourceIndex : groupIndexes) {
- PlacesItem *item = placesItem(mapFromSource(sourceIndex));
- if (item) {
- item->setGroupHidden(hidden);
- }
- }
-}
-
-void PlacesItemModel::cleanupBookmarks()
-{
- // KIO model now provides support for baloo urls, and because of that we
- // need to remove old URLs that were visible only in Dolphin to avoid duplication
-
- static const QVector<QUrl> balooURLs = {
- QUrl(QStringLiteral("timeline:/today")),
- QUrl(QStringLiteral("timeline:/yesterday")),
- QUrl(QStringLiteral("timeline:/thismonth")),
- QUrl(QStringLiteral("timeline:/lastmonth")),
- QUrl(QStringLiteral("search:/documents")),
- QUrl(QStringLiteral("search:/images")),
- QUrl(QStringLiteral("search:/audio")),
- QUrl(QStringLiteral("search:/videos"))
- };
-
- int row = 0;
- do {
- const QModelIndex sourceIndex = m_sourceModel->index(row, 0);
- const KBookmark bookmark = m_sourceModel->bookmarkForIndex(sourceIndex);
- const QUrl url = bookmark.url();
- const QString appName = bookmark.metaDataItem(QStringLiteral("OnlyInApp"));
-
- if ((appName == KAboutData::applicationData().componentName() ||
- appName == KAboutData::applicationData().componentName() + DolphinPlacesModelSingleton::applicationNameSuffix()) && balooURLs.contains(url)) {
- qCDebug(DolphinDebug) << "Removing old baloo url:" << url;
- m_sourceModel->removePlace(sourceIndex);
- } else {
- row++;
- }
- } while (row < m_sourceModel->rowCount());
-}
-
-void PlacesItemModel::loadBookmarks()
-{
- for(int r = 0, rMax = m_sourceModel->rowCount(); r < rMax; r++) {
- const QModelIndex sourceIndex = m_sourceModel->index(r, 0);
- if (m_hiddenItemsShown || !m_sourceModel->isHidden(sourceIndex)) {
- addItemFromSourceModel(sourceIndex);
- }
- }
-}
-
-void PlacesItemModel::clear() {
- KStandardItemModel::clear();
-}
-
-void PlacesItemModel::proceedWithTearDown()
-{
- Q_ASSERT(m_deviceToTearDown);
-
- connect(m_deviceToTearDown, &Solid::StorageAccess::teardownDone,
- this, &PlacesItemModel::slotStorageTearDownDone);
- m_deviceToTearDown->teardown();
-}
-
-void PlacesItemModel::deleteItem(int index)
-{
- QModelIndex sourceIndex = mapToSource(index);
- Q_ASSERT(sourceIndex.isValid());
- m_sourceModel->removePlace(sourceIndex);
-}
-
-void PlacesItemModel::refresh()
-{
- m_sourceModel->refresh();
-}
-
-void PlacesItemModel::hideItem(int index)
-{
- PlacesItem* shownItem = placesItem(index);
- if (!shownItem) {
- return;
- }
-
- shownItem->setHidden(true);
-}
-
-QString PlacesItemModel::internalMimeType() const
-{
- return "application/x-dolphinplacesmodel-" +
- QString::number((qptrdiff)this);
-}
-
-int PlacesItemModel::groupedDropIndex(int index, const PlacesItem* item) const
-{
- Q_ASSERT(item);
-
- int dropIndex = index;
- const QString group = item->group();
-
- const int itemCount = count();
- if (index < 0) {
- dropIndex = itemCount;
- }
-
- // Search nearest previous item with the same group
- int previousIndex = -1;
- for (int i = dropIndex - 1; i >= 0; --i) {
- if (placesItem(i)->group() == group) {
- previousIndex = i;
- break;
- }
- }
-
- // Search nearest next item with the same group
- int nextIndex = -1;
- for (int i = dropIndex; i < count(); ++i) {
- if (placesItem(i)->group() == group) {
- nextIndex = i;
- break;
- }
- }
-
- // Adjust the drop-index to be inserted to the
- // nearest item with the same group.
- if (previousIndex >= 0 && nextIndex >= 0) {
- dropIndex = (dropIndex - previousIndex < nextIndex - dropIndex) ?
- previousIndex + 1 : nextIndex;
- } else if (previousIndex >= 0) {
- dropIndex = previousIndex + 1;
- } else if (nextIndex >= 0) {
- dropIndex = nextIndex;
- }
-
- return dropIndex;
-}
-
-bool PlacesItemModel::equalBookmarkIdentifiers(const KBookmark& b1, const KBookmark& b2)
-{
- const QString udi1 = b1.metaDataItem(QStringLiteral("UDI"));
- const QString udi2 = b2.metaDataItem(QStringLiteral("UDI"));
- if (!udi1.isEmpty() && !udi2.isEmpty()) {
- return udi1 == udi2;
- } else {
- return b1.metaDataItem(QStringLiteral("ID")) == b2.metaDataItem(QStringLiteral("ID"));
- }
-}
-
-int PlacesItemModel::mapFromSource(const QModelIndex &index) const
-{
- if (!index.isValid()) {
- return -1;
- }
-
- return m_indexMap.indexOf(index);
-}
-
-bool PlacesItemModel::isDir(int index) const
-{
- Q_UNUSED(index)
- return true;
-}
-
-KFilePlacesModel::GroupType PlacesItemModel::groupType(int row) const
-{
- return m_sourceModel->groupType(mapToSource(row));
-}
-
-bool PlacesItemModel::isGroupHidden(KFilePlacesModel::GroupType type) const
-{
- return m_sourceModel->isGroupHidden(type);
-}
-
-void PlacesItemModel::setGroupHidden(KFilePlacesModel::GroupType type, bool hidden)
-{
- return m_sourceModel->setGroupHidden(type, hidden);
-}
-
-QModelIndex PlacesItemModel::mapToSource(int row) const
-{
- return m_indexMap.value(row);
-}
-
-PlacesItem *PlacesItemModel::itemFromBookmark(const KBookmark &bookmark) const
-{
- const QString id = bookmarkId(bookmark);
- for (int i = 0, iMax = count(); i < iMax; i++) {
- PlacesItem *item = placesItem(i);
- const KBookmark itemBookmark = item->bookmark();
- if (bookmarkId(itemBookmark) == id) {
- return item;
- }
- }
- return nullptr;
-}
-
+++ /dev/null
-/*
- * SPDX-FileCopyrightText: 2012 Peter Penz <peter.penz19@gmail.com>
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#ifndef PLACESITEMMODEL_H
-#define PLACESITEMMODEL_H
-
-#include "kitemviews/kstandarditemmodel.h"
-
-#include <KFilePlacesModel>
-#include <Solid/Predicate>
-#include <Solid/StorageAccess>
-
-#include <QHash>
-#include <QList>
-#include <QSet>
-#include <QUrl>
-
-class KBookmark;
-class PlacesItem;
-class QAction;
-
-/**
- * @brief Model for maintaining the bookmarks of the places panel.
- *
- * It is based on KFilePlacesModel from KIO.
- */
-class PlacesItemModel: public KStandardItemModel
-{
- Q_OBJECT
-
-public:
- explicit PlacesItemModel(QObject* parent = nullptr);
- ~PlacesItemModel() override;
-
- /**
- * @brief Create a new place entry in the bookmark file
- * and add it to the model
- */
- void createPlacesItem(const QString& text, const QUrl& url, const QString& iconName = {}, const QString& appName = {});
- void createPlacesItem(const QString& text, const QUrl& url, const QString& iconName, const QString& appName, int after);
-
- PlacesItem* placesItem(int index) const;
-
- /**
- * @brief Mark an item as hidden
- * @param index of the item to be hidden
- */
- void hideItem(int index);
-
- /**
- * If set to true, all items that are marked as hidden
- * will be shown in the view. The items will
- * stay marked as hidden, which is visually indicated
- * by the view by desaturating the icon and the text.
- */
- void setHiddenItemsShown(bool show);
- bool hiddenItemsShown() const;
-
- /**
- * @return Number of items that are marked as hidden.
- * Note that this does not mean that the items
- * are really hidden
- * (see PlacesItemModel::setHiddenItemsShown()).
- */
- int hiddenCount() const;
-
- /**
- * Search the item which is equal to the URL or at least
- * is a parent URL. If there are more than one possible
- * candidates, return the item which covers the biggest
- * range of the URL. -1 is returned if no closest item
- * could be found.
- */
- int closestItem(const QUrl& url) const;
-
- QAction* ejectAction(int index) const;
- QAction* teardownAction(int index) const;
-
- void requestEject(int index);
- void requestTearDown(int index);
-
- bool storageSetupNeeded(int index) const;
- void requestStorageSetup(int index);
-
- QMimeData* createMimeData(const KItemSet& indexes) const override;
-
- bool supportsDropping(int index) const override;
-
- void dropMimeDataBefore(int index, const QMimeData* mimeData);
-
- /**
- * @return Converts the URL, which contains "virtual" URLs for system-items like
- * "search:/documents" into a Query-URL that will be handled by
- * the corresponding IO-slave. Virtual URLs for bookmarks are used to
- * be independent from internal format changes.
- */
- static QUrl convertedUrl(const QUrl& url);
-
- void clear() override;
-
- void proceedWithTearDown();
-
- /**
- * @brief Remove item from bookmark
- *
- * This function remove the index from bookmark file permanently
- *
- * @param index - the item to be removed
- */
- void deleteItem(int index);
-
- /**
- * Force a sync on the bookmarks and indicates to other applications that the
- * state of the bookmarks has been changed.
- */
- void refresh();
-
- bool isDir(int index) const override;
-
-
- KFilePlacesModel::GroupType groupType(int row) const;
- bool isGroupHidden(KFilePlacesModel::GroupType type) const;
- void setGroupHidden(KFilePlacesModel::GroupType type, bool hidden);
-
-Q_SIGNALS:
- void errorMessage(const QString& message);
- void storageSetupDone(int index, bool success);
- void storageTearDownRequested(const QString& mountPath);
- void storageTearDownExternallyRequested(const QString& mountPath);
- void storageTearDownSuccessful();
-
-protected:
- void onItemInserted(int index) override;
- void onItemRemoved(int index, KStandardItem* removedItem) override;
- void onItemChanged(int index, const QSet<QByteArray>& changedRoles) override;
-
-private Q_SLOTS:
- void slotStorageTearDownDone(Solid::ErrorType error, const QVariant& errorData);
- void slotStorageSetupDone(Solid::ErrorType error, const QVariant& errorData, const QString& udi);
-
- // source model control
- void onSourceModelRowsInserted(const QModelIndex &parent, int first, int last);
- void onSourceModelRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last);
- void onSourceModelRowsAboutToBeMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row);
- void onSourceModelRowsMoved(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row);
- void onSourceModelDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles);
- void onSourceModelGroupHiddenChanged(KFilePlacesModel::GroupType group, bool hidden);
-
-private:
- /**
- * Remove bookmarks created by the previous version of dolphin that are
- * not valid anymore
- */
- void cleanupBookmarks();
-
- /**
- * Loads the bookmarks from the bookmark-manager and creates items for
- * the model or moves hidden items to m_bookmarkedItems.
- */
- void loadBookmarks();
-
- QString internalMimeType() const;
-
- /**
- * @return Adjusted drop index which assures that the item is aligned
- * into the same group as specified by PlacesItem::groupType().
- */
- int groupedDropIndex(int index, const PlacesItem* item) const;
-
- /**
- * @return True if the bookmarks have the same identifiers. The identifier
- * is the unique "ID"-property in case if no UDI is set, otherwise
- * the UDI is used as identifier.
- */
- static bool equalBookmarkIdentifiers(const KBookmark& b1, const KBookmark& b2);
-
- /**
- * Appends the item \a item as last element of the group
- * the item belongs to. If no item with the same group is
- * present, the item gets appended as last element of the
- * model. PlacesItemModel takes the ownership
- * of the item.
- */
- void insertSortedItem(PlacesItem* item);
-
- PlacesItem *itemFromBookmark(const KBookmark &bookmark) const;
-
- void addItemFromSourceModel(const QModelIndex &index);
- void removeItemByIndex(const QModelIndex &mapToSource);
-
- QString bookmarkId(const KBookmark &bookmark) const;
- void initializeDefaultViewProperties() const;
-
- int mapFromSource(const QModelIndex &index) const;
- QModelIndex mapToSource(int row) const;
-
- static void updateItem(PlacesItem *item, const QModelIndex &index);
-
-private:
- bool m_hiddenItemsShown;
-
- Solid::StorageAccess *m_deviceToTearDown;
-
- QHash<QObject*, int> m_storageSetupInProgress;
-
- KFilePlacesModel *m_sourceModel;
-
- QVector<QPersistentModelIndex> m_indexMap;
-};
-
-#endif
-
-
+++ /dev/null
-/*
- * SPDX-FileCopyrightText: 2012 Peter Penz <peter.penz19@gmail.com>
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#include "placesitemsignalhandler.h"
-
-#include "placesitem.h"
-
-PlacesItemSignalHandler::PlacesItemSignalHandler(PlacesItem* item,
- QObject* parent) :
- QObject(parent),
- m_item(item)
-{
-}
-
-PlacesItemSignalHandler::~PlacesItemSignalHandler()
-{
-}
-
-void PlacesItemSignalHandler::onAccessibilityChanged()
-{
- if (m_item) {
- m_item->onAccessibilityChanged();
- }
-}
-
-void PlacesItemSignalHandler::onTearDownRequested(const QString& udi)
-{
- Q_UNUSED(udi)
- if (m_item) {
- Solid::StorageAccess *tmp = m_item->device().as<Solid::StorageAccess>();
- if (tmp) {
- Q_EMIT tearDownExternallyRequested(tmp->filePath());
- }
- }
-}
-
-void PlacesItemSignalHandler::onTrashEmptinessChanged(bool isTrashEmpty)
-{
- if (m_item) {
- m_item->setIcon(isTrashEmpty ? QStringLiteral("user-trash") : QStringLiteral("user-trash-full"));
- }
-}
-
+++ /dev/null
-/*
- * SPDX-FileCopyrightText: 2012 Peter Penz <peter.penz19@gmail.com>
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#ifndef PLACESITEMSIGNALHANDLER_H
-#define PLACESITEMSIGNALHANDLER_H
-
-#include <QObject>
-
-class PlacesItem;
-
-/**
- * @brief Helper class for PlacesItem to be able to listen to signals
- * and performing a corresponding action.
- *
- * PlacesItem is derived from KStandardItem, which is no QObject-class
- * on purpose. To be able to internally listen to signals and performing a
- * corresponding action, PlacesItemSignalHandler is used.
- *
- * E.g. if the PlacesItem wants to react on accessibility-changes of a storage-access,
- * the signal-handler can be used like this:
- * <code>
- * QObject::connect(storageAccess, SIGNAL(accessibilityChanged(bool,QString)),
- * signalHandler, SLOT(onAccessibilityChanged()));
- * </code>
- *
- * The slot PlacesItemSignalHandler::onAccessibilityChanged() will call
- * the method PlacesItem::onAccessibilityChanged().
- */
-class PlacesItemSignalHandler: public QObject
-{
- Q_OBJECT
-
-public:
- explicit PlacesItemSignalHandler(PlacesItem* item, QObject* parent = nullptr);
- ~PlacesItemSignalHandler() override;
-
-public Q_SLOTS:
- /**
- * Calls PlacesItem::onAccessibilityChanged()
- */
- void onAccessibilityChanged();
-
- void onTearDownRequested(const QString& udi);
-
- void onTrashEmptinessChanged(bool isTrashEmpty);
-
-Q_SIGNALS:
- void tearDownExternallyRequested(const QString& udi);
-
-private:
- PlacesItem* m_item;
-};
-
-#endif
/*
* SPDX-FileCopyrightText: 2008-2012 Peter Penz <peter.penz19@gmail.com>
+ * SPDX-FileCopyrightText: 2021 Kai Uwe Broulik <kde@broulik.de>
*
* Based on KFilePlacesView from kdelibs:
* SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
#include "placespanel.h"
+#include "dolphinplacesmodelsingleton.h"
#include "dolphin_generalsettings.h"
+#include "dolphin_placespanelsettings.h"
#include "global.h"
-#include "kitemviews/kitemlistcontainer.h"
-#include "kitemviews/kitemlistcontroller.h"
-#include "kitemviews/kitemlistselectionmanager.h"
-#include "kitemviews/kstandarditem.h"
-#include "placesitem.h"
-#include "placesitemlistgroupheader.h"
-#include "placesitemlistwidget.h"
-#include "placesitemmodel.h"
-#include "placesview.h"
-#include "trash/dolphintrash.h"
#include "views/draganddrophelper.h"
#include "settings/dolphinsettingsdialog.h"
-#include <KFilePlaceEditDialog>
#include <KFilePlacesModel>
#include <KIO/DropJob>
-#include <KIO/EmptyTrashJob>
#include <KIO/Job>
-#include <KIconLoader>
+#include <KListOpenFilesJob>
#include <KLocalizedString>
-#include <KMountPoint>
-#include <KPropertiesDialog>
-#include <QActionGroup>
-#include <QApplication>
-#include <QGraphicsSceneDragDropEvent>
#include <QIcon>
#include <QMenu>
-#include <QMimeData>
-#include <QVBoxLayout>
-#include <QToolTip>
-
-PlacesPanel::PlacesPanel(QWidget* parent) :
- Panel(parent),
- m_controller(nullptr),
- m_model(nullptr),
- m_view(nullptr),
- m_storageSetupFailedUrl(),
- m_triggerStorageSetupModifier(),
- m_itemDropEventIndex(-1),
- m_itemDropEventMimeData(nullptr),
- m_itemDropEvent(nullptr),
- m_tooltipTimer()
-{
- m_tooltipTimer.setInterval(500);
- m_tooltipTimer.setSingleShot(true);
- connect(&m_tooltipTimer, &QTimer::timeout, this, &PlacesPanel::slotShowTooltip);
-}
+#include <QShowEvent>
+#include <QTimer>
-PlacesPanel::~PlacesPanel()
-{
-}
+#include <Solid/StorageAccess>
-void PlacesPanel::proceedWithTearDown()
+PlacesPanel::PlacesPanel(QWidget* parent)
+ : KFilePlacesView(parent)
{
- m_model->proceedWithTearDown();
-}
+ setDropOnPlaceEnabled(true);
+ connect(this, &PlacesPanel::urlsDropped,
+ this, &PlacesPanel::slotUrlsDropped);
-bool PlacesPanel::urlChanged()
-{
- if (!url().isValid() || url().scheme().contains(QLatin1String("search"))) {
- // Skip results shown by a search, as possible identical
- // directory names are useless without parent-path information.
- return false;
- }
+ setAutoResizeItemsEnabled(false);
- if (m_controller) {
- selectItem();
- }
+ setTeardownFunction([this](const QModelIndex &index) {
+ slotTearDownRequested(index);
+ });
+
+ m_configureTrashAction = new QAction(QIcon::fromTheme(QStringLiteral("configure")), i18nc("@action:inmenu", "Configure Trash…"));
+ m_configureTrashAction->setPriority(QAction::HighPriority);
+ connect(m_configureTrashAction, &QAction::triggered, this, &PlacesPanel::slotConfigureTrash);
+ addAction(m_configureTrashAction);
+
+ connect(this, &PlacesPanel::contextMenuAboutToShow, this, &PlacesPanel::slotContextMenuAboutToShow);
- return true;
+ connect(this, &PlacesPanel::iconSizeChanged, this, [this](const QSize &newSize) {
+ int iconSize = qMin(newSize.width(), newSize.height());
+ if (iconSize == 0) {
+ // Don't store 0 size, let's keep -1 for default/small/automatic
+ iconSize = -1;
+ }
+ PlacesPanelSettings* settings = PlacesPanelSettings::self();
+ settings->setIconSize(iconSize);
+ settings->save();
+ });
}
-void PlacesPanel::readSettings()
+PlacesPanel::~PlacesPanel() = default;
+
+void PlacesPanel::setUrl(const QUrl &url)
{
- if (m_controller) {
- const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1;
- m_controller->setAutoActivationDelay(delay);
- }
+ // KFilePlacesView::setUrl no-ops when no model is set but we only set it in showEvent()
+ // Remember the URL and set it in showEvent
+ m_url = url;
+ KFilePlacesView::setUrl(url);
}
-void PlacesPanel::showEvent(QShowEvent* event)
+QList<QAction*> PlacesPanel::customContextMenuActions() const
{
- if (event->spontaneous()) {
- Panel::showEvent(event);
- return;
- }
-
- if (!m_controller) {
- // Postpone the creating of the controller to the first show event.
- // This assures that no performance and memory overhead is given when the folders panel is not
- // used at all and stays invisible.
- m_model = new PlacesItemModel(this);
- m_model->setGroupedSorting(true);
- connect(m_model, &PlacesItemModel::errorMessage,
- this, &PlacesPanel::errorMessage);
- connect(m_model, &PlacesItemModel::storageTearDownRequested,
- this, &PlacesPanel::storageTearDownRequested);
- connect(m_model, &PlacesItemModel::storageTearDownExternallyRequested,
- this, &PlacesPanel::storageTearDownExternallyRequested);
- connect(m_model, &PlacesItemModel::storageTearDownSuccessful,
- this, &PlacesPanel::storageTearDownSuccessful);
-
- m_view = new PlacesView();
- m_view->setWidgetCreator(new KItemListWidgetCreator<PlacesItemListWidget>());
- m_view->setGroupHeaderCreator(new KItemListGroupHeaderCreator<PlacesItemListGroupHeader>());
-
- installEventFilter(this);
-
- m_controller = new KItemListController(m_model, m_view, this);
- m_controller->setSelectionBehavior(KItemListController::SingleSelection);
- m_controller->setSingleClickActivationEnforced(true);
-
- readSettings();
-
- connect(m_controller, &KItemListController::itemActivated, this, &PlacesPanel::slotItemActivated);
- connect(m_controller, &KItemListController::itemMiddleClicked, this, &PlacesPanel::slotItemMiddleClicked);
- connect(m_controller, &KItemListController::itemContextMenuRequested, this, &PlacesPanel::slotItemContextMenuRequested);
- connect(m_controller, &KItemListController::viewContextMenuRequested, this, &PlacesPanel::slotViewContextMenuRequested);
- connect(m_controller, &KItemListController::itemDropEvent, this, &PlacesPanel::slotItemDropEvent);
- connect(m_controller, &KItemListController::aboveItemDropEvent, this, &PlacesPanel::slotAboveItemDropEvent);
-
- KItemListContainer* container = new KItemListContainer(m_controller, this);
- container->setEnabledFrame(false);
-
- QVBoxLayout* layout = new QVBoxLayout(this);
- layout->setContentsMargins(0, 0, 0, 0);
- layout->addWidget(container);
-
- selectItem();
- }
-
- Panel::showEvent(event);
+ return m_customContextMenuActions;
}
-bool PlacesPanel::eventFilter(QObject * /* obj */, QEvent *event)
+void PlacesPanel::setCustomContextMenuActions(const QList<QAction *> &actions)
{
- if (event->type() == QEvent::ToolTip) {
-
- QHelpEvent *hoverEvent = reinterpret_cast<QHelpEvent *>(event);
-
- m_hoveredIndex = m_view->itemAt(hoverEvent->pos());
- m_hoverPos = mapToGlobal(hoverEvent->pos());
-
- m_tooltipTimer.start();
- return true;
- }
- return false;
+ m_customContextMenuActions = actions;
}
-void PlacesPanel::slotItemActivated(int index)
+void PlacesPanel::proceedWithTearDown()
{
- const auto modifiers = QGuiApplication::keyboardModifiers();
- // keep in sync with KUrlNavigator::slotNavigatorButtonClicked
- if (modifiers & Qt::ControlModifier && modifiers & Qt::ShiftModifier) {
- triggerItem(index, TriggerItemModifier::ToNewActiveTab);
- } else if (modifiers & Qt::ControlModifier) {
- triggerItem(index, TriggerItemModifier::ToNewTab);
- } else if (modifiers & Qt::ShiftModifier) {
- triggerItem(index, TriggerItemModifier::ToNewWindow);
- } else {
- triggerItem(index, TriggerItemModifier::None);
- }
+ Q_ASSERT(m_deviceToTearDown);
+
+ connect(m_deviceToTearDown, &Solid::StorageAccess::teardownDone,
+ this, &PlacesPanel::slotTearDownDone);
+ m_deviceToTearDown->teardown();
}
-void PlacesPanel::slotItemMiddleClicked(int index)
+void PlacesPanel::readSettings()
{
- const auto modifiers = QGuiApplication::keyboardModifiers();
- // keep in sync with KUrlNavigator::slotNavigatorButtonClicked
- if (modifiers & Qt::ShiftModifier) {
- triggerItem(index, TriggerItemModifier::ToNewActiveTab);
+ if (GeneralSettings::autoExpandFolders()) {
+ if (!m_dragActivationTimer) {
+ m_dragActivationTimer = new QTimer(this);
+ m_dragActivationTimer->setInterval(750);
+ m_dragActivationTimer->setSingleShot(true);
+ connect(m_dragActivationTimer, &QTimer::timeout,
+ this, &PlacesPanel::slotDragActivationTimeout);
+ }
} else {
- triggerItem(index, TriggerItemModifier::ToNewTab);
+ delete m_dragActivationTimer;
+ m_dragActivationTimer = nullptr;
+ m_pendingDragActivation = QPersistentModelIndex();
}
+
+ const int iconSize = qMax(0, PlacesPanelSettings::iconSize());
+ setIconSize(QSize(iconSize, iconSize));
}
-void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos)
+void PlacesPanel::showEvent(QShowEvent* event)
{
- PlacesItem* item = m_model->placesItem(index);
- if (!item) {
- return;
- }
-
- QMenu menu(this);
-
- QAction* emptyTrashAction = nullptr;
- QAction* configureTrashAction = nullptr;
- QAction* editAction = nullptr;
- QAction* teardownAction = nullptr;
- QAction* ejectAction = nullptr;
- QAction* mountAction = nullptr;
-
- const bool isDevice = !item->udi().isEmpty();
- const bool isTrash = (item->url().scheme() == QLatin1String("trash"));
- if (isTrash) {
- emptyTrashAction = menu.addAction(QIcon::fromTheme(QStringLiteral("trash-empty")), i18nc("@action:inmenu", "Empty Trash"));
- emptyTrashAction->setEnabled(item->icon() == QLatin1String("user-trash-full"));
- menu.addSeparator();
- }
-
- QAction* openInNewTabAction = menu.addAction(QIcon::fromTheme(QStringLiteral("tab-new")), i18nc("@item:inmenu", "Open in New Tab"));
- QAction* openInNewWindowAction = menu.addAction(QIcon::fromTheme(QStringLiteral("window-new")), i18nc("@item:inmenu", "Open in New Window"));
- QAction* propertiesAction = nullptr;
- if (item->url().isLocalFile()) {
- propertiesAction = menu.addAction(QIcon::fromTheme(QStringLiteral("document-properties")), i18nc("@action:inmenu", "Properties"));
- }
- if (!isDevice) {
- menu.addSeparator();
- }
-
- if (isDevice) {
- ejectAction = m_model->ejectAction(index);
- if (ejectAction) {
- ejectAction->setParent(&menu);
- menu.addAction(ejectAction);
- }
+ if (!event->spontaneous() && !model()) {
+ readSettings();
- teardownAction = m_model->teardownAction(index);
- if (teardownAction) {
- // Disable teardown option for root and home partitions
- bool teardownEnabled = item->url() != QUrl::fromLocalFile(QDir::rootPath());
- if (teardownEnabled) {
- KMountPoint::Ptr mountPoint = KMountPoint::currentMountPoints().findByPath(QDir::homePath());
- if (mountPoint && item->url() == QUrl::fromLocalFile(mountPoint->mountPoint())) {
- teardownEnabled = false;
- }
- }
- teardownAction->setEnabled(teardownEnabled);
+ auto *placesModel = DolphinPlacesModelSingleton::instance().placesModel();
+ setModel(placesModel);
- teardownAction->setParent(&menu);
- menu.addAction(teardownAction);
- }
+ connect(placesModel, &KFilePlacesModel::errorMessage, this, &PlacesPanel::errorMessage);
- if (item->storageSetupNeeded()) {
- mountAction = menu.addAction(QIcon::fromTheme(QStringLiteral("media-mount")), i18nc("@action:inmenu", "Mount"));
- }
+ connect(placesModel, &QAbstractItemModel::rowsInserted, this, &PlacesPanel::slotRowsInserted);
+ connect(placesModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &PlacesPanel::slotRowsAboutToBeRemoved);
- if (teardownAction || ejectAction || mountAction) {
- menu.addSeparator();
+ for (int i = 0; i < model()->rowCount(); ++i) {
+ connectDeviceSignals(model()->index(i, 0, QModelIndex()));
}
- }
-
- if (isTrash) {
- configureTrashAction = menu.addAction(QIcon::fromTheme(QStringLiteral("configure")), i18nc("@action:inmenu", "Configure Trash..."));
- }
-
- if (!isDevice) {
- editAction = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-entry")), i18nc("@item:inmenu", "Edit..."));
- }
-
- QAction* removeAction = nullptr;
- if (!isDevice && !item->isSystemItem()) {
- removeAction = menu.addAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@item:inmenu", "Remove"));
- }
-
- QAction* hideAction = menu.addAction(QIcon::fromTheme(QStringLiteral("view-hidden")), i18nc("@item:inmenu", "Hide"));
- hideAction->setCheckable(true);
- hideAction->setChecked(item->isHidden());
-
- buildGroupContextMenu(&menu, index);
-
- QAction* action = menu.exec(pos.toPoint());
- if (action) {
- if (action == emptyTrashAction) {
- Trash::empty(this);
- } else if (action == configureTrashAction) {
- DolphinSettingsDialog* settingsDialog = new DolphinSettingsDialog(item->url(), this);
- settingsDialog->setCurrentPage(settingsDialog->trashSettings);
- settingsDialog->setAttribute(Qt::WA_DeleteOnClose);
- settingsDialog->show();
- } else {
- // The index might have changed if devices were added/removed while
- // the context menu was open.
- index = m_model->index(item);
- if (index < 0) {
- // The item is not in the model any more, probably because it was an
- // external device that has been removed while the context menu was open.
- return;
- }
- if (action == editAction) {
- editEntry(index);
- } else if (action == removeAction) {
- m_model->deleteItem(index);
- } else if (action == hideAction) {
- item->setHidden(hideAction->isChecked());
- if (!m_model->hiddenCount()) {
- showHiddenEntries(false);
- }
- } else if (action == openInNewWindowAction) {
- Dolphin::openNewWindow({KFilePlacesModel::convertedUrl(m_model->data(index).value("url").toUrl())}, this);
- } else if (action == openInNewTabAction) {
- triggerItem(index, TriggerItemModifier::ToNewTab);
- } else if (action == mountAction) {
- m_model->requestStorageSetup(index);
- } else if (action == teardownAction) {
- m_model->requestTearDown(index);
- } else if (action == ejectAction) {
- m_model->requestEject(index);
- } else if (action == propertiesAction) {
- KPropertiesDialog* dialog = new KPropertiesDialog(item->url(), this);
- dialog->setAttribute(Qt::WA_DeleteOnClose);
- dialog->show();
- }
- }
+ setUrl(m_url);
}
- selectItem();
+ KFilePlacesView::showEvent(event);
}
-void PlacesPanel::slotViewContextMenuRequested(const QPointF& pos)
+void PlacesPanel::dragMoveEvent(QDragMoveEvent *event)
{
- QMenu menu(this);
-
- QAction* addAction = menu.addAction(QIcon::fromTheme(QStringLiteral("document-new")), i18nc("@item:inmenu", "Add Entry..."));
-
- QAction* showAllAction = menu.addAction(i18nc("@item:inmenu", "Show Hidden Places"));
- showAllAction->setCheckable(true);
- showAllAction->setChecked(m_model->hiddenItemsShown());
- showAllAction->setIcon(QIcon::fromTheme(m_model->hiddenItemsShown() ? QStringLiteral("view-visible") : QStringLiteral("view-hidden")));
- showAllAction->setEnabled(m_model->hiddenCount());
-
- buildGroupContextMenu(&menu, m_controller->indexCloseToMousePressedPosition());
-
- QMenu* iconSizeSubMenu = new QMenu(i18nc("@item:inmenu", "Icon Size"), &menu);
-
- struct IconSizeInfo
- {
- int size;
- const char* context;
- const char* text;
- };
-
- const int iconSizeCount = 4;
- static const IconSizeInfo iconSizes[iconSizeCount] = {
- {KIconLoader::SizeSmall, I18NC_NOOP("Small icon size", "Small (%1x%2)")},
- {KIconLoader::SizeSmallMedium, I18NC_NOOP("Medium icon size", "Medium (%1x%2)")},
- {KIconLoader::SizeMedium, I18NC_NOOP("Large icon size", "Large (%1x%2)")},
- {KIconLoader::SizeLarge, I18NC_NOOP("Huge icon size", "Huge (%1x%2)")}
- };
-
- QHash<QAction*, int> iconSizeActionMap;
- QActionGroup* iconSizeGroup = new QActionGroup(iconSizeSubMenu);
-
- for (int i = 0; i < iconSizeCount; ++i) {
- const int size = iconSizes[i].size;
- const QString text = i18nc(iconSizes[i].context, iconSizes[i].text,
- size, size);
-
- QAction* action = iconSizeSubMenu->addAction(text);
- iconSizeActionMap.insert(action, size);
- action->setActionGroup(iconSizeGroup);
- action->setCheckable(true);
- action->setChecked(m_view->iconSize() == size);
- }
+ KFilePlacesView::dragMoveEvent(event);
- menu.addMenu(iconSizeSubMenu);
-
- menu.addSeparator();
- const auto actions = customContextMenuActions();
- for (QAction* action : actions) {
- menu.addAction(action);
+ if (!m_dragActivationTimer) {
+ return;
}
- QAction* action = menu.exec(pos.toPoint());
- if (action) {
- if (action == addAction) {
- addEntry();
- } else if (action == showAllAction) {
- showHiddenEntries(showAllAction->isChecked());
- } else if (iconSizeActionMap.contains(action)) {
- m_view->setIconSize(iconSizeActionMap.value(action));
- }
+ const QModelIndex index = indexAt(event->pos());
+ if (!index.isValid()) {
+ return;
}
- selectItem();
-}
-
-QAction *PlacesPanel::buildGroupContextMenu(QMenu *menu, int index)
-{
- if (index == -1) {
- return nullptr;
+ QPersistentModelIndex persistentIndex(index);
+ if (!m_pendingDragActivation.isValid() || m_pendingDragActivation != persistentIndex) {
+ m_pendingDragActivation = persistentIndex;
+ m_dragActivationTimer->start();
}
-
- KFilePlacesModel::GroupType groupType = m_model->groupType(index);
- QAction *hideGroupAction = menu->addAction(QIcon::fromTheme(QStringLiteral("view-hidden")), i18nc("@item:inmenu", "Hide Section '%1'", m_model->item(index)->group()));
- hideGroupAction->setCheckable(true);
- hideGroupAction->setChecked(m_model->isGroupHidden(groupType));
-
- connect(hideGroupAction, &QAction::triggered, this, [this, groupType, hideGroupAction]{
- m_model->setGroupHidden(groupType, hideGroupAction->isChecked());
- if (!m_model->hiddenCount()) {
- showHiddenEntries(false);
- }
- });
-
- return hideGroupAction;
}
-void PlacesPanel::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event)
+void PlacesPanel::dragLeaveEvent(QDragLeaveEvent *event)
{
- if (index < 0) {
- return;
- }
-
- const PlacesItem* destItem = m_model->placesItem(index);
-
- if (destItem->isSearchOrTimelineUrl()) {
- return;
- }
+ KFilePlacesView::dragLeaveEvent(event);
- if (m_model->storageSetupNeeded(index)) {
- connect(m_model, &PlacesItemModel::storageSetupDone,
- this, &PlacesPanel::slotItemDropEventStorageSetupDone);
-
- m_itemDropEventIndex = index;
-
- // Make a full copy of the Mime-Data
- m_itemDropEventMimeData = new QMimeData;
- m_itemDropEventMimeData->setText(event->mimeData()->text());
- m_itemDropEventMimeData->setHtml(event->mimeData()->html());
- m_itemDropEventMimeData->setUrls(event->mimeData()->urls());
- m_itemDropEventMimeData->setImageData(event->mimeData()->imageData());
- m_itemDropEventMimeData->setColorData(event->mimeData()->colorData());
-
- m_itemDropEvent = new QDropEvent(event->pos().toPoint(),
- event->possibleActions(),
- m_itemDropEventMimeData,
- event->buttons(),
- event->modifiers());
-
- m_model->requestStorageSetup(index);
- return;
+ if (m_dragActivationTimer) {
+ m_dragActivationTimer->stop();
+ m_pendingDragActivation = QPersistentModelIndex();
}
-
- QUrl destUrl = destItem->url();
- QDropEvent dropEvent(event->pos().toPoint(),
- event->possibleActions(),
- event->mimeData(),
- event->buttons(),
- event->modifiers());
-
- slotUrlsDropped(destUrl, &dropEvent, this);
}
-void PlacesPanel::slotItemDropEventStorageSetupDone(int index, bool success)
+void PlacesPanel::slotConfigureTrash()
{
- disconnect(m_model, &PlacesItemModel::storageSetupDone,
- this, &PlacesPanel::slotItemDropEventStorageSetupDone);
+ const QUrl url = currentIndex().data(KFilePlacesModel::UrlRole).toUrl();
- if ((index == m_itemDropEventIndex) && m_itemDropEvent && m_itemDropEventMimeData) {
- if (success) {
- QUrl destUrl = m_model->placesItem(index)->url();
- slotUrlsDropped(destUrl, m_itemDropEvent, this);
- }
-
- delete m_itemDropEventMimeData;
- delete m_itemDropEvent;
-
- m_itemDropEventIndex = -1;
- m_itemDropEventMimeData = nullptr;
- m_itemDropEvent = nullptr;
- }
+ DolphinSettingsDialog* settingsDialog = new DolphinSettingsDialog(url, this);
+ settingsDialog->setCurrentPage(settingsDialog->trashSettings);
+ settingsDialog->setAttribute(Qt::WA_DeleteOnClose);
+ settingsDialog->show();
}
-void PlacesPanel::slotAboveItemDropEvent(int index, QGraphicsSceneDragDropEvent* event)
+void PlacesPanel::slotDragActivationTimeout()
{
- m_model->dropMimeDataBefore(index, event->mimeData());
+ if (!m_pendingDragActivation.isValid()) {
+ return;
+ }
+
+ auto *placesModel = static_cast<KFilePlacesModel *>(model());
+ Q_EMIT placeActivated(KFilePlacesModel::convertedUrl(placesModel->url(m_pendingDragActivation)));
}
void PlacesPanel::slotUrlsDropped(const QUrl& dest, QDropEvent* event, QWidget* parent)
}
}
-void PlacesPanel::slotStorageSetupDone(int index, bool success)
+void PlacesPanel::slotContextMenuAboutToShow(const QModelIndex &index, QMenu *menu)
{
- disconnect(m_model, &PlacesItemModel::storageSetupDone,
- this, &PlacesPanel::slotStorageSetupDone);
+ Q_UNUSED(menu);
- if (m_triggerStorageSetupModifier == TriggerItemModifier::None) {
- return;
- }
+ auto *placesModel = static_cast<KFilePlacesModel *>(model());
+ const QUrl url = placesModel->url(index);
+ const Solid::Device device = placesModel->deviceForIndex(index);
- if (success) {
- Q_ASSERT(!m_model->storageSetupNeeded(index));
- triggerItem(index, m_triggerStorageSetupModifier);
- m_triggerStorageSetupModifier = TriggerItemModifier::None;
+ m_configureTrashAction->setVisible(url.scheme() == QLatin1String("trash"));
+
+ // show customContextMenuActions only on the view's context menu
+ if (!url.isValid() && !device.isValid()) {
+ addActions(m_customContextMenuActions);
} else {
- setUrl(m_storageSetupFailedUrl);
- m_storageSetupFailedUrl = QUrl();
+ const auto actions = this->actions();
+ for (QAction *action : actions) {
+ if (m_customContextMenuActions.contains(action)) {
+ removeAction(action);
+ }
+ }
}
}
-void PlacesPanel::slotShowTooltip()
+void PlacesPanel::slotTearDownRequested(const QModelIndex &index)
{
- const QUrl url = m_model->data(m_hoveredIndex.value_or(-1)).value("url").value<QUrl>();
- const QString text = url.toDisplayString(QUrl::PreferLocalFile);
- QToolTip::showText(m_hoverPos, text);
-}
+ auto *placesModel = static_cast<KFilePlacesModel *>(model());
-void PlacesPanel::addEntry()
-{
- const int index = m_controller->selectionManager()->currentItem();
- const QUrl url = m_model->data(index).value("url").toUrl();
- const QString text = url.fileName().isEmpty() ? url.toDisplayString(QUrl::PreferLocalFile) : url.fileName();
-
- QPointer<KFilePlaceEditDialog> dialog = new KFilePlaceEditDialog(true, url, text, QString(), true, false, KIconLoader::SizeMedium, this);
- if (dialog->exec() == QDialog::Accepted) {
- const QString appName = dialog->applicationLocal() ? QCoreApplication::applicationName() : QString();
- m_model->createPlacesItem(dialog->label(), dialog->url(), dialog->icon(), appName);
+ Solid::StorageAccess *storageAccess = placesModel->deviceForIndex(index).as<Solid::StorageAccess>();
+ if (!storageAccess) {
+ return;
}
- delete dialog;
+ m_deviceToTearDown = storageAccess;
+
+ // disconnect the Solid::StorageAccess::teardownRequested
+ // to prevent emitting PlacesPanel::storageTearDownExternallyRequested
+ // after we have emitted PlacesPanel::storageTearDownRequested
+ disconnect(storageAccess, &Solid::StorageAccess::teardownRequested, this, &PlacesPanel::slotTearDownRequestedExternally);
+ Q_EMIT storageTearDownRequested(storageAccess->filePath());
}
-void PlacesPanel::editEntry(int index)
+void PlacesPanel::slotTearDownRequestedExternally(const QString &udi)
{
- QHash<QByteArray, QVariant> data = m_model->data(index);
- const QUrl url = data.value("url").toUrl();
- const QString text = data.value("text").toString();
- const QString iconName = data.value("iconName").toString();
- const bool applicationLocal = !data.value("applicationName").toString().isEmpty();
-
- QPointer<KFilePlaceEditDialog> dialog = new KFilePlaceEditDialog(true, url, text, iconName, true, applicationLocal, KIconLoader::SizeMedium, this);
- if (dialog->exec() == QDialog::Accepted) {
- PlacesItem* oldItem = m_model->placesItem(index);
- if (oldItem) {
- const QString appName = dialog->applicationLocal() ? QCoreApplication::applicationName() : QString();
- oldItem->setApplicationName(appName);
- oldItem->setText(dialog->label());
- oldItem->setUrl(dialog->url());
- oldItem->setIcon(dialog->icon());
- m_model->refresh();
- }
- }
+ Q_UNUSED(udi);
+ auto *storageAccess = static_cast<Solid::StorageAccess*>(sender());
- delete dialog;
+ Q_EMIT storageTearDownExternallyRequested(storageAccess->filePath());
}
-void PlacesPanel::selectItem()
+void PlacesPanel::slotTearDownDone(Solid::ErrorType error, const QVariant& errorData)
{
- const int index = m_model->closestItem(url());
- KItemListSelectionManager* selectionManager = m_controller->selectionManager();
- selectionManager->setCurrentItem(index);
- selectionManager->clearSelection();
-
- const QUrl closestUrl = m_model->url(index);
- if (!closestUrl.path().isEmpty() && url() == closestUrl) {
- selectionManager->setSelected(index);
+ if (error && errorData.isValid()) {
+ if (error == Solid::ErrorType::DeviceBusy) {
+ KListOpenFilesJob* listOpenFilesJob = new KListOpenFilesJob(m_deviceToTearDown->filePath());
+ connect(listOpenFilesJob, &KIO::Job::result, this, [this, listOpenFilesJob](KJob*) {
+ const KProcessList::KProcessInfoList blockingProcesses = listOpenFilesJob->processInfoList();
+ QString errorString;
+ if (blockingProcesses.isEmpty()) {
+ errorString = i18n("One or more files on this device are open within an application.");
+ } else {
+ QStringList blockingApps;
+ for (const auto& process : blockingProcesses) {
+ blockingApps << process.name();
+ }
+ blockingApps.removeDuplicates();
+ errorString = xi18np("One or more files on this device are opened in application <application>\"%2\"</application>.",
+ "One or more files on this device are opened in following applications: <application>%2</application>.",
+ blockingApps.count(), blockingApps.join(i18nc("separator in list of apps blocking device unmount", ", ")));
+ }
+ Q_EMIT errorMessage(errorString);
+ });
+ listOpenFilesJob->start();
+ } else {
+ Q_EMIT errorMessage(errorData.toString());
+ }
+ } else {
+ // No error; it must have been unmounted successfully
+ Q_EMIT storageTearDownSuccessful();
}
+ disconnect(m_deviceToTearDown, &Solid::StorageAccess::teardownDone,
+ this, &PlacesPanel::slotTearDownDone);
+ m_deviceToTearDown = nullptr;
}
-void PlacesPanel::triggerItem(int index, TriggerItemModifier modifier)
+void PlacesPanel::slotRowsInserted(const QModelIndex &parent, int first, int last)
{
- const PlacesItem* item = m_model->placesItem(index);
- if (!item) {
- return;
+ for (int i = first; i <= last; ++i) {
+ connectDeviceSignals(model()->index(first, 0, parent));
}
+}
- if (m_model->storageSetupNeeded(index)) {
- m_triggerStorageSetupModifier = modifier;
- m_storageSetupFailedUrl = url();
+void PlacesPanel::slotRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
+{
+ auto *placesModel = static_cast<KFilePlacesModel *>(model());
- connect(m_model, &PlacesItemModel::storageSetupDone,
- this, &PlacesPanel::slotStorageSetupDone);
+ for (int i = first; i <= last; ++i) {
+ const QModelIndex index = placesModel->index(i, 0, parent);
- m_model->requestStorageSetup(index);
- } else {
- m_triggerStorageSetupModifier = TriggerItemModifier::None;
-
- const QUrl url = m_model->data(index).value("url").toUrl();
- if (!url.isEmpty()) {
- switch (modifier) {
- case TriggerItemModifier::ToNewTab:
- Q_EMIT placeActivatedInNewTab(KFilePlacesModel::convertedUrl(url));
- break;
- case TriggerItemModifier::ToNewActiveTab:
- Q_EMIT placeActivatedInNewActiveTab(KFilePlacesModel::convertedUrl(url));
- break;
- case TriggerItemModifier::ToNewWindow:
- Dolphin::openNewWindow({KFilePlacesModel::convertedUrl(url)}, this);
- break;
- case TriggerItemModifier::None:
- Q_EMIT placeActivated(KFilePlacesModel::convertedUrl(url));
- break;
- }
+ Solid::StorageAccess *storageAccess = placesModel->deviceForIndex(index).as<Solid::StorageAccess>();
+ if (!storageAccess) {
+ continue;
}
+
+ disconnect(storageAccess, &Solid::StorageAccess::teardownRequested, this, nullptr);
}
}
-void PlacesPanel::showHiddenEntries(bool shown)
+void PlacesPanel::connectDeviceSignals(const QModelIndex &index)
{
- m_model->setHiddenItemsShown(shown);
- Q_EMIT showHiddenEntriesChanged(shown);
-}
+ auto *placesModel = static_cast<KFilePlacesModel *>(model());
-int PlacesPanel::hiddenListCount()
-{
- if(!m_model) {
- return 0;
+ Solid::StorageAccess *storageAccess = placesModel->deviceForIndex(index).as<Solid::StorageAccess>();
+ if (!storageAccess) {
+ return;
}
- return m_model->hiddenCount();
+
+ connect(storageAccess, &Solid::StorageAccess::teardownRequested, this, &PlacesPanel::slotTearDownRequestedExternally);
}
/*
* SPDX-FileCopyrightText: 2008-2012 Peter Penz <peter.penz19@gmail.com>
* SPDX-FileCopyrightText: 2010 Christian Muehlhaeuser <muesli@gmail.com>
+ * SPDX-FileCopyrightText: 2021 Kai Uwe Broulik <kde@broulik.de>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef PLACESPANEL_H
#define PLACESPANEL_H
-#include <optional>
-
#include "panels/panel.h"
#include <QUrl>
-#include <QTimer>
-
-class KItemListController;
-class PlacesItemModel;
-class PlacesView;
-class QGraphicsSceneDragDropEvent;
-class QMenu;
-class QMimeData;
+#include <KFilePlacesView>
+
+#include <Solid/SolidNamespace> // Solid::ErrorType
+
+class QTimer;
+namespace Solid
+{
+class StorageAccess;
+}
+
/**
* @brief Combines bookmarks and mounted devices as list.
*/
-class PlacesPanel : public Panel
+class PlacesPanel : public KFilePlacesView
{
Q_OBJECT
public:
explicit PlacesPanel(QWidget* parent);
~PlacesPanel() override;
+
+ void setUrl(const QUrl &url); // override
+
+ // for compatibility with Panel, actions that are shown
+ // on the view's context menu
+ QList<QAction*> customContextMenuActions() const;
+ void setCustomContextMenuActions(const QList<QAction*>& actions);
+
+ void requestTearDown();
void proceedWithTearDown();
- bool eventFilter(QObject *obj, QEvent *event) override;
+public Q_SLOTS:
+ void readSettings();
Q_SIGNALS:
- void placeActivated(const QUrl& url);
- void placeActivatedInNewTab(const QUrl &url);
- void placeActivatedInNewActiveTab(const QUrl &url);
void errorMessage(const QString& error);
void storageTearDownRequested(const QString& mountPath);
void storageTearDownExternallyRequested(const QString& mountPath);
- void showHiddenEntriesChanged(bool shown);
void storageTearDownSuccessful();
protected:
- bool urlChanged() override;
void showEvent(QShowEvent* event) override;
-
-public Q_SLOTS:
- void readSettings() override;
- void showHiddenEntries(bool shown);
- int hiddenListCount();
+ void dragMoveEvent(QDragMoveEvent *event) override;
+ void dragLeaveEvent(QDragLeaveEvent *event) override;
private Q_SLOTS:
- void slotItemActivated(int index);
- void slotItemMiddleClicked(int index);
- void slotItemContextMenuRequested(int index, const QPointF& pos);
- void slotViewContextMenuRequested(const QPointF& pos);
- void slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event);
- void slotItemDropEventStorageSetupDone(int index, bool success);
- void slotAboveItemDropEvent(int index, QGraphicsSceneDragDropEvent* event);
+ void slotConfigureTrash();
+ void slotDragActivationTimeout();
void slotUrlsDropped(const QUrl& dest, QDropEvent* event, QWidget* parent);
- void slotStorageSetupDone(int index, bool success);
- void slotShowTooltip();
+ void slotContextMenuAboutToShow(const QModelIndex &index, QMenu *menu);
+ void slotTearDownRequested(const QModelIndex &index);
+ void slotTearDownRequestedExternally(const QString &udi);
+ void slotTearDownDone(Solid::ErrorType error, const QVariant& errorData);
+ void slotRowsInserted(const QModelIndex &parent, int first, int last);
+ void slotRowsAboutToBeRemoved(const QModelIndex &parent, int first, int last);
private:
- enum class TriggerItemModifier { None, ToNewTab, ToNewActiveTab, ToNewWindow };
+ void connectDeviceSignals(const QModelIndex &idx);
-private:
- void addEntry();
- void editEntry(int index);
-
- /**
- * Selects the item that matches the URL set
- * for the panel (see Panel::setUrl()).
- */
- void selectItem();
+ QUrl m_url; // only used for initial setUrl
+ QList<QAction*> m_customContextMenuActions;
- void triggerItem(int index, TriggerItemModifier modifier);
+ QTimer *m_dragActivationTimer = nullptr;
+ QPersistentModelIndex m_pendingDragActivation;
- QAction* buildGroupContextMenu(QMenu* menu, int index);
+ Solid::StorageAccess *m_deviceToTearDown = nullptr;
-private:
- KItemListController* m_controller;
- PlacesItemModel* m_model;
- PlacesView* m_view;
-
- QUrl m_storageSetupFailedUrl;
- TriggerItemModifier m_triggerStorageSetupModifier;
-
- int m_itemDropEventIndex;
- QMimeData* m_itemDropEventMimeData;
- QDropEvent* m_itemDropEvent;
- QTimer m_tooltipTimer;
- std::optional<int> m_hoveredIndex;
- QPoint m_hoverPos;
+ QAction *m_configureTrashAction;
+ QAction *m_lockPanelsAction;
};
#endif // PLACESPANEL_H
+++ /dev/null
-/*
- * SPDX-FileCopyrightText: 2012 Frank Reininghaus <frank78ac@googlemail.com>
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#include "placesview.h"
-
-#include "dolphin_placespanelsettings.h"
-#include "kitemviews/kitemlistheader.h"
-
-#include <QGraphicsSceneResizeEvent>
-
-PlacesView::PlacesView(QGraphicsWidget* parent) :
- KStandardItemListView(parent)
-{
- header()->setAutomaticColumnResizing(false);
-
- const int iconSize = PlacesPanelSettings::iconSize();
- if (iconSize >= 0) {
- setIconSize(iconSize);
- }
-}
-
-void PlacesView::setIconSize(int size)
-{
- if (size != iconSize()) {
- PlacesPanelSettings* settings = PlacesPanelSettings::self();
- settings->setIconSize(size);
- settings->save();
-
- KItemListStyleOption option = styleOption();
- option.iconSize = size;
- setStyleOption(option);
- }
-}
-
-int PlacesView::iconSize() const
-{
- const KItemListStyleOption option = styleOption();
- return option.iconSize;
-}
-
-void PlacesView::resizeEvent(QGraphicsSceneResizeEvent *event)
-{
- KStandardItemListView::resizeEvent(event);
-
- header()->setColumnWidth(QByteArrayLiteral("text"), event->newSize().width());
-}
+++ /dev/null
-/*
- * SPDX-FileCopyrightText: 2012 Frank Reininghaus <frank78ac@googlemail.com>
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#ifndef PLACESVIEW_H
-#define PLACESVIEW_H
-
-#include "kitemviews/kstandarditemlistview.h"
-
-/**
- * @brief View class for the Places Panel.
- *
- * Reads the icon size from GeneralSettings::placesPanelIconSize().
- */
-class PlacesView : public KStandardItemListView
-{
- Q_OBJECT
-
-public:
- explicit PlacesView(QGraphicsWidget* parent = nullptr);
-
- void setIconSize(int size);
- int iconSize() const;
-
-protected:
- void resizeEvent(QGraphicsSceneResizeEvent *event) override;
-};
-
-#endif
#include "dolphin_searchsettings.h"
#include "dolphinfacetswidget.h"
+#include "dolphinplacesmodelsingleton.h"
#include "dolphinquery.h"
-#include "panels/places/placesitemmodel.h"
#include <KLocalizedString>
#include <KNS3/KMoreToolsMenuFactory>
{
const QUrl searchURL = urlForSearching();
if (searchURL.isValid()) {
- PlacesItemModel model;
const QString label = i18n("Search for %1 in %2", text(), searchPath().fileName());
- model.createPlacesItem(label,
- searchURL,
- QStringLiteral("folder-saved-search-symbolic"));
+ DolphinPlacesModelSingleton::instance().placesModel()->addPlace(label, searchURL, QStringLiteral("folder-saved-search-symbolic"));
}
}
# DragAndDropHelperTest
ecm_add_test(draganddrophelpertest.cpp LINK_LIBRARIES dolphinprivate Qt5::Test)
-# PlacesItemModelTest
-ecm_add_test(placesitemmodeltest.cpp
-TEST_NAME placesitemmodeltest
-LINK_LIBRARIES dolphinprivate dolphinstatic Qt5::Test)
-
find_gem(test-unit)
set_package_properties(Gem:test-unit PROPERTIES
TYPE RECOMMENDED
+++ /dev/null
-/*
- * SPDX-FileCopyrightText: 2017 Renato Araujo Oliveira <renato.araujo@kdab.com>
- *
- * SPDX-License-Identifier: GPL-2.0-or-later
- */
-
-#include <QTest>
-#include <QSignalSpy>
-#include <QStandardPaths>
-#include <QAction>
-#include <QDBusInterface>
-
-#include <KBookmarkManager>
-#include <KConfig>
-#include <KConfigGroup>
-#include <KAboutData>
-#include <KFilePlacesModel>
-#include <KProtocolInfo>
-
-#include "dolphin_generalsettings.h"
-#include "panels/places/placesitemmodel.h"
-#include "panels/places/placesitem.h"
-#include "views/viewproperties.h"
-
-Q_DECLARE_METATYPE(KItemRangeList)
-Q_DECLARE_METATYPE(KItemRange)
-
-static QString bookmarksFile()
-{
- return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/user-places.xbel";
-}
-
-class PlacesItemModelTest : public QObject
-{
- Q_OBJECT
-
-private Q_SLOTS:
- void init();
- void cleanup();
-
- void initTestCase();
- void cleanupTestCase();
-
- void testModelSort();
- void testGroups();
- void testDeletePlace();
- void testPlaceItem_data();
- void testPlaceItem();
- void testTearDownDevice();
- void testDefaultViewProperties_data();
- void testDefaultViewProperties();
- void testClear();
- void testHideItem();
- void testSystemItems();
- void testEditBookmark();
- void testEditAfterCreation();
- void testEditMetadata();
- void testRefresh();
- void testIcons_data();
- void testIcons();
- void testDragAndDrop();
- void testHideDevices();
- void testDuplicatedEntries();
- void renameAfterCreation();
-
-private:
- PlacesItemModel* m_model;
- QSet<int> m_tobeRemoved;
- QMap<QString, QDBusInterface *> m_interfacesMap;
- int m_expectedModelCount = 14;
- bool m_hasDesktopFolder = false;
- bool m_hasDocumentsFolder = false;
- bool m_hasDownloadsFolder = false;
- bool m_hasMusicFolder = false;
- bool m_hasPicturesFolder = false;
- bool m_hasVideosFolder = false;
-
- void setBalooEnabled(bool enabled);
- int indexOf(const QUrl &url);
- QDBusInterface *fakeManager();
- QDBusInterface *fakeDevice(const QString &udi);
- QStringList placesUrls(PlacesItemModel *model = nullptr) const;
- QStringList initialUrls() const;
- void createPlaceItem(const QString &text, const QUrl &url, const QString &icon);
- void schedulePlaceRemoval(int index);
- void cancelPlaceRemoval(int index);
- QMimeData *createMimeData(const QList<int> &indexes) const;
- void increaseIndexIfNeeded(int &index) const;
- QTemporaryDir m_tempHomeDir;
-};
-
-#define CHECK_PLACES_URLS(urls) \
- { \
- QStringList places = placesUrls(); \
- if (places != urls) { \
- qWarning() << "Expected:" << urls; \
- qWarning() << "Got:" << places; \
- QCOMPARE(places, urls); \
- } \
- }
-
-void PlacesItemModelTest::setBalooEnabled(bool enabled)
-{
- KConfig config(QStringLiteral("baloofilerc"));
- KConfigGroup basicSettings = config.group("Basic Settings");
- basicSettings.writeEntry("Indexing-Enabled", enabled);
- config.sync();
-}
-
-int PlacesItemModelTest::indexOf(const QUrl &url)
-{
- for (int r = 0; r < m_model->count(); r++) {
- if (m_model->placesItem(r)->url() == url) {
- return r;
- }
- }
- return -1;
-}
-
-QDBusInterface *PlacesItemModelTest::fakeManager()
-{
- return fakeDevice(QStringLiteral("/org/kde/solid/fakehw"));
-}
-
-QDBusInterface *PlacesItemModelTest::fakeDevice(const QString &udi)
-{
- if (m_interfacesMap.contains(udi)) {
- return m_interfacesMap[udi];
- }
-
- QDBusInterface *iface = new QDBusInterface(QDBusConnection::sessionBus().baseService(), udi);
- m_interfacesMap[udi] = iface;
-
- return iface;
-}
-
-QStringList PlacesItemModelTest::placesUrls(PlacesItemModel *model) const
-{
- QStringList urls;
- if (!model) {
- model = m_model;
- }
-
- for (int row = 0; row < model->count(); ++row) {
- urls << model->placesItem(row)->url().toDisplayString(QUrl::PreferLocalFile);
- }
- return urls;
-}
-
-QStringList PlacesItemModelTest::initialUrls() const
-{
- static QStringList urls;
- if (urls.isEmpty()) {
- urls << QDir::homePath();
-
- if (m_hasDesktopFolder) {
- urls << QDir::homePath() + QStringLiteral("/Desktop");
- }
-
- if (m_hasDocumentsFolder) {
- urls << QDir::homePath() + QStringLiteral("/Documents");
- }
-
- if (m_hasDownloadsFolder) {
- urls << QDir::homePath() + QStringLiteral("/Downloads");
- }
-
- if (m_hasMusicFolder) {
- urls << QDir::homePath() + QStringLiteral("/Music");
- }
-
- if (m_hasPicturesFolder) {
- urls << QDir::homePath() + QStringLiteral("/Pictures");
- }
-
- if (m_hasVideosFolder) {
- urls << QDir::homePath() + QStringLiteral("/Videos");
- }
-
- urls << QStringLiteral("trash:/")
- << QStringLiteral("remote:/")
- << QStringLiteral("/media/nfs");
-
- if (qEnvironmentVariableIsSet("KDE_FULL_SESSION") && KProtocolInfo::isKnownProtocol(QStringLiteral("recentlyused"))) {
- urls << QStringLiteral("recentlyused:/files");
- urls << QStringLiteral("recentlyused:/locations");
- } else {
- urls << QStringLiteral("timeline:/today")
- << QStringLiteral("timeline:/yesterday");
- }
-
- urls << QStringLiteral("search:/documents") << QStringLiteral("search:/images") << QStringLiteral("search:/audio") << QStringLiteral("search:/videos")
- << QStringLiteral("/foreign")
- << QStringLiteral("/media/floppy0") << QStringLiteral("/media/XO-Y4") << QStringLiteral("/media/cdrom");
- }
- return urls;
-}
-
-void PlacesItemModelTest::createPlaceItem(const QString &text, const QUrl &url, const QString &icon)
-{
- m_model->createPlacesItem(text, url, icon);
-}
-
-void PlacesItemModelTest::schedulePlaceRemoval(int index)
-{
- m_tobeRemoved.insert(index);
-}
-
-void PlacesItemModelTest::cancelPlaceRemoval(int index)
-{
- m_tobeRemoved.remove(index);
-}
-
-QMimeData *PlacesItemModelTest::createMimeData(const QList<int> &indexes) const
-{
- QByteArray itemData;
- QDataStream stream(&itemData, QIODevice::WriteOnly);
- QList<QUrl> urls;
-
- for (int index : indexes) {
- const QUrl itemUrl = m_model->placesItem(index)->url();
- if (itemUrl.isValid()) {
- urls << itemUrl;
- }
- stream << index;
- }
-
- QMimeData* mimeData = new QMimeData();
- mimeData->setUrls(urls);
- // copied from PlacesItemModel::internalMimeType()
- const QString internalMimeType = "application/x-dolphinplacesmodel-" +
- QString::number((qptrdiff)m_model);
- mimeData->setData(internalMimeType, itemData);
- return mimeData;
-}
-
-void PlacesItemModelTest::increaseIndexIfNeeded(int &index) const
-{
- if (m_hasDesktopFolder) {
- index++;
- }
- if (m_hasDocumentsFolder) {
- index++;
- }
- if (m_hasDownloadsFolder) {
- index++;
- }
- if (m_hasMusicFolder) {
- index++;
- }
- if (m_hasPicturesFolder) {
- index++;
- }
- if (m_hasVideosFolder) {
- index++;
- }
-}
-
-void PlacesItemModelTest::init()
-{
- m_model = new PlacesItemModel();
- // WORKAROUND: need to wait for bookmark to load
- QTest::qWait(300);
- QCOMPARE(m_model->count(), m_expectedModelCount);
-}
-
-void PlacesItemModelTest::cleanup()
-{
- const auto tobeRemoved = m_tobeRemoved;
- for (const int i : tobeRemoved) {
- int before = m_model->count();
- m_model->deleteItem(i);
- QTRY_COMPARE(m_model->count(), before - 1);
- }
- m_tobeRemoved.clear();
- delete m_model;
- m_model = nullptr;
-}
-
-void PlacesItemModelTest::initTestCase()
-{
- QVERIFY(m_tempHomeDir.isValid());
- QVERIFY(qputenv("HOME", m_tempHomeDir.path().toUtf8()));
- QVERIFY(qputenv("KDE_FORK_SLAVES", "yes"));
-
- QStandardPaths::setTestModeEnabled(true);
-
- const QString fakeHw = QFINDTESTDATA("data/fakecomputer.xml");
- QVERIFY(!fakeHw.isEmpty());
- qputenv("SOLID_FAKEHW", QFile::encodeName(fakeHw));
-
- setBalooEnabled(true);
- const QString bookmarsFileName = bookmarksFile();
- if (QFileInfo::exists(bookmarsFileName)) {
- // Ensure we'll have a clean bookmark file to start
- QVERIFY(QFile::remove(bookmarsFileName));
- }
-
- if (QDir(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).exists()) {
- m_hasDesktopFolder = true;
- m_expectedModelCount++;
- }
-
- if (QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).exists()) {
- m_hasDocumentsFolder = true;
- m_expectedModelCount++;
- }
-
- if (QDir(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)).exists()) {
- m_hasDownloadsFolder = true;
- m_expectedModelCount++;
- }
-
- if (QDir(QStandardPaths::writableLocation(QStandardPaths::MusicLocation)).exists()) {
- m_hasMusicFolder = true;
- m_expectedModelCount++;
- }
-
- if (QDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)).exists()) {
- m_hasPicturesFolder = true;
- m_expectedModelCount++;
- }
-
- if (QDir(QStandardPaths::writableLocation(QStandardPaths::MoviesLocation)).exists()) {
- m_hasVideosFolder = true;
- m_expectedModelCount++;
- }
-
- qRegisterMetaType<KItemRangeList>();
- qRegisterMetaType<KItemRange>();
-}
-
-void PlacesItemModelTest::cleanupTestCase()
-{
- qDeleteAll(m_interfacesMap);
- QFile::remove(bookmarksFile());
-}
-
-void PlacesItemModelTest::testModelSort()
-{
- CHECK_PLACES_URLS(initialUrls());
-}
-
-void PlacesItemModelTest::testGroups()
-{
- const auto groups = m_model->groups();
- int expectedRemoteIndex = 2;
- increaseIndexIfNeeded(expectedRemoteIndex);
-
- QCOMPARE(groups.size(), 6);
-
- QCOMPARE(groups.at(0).first, 0);
- QCOMPARE(groups.at(0).second.toString(), QStringLiteral("Places"));
-
- QCOMPARE(groups.at(1).first, expectedRemoteIndex);
- QCOMPARE(groups.at(1).second.toString(), QStringLiteral("Remote"));
-
- QCOMPARE(groups.at(2).first, expectedRemoteIndex + 2);
- QCOMPARE(groups.at(2).second.toString(), QStringLiteral("Recent"));
-
- QCOMPARE(groups.at(3).first, expectedRemoteIndex + 4);
- QCOMPARE(groups.at(3).second.toString(), QStringLiteral("Search For"));
-
- QCOMPARE(groups.at(4).first, expectedRemoteIndex + 8);
- QCOMPARE(groups.at(4).second.toString(), QStringLiteral("Devices"));
-
- QCOMPARE(groups.at(5).first, expectedRemoteIndex + 9);
- QCOMPARE(groups.at(5).second.toString(), QStringLiteral("Removable Devices"));
-}
-
-void PlacesItemModelTest::testPlaceItem_data()
-{
- QTest::addColumn<QUrl>("url");
- QTest::addColumn<bool>("expectedIsHidden");
- QTest::addColumn<bool>("expectedIsSystemItem");
- QTest::addColumn<QString>("expectedGroup");
- QTest::addColumn<bool>("expectedStorageSetupNeeded");
-
- // places
- QTest::newRow("Places - Home") << QUrl::fromLocalFile(QDir::homePath()) << false << true << QStringLiteral("Places") << false;
-
- // baloo -search
- QTest::newRow("Baloo - Documents") << QUrl("search:/documents") << false << true << QStringLiteral("Search For") << false;
-
- // devices
- QTest::newRow("Devices - Floppy") << QUrl("file:///media/floppy0") << false << false << QStringLiteral("Removable Devices") << false;
-}
-
-void PlacesItemModelTest::testPlaceItem()
-{
- QFETCH(QUrl, url);
- QFETCH(bool, expectedIsHidden);
- QFETCH(bool, expectedIsSystemItem);
- QFETCH(QString, expectedGroup);
- QFETCH(bool, expectedStorageSetupNeeded);
-
- const int index = indexOf(url);
- PlacesItem *item = m_model->placesItem(index);
- QCOMPARE(item->url(), url);
- QCOMPARE(item->isHidden(), expectedIsHidden);
- QCOMPARE(item->isSystemItem(), expectedIsSystemItem);
- QCOMPARE(item->group(), expectedGroup);
- QCOMPARE(item->storageSetupNeeded(), expectedStorageSetupNeeded);
-}
-
-void PlacesItemModelTest::testDeletePlace()
-{
- const QUrl tempUrl = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
- QStringList urls = initialUrls();
- QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
- QSignalSpy itemsRemovedSpy(m_model, &PlacesItemModel::itemsRemoved);
-
- PlacesItemModel *model = new PlacesItemModel();
-
- int tempDirIndex = 2;
- increaseIndexIfNeeded(tempDirIndex);
-
- // create a new place
- createPlaceItem(QStringLiteral("Temporary Dir"), tempUrl, QString());
- urls.insert(tempDirIndex, tempUrl.toLocalFile());
-
- // check if the new entry was created
- QTRY_COMPARE(itemsInsertedSpy.count(), 1);
- CHECK_PLACES_URLS(urls);
- QTRY_COMPARE(model->count(), m_model->count());
-
- // delete item
- m_model->deleteItem(tempDirIndex);
-
- // make sure that the new item is removed
- QTRY_COMPARE(itemsRemovedSpy.count(), 1);
- QTRY_COMPARE(m_model->count(), m_expectedModelCount);
- CHECK_PLACES_URLS(initialUrls());
- QTRY_COMPARE(model->count(), m_model->count());
-}
-
-void PlacesItemModelTest::testTearDownDevice()
-{
- const QUrl mediaUrl = QUrl::fromLocalFile(QStringLiteral("/media/XO-Y4"));
- int index = indexOf(mediaUrl);
- QVERIFY(index != -1);
-
- auto ejectAction = m_model->ejectAction(index);
- QVERIFY(!ejectAction);
-
- auto teardownAction = m_model->teardownAction(index);
- QVERIFY(teardownAction);
-
- QCOMPARE(m_model->count(), m_expectedModelCount);
-
- QSignalSpy spyItemsRemoved(m_model, &PlacesItemModel::itemsRemoved);
- fakeManager()->call(QStringLiteral("unplug"), "/org/kde/solid/fakehw/volume_part1_size_993284096");
- QTRY_COMPARE(m_model->count(), m_expectedModelCount - 1);
- QCOMPARE(spyItemsRemoved.count(), 1);
- const QList<QVariant> spyItemsRemovedArgs = spyItemsRemoved.takeFirst();
- const KItemRangeList removedRange = spyItemsRemovedArgs.at(0).value<KItemRangeList>();
- QCOMPARE(removedRange.size(), 1);
- QCOMPARE(removedRange.first().index, index);
- QCOMPARE(removedRange.first().count, 1);
-
- QCOMPARE(indexOf(mediaUrl), -1);
-
- QSignalSpy spyItemsInserted(m_model, &PlacesItemModel::itemsInserted);
- fakeManager()->call(QStringLiteral("plug"), "/org/kde/solid/fakehw/volume_part1_size_993284096");
- QTRY_COMPARE(m_model->count(), m_expectedModelCount);
- QCOMPARE(spyItemsInserted.count(), 1);
- index = indexOf(mediaUrl);
-
- const QList<QVariant> args = spyItemsInserted.takeFirst();
- const KItemRangeList insertedRange = args.at(0).value<KItemRangeList>();
- QCOMPARE(insertedRange.size(), 1);
- QCOMPARE(insertedRange.first().index, index);
- QCOMPARE(insertedRange.first().count, 1);
-}
-
-void PlacesItemModelTest::testDefaultViewProperties_data()
-{
- QTest::addColumn<QUrl>("url");
- QTest::addColumn<DolphinView::Mode>("expectedViewMode");
- QTest::addColumn<bool>("expectedPreviewShow");
- QTest::addColumn<QList<QByteArray> >("expectedVisibleRole");
-
- // places
- QTest::newRow("Places - Home") << QUrl::fromLocalFile(QDir::homePath()) << DolphinView::IconsView << true << QList<QByteArray>({"text"});
-
- // baloo -search
- QTest::newRow("Baloo - Documents") << QUrl("search:/documents") << DolphinView::DetailsView << false << QList<QByteArray>({"text", "path"});
-
- // audio files
- QTest::newRow("Places - Audio") << QUrl("search:/audio") << DolphinView::DetailsView << false << QList<QByteArray>({"text", "artist", "album"});
-
- // devices
- QTest::newRow("Devices - Floppy") << QUrl("file:///media/floppy0") << DolphinView::IconsView << true << QList<QByteArray>({"text"});
-
-}
-
-void PlacesItemModelTest::testDefaultViewProperties()
-{
- QFETCH(QUrl, url);
- QFETCH(DolphinView::Mode, expectedViewMode);
- QFETCH(bool, expectedPreviewShow);
- QFETCH(QList<QByteArray>, expectedVisibleRole);
-
- // In order to test the default view properties, turn off the global view properties and re-init the test to reload the model.
- GeneralSettings* settings = GeneralSettings::self();
- settings->setGlobalViewProps(false);
- settings->save();
- cleanup();
- init();
-
- ViewProperties properties(KFilePlacesModel::convertedUrl(url));
- QCOMPARE(properties.viewMode(), expectedViewMode);
- QCOMPARE(properties.previewsShown(), expectedPreviewShow);
- QCOMPARE(properties.visibleRoles(), expectedVisibleRole);
-
- settings->setGlobalViewProps(true);
- settings->save();
-}
-
-void PlacesItemModelTest::testClear()
-{
- QCOMPARE(m_model->count(), m_expectedModelCount);
- m_model->clear();
- QCOMPARE(m_model->count(), 0);
- QCOMPARE(m_model->hiddenCount(), 0);
- m_model->refresh();
- QTRY_COMPARE(m_model->count(), m_expectedModelCount);
-}
-
-void PlacesItemModelTest::testHideItem()
-{
- const QUrl mediaUrl = QUrl::fromLocalFile(QStringLiteral("/media/XO-Y4"));
- const int index = indexOf(mediaUrl);
-
- PlacesItem *item = m_model->placesItem(index);
-
- QSignalSpy spyItemsRemoved(m_model, &PlacesItemModel::itemsRemoved);
- QList<QVariant> spyItemsRemovedArgs;
- KItemRangeList removedRange;
-
- QSignalSpy spyItemsInserted(m_model, &PlacesItemModel::itemsInserted);
- QList<QVariant> spyItemsInsertedArgs;
- KItemRangeList insertedRange;
- QVERIFY(item);
-
- // hide an item
- item->setHidden(true);
-
- // check if items removed was fired
- QTRY_COMPARE(m_model->count(), m_expectedModelCount - 1);
- QCOMPARE(spyItemsRemoved.count(), 1);
- spyItemsRemovedArgs = spyItemsRemoved.takeFirst();
- removedRange = spyItemsRemovedArgs.at(0).value<KItemRangeList>();
- QCOMPARE(removedRange.size(), 1);
- QCOMPARE(removedRange.first().index, index);
- QCOMPARE(removedRange.first().count, 1);
-
- // allow model to show hidden items
- m_model->setHiddenItemsShown(true);
-
- // check if the items inserted was fired
- spyItemsInsertedArgs = spyItemsInserted.takeFirst();
- insertedRange = spyItemsInsertedArgs.at(0).value<KItemRangeList>();
- QCOMPARE(insertedRange.size(), 1);
- QCOMPARE(insertedRange.first().index, index);
- QCOMPARE(insertedRange.first().count, 1);
-
- // mark item as visible
- item = m_model->placesItem(index);
- item->setHidden(false);
-
- // mark model to hide invisible items
- m_model->setHiddenItemsShown(true);
-
- QTRY_COMPARE(m_model->count(), m_expectedModelCount);
-}
-
-void PlacesItemModelTest::testSystemItems()
-{
- int tempDirIndex = 2;
- increaseIndexIfNeeded(tempDirIndex);
-
- QCOMPARE(m_model->count(), m_expectedModelCount);
- for (int r = 0; r < m_model->count(); r++) {
- QCOMPARE(m_model->placesItem(r)->isSystemItem(), !m_model->placesItem(r)->device().isValid());
- }
-
- QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
-
- // create a new entry (non system item)
- createPlaceItem(QStringLiteral("Temporary Dir"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation)), QString());
-
- // check if the new entry was created
- QTRY_COMPARE(itemsInsertedSpy.count(), 1);
-
- // make sure the new place get removed
- schedulePlaceRemoval(tempDirIndex);
-
- QList<QVariant> args = itemsInsertedSpy.takeFirst();
- KItemRangeList range = args.at(0).value<KItemRangeList>();
- QCOMPARE(range.first().index, tempDirIndex);
- QCOMPARE(range.first().count, 1);
- QVERIFY(!m_model->placesItem(tempDirIndex)->isSystemItem());
- QCOMPARE(m_model->count(), m_expectedModelCount + 1);
-
- QTest::qWait(300);
- // check if the removal signal is correct
- QSignalSpy itemsRemovedSpy(m_model, &PlacesItemModel::itemsRemoved);
- m_model->deleteItem(tempDirIndex);
- QTRY_COMPARE(itemsRemovedSpy.count(), 1);
- args = itemsRemovedSpy.takeFirst();
- range = args.at(0).value<KItemRangeList>();
- QCOMPARE(range.first().index, tempDirIndex);
- QCOMPARE(range.first().count, 1);
- QTRY_COMPARE(m_model->count(), m_expectedModelCount);
-
- //cancel removal (it was removed above)
- cancelPlaceRemoval(tempDirIndex);
-}
-
-void PlacesItemModelTest::testEditBookmark()
-{
- int tempDirIndex = 2;
- increaseIndexIfNeeded(tempDirIndex);
-
- QScopedPointer<PlacesItemModel> other(new PlacesItemModel());
-
- createPlaceItem(QStringLiteral("Temporary Dir"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation)), QString());
-
- // make sure that the new item will be removed later
- schedulePlaceRemoval(tempDirIndex);
-
- QSignalSpy itemsChangedSply(m_model, &PlacesItemModel::itemsChanged);
-
- // modify place text
- m_model->item(tempDirIndex)->setText(QStringLiteral("Renamed place"));
- m_model->refresh();
-
- // check if the correct signal was fired
- QTRY_COMPARE(itemsChangedSply.count(), 1);
- QList<QVariant> args = itemsChangedSply.takeFirst();
- KItemRangeList range = args.at(0).value<KItemRangeList>();
- QCOMPARE(range.first().index, tempDirIndex);
- QCOMPARE(range.first().count, 1);
- QSet<QByteArray> roles = args.at(1).value<QSet<QByteArray> >();
- QCOMPARE(roles.size(), 1);
- QCOMPARE(*roles.begin(), QByteArrayLiteral("text"));
- QCOMPARE(m_model->item(tempDirIndex)->text(), QStringLiteral("Renamed place"));
-
- // check if the item was updated in the other model
- QTRY_COMPARE(other->item(tempDirIndex)->text(), QStringLiteral("Renamed place"));
-}
-
-void PlacesItemModelTest::testEditAfterCreation()
-{
- int tempDirIndex = 2;
- increaseIndexIfNeeded(tempDirIndex);
-
- const QUrl tempUrl = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
- QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
-
- // create a new place
- createPlaceItem(QStringLiteral("Temporary Dir"), tempUrl, QString());
- QTRY_COMPARE(itemsInsertedSpy.count(), 1);
-
- PlacesItemModel *model = new PlacesItemModel();
- QTRY_COMPARE(model->count(), m_model->count());
-
- // make sure that the new item will be removed later
- schedulePlaceRemoval(tempDirIndex);
-
- // modify place text
- PlacesItem *item = m_model->placesItem(tempDirIndex);
- item->setText(QStringLiteral("Renamed place"));
- m_model->refresh();
-
- // check if the second model got the changes
- QTRY_COMPARE(model->count(), m_model->count());
- QTRY_COMPARE(model->placesItem(tempDirIndex)->text(), m_model->placesItem(tempDirIndex)->text());
- QTRY_COMPARE(model->placesItem(tempDirIndex)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")),
- m_model->placesItem(tempDirIndex)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")));
- QTRY_COMPARE(model->placesItem(tempDirIndex)->icon(), m_model->placesItem(tempDirIndex)->icon());
- QTRY_COMPARE(model->placesItem(tempDirIndex)->url(), m_model->placesItem(tempDirIndex)->url());
-}
-
-void PlacesItemModelTest::testEditMetadata()
-{
- int tempDirIndex = 2;
- increaseIndexIfNeeded(tempDirIndex);
-
- const QUrl tempUrl = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
- QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
-
- // create a new place
- createPlaceItem(QStringLiteral("Temporary Dir"), tempUrl, QString());
- QTRY_COMPARE(itemsInsertedSpy.count(), 1);
-
- // check if the new entry was created
- PlacesItemModel *model = new PlacesItemModel();
- QTRY_COMPARE(model->count(), m_model->count());
-
- // make sure that the new item will be removed later
- schedulePlaceRemoval(tempDirIndex);
-
- // modify place metadata
- auto bookmark = m_model->placesItem(tempDirIndex)->bookmark();
- bookmark.setMetaDataItem(QStringLiteral("OnlyInApp"), KAboutData::applicationData().componentName());
- m_model->refresh();
-
- // check if the place was modified in both models
- QTRY_COMPARE(model->placesItem(tempDirIndex)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")),
- KAboutData::applicationData().componentName());
- QTRY_COMPARE(model->placesItem(tempDirIndex)->text(), m_model->placesItem(tempDirIndex)->text());
- QTRY_COMPARE(model->placesItem(tempDirIndex)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")),
- m_model->placesItem(tempDirIndex)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")));
- QTRY_COMPARE(model->placesItem(tempDirIndex)->icon(), m_model->placesItem(tempDirIndex)->icon());
- QTRY_COMPARE(model->placesItem(tempDirIndex)->url(), m_model->placesItem(tempDirIndex)->url());
-}
-
-void PlacesItemModelTest::testRefresh()
-{
- int tempDirIndex = 2;
- increaseIndexIfNeeded(tempDirIndex);
-
- const QUrl tempUrl = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
- QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
-
- // create a new place
- createPlaceItem(QStringLiteral("Temporary Dir"), tempUrl, QString());
- QTRY_COMPARE(itemsInsertedSpy.count(), 1);
-
- PlacesItemModel *model = new PlacesItemModel();
- QTRY_COMPARE(model->count(), m_model->count());
-
- // make sure that the new item will be removed later
- schedulePlaceRemoval(tempDirIndex);
-
- PlacesItem *item = m_model->placesItem(tempDirIndex);
- PlacesItem *sameItem = model->placesItem(tempDirIndex);
- QCOMPARE(item->text(), sameItem->text());
-
- // modify place text
- item->setText(QStringLiteral("Renamed place"));
-
- // item from another model is not affected at the moment
- QVERIFY(item->text() != sameItem->text());
-
- // propagate change
- m_model->refresh();
-
- // item must be equal
- QTRY_COMPARE(item->text(), sameItem->text());
-}
-
-void PlacesItemModelTest::testIcons_data()
-{
- QTest::addColumn<QUrl>("url");
- QTest::addColumn<QString>("expectedIconName");
-
- // places
- QTest::newRow("Places - Home") << QUrl::fromLocalFile(QDir::homePath()) << QStringLiteral("user-home");
-
- // baloo -search
- QTest::newRow("Baloo - Documents") << QUrl("search:/documents") << QStringLiteral("folder-text");
-
- // devices
- QTest::newRow("Devices - Floppy") << QUrl("file:///media/floppy0") << QStringLiteral("blockdevice");
-}
-
-void PlacesItemModelTest::testIcons()
-{
- QFETCH(QUrl, url);
- QFETCH(QString, expectedIconName);
-
- PlacesItem *item = m_model->placesItem(indexOf(url));
- QCOMPARE(item->icon(), expectedIconName);
-
- for (int r = 0; r < m_model->count(); r++) {
- QVERIFY(!m_model->placesItem(r)->icon().isEmpty());
- }
-}
-
-void PlacesItemModelTest::testDragAndDrop()
-{
- int lastIndex = 1; // last index of places group
- increaseIndexIfNeeded(lastIndex);
-
- QList<QVariant> args;
- KItemRangeList range;
- QStringList urls = initialUrls();
-
- QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
- QSignalSpy itemsRemovedSpy(m_model, &PlacesItemModel::itemsRemoved);
-
- CHECK_PLACES_URLS(initialUrls());
- // Move the home directory to the end of the places group
- QMimeData *dropData = createMimeData(QList<int>() << 0);
- m_model->dropMimeDataBefore(m_model->count() - 1, dropData);
- urls.move(0, lastIndex);
- delete dropData;
-
- QTRY_COMPARE(itemsInsertedSpy.count(), 1);
- QTRY_COMPARE(itemsRemovedSpy.count(), 1);
-
- // remove item from actual position
- args = itemsRemovedSpy.takeFirst();
- range = args.at(0).value<KItemRangeList>();
- QCOMPARE(range.size(), 1);
- QCOMPARE(range.at(0).count, 1);
- QCOMPARE(range.at(0).index, 0);
-
- // insert intem in his group
- args = itemsInsertedSpy.takeFirst();
- range = args.at(0).value<KItemRangeList>();
- QCOMPARE(range.size(), 1);
- QCOMPARE(range.at(0).count, 1);
- QCOMPARE(range.at(0).index, lastIndex);
-
- CHECK_PLACES_URLS(urls);
-
- itemsInsertedSpy.clear();
- itemsRemovedSpy.clear();
-
- // Move home directory item back to its original position
- dropData = createMimeData(QList<int>() << lastIndex);
- m_model->dropMimeDataBefore(0, dropData);
- urls.move(lastIndex, 0);
- delete dropData;
-
- QTRY_COMPARE(itemsInsertedSpy.count(), 1);
- QTRY_COMPARE(itemsRemovedSpy.count(), 1);
-
- // remove item from actual position
- args = itemsRemovedSpy.takeFirst();
- range = args.at(0).value<KItemRangeList>();
- QCOMPARE(range.size(), 1);
- QCOMPARE(range.at(0).count, 1);
- QCOMPARE(range.at(0).index, lastIndex);
-
- // insert intem in the requested position
- args = itemsInsertedSpy.takeFirst();
- range = args.at(0).value<KItemRangeList>();
- QCOMPARE(range.size(), 1);
- QCOMPARE(range.at(0).count, 1);
- QCOMPARE(range.at(0).index, 0);
-
- CHECK_PLACES_URLS(urls);
-}
-
-void PlacesItemModelTest::testHideDevices()
-{
- QSignalSpy itemsRemoved(m_model, &PlacesItemModel::itemsRemoved);
- QStringList urls = initialUrls();
-
- m_model->setGroupHidden(KFilePlacesModel::RemovableDevicesType, true);
- QTRY_VERIFY(m_model->isGroupHidden(KFilePlacesModel::RemovableDevicesType));
- QTRY_COMPARE(itemsRemoved.count(), 3);
-
- // remove removable-devices
- urls.removeOne(QStringLiteral("/media/floppy0"));
- urls.removeOne(QStringLiteral("/media/XO-Y4"));
- urls.removeOne(QStringLiteral("/media/cdrom"));
-
- // check if the correct urls was removed
- CHECK_PLACES_URLS(urls);
-
- delete m_model;
- m_model = new PlacesItemModel();
- QTRY_COMPARE(m_model->count(), urls.count());
- CHECK_PLACES_URLS(urls);
-
- // revert changes
- m_model->setGroupHidden(KFilePlacesModel::RemovableDevicesType, false);
- urls = initialUrls();
- QTRY_COMPARE(m_model->count(), urls.count());
- CHECK_PLACES_URLS(urls);
-}
-
-void PlacesItemModelTest::testDuplicatedEntries()
-{
- QStringList urls = initialUrls();
- // create a duplicated entry on bookmark
- KBookmarkManager *bookmarkManager = KBookmarkManager::managerForFile(bookmarksFile(), QStringLiteral("kfilePlaces"));
- KBookmarkGroup root = bookmarkManager->root();
- KBookmark bookmark = root.addBookmark(QStringLiteral("Duplicated Search Videos"), QUrl("search:/videos"), {});
-
- const QString id = QUuid::createUuid().toString();
- bookmark.setMetaDataItem(QStringLiteral("ID"), id);
- bookmark.setMetaDataItem(QStringLiteral("OnlyInApp"), KAboutData::applicationData().componentName());
- bookmarkManager->emitChanged(bookmarkManager->root());
-
- PlacesItemModel *newModel = new PlacesItemModel();
- QTRY_COMPARE(placesUrls(newModel).count(QStringLiteral("search:/videos")), 1);
- QTRY_COMPARE(urls, placesUrls(newModel));
- delete newModel;
-}
-
-void PlacesItemModelTest::renameAfterCreation()
-{
- int tempDirIndex = 2;
- increaseIndexIfNeeded(tempDirIndex);
-
- const QUrl tempUrl = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
- QStringList urls = initialUrls();
- PlacesItemModel *model = new PlacesItemModel();
-
- CHECK_PLACES_URLS(urls);
- QTRY_COMPARE(model->count(), m_model->count());
-
- // create a new place
- createPlaceItem(QStringLiteral("Temporary Dir"), tempUrl, QString());
- urls.insert(tempDirIndex, tempUrl.toLocalFile());
-
- // make sure that the new item will be removed later
- schedulePlaceRemoval(tempDirIndex);
-
- CHECK_PLACES_URLS(urls);
- QCOMPARE(model->count(), m_model->count());
-
-
- // modify place text
- QSignalSpy changedSpy(m_model, &PlacesItemModel::itemsChanged);
-
- PlacesItem *item = m_model->placesItem(tempDirIndex);
- item->setText(QStringLiteral("New Temporary Dir"));
- item->setUrl(item->url());
- item->setIcon(item->icon());
- m_model->refresh();
-
- QTRY_COMPARE(changedSpy.count(), 1);
-
- // check if the place was modified in both models
- QTRY_COMPARE(m_model->placesItem(tempDirIndex)->text(), QStringLiteral("New Temporary Dir"));
- QTRY_COMPARE(model->placesItem(tempDirIndex)->text(), QStringLiteral("New Temporary Dir"));
-}
-
-QTEST_MAIN(PlacesItemModelTest)
-
-#include "placesitemmodeltest.moc"