diff options
Diffstat (limited to 'src')
53 files changed, 751 insertions, 293 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 02a43a209..eabe80f61 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,6 +4,8 @@ configure_file(config-baloo.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-baloo.h) configure_file(config-kactivities.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kactivities.h) +configure_file(config-packagekit.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-packagekit.h) + configure_file(config-terminal.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-terminal.h) add_definitions( @@ -272,6 +274,7 @@ qt5_add_resources(dolphinstatic_SRCS dolphin.qrc) qt5_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/dolphinmainwindow.h org.kde.DolphinMainWindow.xml) qt5_add_dbus_adaptor(dolphinstatic_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.DolphinMainWindow.xml dolphinmainwindow.h DolphinMainWindow) qt5_add_dbus_interface(dolphinstatic_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.DolphinMainWindow.xml dolphinmainwindowinterface) +qt5_add_dbus_interface(dolphinstatic_SRCS panels/terminal/org.kde.KIOFuse.VFS.xml kiofuse_interface) add_library(dolphinstatic STATIC ${dolphinstatic_SRCS}) diff --git a/src/config-packagekit.h.cmake b/src/config-packagekit.h.cmake new file mode 100644 index 000000000..780fff56e --- /dev/null +++ b/src/config-packagekit.h.cmake @@ -0,0 +1 @@ +#cmakedefine HAVE_PACKAGEKIT diff --git a/src/dbusinterface.h b/src/dbusinterface.h index 391916d62..c1029ea23 100644 --- a/src/dbusinterface.h +++ b/src/dbusinterface.h @@ -34,12 +34,12 @@ public: Q_SCRIPTABLE void ShowItemProperties(const QStringList& uriList, const QString& startUpId); /** - * Set whether this interface has been created by dolphin --deamon. + * Set whether this interface has been created by dolphin --daemon. */ void setAsDaemon(); /** - * @return Whether this interface has been created by dolphin --deamon. + * @return Whether this interface has been created by dolphin --daemon. */ bool isDaemon() const; diff --git a/src/dolphinbookmarkhandler.cpp b/src/dolphinbookmarkhandler.cpp index ded83d6bb..0d31b8984 100644 --- a/src/dolphinbookmarkhandler.cpp +++ b/src/dolphinbookmarkhandler.cpp @@ -21,6 +21,7 @@ #include "dolphinmainwindow.h" #include "dolphinviewcontainer.h" #include "global.h" +#include <KActionCollection> #include <KBookmarkMenu> #include <KIO/Global> #include <QDebug> @@ -47,7 +48,11 @@ DolphinBookmarkHandler::DolphinBookmarkHandler(DolphinMainWindow *mainWindow, } m_bookmarkManager = KBookmarkManager::managerForFile(bookmarksFile, QStringLiteral("dolphin")); m_bookmarkManager->setUpdate(true); - m_bookmarkMenu.reset(new KBookmarkMenu(m_bookmarkManager, this, menu, collection)); + m_bookmarkMenu.reset(new KBookmarkMenu(m_bookmarkManager, this, menu)); + + collection->addAction(QStringLiteral("add_bookmark"), m_bookmarkMenu->addBookmarkAction()); + collection->addAction(QStringLiteral("edit_bookmarks"), m_bookmarkMenu->editBookmarksAction()); + collection->addAction(QStringLiteral("add_bookmarks_list"), m_bookmarkMenu->bookmarkTabsAsFolderAction()); } DolphinBookmarkHandler::~DolphinBookmarkHandler() diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index f374db41d..77937790a 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -50,9 +50,11 @@ #include <KActionMenu> #include <KAuthorized> #include <KConfig> +#include <KConfigGui> #include <KDualAction> #include <KFileItemListProperties> #include <KHelpMenu> +#include <KIO/CommandLauncherJob> #include <KIO/JobUiDelegate> #include <KIO/OpenFileManagerWindowJob> #include <KJobWidgets> @@ -579,6 +581,14 @@ void DolphinMainWindow::closeEvent(QCloseEvent* event) } } + if (GeneralSettings::rememberOpenedTabs()) { + KConfigGui::setSessionConfig(QStringLiteral("dolphin"), QStringLiteral("dolphin")); + KConfig *config = KConfigGui::sessionConfig(); + saveGlobalProperties(config); + savePropertiesInternal(config, 1); + config->sync(); + } + GeneralSettings::setVersion(CurrentDolphinVersion); GeneralSettings::self()->save(); @@ -926,7 +936,10 @@ void DolphinMainWindow::compareFiles() command.append("\" \""); command.append(urlB.toDisplayString(QUrl::PreferLocalFile)); command.append('\"'); - KRun::runCommand(command, QStringLiteral("Kompare"), QStringLiteral("kompare"), this); + + KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob(command, this); + job->setDesktopName(QStringLiteral("org.kde.kompare")); + job->start(); } void DolphinMainWindow::toggleShowMenuBar() @@ -1265,7 +1278,7 @@ void DolphinMainWindow::tabCountChanged(int count) void DolphinMainWindow::updateWindowTitle() { - const QString newTitle = m_activeViewContainer->caption(); + const QString newTitle = m_activeViewContainer->captionWindowTitle(); if (windowTitle() != newTitle) { setWindowTitle(newTitle); } diff --git a/src/dolphinpart.cpp b/src/dolphinpart.cpp index 7e7425121..0c41b2bec 100644 --- a/src/dolphinpart.cpp +++ b/src/dolphinpart.cpp @@ -34,6 +34,7 @@ #include <KActionCollection> #include <KAuthorized> #include <KConfigGroup> +#include <KDialogJobUiDelegate> #include <KFileItemListProperties> #include <KIconLoader> #include <KJobWidgets> @@ -42,7 +43,7 @@ #include <KMimeTypeEditor> #include <KNS3/KMoreToolsMenuFactory> #include <KPluginFactory> -#include <KRun> +#include <KIO/CommandLauncherJob> #include <KSharedConfig> #include <KToolInvocation> @@ -546,7 +547,10 @@ void DolphinPart::slotFindFile() if (!(actions.isEmpty())) { actions.first()->trigger(); } else { - KRun::run(QStringLiteral("kfind"), {url()}, widget()); + KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob(QStringLiteral("kfind"), {url().toString()}, this); + job->setDesktopName(QStringLiteral("org.kde.kfind")); + job->setUiDelegate(new KDialogJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, widget())); + job->start(); } } diff --git a/src/dolphintabwidget.cpp b/src/dolphintabwidget.cpp index 89c54baf5..fba6fe084 100644 --- a/src/dolphintabwidget.cpp +++ b/src/dolphintabwidget.cpp @@ -25,9 +25,9 @@ #include "dolphinviewcontainer.h" #include <KConfigGroup> -#include <KRun> #include <KShell> #include <kio/global.h> +#include <KIO/CommandLauncherJob> #include <KAcceleratorManager> #include <QApplication> @@ -334,8 +334,9 @@ void DolphinTabWidget::detachTab(int index) } args << QStringLiteral("--new-window"); - const QString command = QStringLiteral("dolphin %1").arg(KShell::joinArgs(args)); - KRun::runCommand(command, this); + KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob("dolphin", args, this); + job->setDesktopName(QStringLiteral("org.kde.dolphin")); + job->start(); closeTab(index); } diff --git a/src/dolphinviewcontainer.cpp b/src/dolphinviewcontainer.cpp index 3a17805ab..4ab34a06a 100644 --- a/src/dolphinviewcontainer.cpp +++ b/src/dolphinviewcontainer.cpp @@ -29,6 +29,8 @@ #include "trash/dolphintrash.h" #include "views/viewmodecontroller.h" #include "views/viewproperties.h" +#include "dolphin_detailsmodesettings.h" +#include "views/dolphinview.h" #ifdef HAVE_KACTIVITIES #include <KActivities/ResourceInstance> @@ -119,7 +121,7 @@ DolphinViewContainer::DolphinViewContainer(const QUrl& url, QWidget* parent) : connect(m_searchBox, &DolphinSearchBox::activated, this, &DolphinViewContainer::activate); connect(m_searchBox, &DolphinSearchBox::closeRequest, this, &DolphinViewContainer::closeSearchBox); connect(m_searchBox, &DolphinSearchBox::searchRequest, this, &DolphinViewContainer::startSearching); - connect(m_searchBox, &DolphinSearchBox::returnPressed, this, &DolphinViewContainer::requestFocus); + connect(m_searchBox, &DolphinSearchBox::focusViewRequest, this, &DolphinViewContainer::requestFocus); m_searchBox->setWhatsThis(xi18nc("@info:whatsthis findbar", "<para>This helps you find files and folders. Enter a <emphasis>" "search term</emphasis> and specify search settings with the " @@ -249,6 +251,12 @@ DolphinViewContainer::DolphinViewContainer(const QUrl& url, QWidget* parent) : setSearchModeEnabled(isSearchUrl(url)); + connect(DetailsModeSettings::self(), &KCoreConfigSkeleton::configChanged, this, [=]() { + if (view()->mode() == DolphinView::Mode::DetailsView) { + view()->reload(); + } + }); + // Initialize kactivities resource instance #ifdef HAVE_KACTIVITIES @@ -449,6 +457,18 @@ void DolphinViewContainer::reload() m_messageWidget->hide(); } +QString DolphinViewContainer::captionWindowTitle() const +{ + if (GeneralSettings::showFullPathInTitlebar() && !isSearchModeEnabled()) { + if (!url().isLocalFile()) { + return url().adjusted(QUrl::StripTrailingSlash).toString(); + } + return url().adjusted(QUrl::StripTrailingSlash).path(); + } else { + return DolphinViewContainer::caption(); + } +} + QString DolphinViewContainer::caption() const { if (isSearchModeEnabled()) { @@ -459,13 +479,6 @@ QString DolphinViewContainer::caption() const } } - if (GeneralSettings::showFullPathInTitlebar()) { - if (!url().isLocalFile()) { - return url().adjusted(QUrl::StripTrailingSlash).toString(); - } - return url().adjusted(QUrl::StripTrailingSlash).path(); - } - KFilePlacesModel *placesModel = DolphinPlacesModelSingleton::instance().placesModel(); const auto& matchedPlaces = placesModel->match(placesModel->index(0,0), KFilePlacesModel::UrlRole, QUrl(url().adjusted(QUrl::StripTrailingSlash).toString(QUrl::FullyEncoded).append("/?")), 1, Qt::MatchRegExp); diff --git a/src/dolphinviewcontainer.h b/src/dolphinviewcontainer.h index 5207d2d35..52e63cbe0 100644 --- a/src/dolphinviewcontainer.h +++ b/src/dolphinviewcontainer.h @@ -134,6 +134,13 @@ public: void reload(); /** + * @return Returns a Caption suitable for display in the window title. + * It is calculated depending on GeneralSettings::showFullPathInTitlebar(). + * If it's false, it calls caption(). + */ + QString captionWindowTitle() const; + + /** * @return Returns a Caption suitable for display to the user. It is * calculated depending on settings, if a search is active and other * factors. diff --git a/src/global.cpp b/src/global.cpp index 5236fa4d1..32a2d4ebb 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -23,7 +23,9 @@ #include "dolphindebug.h" #include "dolphinmainwindowinterface.h" -#include <KRun> +#include <KDialogJobUiDelegate> +#include <KIO/ApplicationLauncherJob> +#include <KService> #include <KWindowSystem> #include <QApplication> @@ -60,13 +62,11 @@ void Dolphin::openNewWindow(const QList<QUrl> &urls, QWidget *window, const Open if (!urls.isEmpty()) { command.append(QLatin1String(" %U")); } - KRun::run( - command, - urls, - window, - QApplication::applicationDisplayName(), - QApplication::windowIcon().name() - ); + KService::Ptr service(new KService(QApplication::applicationDisplayName(), command, QApplication::windowIcon().name())); + auto *job = new KIO::ApplicationLauncherJob(service, window); + job->setUrls(urls); + job->setUiDelegate(new KDialogJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, window)); + job->start(); } bool Dolphin::attachToExistingInstance(const QList<QUrl>& inputUrls, bool openFiles, bool splitView, const QString& preferredService) @@ -78,36 +78,7 @@ bool Dolphin::attachToExistingInstance(const QList<QUrl>& inputUrls, bool openFi return false; } - QVector<QPair<QSharedPointer<OrgKdeDolphinMainWindowInterface>, QStringList>> dolphinInterfaces; - if (!preferredService.isEmpty()) { - QSharedPointer<OrgKdeDolphinMainWindowInterface> preferredInterface( - new OrgKdeDolphinMainWindowInterface(preferredService, - QStringLiteral("/dolphin/Dolphin_1"), - QDBusConnection::sessionBus())); - if (preferredInterface->isValid() && !preferredInterface->lastError().isValid()) { - dolphinInterfaces.append(qMakePair(preferredInterface, QStringList())); - } - } - - // Look for dolphin instances among all available dbus services. - const QStringList dbusServices = QDBusConnection::sessionBus().interface()->registeredServiceNames().value(); - // Don't match the service without trailing "-" (unique instance) - const QString pattern = QStringLiteral("org.kde.dolphin-"); - // Don't match the pid without leading "-" - const QString myPid = QLatin1Char('-') + QString::number(QCoreApplication::applicationPid()); - for (const QString& service : dbusServices) { - if (service.startsWith(pattern) && !service.endsWith(myPid)) { - // Check if instance can handle our URLs - QSharedPointer<OrgKdeDolphinMainWindowInterface> interface( - new OrgKdeDolphinMainWindowInterface(service, - QStringLiteral("/dolphin/Dolphin_1"), - QDBusConnection::sessionBus())); - if (interface->isValid() && !interface->lastError().isValid()) { - dolphinInterfaces.append(qMakePair(interface, QStringList())); - } - } - } - + auto dolphinInterfaces = dolphinGuiInstances(preferredService); if (dolphinInterfaces.isEmpty()) { return false; } @@ -145,3 +116,38 @@ bool Dolphin::attachToExistingInstance(const QList<QUrl>& inputUrls, bool openFi } return attached; } + +QVector<QPair<QSharedPointer<OrgKdeDolphinMainWindowInterface>, QStringList>> Dolphin::dolphinGuiInstances(const QString& preferredService) +{ + QVector<QPair<QSharedPointer<OrgKdeDolphinMainWindowInterface>, QStringList>> dolphinInterfaces; + if (!preferredService.isEmpty()) { + QSharedPointer<OrgKdeDolphinMainWindowInterface> preferredInterface( + new OrgKdeDolphinMainWindowInterface(preferredService, + QStringLiteral("/dolphin/Dolphin_1"), + QDBusConnection::sessionBus())); + if (preferredInterface->isValid() && !preferredInterface->lastError().isValid()) { + dolphinInterfaces.append(qMakePair(preferredInterface, QStringList())); + } + } + + // Look for dolphin instances among all available dbus services. + const QStringList dbusServices = QDBusConnection::sessionBus().interface()->registeredServiceNames().value(); + // Don't match the service without trailing "-" (unique instance) + const QString pattern = QStringLiteral("org.kde.dolphin-"); + // Don't match the pid without leading "-" + const QString myPid = QLatin1Char('-') + QString::number(QCoreApplication::applicationPid()); + for (const QString& service : dbusServices) { + if (service.startsWith(pattern) && !service.endsWith(myPid)) { + // Check if instance can handle our URLs + QSharedPointer<OrgKdeDolphinMainWindowInterface> interface( + new OrgKdeDolphinMainWindowInterface(service, + QStringLiteral("/dolphin/Dolphin_1"), + QDBusConnection::sessionBus())); + if (interface->isValid() && !interface->lastError().isValid()) { + dolphinInterfaces.append(qMakePair(interface, QStringList())); + } + } + } + + return dolphinInterfaces; +} diff --git a/src/global.h b/src/global.h index 7ee564581..daf86134e 100644 --- a/src/global.h +++ b/src/global.h @@ -24,6 +24,8 @@ #include <QUrl> #include <QWidget> +class OrgKdeDolphinMainWindowInterface; + namespace Dolphin { QList<QUrl> validateUris(const QStringList& uriList); @@ -52,6 +54,11 @@ namespace Dolphin { bool attachToExistingInstance(const QList<QUrl>& inputUrls, bool openFiles, bool splitView, const QString& preferredService = QString()); /** + * Returns a QVector with all GUI-capable Dolphin instances + */ + QVector<QPair<QSharedPointer<OrgKdeDolphinMainWindowInterface>, QStringList>> dolphinGuiInstances(const QString& preferredService); + + /** * TODO: Move this somewhere global to all KDE apps, not just Dolphin */ const int VERTICAL_SPACER_HEIGHT = 18; diff --git a/src/kitemviews/kfileitemlistwidget.cpp b/src/kitemviews/kfileitemlistwidget.cpp index 40b8ccf37..a495a4c2f 100644 --- a/src/kitemviews/kfileitemlistwidget.cpp +++ b/src/kitemviews/kfileitemlistwidget.cpp @@ -21,6 +21,8 @@ #include "kfileitemmodel.h" #include "kitemlistview.h" +#include "dolphin_detailsmodesettings.h" + #include <KFormat> #include <KLocalizedString> @@ -64,14 +66,24 @@ QString KFileItemListWidgetInformant::roleText(const QByteArray& role, if (role == "size") { if (values.value("isDir").toBool()) { - // The item represents a directory. Show the number of sub directories - // instead of the file size of the directory. + // The item represents a directory. if (!roleValue.isNull()) { - const int count = roleValue.toInt(); + const int count = values.value("count").toInt(); if (count < 0) { text = i18nc("@item:intable", "Unknown"); } else { - text = i18ncp("@item:intable", "%1 item", "%1 items", count); + if (DetailsModeSettings::directorySizeCount()) { + // Show the number of sub directories instead of the file size of the directory. + text = i18ncp("@item:intable", "%1 item", "%1 items", count); + } else { + // if we have directory size available + if (roleValue == -1) { + text = i18nc("@item:intable", "Unknown"); + } else { + const KIO::filesize_t size = roleValue.value<KIO::filesize_t>(); + text = KFormat().formatByteSize(size); + } + } } } } else { diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index e4dca2734..ac3c33e22 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -22,6 +22,7 @@ #include "kfileitemmodel.h" #include "dolphin_generalsettings.h" +#include "dolphin_detailsmodesettings.h" #include "dolphindebug.h" #include "private/kfileitemmodeldirlister.h" #include "private/kfileitemmodelsortalgorithm.h" @@ -1767,8 +1768,15 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b, const // See "if (m_sortFoldersFirst || m_sortRole == SizeRole)" in KFileItemModel::lessThan(): Q_ASSERT(itemB.isDir()); - const QVariant valueA = a->values.value("size"); - const QVariant valueB = b->values.value("size"); + QVariant valueA, valueB; + if (DetailsModeSettings::directorySizeCount()) { + valueA = a->values.value("count"); + valueB = b->values.value("count"); + } else { + // use dir size then + valueA = a->values.value("size"); + valueB = b->values.value("size"); + } if (valueA.isNull() && valueB.isNull()) { result = 0; } else if (valueA.isNull()) { @@ -1776,7 +1784,11 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b, const } else if (valueB.isNull()) { result = +1; } else { - result = valueA.toInt() - valueB.toInt(); + if (valueA < valueB) { + return -1; + } else { + return +1; + } } } else { // See "if (m_sortFoldersFirst || m_sortRole == SizeRole)" in KFileItemModel::lessThan(): diff --git a/src/kitemviews/kfileitemmodelrolesupdater.cpp b/src/kitemviews/kfileitemmodelrolesupdater.cpp index bf2c84c40..c28e240a5 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.cpp +++ b/src/kitemviews/kfileitemmodelrolesupdater.cpp @@ -44,7 +44,6 @@ #include <QElapsedTimer> #include <QTimer> - // #define KFILEITEMMODELROLESUPDATER_DEBUG namespace { @@ -108,9 +107,9 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QO this, &KFileItemModelRolesUpdater::slotSortRoleChanged); // Use a timer to prevent that each call of slotItemsChanged() results in a synchronous - // resolving of the roles. Postpone the resolving until no update has been done for 1 second. + // resolving of the roles. Postpone the resolving until no update has been done for 100 ms. m_recentlyChangedItemsTimer = new QTimer(this); - m_recentlyChangedItemsTimer->setInterval(1000); + m_recentlyChangedItemsTimer->setInterval(100); m_recentlyChangedItemsTimer->setSingleShot(true); connect(m_recentlyChangedItemsTimer, &QTimer::timeout, this, &KFileItemModelRolesUpdater::resolveRecentlyChangedItems); @@ -750,7 +749,7 @@ void KFileItemModelRolesUpdater::applyChangedBalooRolesForItem(const KFileItem & #endif } -void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QString& path, int count) +void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QString& path, int count, long size) { const bool getSizeRole = m_roles.contains("size"); const bool getIsExpandableRole = m_roles.contains("isExpandable"); @@ -761,17 +760,16 @@ void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QStrin QHash<QByteArray, QVariant> data; if (getSizeRole) { - data.insert("size", count); + data.insert("count", count); + if (size != -1) { + data.insert("size", QVariant::fromValue(size)); + } } if (getIsExpandableRole) { data.insert("isExpandable", count > 0); } - disconnect(m_model, &KFileItemModel::itemsChanged, - this, &KFileItemModelRolesUpdater::slotItemsChanged); m_model->setData(index, data); - connect(m_model, &KFileItemModel::itemsChanged, - this, &KFileItemModelRolesUpdater::slotItemsChanged); } } } @@ -997,7 +995,7 @@ void KFileItemModelRolesUpdater::applySortRole(int index) data.insert("type", item.mimeComment()); } else if (m_model->sortRole() == "size" && item.isLocalFile() && item.isDir()) { const QString path = item.localPath(); - data.insert("size", m_directoryContentsCounter->countDirectoryContentsSynchronously(path)); + m_directoryContentsCounter->scanDirectory(path); } else { // Probably the sort role is a baloo role - just determine all roles. data = rolesData(item); @@ -1070,7 +1068,7 @@ QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileIte // Tell m_directoryContentsCounter that we want to count the items // inside the directory. The result will be received in slotDirectoryContentsCountReceived. const QString path = item.localPath(); - m_directoryContentsCounter->addDirectory(path); + m_directoryContentsCounter->scanDirectory(path); } else if (getSizeRole) { data.insert("size", -1); // -1 indicates an unknown number of items } diff --git a/src/kitemviews/kfileitemmodelrolesupdater.h b/src/kitemviews/kfileitemmodelrolesupdater.h index 9078c8e0d..e21cd30df 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.h +++ b/src/kitemviews/kfileitemmodelrolesupdater.h @@ -212,7 +212,7 @@ private slots: void applyChangedBalooRoles(const QString& file); void applyChangedBalooRolesForItem(const KFileItem& file); - void slotDirectoryContentsCountReceived(const QString& path, int count); + void slotDirectoryContentsCountReceived(const QString& path, int count, long size); private: /** diff --git a/src/kitemviews/kitemlistcontroller.cpp b/src/kitemviews/kitemlistcontroller.cpp index 5ddf52e5f..0c25ebb8b 100644 --- a/src/kitemviews/kitemlistcontroller.cpp +++ b/src/kitemviews/kitemlistcontroller.cpp @@ -1219,7 +1219,7 @@ void KItemListController::startDragging() const QPoint hotSpot((pixmap.width() / pixmap.devicePixelRatio()) / 2, 0); drag->setHotSpot(hotSpot); - drag->exec(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction, Qt::CopyAction); + drag->exec(Qt::MoveAction | Qt::CopyAction | Qt::LinkAction, Qt::MoveAction); QAccessibleEvent accessibilityEvent(view(), QAccessible::DragDropStart); QAccessible::updateAccessibility(&accessibilityEvent); diff --git a/src/kitemviews/kitemlistwidget.cpp b/src/kitemviews/kitemlistwidget.cpp index 49a13f68f..42bf9ebdc 100644 --- a/src/kitemviews/kitemlistwidget.cpp +++ b/src/kitemviews/kitemlistwidget.cpp @@ -378,7 +378,6 @@ QPixmap KItemListWidget::createDragPixmap(const QStyleOptionGraphicsItem* option const bool wasHovered = m_hovered; setAlternateBackground(false); - setSelected(false); setHovered(false); paint(&painter, option, widget); diff --git a/src/kitemviews/private/kdirectorycontentscounter.cpp b/src/kitemviews/private/kdirectorycontentscounter.cpp index bd204fe8e..a19bce8b3 100644 --- a/src/kitemviews/private/kdirectorycontentscounter.cpp +++ b/src/kitemviews/private/kdirectorycontentscounter.cpp @@ -24,8 +24,14 @@ #include <KDirWatch> #include <QFileInfo> +#include <QDir> #include <QThread> +namespace { + /// cache of directory counting result + static QHash<QString, QPair<int, long>> *s_cache; +} + KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel* model, QObject* parent) : QObject(parent), m_model(model), @@ -43,9 +49,12 @@ KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel* model, QObj m_workerThread->start(); } + if (s_cache == nullptr) { + s_cache = new QHash<QString, QPair<int, long>>(); + } + m_worker = new KDirectoryContentsCounterWorker(); m_worker->moveToThread(m_workerThread); - ++m_workersCount; connect(this, &KDirectoryContentsCounter::requestDirectoryContentsCount, m_worker, &KDirectoryContentsCounterWorker::countDirectoryContents); @@ -58,9 +67,7 @@ KDirectoryContentsCounter::KDirectoryContentsCounter(KFileItemModel* model, QObj KDirectoryContentsCounter::~KDirectoryContentsCounter() { - --m_workersCount; - - if (m_workersCount > 0) { + if (m_workerThread->isRunning()) { // The worker thread will continue running. It could even be running // a method of m_worker at the moment, so we delete it using // deleteLater() to prevent a crash. @@ -79,38 +86,17 @@ KDirectoryContentsCounter::~KDirectoryContentsCounter() } } -void KDirectoryContentsCounter::addDirectory(const QString& path) +void KDirectoryContentsCounter::scanDirectory(const QString& path) { startWorker(path); } -int KDirectoryContentsCounter::countDirectoryContentsSynchronously(const QString& path) -{ - const QString resolvedPath = QFileInfo(path).canonicalFilePath(); - - if (!m_dirWatcher->contains(resolvedPath)) { - m_dirWatcher->addDir(resolvedPath); - m_watchedDirs.insert(resolvedPath); - } - - KDirectoryContentsCounterWorker::Options options; - - if (m_model->showHiddenFiles()) { - options |= KDirectoryContentsCounterWorker::CountHiddenFiles; - } - - if (m_model->showDirectoriesOnly()) { - options |= KDirectoryContentsCounterWorker::CountDirectoriesOnly; - } - - return KDirectoryContentsCounterWorker::subItemsCount(path, options); -} - -void KDirectoryContentsCounter::slotResult(const QString& path, int count) +void KDirectoryContentsCounter::slotResult(const QString& path, int count, long size) { m_workerIsBusy = false; - const QString resolvedPath = QFileInfo(path).canonicalFilePath(); + const QFileInfo info = QFileInfo(path); + const QString resolvedPath = info.canonicalFilePath(); if (!m_dirWatcher->contains(resolvedPath)) { m_dirWatcher->addDir(resolvedPath); @@ -121,7 +107,22 @@ void KDirectoryContentsCounter::slotResult(const QString& path, int count) startWorker(m_queue.dequeue()); } - emit result(path, count); + if (s_cache->contains(resolvedPath)) { + const auto pair = s_cache->value(resolvedPath); + if (pair.first == count && pair.second == size) { + // no change no need to send another result event + return; + } + } + + if (info.dir().path() == m_model->rootItem().url().path()) { + // update cache or overwrite value + // when path is a direct children of the current model root + s_cache->insert(resolvedPath, QPair<int, long>(count, size)); + } + + // sends the results + emit result(resolvedPath, count, size); } void KDirectoryContentsCounter::slotDirWatchDirty(const QString& path) @@ -146,7 +147,7 @@ void KDirectoryContentsCounter::slotItemsRemoved() if (!m_watchedDirs.isEmpty()) { // Don't let KDirWatch watch for removed items if (allItemsRemoved) { - foreach (const QString& path, m_watchedDirs) { + for (const QString& path : qAsConst(m_watchedDirs)) { m_dirWatcher->removeDir(path); } m_watchedDirs.clear(); @@ -166,6 +167,13 @@ void KDirectoryContentsCounter::slotItemsRemoved() void KDirectoryContentsCounter::startWorker(const QString& path) { + if (s_cache->contains(path)) { + // fast path when in cache + // will be updated later if result has changed + const auto pair = s_cache->value(path); + emit result(path, pair.first, pair.second); + } + if (m_workerIsBusy) { m_queue.enqueue(path); } else { @@ -185,4 +193,3 @@ void KDirectoryContentsCounter::startWorker(const QString& path) } QThread* KDirectoryContentsCounter::m_workerThread = nullptr; -int KDirectoryContentsCounter::m_workersCount = 0; diff --git a/src/kitemviews/private/kdirectorycontentscounter.h b/src/kitemviews/private/kdirectorycontentscounter.h index 349860757..0c900ec64 100644 --- a/src/kitemviews/private/kdirectorycontentscounter.h +++ b/src/kitemviews/private/kdirectorycontentscounter.h @@ -25,6 +25,7 @@ #include <QQueue> #include <QSet> +#include <QHash> class KDirWatch; class KFileItemModel; @@ -45,28 +46,23 @@ public: * * The directory \a path is watched for changes, and the signal is emitted * again if a change occurs. - */ - void addDirectory(const QString& path); - - /** - * In contrast to \a addDirectory, this function counts the items inside - * the directory \a path synchronously and returns the result. * - * The directory is watched for changes, and the signal \a result is - * emitted if a change occurs. + * Uses a cache internally to speed up first result, + * but emit again result when the cache was updated */ - int countDirectoryContentsSynchronously(const QString& path); + void scanDirectory(const QString& path); signals: /** - * Signals that the directory \a path contains \a count items. + * Signals that the directory \a path contains \a count items of size \a + * Size calculation depends on parameter DetailsModeSettings::recursiveDirectorySizeLimit */ - void result(const QString& path, int count); + void result(const QString& path, int count, long size); void requestDirectoryContentsCount(const QString& path, KDirectoryContentsCounterWorker::Options options); private slots: - void slotResult(const QString& path, int count); + void slotResult(const QString& path, int count, long size); void slotDirWatchDirty(const QString& path); void slotItemsRemoved(); @@ -79,7 +75,6 @@ private: QQueue<QString> m_queue; static QThread* m_workerThread; - static int m_workersCount; KDirectoryContentsCounterWorker* m_worker; bool m_workerIsBusy; diff --git a/src/kitemviews/private/kdirectorycontentscounterworker.cpp b/src/kitemviews/private/kdirectorycontentscounterworker.cpp index e9c954ed9..1e4a0b3b4 100644 --- a/src/kitemviews/private/kdirectorycontentscounterworker.cpp +++ b/src/kitemviews/private/kdirectorycontentscounterworker.cpp @@ -22,44 +22,34 @@ // Required includes for subItemsCount(): #ifdef Q_OS_WIN - #include <QDir> +#include <QDir> #else - #include <QFile> - #include <qplatformdefs.h> +#include <QFile> +#include <qplatformdefs.h> #endif +#include "dolphin_detailsmodesettings.h" + KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject* parent) : QObject(parent) { qRegisterMetaType<KDirectoryContentsCounterWorker::Options>(); } -int KDirectoryContentsCounterWorker::subItemsCount(const QString& path, Options options) +#ifndef Q_OS_WIN +KDirectoryContentsCounterWorker::CountResult walkDir(const QString &dirPath, + const bool countHiddenFiles, + const bool countDirectoriesOnly, + QT_DIRENT *dirEntry, + const uint allowedRecursiveLevel) { - const bool countHiddenFiles = options & CountHiddenFiles; - const bool countDirectoriesOnly = options & CountDirectoriesOnly; - -#ifdef Q_OS_WIN - QDir dir(path); - QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System; - if (countHiddenFiles) { - filters |= QDir::Hidden; - } - if (countDirectoriesOnly) { - filters |= QDir::Dirs; - } else { - filters |= QDir::AllEntries; - } - return dir.entryList(filters).count(); -#else - // Taken from kio/src/widgets/kdirmodel.cpp - // Copyright (C) 2006 David Faure <[email protected]> - int count = -1; - auto dir = QT_OPENDIR(QFile::encodeName(path)); + long size = -1; + auto dir = QT_OPENDIR(QFile::encodeName(dirPath)); if (dir) { count = 0; - QT_DIRENT *dirEntry = nullptr; + QT_STATBUF buf; + while ((dirEntry = QT_READDIR(dir))) { if (dirEntry->d_name[0] == '.') { if (dirEntry->d_name[1] == '\0' || !countHiddenFiles) { @@ -76,20 +66,70 @@ int KDirectoryContentsCounterWorker::subItemsCount(const QString& path, Options // as directory instead of trying to do an expensive stat() // (see bugs 292642 and 299997). const bool countEntry = !countDirectoriesOnly || - dirEntry->d_type == DT_DIR || - dirEntry->d_type == DT_LNK || - dirEntry->d_type == DT_UNKNOWN; + dirEntry->d_type == DT_DIR || + dirEntry->d_type == DT_LNK || + dirEntry->d_type == DT_UNKNOWN; if (countEntry) { ++count; } + + if (allowedRecursiveLevel > 0) { + + bool linkFound = false; + QString nameBuf = QStringLiteral("%1/%2").arg(dirPath, dirEntry->d_name); + + if (dirEntry->d_type == DT_REG || dirEntry->d_type == DT_LNK) { + if (QT_STAT(nameBuf.toLocal8Bit(), &buf) == 0) { + if (S_ISDIR(buf.st_mode)) { + // was a dir link, recurse + linkFound = true; + } + size += buf.st_size; + } + } + if (dirEntry->d_type == DT_DIR || linkFound) { + // recursion for dirs and dir links + size += walkDir(nameBuf, countHiddenFiles, countDirectoriesOnly, dirEntry, allowedRecursiveLevel - 1).size; + } + } } QT_CLOSEDIR(dir); } - return count; + return KDirectoryContentsCounterWorker::CountResult{count, size}; +} +#endif + +KDirectoryContentsCounterWorker::CountResult KDirectoryContentsCounterWorker::subItemsCount(const QString& path, Options options) +{ + const bool countHiddenFiles = options & CountHiddenFiles; + const bool countDirectoriesOnly = options & CountDirectoriesOnly; + +#ifdef Q_OS_WIN + QDir dir(path); + QDir::Filters filters = QDir::NoDotAndDotDot | QDir::System; + if (countHiddenFiles) { + filters |= QDir::Hidden; + } + if (countDirectoriesOnly) { + filters |= QDir::Dirs; + } else { + filters |= QDir::AllEntries; + } + return {dir.entryList(filters).count(), 0}; +#else + + const uint maxRecursiveLevel = DetailsModeSettings::directorySizeCount() ? 1 : DetailsModeSettings::recursiveDirectorySizeLimit(); + + QT_DIRENT *dirEntry = nullptr; + + auto res = walkDir(QFile::encodeName(path), countHiddenFiles, countDirectoriesOnly, dirEntry, maxRecursiveLevel); + + return res; #endif } void KDirectoryContentsCounterWorker::countDirectoryContents(const QString& path, Options options) { - emit result(path, subItemsCount(path, options)); + auto res = subItemsCount(path, options); + emit result(path, res.count, res.size); } diff --git a/src/kitemviews/private/kdirectorycontentscounterworker.h b/src/kitemviews/private/kdirectorycontentscounterworker.h index b40da6e87..fac9978d5 100644 --- a/src/kitemviews/private/kdirectorycontentscounterworker.h +++ b/src/kitemviews/private/kdirectorycontentscounterworker.h @@ -37,6 +37,14 @@ public: }; Q_DECLARE_FLAGS(Options, Option) + struct CountResult { + /// number of elements in the directory + int count; + /// Recursive sum of the size of the directory content files and folders + /// Calculation depends on DetailsModeSettings::recursiveDirectorySizeLimit + long size; + }; + explicit KDirectoryContentsCounterWorker(QObject* parent = nullptr); /** @@ -45,13 +53,13 @@ public: * * @return The number of items. */ - static int subItemsCount(const QString& path, Options options); + static CountResult subItemsCount(const QString& path, Options options); signals: /** - * Signals that the directory \a path contains \a count items. + * Signals that the directory \a path contains \a count items and optionally the size of its content. */ - void result(const QString& path, int count); + void result(const QString& path, int count, long size); public slots: /** diff --git a/src/main.cpp b/src/main.cpp index 5932df5ce..802e64d25 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -31,6 +31,7 @@ #include <KDBusService> #include <KLocalizedString> #include <Kdelibs4ConfigMigrator> +#include <KConfigGui> #include <QApplication> #include <QCommandLineParser> @@ -139,6 +140,9 @@ extern "C" Q_DECL_EXPORT int kdemain(int argc, char **argv) const bool openFiles = parser.isSet(QStringLiteral("select")); const QStringList args = parser.positionalArguments(); QList<QUrl> urls = Dolphin::validateUris(args); + // We later mutate urls, so we need to store if it was empty originally + const bool startedWithURLs = !urls.isEmpty(); + if (parser.isSet(QStringLiteral("daemon"))) { KDBusService dolphinDBusService; @@ -154,7 +158,7 @@ extern "C" Q_DECL_EXPORT int kdemain(int argc, char **argv) } } - if (urls.isEmpty()) { + if (!startedWithURLs) { // We need at least one URL to open Dolphin urls.append(Dolphin::homeUrl()); } @@ -174,12 +178,25 @@ extern "C" Q_DECL_EXPORT int kdemain(int argc, char **argv) mainWindow->show(); - if (app.isSessionRestored()) { - const QString className = KXmlGuiWindow::classNameOfToplevel(1); - if (className == QLatin1String("DolphinMainWindow")) { - mainWindow->restore(1); - } else { - qCWarning(DolphinDebug) << "Unknown class " << className << " in session saved data!"; + if (!app.isSessionRestored()) { + KConfigGui::setSessionConfig(QStringLiteral("dolphin"), QStringLiteral("dolphin")); + } + + // Only restore session if: + // 1. Dolphin was not started with command line args + // 2. The "remember state" setting is enabled or session restoration after + // reboot is in use + // 3. There is a session available to restore + if (!startedWithURLs && (app.isSessionRestored() || GeneralSettings::rememberOpenedTabs()) ) { + // Get saved state data for the last-closed Dolphin instance + const QString serviceName = QStringLiteral("org.kde.dolphin-%1").arg(QCoreApplication::applicationPid()); + if (Dolphin::dolphinGuiInstances(serviceName).size() > 0) { + const QString className = KXmlGuiWindow::classNameOfToplevel(1); + if (className == QLatin1String("DolphinMainWindow")) { + mainWindow->restore(1); + } else { + qCWarning(DolphinDebug) << "Unknown class " << className << " in session saved data!"; + } } } diff --git a/src/org.kde.dolphin.appdata.xml b/src/org.kde.dolphin.appdata.xml index c3d2c7fc0..05228bdbb 100644 --- a/src/org.kde.dolphin.appdata.xml +++ b/src/org.kde.dolphin.appdata.xml @@ -576,6 +576,7 @@ <caption xml:lang="pt-BR">Gerenciamento de arquivos no Dolphin</caption> <caption xml:lang="ru">Управление файлами</caption> <caption xml:lang="sk">Správa súborov v Dolphin</caption> + <caption xml:lang="sl">Upravljanje datotek v Dolphinu</caption> <caption xml:lang="sv">Filhantering i Dolphin</caption> <caption xml:lang="uk">Керування файлами у Dolphin</caption> <caption xml:lang="x-test">xxFile management in Dolphinxx</caption> diff --git a/src/panels/information/informationpanel.cpp b/src/panels/information/informationpanel.cpp index 23e7f1922..4f0e4e5eb 100644 --- a/src/panels/information/informationpanel.cpp +++ b/src/panels/information/informationpanel.cpp @@ -248,7 +248,7 @@ void InformationPanel::showItemInfo() if (item.isNull()) { // No item is hovered and no selection has been done: provide // an item for the currently shown directory. - m_folderStatJob = KIO::stat(url(), KIO::HideProgressInfo); + m_folderStatJob = KIO::statDetails(url(), KIO::StatJob::SourceSide, KIO::StatDefaultDetails | KIO::StatRecursiveSize, KIO::HideProgressInfo); if (m_folderStatJob->uiDelegate()) { KJobWidgets::setWindow(m_folderStatJob, this); } diff --git a/src/panels/information/informationpanelcontent.cpp b/src/panels/information/informationpanelcontent.cpp index 5c7c7c3f1..a0c872560 100644 --- a/src/panels/information/informationpanelcontent.cpp +++ b/src/panels/information/informationpanelcontent.cpp @@ -78,7 +78,7 @@ InformationPanelContent::InformationPanelContent(QWidget* parent) : // delay. This prevents flickering if the new preview can be generated // within a very small timeframe. m_outdatedPreviewTimer = new QTimer(this); - m_outdatedPreviewTimer->setInterval(300); + m_outdatedPreviewTimer->setInterval(100); m_outdatedPreviewTimer->setSingleShot(true); connect(m_outdatedPreviewTimer, &QTimer::timeout, this, &InformationPanelContent::markOutdatedPreview); @@ -189,12 +189,8 @@ void InformationPanelContent::refreshPixmapView() // Mark the currently shown preview as outdated. This is done // with a small delay to prevent a flickering when the next preview - // can be shown within a short timeframe. This timer is not started - // for directories, as directory previews might fail and return the - // same icon. - if (!m_item.isDir()) { - m_outdatedPreviewTimer->start(); - } + // can be shown within a short timeframe. + m_outdatedPreviewTimer->start(); QStringList plugins = KIO::PreviewJob::availablePlugins(); m_previewJob = new KIO::PreviewJob(KFileItemList() << m_item, @@ -221,7 +217,6 @@ void InformationPanelContent::refreshPreview() } m_preview->setCursor(Qt::ArrowCursor); - bool usePhonon = false; setNameLabelText(m_item.text()); if (InformationPanelSettings::previewsShown()) { @@ -229,11 +224,12 @@ void InformationPanelContent::refreshPreview() const bool isSearchUrl = itemUrl.scheme().contains(QLatin1String("search")) && m_item.localPath().isEmpty(); if (isSearchUrl) { m_preview->show(); + m_phononWidget->hide(); // in the case of a search-URL the URL is not readable for humans // (at least not useful to show in the Information Panel) m_preview->setPixmap( - QIcon::fromTheme(QStringLiteral("baloo")).pixmap(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous) + QIcon::fromTheme(QStringLiteral("baloo")).pixmap(m_preview->height(), m_preview->width()) ); } else { @@ -242,12 +238,13 @@ void InformationPanelContent::refreshPreview() const QString mimeType = m_item.mimetype(); const bool isAnimatedImage = m_preview->isAnimatedImage(itemUrl.toLocalFile()); m_isVideo = !isAnimatedImage && mimeType.startsWith(QLatin1String("video/")); - usePhonon = m_isVideo || mimeType.startsWith(QLatin1String("audio/")); + bool usePhonon = m_isVideo || mimeType.startsWith(QLatin1String("audio/")); if (usePhonon) { // change the cursor of the preview m_preview->setCursor(Qt::PointingHandCursor); m_preview->installEventFilter(m_phononWidget); + m_phononWidget->show(); // if the video is playing, has been paused or stopped // we don't need to update the preview/phonon widget states @@ -267,7 +264,6 @@ void InformationPanelContent::refreshPreview() m_preview->show(); } - m_phononWidget->show(); m_phononWidget->setUrl(m_item.targetUrl(), m_isVideo ? PhononWidget::MediaKind::Video : PhononWidget::MediaKind::Audio); adjustWidgetSizes(parentWidget()->width()); } @@ -314,7 +310,7 @@ void InformationPanelContent::showItems(const KFileItemList& items) m_preview->stopAnimatedImage(); m_preview->setPixmap( - QIcon::fromTheme(QStringLiteral("dialog-information")).pixmap(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous) + QIcon::fromTheme(QStringLiteral("dialog-information")).pixmap(m_preview->height(), m_preview->width()) ); setNameLabelText(i18ncp("@label", "%1 item selected", "%1 items selected", items.count())); @@ -358,7 +354,7 @@ bool InformationPanelContent::eventFilter(QObject* obj, QEvent* event) void InformationPanelContent::showIcon(const KFileItem& item) { m_outdatedPreviewTimer->stop(); - QPixmap pixmap = QIcon::fromTheme(item.iconName()).pixmap(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous); + QPixmap pixmap = QIcon::fromTheme(item.iconName()).pixmap(m_preview->height(), m_preview->width()); KIconLoader::global()->drawOverlays(item.overlays(), pixmap, KIconLoader::Desktop); m_preview->setPixmap(pixmap); } @@ -411,11 +407,18 @@ void InformationPanelContent::showPreview(const KFileItem& item, void InformationPanelContent::markOutdatedPreview() { - KIconEffect *iconEffect = KIconLoader::global()->iconEffect(); - QPixmap disabledPixmap = iconEffect->apply(m_preview->pixmap(), - KIconLoader::Desktop, - KIconLoader::DisabledState); - m_preview->setPixmap(disabledPixmap); + if (m_item.isDir()) { + // directory preview can be long + // but since we always have icons to display + // use it until the preview is done + showIcon(m_item); + } else { + KIconEffect *iconEffect = KIconLoader::global()->iconEffect(); + QPixmap disabledPixmap = iconEffect->apply(m_preview->pixmap(), + KIconLoader::Desktop, + KIconLoader::DisabledState); + m_preview->setPixmap(disabledPixmap); + } } KFileItemList InformationPanelContent::items() diff --git a/src/panels/terminal/org.kde.KIOFuse.VFS.xml b/src/panels/terminal/org.kde.KIOFuse.VFS.xml new file mode 100644 index 000000000..56f753e41 --- /dev/null +++ b/src/panels/terminal/org.kde.KIOFuse.VFS.xml @@ -0,0 +1,13 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="org.kde.KIOFuse.VFS"> + <method name="mountUrl"> + <arg name="remoteUrl" type="s" direction="in"/> + <arg type="s" direction="out"/> + </method> + <method name="remoteUrl"> + <arg name="localUrl" type="s" direction="in"/> + <arg type="s" direction="out"/> + </method> + </interface> +</node> diff --git a/src/panels/terminal/terminalpanel.cpp b/src/panels/terminal/terminalpanel.cpp index 59b2694fb..ac0bcd0fe 100644 --- a/src/panels/terminal/terminalpanel.cpp +++ b/src/panels/terminal/terminalpanel.cpp @@ -18,6 +18,7 @@ ***************************************************************************/ #include "terminalpanel.h" +#include "kiofuse_interface.h" #include <KIO/DesktopExecParser> #include <KIO/Job> @@ -25,6 +26,7 @@ #include <KJobWidgets> #include <KLocalizedString> #include <KMessageWidget> +#include <KMountPoint> #include <KParts/ReadOnlyPart> #include <KPluginFactory> #include <KPluginLoader> @@ -50,7 +52,10 @@ TerminalPanel::TerminalPanel(QWidget* parent) : m_konsolePartMissingMessage(nullptr), m_konsolePart(nullptr), m_konsolePartCurrentDirectory(), - m_sendCdToTerminalHistory() + m_sendCdToTerminalHistory(), + m_kiofuseInterface(QStringLiteral("org.kde.KIOFuse"), + QStringLiteral("/org/kde/KIOFuse"), + QDBusConnection::sessionBus()) { m_layout = new QVBoxLayout(this); m_layout->setContentsMargins(0, 0, 0, 0); @@ -244,6 +249,19 @@ void TerminalPanel::slotMostLocalUrlResult(KJob* job) const QUrl url = statJob->mostLocalUrl(); if (url.isLocalFile()) { sendCdToTerminal(url.toLocalFile()); + } else { + // URL isn't local, only hope for the terminal to be in sync with the + // DolphinView is to mount the remote URL in KIOFuse and point to it. + // If we can't do that for any reason, silently fail. + auto reply = m_kiofuseInterface.mountUrl(url.toString()); + QDBusPendingCallWatcher * watcher = new QDBusPendingCallWatcher(reply, this); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [=] (QDBusPendingCallWatcher* watcher) { + watcher->deleteLater(); + if (!reply.isError()) { + // Successfully mounted, point to the KIOFuse equivalent path. + sendCdToTerminal(reply.value()); + } + }); } m_mostLocalUrlJob = nullptr; @@ -261,8 +279,31 @@ void TerminalPanel::slotKonsolePartCurrentDirectoryChanged(const QString& dir) } } + // User may potentially be browsing inside a KIOFuse mount. + // If so lets try and change the DolphinView to point to the remote URL equivalent. + // instead of into the KIOFuse mount itself (which can cause performance issues!) const QUrl url(QUrl::fromLocalFile(dir)); - emit changeUrl(url); + + KMountPoint::Ptr mountPoint = KMountPoint::currentMountPoints().findByPath(m_konsolePartCurrentDirectory); + if (mountPoint && mountPoint->mountType() != QStringLiteral("fuse.kio-fuse")) { + // Not in KIOFUse mount, so just switch to the corresponding URL. + emit changeUrl(url); + return; + } + + auto reply = m_kiofuseInterface.remoteUrl(m_konsolePartCurrentDirectory); + QDBusPendingCallWatcher * watcher = new QDBusPendingCallWatcher(reply, this); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [=] (QDBusPendingCallWatcher* watcher) { + watcher->deleteLater(); + if (reply.isError()) { + // KIOFuse errored out... just show the normal URL + emit changeUrl(url); + } else { + // Our location happens to be in a KIOFuse mount and is mounted. + // Let's change the DolphinView to point to the remote URL equivalent. + emit changeUrl(QUrl::fromUserInput(reply.value())); + } + }); } bool TerminalPanel::terminalHasFocus() const diff --git a/src/panels/terminal/terminalpanel.h b/src/panels/terminal/terminalpanel.h index 6ab205fe3..661fee4d4 100644 --- a/src/panels/terminal/terminalpanel.h +++ b/src/panels/terminal/terminalpanel.h @@ -21,6 +21,7 @@ #define TERMINALPANEL_H #include "panels/panel.h" +#include "kiofuse_interface.h" #include <QQueue> @@ -101,6 +102,7 @@ private: KParts::ReadOnlyPart* m_konsolePart; QString m_konsolePartCurrentDirectory; QQueue<QString> m_sendCdToTerminalHistory; + org::kde::KIOFuse::VFS m_kiofuseInterface; }; #endif // TERMINALPANEL_H diff --git a/src/search/dolphinsearchbox.cpp b/src/search/dolphinsearchbox.cpp index 23f520de1..cdc0718df 100644 --- a/src/search/dolphinsearchbox.cpp +++ b/src/search/dolphinsearchbox.cpp @@ -136,6 +136,7 @@ QUrl DolphinSearchBox::urlForSearching() const } query.addQueryItem(QStringLiteral("url"), searchPath().url()); + query.addQueryItem(QStringLiteral("title"), queryTitle(m_searchInput->text())); url.setQuery(query); } @@ -219,6 +220,9 @@ void DolphinSearchBox::keyReleaseEvent(QKeyEvent* event) m_searchInput->clear(); } } + else if (event->key() == Qt::Key_Down) { + emit focusViewRequest(); + } } bool DolphinSearchBox::eventFilter(QObject* obj, QEvent* event) @@ -283,7 +287,7 @@ void DolphinSearchBox::slotSearchTextChanged(const QString& text) void DolphinSearchBox::slotReturnPressed() { emitSearchRequest(); - emit returnPressed(); + emit focusViewRequest(); } void DolphinSearchBox::slotFacetChanged() @@ -469,6 +473,12 @@ void DolphinSearchBox::init() connect(m_startSearchTimer, &QTimer::timeout, this, &DolphinSearchBox::emitSearchRequest); } +QString DolphinSearchBox::queryTitle(const QString& text) const +{ + return i18nc("@title UDS_DISPLAY_NAME for a KIO directory listing. %1 is the query the user entered.", + "Query Results from '%1'", text); +} + QUrl DolphinSearchBox::balooUrlForSearching() const { #ifdef HAVE_BALOO @@ -491,8 +501,7 @@ QUrl DolphinSearchBox::balooUrlForSearching() const query.setSearchString(queryStrings.join(QLatin1Char(' '))); - return query.toSearchUrl(i18nc("@title UDS_DISPLAY_NAME for a KIO directory listing. %1 is the query the user entered.", - "Query Results from '%1'", text)); + return query.toSearchUrl(queryTitle(text)); #else return QUrl(); #endif @@ -541,7 +550,7 @@ bool DolphinSearchBox::isIndexingEnabled() const { #ifdef HAVE_BALOO const Baloo::IndexerConfig searchInfo; - return searchInfo.fileIndexingEnabled() && searchInfo.shouldBeIndexed(searchPath().toLocalFile()); + return searchInfo.fileIndexingEnabled() && !searchPath().isEmpty() && searchInfo.shouldBeIndexed(searchPath().toLocalFile()); #else return false; #endif diff --git a/src/search/dolphinsearchbox.h b/src/search/dolphinsearchbox.h index 5fef4ec5a..4afd752bc 100644 --- a/src/search/dolphinsearchbox.h +++ b/src/search/dolphinsearchbox.h @@ -118,8 +118,6 @@ signals: */ void searchTextChanged(const QString& text); - void returnPressed(); - /** * Emitted as soon as the search box should get closed. */ @@ -131,6 +129,7 @@ signals: * @see DolphinSearchBox::setActive() */ void activated(); + void focusViewRequest(); private slots: void emitSearchRequest(); @@ -162,6 +161,8 @@ private: bool isIndexingEnabled() const; private: + QString queryTitle(const QString& text) const; + bool m_startedSearching; bool m_active; diff --git a/src/settings/dolphin_detailsmodesettings.kcfg b/src/settings/dolphin_detailsmodesettings.kcfg index e9a8fb28d..6ef344ac4 100644 --- a/src/settings/dolphin_detailsmodesettings.kcfg +++ b/src/settings/dolphin_detailsmodesettings.kcfg @@ -44,5 +44,13 @@ <label>Expandable folders</label> <default>true</default> </entry> + <entry name="DirectorySizeCount" type="Bool"> + <label>Whether or not content count is use as directory size</label> + <default>true</default> + </entry> + <entry name="RecursiveDirectorySizeLimit" type="UInt"> + <label>Recursive directory size limit</label> + <default>10</default> + </entry> </group> </kcfg> diff --git a/src/settings/dolphin_generalsettings.kcfg b/src/settings/dolphin_generalsettings.kcfg index fca70656d..c397b2945 100644 --- a/src/settings/dolphin_generalsettings.kcfg +++ b/src/settings/dolphin_generalsettings.kcfg @@ -42,6 +42,10 @@ <label>Home URL</label> <default code="true">QUrl::fromLocalFile(QDir::homePath()).toDisplayString(QUrl::PreferLocalFile)</default> </entry> + <entry name="RememberOpenedTabs" type="Bool"> + <label>Remember open folders and tabs</label> + <default>true</default> + </entry> <entry name="SplitView" type="Bool"> <label>Split the view into two panes</label> <default>false</default> diff --git a/src/settings/kcm/kcmdolphingeneral.cpp b/src/settings/kcm/kcmdolphingeneral.cpp index a82cb3858..39eccff76 100644 --- a/src/settings/kcm/kcmdolphingeneral.cpp +++ b/src/settings/kcm/kcmdolphingeneral.cpp @@ -33,12 +33,10 @@ K_PLUGIN_FACTORY(KCMDolphinGeneralConfigFactory, registerPlugin<DolphinGeneralConfigModule>(QStringLiteral("dolphingeneral"));) -DolphinGeneralConfigModule::DolphinGeneralConfigModule(QWidget* parent, const QVariantList& args) : - KCModule(parent), +DolphinGeneralConfigModule::DolphinGeneralConfigModule(QWidget *parent, const QVariantList &args) : + KCModule(parent, args), m_pages() { - Q_UNUSED(args) - setButtons(KCModule::Default | KCModule::Help); QVBoxLayout* topLayout = new QVBoxLayout(this); @@ -49,29 +47,17 @@ DolphinGeneralConfigModule::DolphinGeneralConfigModule(QWidget* parent, const QV // initialize 'Behavior' tab BehaviorSettingsPage* behaviorPage = new BehaviorSettingsPage(QUrl::fromLocalFile(QDir::homePath()), tabWidget); tabWidget->addTab(behaviorPage, i18nc("@title:tab Behavior settings", "Behavior")); -#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 64, 0) - connect(behaviorPage, &BehaviorSettingsPage::changed, this, QOverload<>::of(&DolphinGeneralConfigModule::changed)); -#else connect(behaviorPage, &BehaviorSettingsPage::changed, this, &DolphinGeneralConfigModule::markAsChanged); -#endif // initialize 'Previews' tab PreviewsSettingsPage* previewsPage = new PreviewsSettingsPage(tabWidget); tabWidget->addTab(previewsPage, i18nc("@title:tab Previews settings", "Previews")); -#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 64, 0) - connect(previewsPage, &PreviewsSettingsPage::changed, this, QOverload<>::of(&DolphinGeneralConfigModule::changed)); -#else connect(previewsPage, &PreviewsSettingsPage::changed, this, &DolphinGeneralConfigModule::markAsChanged); -#endif // initialize 'Confirmations' tab ConfirmationsSettingsPage* confirmationsPage = new ConfirmationsSettingsPage(tabWidget); tabWidget->addTab(confirmationsPage, i18nc("@title:tab Confirmations settings", "Confirmations")); -#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 64, 0) - connect(confirmationsPage, &ConfirmationsSettingsPage::changed, this, QOverload<>::of(&DolphinGeneralConfigModule::changed)); -#else connect(confirmationsPage, &ConfirmationsSettingsPage::changed, this, &DolphinGeneralConfigModule::markAsChanged); -#endif m_pages.append(behaviorPage); m_pages.append(previewsPage); m_pages.append(confirmationsPage); @@ -85,14 +71,14 @@ DolphinGeneralConfigModule::~DolphinGeneralConfigModule() void DolphinGeneralConfigModule::save() { - foreach (SettingsPageBase* page, m_pages) { + for (SettingsPageBase* page : qAsConst(m_pages)) { page->applySettings(); } } void DolphinGeneralConfigModule::defaults() { - foreach (SettingsPageBase* page, m_pages) { + for (SettingsPageBase* page : qAsConst(m_pages)) { page->applySettings(); } } diff --git a/src/settings/kcm/kcmdolphingeneral.h b/src/settings/kcm/kcmdolphingeneral.h index c542c0139..2b60c7591 100644 --- a/src/settings/kcm/kcmdolphingeneral.h +++ b/src/settings/kcm/kcmdolphingeneral.h @@ -34,7 +34,7 @@ class DolphinGeneralConfigModule : public KCModule Q_OBJECT public: - DolphinGeneralConfigModule(QWidget* parent, const QVariantList& args); + DolphinGeneralConfigModule(QWidget *parent, const QVariantList &args); ~DolphinGeneralConfigModule() override; void save() override; diff --git a/src/settings/kcm/kcmdolphinnavigation.cpp b/src/settings/kcm/kcmdolphinnavigation.cpp index 2cdabdeee..f8de4eed2 100644 --- a/src/settings/kcm/kcmdolphinnavigation.cpp +++ b/src/settings/kcm/kcmdolphinnavigation.cpp @@ -29,23 +29,17 @@ K_PLUGIN_FACTORY(KCMDolphinNavigationConfigFactory, registerPlugin<DolphinNavigationConfigModule>(QStringLiteral("dolphinnavigation"));) -DolphinNavigationConfigModule::DolphinNavigationConfigModule(QWidget* parent, const QVariantList& args) : - KCModule(parent), +DolphinNavigationConfigModule::DolphinNavigationConfigModule(QWidget *parent, const QVariantList &args) : + KCModule(parent, args), m_navigation(nullptr) { - Q_UNUSED(args) - setButtons(KCModule::Default | KCModule::Help); QVBoxLayout* topLayout = new QVBoxLayout(this); topLayout->setContentsMargins(0, 0, 0, 0); m_navigation = new NavigationSettingsPage(this); -#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 64, 0) - connect(m_navigation, &NavigationSettingsPage::changed, this, QOverload<>::of(&DolphinNavigationConfigModule::changed)); -#else connect(m_navigation, &NavigationSettingsPage::changed, this, &DolphinNavigationConfigModule::markAsChanged); -#endif topLayout->addWidget(m_navigation, 0, {}); } diff --git a/src/settings/kcm/kcmdolphinnavigation.h b/src/settings/kcm/kcmdolphinnavigation.h index 2bcc7abab..7eb6b26e7 100644 --- a/src/settings/kcm/kcmdolphinnavigation.h +++ b/src/settings/kcm/kcmdolphinnavigation.h @@ -32,14 +32,14 @@ class DolphinNavigationConfigModule : public KCModule Q_OBJECT public: - DolphinNavigationConfigModule(QWidget* parent, const QVariantList& args); + DolphinNavigationConfigModule(QWidget *parent, const QVariantList &args); ~DolphinNavigationConfigModule() override; void save() override; void defaults() override; private: - NavigationSettingsPage* m_navigation; + NavigationSettingsPage *m_navigation; }; #endif diff --git a/src/settings/kcm/kcmdolphinservices.cpp b/src/settings/kcm/kcmdolphinservices.cpp index e6a8867d7..92e71bae0 100644 --- a/src/settings/kcm/kcmdolphinservices.cpp +++ b/src/settings/kcm/kcmdolphinservices.cpp @@ -30,22 +30,16 @@ K_PLUGIN_FACTORY(KCMDolphinServicesConfigFactory, registerPlugin<DolphinServicesConfigModule>(QStringLiteral("dolphinservices"));) DolphinServicesConfigModule::DolphinServicesConfigModule(QWidget* parent, const QVariantList& args) : - KCModule(parent), + KCModule(parent, args), m_services(nullptr) { - Q_UNUSED(args) - setButtons(KCModule::Default | KCModule::Help); QVBoxLayout* topLayout = new QVBoxLayout(this); topLayout->setContentsMargins(0, 0, 0, 0); m_services = new ServicesSettingsPage(this); -#if KCONFIGWIDGETS_VERSION < QT_VERSION_CHECK(5, 64, 0) - connect(m_services, &ServicesSettingsPage::changed, this, QOverload<>::of(&DolphinServicesConfigModule::changed)); -#else connect(m_services, &ServicesSettingsPage::changed, this, &DolphinServicesConfigModule::markAsChanged); -#endif topLayout->addWidget(m_services, 0, {}); } diff --git a/src/settings/kcm/kcmdolphinservices.h b/src/settings/kcm/kcmdolphinservices.h index 6c6af6728..a567450ca 100644 --- a/src/settings/kcm/kcmdolphinservices.h +++ b/src/settings/kcm/kcmdolphinservices.h @@ -39,7 +39,7 @@ public: void defaults() override; private: - ServicesSettingsPage* m_services; + ServicesSettingsPage *m_services; }; #endif diff --git a/src/settings/kcm/kcmdolphinviewmodes.cpp b/src/settings/kcm/kcmdolphinviewmodes.cpp index 4fac11600..91abe5cd4 100644 --- a/src/settings/kcm/kcmdolphinviewmodes.cpp +++ b/src/settings/kcm/kcmdolphinviewmodes.cpp @@ -33,12 +33,10 @@ K_PLUGIN_FACTORY(KCMDolphinViewModesConfigFactory, registerPlugin<DolphinViewModesConfigModule>(QStringLiteral("dolphinviewmodes"));) -DolphinViewModesConfigModule::DolphinViewModesConfigModule(QWidget* parent, const QVariantList& args) : - KCModule(parent), +DolphinViewModesConfigModule::DolphinViewModesConfigModule(QWidget *parent, const QVariantList &args) : + KCModule(parent, args), m_tabs() { - Q_UNUSED(args) - setButtons(KCModule::Default | KCModule::Help); QVBoxLayout* topLayout = new QVBoxLayout(this); @@ -74,7 +72,7 @@ DolphinViewModesConfigModule::~DolphinViewModesConfigModule() void DolphinViewModesConfigModule::save() { - foreach (ViewSettingsTab* tab, m_tabs) { + for (ViewSettingsTab *tab : qAsConst(m_tabs)) { tab->applySettings(); } reparseConfiguration(); @@ -82,7 +80,7 @@ void DolphinViewModesConfigModule::save() void DolphinViewModesConfigModule::defaults() { - foreach (ViewSettingsTab* tab, m_tabs) { + for (ViewSettingsTab *tab : qAsConst(m_tabs)) { tab->restoreDefaultSettings(); } reparseConfiguration(); @@ -90,13 +88,15 @@ void DolphinViewModesConfigModule::defaults() void DolphinViewModesConfigModule::reparseConfiguration() { - QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KonqMain"), QStringLiteral("org.kde.Konqueror.Main"), QStringLiteral("reparseConfiguration")); + QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KonqMain"), + QStringLiteral("org.kde.Konqueror.Main"), + QStringLiteral("reparseConfiguration")); QDBusConnection::sessionBus().send(message); } void DolphinViewModesConfigModule::viewModeChanged() { - emit changed(true); + emit markAsChanged(); } #include "kcmdolphinviewmodes.moc" diff --git a/src/settings/kcm/kcmdolphinviewmodes.h b/src/settings/kcm/kcmdolphinviewmodes.h index c3775adff..40965b0e6 100644 --- a/src/settings/kcm/kcmdolphinviewmodes.h +++ b/src/settings/kcm/kcmdolphinviewmodes.h @@ -32,7 +32,7 @@ class DolphinViewModesConfigModule : public KCModule Q_OBJECT public: - DolphinViewModesConfigModule(QWidget* parent, const QVariantList& args); + DolphinViewModesConfigModule(QWidget *parent, const QVariantList &args); ~DolphinViewModesConfigModule() override; void save() override; diff --git a/src/settings/services/servicemenuinstaller/CMakeLists.txt b/src/settings/services/servicemenuinstaller/CMakeLists.txt index 988899936..46b159079 100644 --- a/src/settings/services/servicemenuinstaller/CMakeLists.txt +++ b/src/settings/services/servicemenuinstaller/CMakeLists.txt @@ -6,5 +6,10 @@ target_link_libraries(servicemenuinstaller PRIVATE Qt5::Core Qt5::Gui KF5::I18n + KF5::CoreAddons ) + +if(HAVE_PACKAGEKIT) + target_link_libraries(servicemenuinstaller PRIVATE PK::packagekitqt5) +endif() install(TARGETS servicemenuinstaller ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/settings/services/servicemenuinstaller/servicemenuinstaller.cpp b/src/settings/services/servicemenuinstaller/servicemenuinstaller.cpp index 06f34c6b9..89f2545f8 100644 --- a/src/settings/services/servicemenuinstaller/servicemenuinstaller.cpp +++ b/src/settings/services/servicemenuinstaller/servicemenuinstaller.cpp @@ -20,28 +20,42 @@ #include <QDebug> #include <QProcess> +#include <QTimer> #include <QStandardPaths> #include <QDir> #include <QDirIterator> #include <QCommandLineParser> #include <QMimeDatabase> #include <QUrl> -#include <QDesktopServices> #include <QGuiApplication> - #include <KLocalizedString> +#include <KShell> + +#include "../../../config-packagekit.h" + +const static QStringList binaryPackages = {QStringLiteral("application/vnd.debian.binary-package"), + QStringLiteral("application/x-rpm"), + QStringLiteral("application/x-xz"), + QStringLiteral("application/zstd")}; +enum PackageOperation { + Install, + Uninstall +}; + +#ifdef HAVE_PACKAGEKIT +#include <PackageKit/Daemon> +#include <PackageKit/Details> +#include <PackageKit/Transaction> +#else +#include <QDesktopServices> +#endif // @param msg Error that gets logged to CLI Q_NORETURN void fail(const QString &str) { qCritical() << str; - - QProcess process; - const QStringList args = {"--passivepopup", i18n("Dolphin service menu installation failed"), "15"}; - process.start("kdialog", args, QIODevice::ReadOnly); - if (!process.waitForStarted()) { - qFatal("Failed to run kdialog"); - } + const QStringList args = {"--detailederror" ,i18n("Dolphin service menu installation failed"), str}; + QProcess::startDetached("kdialog", args); exit(1); } @@ -52,6 +66,84 @@ QString getServiceMenusDir() return QDir(dataLocation).absoluteFilePath("kservices5/ServiceMenus"); } +#ifdef HAVE_PACKAGEKIT +void packageKitInstall(const QString &fileName) +{ + PackageKit::Transaction *transaction = PackageKit::Daemon::installFile(fileName); + + const auto exitWithError = [=](PackageKit::Transaction::Error, const QString &details) { + fail(details); + }; + + QObject::connect(transaction, &PackageKit::Transaction::finished, + [=](PackageKit::Transaction::Exit status, uint) { + if (status == PackageKit::Transaction::ExitSuccess) { + exit(0); + } + // Fallback error handling + QTimer::singleShot(500, [=](){ + fail(i18n("Failed to install \"%1\", exited with status \"%2\"", + fileName, QVariant::fromValue(status).toString())); + }); + }); + QObject::connect(transaction, &PackageKit::Transaction::errorCode, exitWithError); +} + +void packageKitUninstall(const QString &fileName) +{ + const auto exitWithError = [=](PackageKit::Transaction::Error, const QString &details) { + fail(details); + }; + const auto uninstallLambda = [=](PackageKit::Transaction::Exit status, uint) { + if (status == PackageKit::Transaction::ExitSuccess) { + exit(0); + } + }; + + PackageKit::Transaction *transaction = PackageKit::Daemon::getDetailsLocal(fileName); + QObject::connect(transaction, &PackageKit::Transaction::details, + [=](const PackageKit::Details &details) { + PackageKit::Transaction *transaction = PackageKit::Daemon::removePackage(details.packageId()); + QObject::connect(transaction, &PackageKit::Transaction::finished, uninstallLambda); + QObject::connect(transaction, &PackageKit::Transaction::errorCode, exitWithError); + }); + + QObject::connect(transaction, &PackageKit::Transaction::errorCode, exitWithError); + // Fallback error handling + QObject::connect(transaction, &PackageKit::Transaction::finished, + [=](PackageKit::Transaction::Exit status, uint) { + if (status != PackageKit::Transaction::ExitSuccess) { + QTimer::singleShot(500, [=]() { + fail(i18n("Failed to uninstall \"%1\", exited with status \"%2\"", + fileName, QVariant::fromValue(status).toString())); + }); + } + }); + } +#endif + +Q_NORETURN void packageKit(PackageOperation operation, const QString &fileName) +{ +#ifdef HAVE_PACKAGEKIT + QFileInfo fileInfo(fileName); + if (!fileInfo.exists()) { + fail(i18n("The file does not exist!")); + } + const QString absPath = fileInfo.absoluteFilePath(); + if (operation == PackageOperation::Install) { + packageKitInstall(absPath); + } else { + packageKitUninstall(absPath); + } + QGuiApplication::exec(); // For event handling, no return after signals finish + fail(i18n("Unknown error when installing package")); +#else + Q_UNUSED(operation) + QDesktopServices::openUrl(QUrl(fileName)); + exit(0); +#endif +} + struct UncompressCommand { QString command; @@ -59,6 +151,11 @@ struct UncompressCommand QStringList args2; }; +enum ScriptExecution{ + Process, + Konsole +}; + void runUncompress(const QString &inputPath, const QString &outputPath) { QVector<QPair<QStringList, UncompressCommand>> mimeTypeToCommand; @@ -126,12 +223,24 @@ QString findRecursive(const QString &dir, const QString &basename) return QString(); } -bool runScriptOnce(const QString &path, const QStringList &args) +bool runScriptOnce(const QString &path, const QStringList &args, ScriptExecution execution) { QProcess process; process.setWorkingDirectory(QFileInfo(path).absolutePath()); - process.start(path, args, QIODevice::NotOpen); + const static bool konsoleAvailable = !QStandardPaths::findExecutable("konsole").isEmpty(); + if (konsoleAvailable && execution == ScriptExecution::Konsole) { + QString bashCommand = KShell::quoteArg(path) + ' '; + if (!args.isEmpty()) { + bashCommand.append(args.join(' ')); + } + bashCommand.append("|| $SHELL"); + // If the install script fails a shell opens and the user can fix the problem + // without an error konsole closes + process.start("konsole", QStringList() << "-e" << "bash" << "-c" << bashCommand, QIODevice::NotOpen); + } else { + process.start(path, args, QIODevice::NotOpen); + } if (!process.waitForStarted()) { fail(i18n("Failed to run installer script %1", path)); } @@ -163,11 +272,11 @@ bool runScriptVariants(const QString &path, bool hasArgVariants, const QStringLi qInfo() << "[servicemenuinstaller]: Trying to run installer/uninstaller" << path; if (hasArgVariants) { for (const auto &arg : argVariants) { - if (runScriptOnce(path, {arg})) { + if (runScriptOnce(path, {arg}, ScriptExecution::Process)) { return true; } } - } else if (runScriptOnce(path, {})) { + } else if (runScriptOnce(path, {}, ScriptExecution::Konsole)) { return true; } @@ -202,9 +311,8 @@ bool cmdInstall(const QString &archive, QString &errorText) return false; } } else { - const QStringList binaryPackages = {"application/vnd.debian.binary-package", "application/x-rpm"}; if (binaryPackages.contains(QMimeDatabase().mimeTypeForFile(archive).name())) { - return QDesktopServices::openUrl(QUrl(archive)); + packageKit(PackageOperation::Install, archive); } const QString dir = generateDirPath(archive); if (QFile::exists(dir)) { @@ -247,7 +355,11 @@ bool cmdInstall(const QString &archive, QString &errorText) } if (!installerPath.isEmpty()) { - return runScriptVariants(installerPath, true, {"--local", "--local-install", "--install"}, errorText); + // Try to run script without variants first + if (!runScriptVariants(installerPath, false, {}, errorText)) { + return runScriptVariants(installerPath, true, {"--local", "--local-install", "--install"}, errorText); + } + return true; } fail(i18n("Failed to find an installation script in %1", dir)); @@ -268,6 +380,9 @@ bool cmdUninstall(const QString &archive, QString &errorText) return false; } } else { + if (binaryPackages.contains(QMimeDatabase().mimeTypeForFile(archive).name())) { + packageKit(PackageOperation::Uninstall, archive); + } const QString dir = generateDirPath(archive); // Try "deinstall" first diff --git a/src/settings/services/servicessettingspage.cpp b/src/settings/services/servicessettingspage.cpp index fe900bc5c..ba6c48f80 100644 --- a/src/settings/services/servicessettingspage.cpp +++ b/src/settings/services/servicessettingspage.cpp @@ -38,6 +38,7 @@ #include <QListWidget> #include <QShowEvent> #include <QSortFilterProxyModel> +#include <QLineEdit> namespace { @@ -61,6 +62,11 @@ ServicesSettingsPage::ServicesSettingsPage(QWidget* parent) : "Select which services should " "be shown in the context menu:"), this); label->setWordWrap(true); + m_searchLineEdit = new QLineEdit(this); + m_searchLineEdit->setPlaceholderText(i18nc("@label:textbox", "Search...")); + connect(m_searchLineEdit, &QLineEdit::textChanged, this, [=](const QString &filter){ + m_sortModel->setFilterFixedString(filter); + }); m_listView = new QListView(this); ServiceItemDelegate* delegate = new ServiceItemDelegate(m_listView, m_listView); @@ -69,6 +75,8 @@ ServicesSettingsPage::ServicesSettingsPage(QWidget* parent) : m_sortModel->setSourceModel(m_serviceModel); m_sortModel->setSortRole(Qt::DisplayRole); m_sortModel->setSortLocaleAware(true); + m_sortModel->setFilterRole(Qt::DisplayRole); + m_sortModel->setFilterCaseSensitivity(Qt::CaseInsensitive); m_listView->setModel(m_sortModel); m_listView->setItemDelegate(delegate); m_listView->setVerticalScrollMode(QListView::ScrollPerPixel); @@ -80,6 +88,7 @@ ServicesSettingsPage::ServicesSettingsPage(QWidget* parent) : connect(downloadButton, &KNS3::Button::dialogFinished, this, &ServicesSettingsPage::loadServices); topLayout->addWidget(label); + topLayout->addWidget(m_searchLineEdit); topLayout->addWidget(m_listView); topLayout->addWidget(downloadButton); @@ -236,6 +245,7 @@ void ServicesSettingsPage::loadServices() } m_sortModel->sort(Qt::DisplayRole); + m_searchLineEdit->setFocus(Qt::OtherFocusReason); } void ServicesSettingsPage::loadVersionControlSystems() diff --git a/src/settings/services/servicessettingspage.h b/src/settings/services/servicessettingspage.h index cd4cbe52f..dd53cf5dd 100644 --- a/src/settings/services/servicessettingspage.h +++ b/src/settings/services/servicessettingspage.h @@ -26,6 +26,7 @@ class QListView; class QSortFilterProxyModel; class ServiceModel; +class QLineEdit; /** * @brief Page for the 'Services' settings of the Dolphin settings dialog. @@ -74,6 +75,7 @@ private: ServiceModel* m_serviceModel; QSortFilterProxyModel* m_sortModel; QListView* m_listView; + QLineEdit *m_searchLineEdit; QStringList m_enabledVcsPlugins; }; diff --git a/src/settings/startup/startupsettingspage.cpp b/src/settings/startup/startupsettingspage.cpp index d7d5fba4c..eb1495746 100644 --- a/src/settings/startup/startupsettingspage.cpp +++ b/src/settings/startup/startupsettingspage.cpp @@ -27,18 +27,24 @@ #include <KLocalizedString> #include <KMessageBox> +#include <QButtonGroup> #include <QCheckBox> #include <QFileDialog> #include <QLineEdit> #include <QPushButton> +#include <QRadioButton> #include <QFormLayout> +#include <QGridLayout> #include <QHBoxLayout> -#include <QVBoxLayout> StartupSettingsPage::StartupSettingsPage(const QUrl& url, QWidget* parent) : SettingsPageBase(parent), m_url(url), m_homeUrl(nullptr), + m_homeUrlBoxLayoutContainer(nullptr), + m_buttonBoxLayoutContainer(nullptr), + m_rememberOpenedTabsRadioButton(nullptr), + m_homeUrlRadioButton(nullptr), m_splitView(nullptr), m_editableUrl(nullptr), m_showFullPath(nullptr), @@ -48,9 +54,19 @@ StartupSettingsPage::StartupSettingsPage(const QUrl& url, QWidget* parent) : { QFormLayout* topLayout = new QFormLayout(this); + m_rememberOpenedTabsRadioButton = new QRadioButton(i18nc("@option:radio Startup Settings", "Folders, tabs, and window state from last time")); + m_homeUrlRadioButton = new QRadioButton(); + // HACK: otherwise the radio button has too much spacing in a grid layout + m_homeUrlRadioButton->setMaximumWidth(24); + + QButtonGroup* initialViewGroup = new QButtonGroup(this); + initialViewGroup->addButton(m_rememberOpenedTabsRadioButton); + initialViewGroup->addButton(m_homeUrlRadioButton); + // create 'Home URL' editor - QHBoxLayout* homeUrlBoxLayout = new QHBoxLayout(); + m_homeUrlBoxLayoutContainer = new QWidget(this); + QHBoxLayout* homeUrlBoxLayout = new QHBoxLayout(m_homeUrlBoxLayoutContainer); homeUrlBoxLayout->setContentsMargins(0, 0, 0, 0); m_homeUrl = new QLineEdit(); @@ -67,7 +83,8 @@ StartupSettingsPage::StartupSettingsPage(const QUrl& url, QWidget* parent) : connect(selectHomeUrlButton, &QPushButton::clicked, this, &StartupSettingsPage::selectHomeUrl); - QHBoxLayout* buttonBoxLayout = new QHBoxLayout(); + m_buttonBoxLayoutContainer = new QWidget(this); + QHBoxLayout* buttonBoxLayout = new QHBoxLayout(m_buttonBoxLayoutContainer); buttonBoxLayout->setContentsMargins(0, 0, 0, 0); QPushButton* useCurrentButton = new QPushButton(i18nc("@action:button", "Use Current Location")); @@ -79,41 +96,50 @@ StartupSettingsPage::StartupSettingsPage(const QUrl& url, QWidget* parent) : connect(useDefaultButton, &QPushButton::clicked, this, &StartupSettingsPage::useDefaultLocation); - QVBoxLayout* homeBoxLayout = new QVBoxLayout(); - homeBoxLayout->setContentsMargins(0, 0, 0, 0); - homeBoxLayout->addLayout(homeUrlBoxLayout); - homeBoxLayout->addLayout(buttonBoxLayout); + QGridLayout* startInLocationLayout = new QGridLayout(); + startInLocationLayout->setHorizontalSpacing(0); + startInLocationLayout->setContentsMargins(0, 0, 0, 0); + startInLocationLayout->addWidget(m_homeUrlRadioButton, 0, 0); + startInLocationLayout->addWidget(m_homeUrlBoxLayoutContainer, 0, 1); + startInLocationLayout->addWidget(m_buttonBoxLayoutContainer, 1, 1); - topLayout->addRow(i18nc("@label:textbox", "Start in:"), homeBoxLayout); + topLayout->addRow(i18nc("@label:textbox", "Show on startup:"), m_rememberOpenedTabsRadioButton); + topLayout->addRow(QString(), startInLocationLayout); topLayout->addItem(new QSpacerItem(0, Dolphin::VERTICAL_SPACER_HEIGHT, QSizePolicy::Fixed, QSizePolicy::Fixed)); - - // create 'Split view', 'Show full path', 'Editable location' and 'Filter bar' checkboxes - m_splitView = new QCheckBox(i18nc("@option:check Startup Settings", "Split view mode")); - topLayout->addRow(i18nc("@label:checkbox", "Window options:"), m_splitView); - m_editableUrl = new QCheckBox(i18nc("@option:check Startup Settings", "Editable location bar")); + m_splitView = new QCheckBox(i18nc("@option:check Startup Settings", "Begin in split view mode")); + topLayout->addRow(i18n("New windows:"), m_splitView); + m_filterBar = new QCheckBox(i18nc("@option:check Startup Settings", "Show filter bar")); + topLayout->addRow(QString(), m_filterBar); + m_editableUrl = new QCheckBox(i18nc("@option:check Startup Settings", "Make location bar editable")); topLayout->addRow(QString(), m_editableUrl); + + topLayout->addItem(new QSpacerItem(0, Dolphin::VERTICAL_SPACER_HEIGHT, QSizePolicy::Fixed, QSizePolicy::Fixed)); + + m_openExternallyCalledFolderInNewTab = new QCheckBox(i18nc("@option:check Startup Settings", "Open new folders in tabs")); + topLayout->addRow(i18nc("@label:checkbox", "General:"), m_openExternallyCalledFolderInNewTab); m_showFullPath = new QCheckBox(i18nc("@option:check Startup Settings", "Show full path inside location bar")); topLayout->addRow(QString(), m_showFullPath); - m_filterBar = new QCheckBox(i18nc("@option:check Startup Settings", "Show filter bar")); - topLayout->addRow(QString(), m_filterBar); m_showFullPathInTitlebar = new QCheckBox(i18nc("@option:check Startup Settings", "Show full path in title bar")); topLayout->addRow(QString(), m_showFullPathInTitlebar); - m_openExternallyCalledFolderInNewTab = new QCheckBox(i18nc("@option:check Startup Settings", "Open new folders in tabs")); - topLayout->addRow(QString(), m_openExternallyCalledFolderInNewTab); - loadSettings(); + updateInitialViewOptions(); + connect(m_homeUrl, &QLineEdit::textChanged, this, &StartupSettingsPage::slotSettingsChanged); + connect(m_rememberOpenedTabsRadioButton, &QRadioButton::toggled, this, &StartupSettingsPage::slotSettingsChanged); + connect(m_homeUrlRadioButton, &QRadioButton::toggled, this, &StartupSettingsPage::slotSettingsChanged); + connect(m_splitView, &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged); connect(m_editableUrl, &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged); - connect(m_showFullPath, &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged); connect(m_filterBar, &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged); - connect(m_showFullPathInTitlebar, &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged); + connect(m_openExternallyCalledFolderInNewTab, &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged); + connect(m_showFullPath, &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged); + connect(m_showFullPathInTitlebar, &QCheckBox::toggled, this, &StartupSettingsPage::slotSettingsChanged); } StartupSettingsPage::~StartupSettingsPage() @@ -132,12 +158,21 @@ void StartupSettingsPage::applySettings() KMessageBox::error(this, i18nc("@info", "The location for the home folder is invalid or does not exist, it will not be applied.")); } + // Remove saved state if "remember open tabs" has been turned off + if (!m_rememberOpenedTabsRadioButton->isChecked()) { + KConfigGroup windowState{KSharedConfig::openConfig(QStringLiteral("dolphinrc")), "WindowState"}; + if (windowState.exists()) { + windowState.deleteGroup(); + } + } + + settings->setRememberOpenedTabs(m_rememberOpenedTabsRadioButton->isChecked()); settings->setSplitView(m_splitView->isChecked()); settings->setEditableUrl(m_editableUrl->isChecked()); - settings->setShowFullPath(m_showFullPath->isChecked()); settings->setFilterBar(m_filterBar->isChecked()); - settings->setShowFullPathInTitlebar(m_showFullPathInTitlebar->isChecked()); settings->setOpenExternallyCalledFolderInNewTab(m_openExternallyCalledFolderInNewTab->isChecked()); + settings->setShowFullPath(m_showFullPath->isChecked()); + settings->setShowFullPathInTitlebar(m_showFullPathInTitlebar->isChecked()); settings->save(); } @@ -155,9 +190,18 @@ void StartupSettingsPage::slotSettingsChanged() // to apply the startup settings only if they have been explicitly changed by the user // (see bug #254947). GeneralSettings::setModifiedStartupSettings(true); + + // Enable and disable home URL controls appropriately + updateInitialViewOptions(); emit changed(); } +void StartupSettingsPage::updateInitialViewOptions() +{ + m_homeUrlBoxLayoutContainer->setEnabled(m_homeUrlRadioButton->isChecked()); + m_buttonBoxLayoutContainer->setEnabled(m_homeUrlRadioButton->isChecked()); +} + void StartupSettingsPage::selectHomeUrl() { const QUrl homeUrl(QUrl::fromUserInput(m_homeUrl->text(), QString(), QUrl::AssumeLocalFile)); @@ -182,6 +226,8 @@ void StartupSettingsPage::loadSettings() { const QUrl url(Dolphin::homeUrl()); m_homeUrl->setText(url.toDisplayString(QUrl::PreferLocalFile)); + m_rememberOpenedTabsRadioButton->setChecked(GeneralSettings::rememberOpenedTabs()); + m_homeUrlRadioButton->setChecked(!GeneralSettings::rememberOpenedTabs()); m_splitView->setChecked(GeneralSettings::splitView()); m_editableUrl->setChecked(GeneralSettings::editableUrl()); m_showFullPath->setChecked(GeneralSettings::showFullPath()); diff --git a/src/settings/startup/startupsettingspage.h b/src/settings/startup/startupsettingspage.h index a5e0b236f..d1c937f1f 100644 --- a/src/settings/startup/startupsettingspage.h +++ b/src/settings/startup/startupsettingspage.h @@ -23,8 +23,9 @@ #include <QUrl> -class QLineEdit; class QCheckBox; +class QLineEdit; +class QRadioButton; /** * @brief Page for the 'Startup' settings of the Dolphin settings dialog. @@ -48,6 +49,7 @@ public: private slots: void slotSettingsChanged(); + void updateInitialViewOptions(); void selectHomeUrl(); void useCurrentLocation(); void useDefaultLocation(); @@ -58,6 +60,10 @@ private: private: QUrl m_url; QLineEdit* m_homeUrl; + QWidget* m_homeUrlBoxLayoutContainer; + QWidget* m_buttonBoxLayoutContainer; + QRadioButton* m_rememberOpenedTabsRadioButton; + QRadioButton* m_homeUrlRadioButton; QCheckBox* m_splitView; QCheckBox* m_editableUrl; diff --git a/src/settings/viewmodes/viewsettingstab.cpp b/src/settings/viewmodes/viewsettingstab.cpp index 06b0b8cf5..fa891133b 100644 --- a/src/settings/viewmodes/viewsettingstab.cpp +++ b/src/settings/viewmodes/viewsettingstab.cpp @@ -33,6 +33,10 @@ #include <QComboBox> #include <QHelpEvent> #include <QFormLayout> +#include <QSpinBox> +#include <QRadioButton> +#include <QButtonGroup> +#include <QLabel> ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget* parent) : QWidget(parent), @@ -42,11 +46,11 @@ ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget* parent) : m_fontRequester(nullptr), m_widthBox(nullptr), m_maxLinesBox(nullptr), - m_expandableFolders(nullptr) + m_expandableFolders(nullptr), + m_recursiveDirectorySizeLimit(nullptr) { QFormLayout* topLayout = new QFormLayout(this); - // Create "Icon Size" section const int minRange = ZoomLevelInfo::minimumLevel(); const int maxRange = ZoomLevelInfo::maximumLevel(); @@ -75,7 +79,6 @@ ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget* parent) : m_fontRequester = new DolphinFontRequester(this); topLayout->addRow(i18nc("@label:listbox", "Label font:"), m_fontRequester); - switch (m_mode) { case IconsMode: { m_widthBox = new QComboBox(); @@ -107,8 +110,30 @@ ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget* parent) : case DetailsMode: m_expandableFolders = new QCheckBox(i18nc("@option:check", "Expandable")); topLayout->addRow(i18nc("@label:checkbox", "Folders:"), m_expandableFolders); - break; - default: + +#ifndef Q_OS_WIN + // Sorting properties + m_numberOfItems = new QRadioButton(i18nc("option:radio", "Number of items")); + m_sizeOfContents = new QRadioButton(i18nc("option:radio", "Size of contents, up to ")); + + QButtonGroup* sortingModeGroup = new QButtonGroup(this); + sortingModeGroup->addButton(m_numberOfItems); + sortingModeGroup->addButton(m_sizeOfContents); + + m_recursiveDirectorySizeLimit = new QSpinBox(); + connect(m_recursiveDirectorySizeLimit, QOverload<int>::of(&QSpinBox::valueChanged), this, [this](int value) { + m_recursiveDirectorySizeLimit->setSuffix(i18np(" level deep", " levels deep", value)); + }); + m_recursiveDirectorySizeLimit->setRange(1, 20); + m_recursiveDirectorySizeLimit->setSingleStep(1); + + QHBoxLayout *contentsSizeLayout = new QHBoxLayout(); + contentsSizeLayout->addWidget(m_sizeOfContents); + contentsSizeLayout->addWidget(m_recursiveDirectorySizeLimit); + + topLayout->addRow(i18nc("@title:group", "Folder size displays:"), m_numberOfItems); + topLayout->addRow(QString(), contentsSizeLayout); +#endif break; } @@ -128,6 +153,11 @@ ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget* parent) : break; case DetailsMode: connect(m_expandableFolders, &QCheckBox::toggled, this, &ViewSettingsTab::changed); + connect(m_recursiveDirectorySizeLimit, QOverload<int>::of(&QSpinBox::valueChanged), this, &ViewSettingsTab::changed); + connect(m_numberOfItems, &QRadioButton::toggled, this, &ViewSettingsTab::changed); + connect(m_sizeOfContents, &QRadioButton::toggled, this, [=]() { + m_recursiveDirectorySizeLimit->setEnabled(m_sizeOfContents->isChecked()); + }); break; default: break; @@ -153,6 +183,8 @@ void ViewSettingsTab::applySettings() break; case DetailsMode: DetailsModeSettings::setExpandableFolders(m_expandableFolders->isChecked()); + DetailsModeSettings::setDirectorySizeCount(m_numberOfItems->isChecked()); + DetailsModeSettings::setRecursiveDirectorySizeLimit(m_recursiveDirectorySizeLimit->value()); break; default: break; @@ -201,6 +233,14 @@ void ViewSettingsTab::loadSettings() break; case DetailsMode: m_expandableFolders->setChecked(DetailsModeSettings::expandableFolders()); + if (DetailsModeSettings::directorySizeCount()) { + m_numberOfItems->setChecked(true); + m_recursiveDirectorySizeLimit->setEnabled(false); + } else { + m_sizeOfContents->setChecked(true); + m_recursiveDirectorySizeLimit->setEnabled(true); + } + m_recursiveDirectorySizeLimit->setValue(DetailsModeSettings::recursiveDirectorySizeLimit()); break; default: break; diff --git a/src/settings/viewmodes/viewsettingstab.h b/src/settings/viewmodes/viewsettingstab.h index fff882e5e..4d459fca2 100644 --- a/src/settings/viewmodes/viewsettingstab.h +++ b/src/settings/viewmodes/viewsettingstab.h @@ -28,6 +28,8 @@ class DolphinFontRequester; class QComboBox; class QCheckBox; class QSlider; +class QSpinBox; +class QRadioButton; /** * @brief Represents one tab of the view-settings page. @@ -72,6 +74,9 @@ private: QComboBox* m_widthBox; QComboBox* m_maxLinesBox; QCheckBox* m_expandableFolders; + QRadioButton* m_numberOfItems; + QRadioButton* m_sizeOfContents; + QSpinBox* m_recursiveDirectorySizeLimit; }; #endif diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp index d8f2aab93..f0dc17837 100644 --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -1200,7 +1200,7 @@ void DolphinView::slotPasteJobResult(KJob *job) emit errorMessage(job->errorString()); } if (!m_selectedUrls.isEmpty()) { - m_selectedUrls << KDirModel::simplifiedUrlList(m_selectedUrls); + m_selectedUrls = KDirModel::simplifiedUrlList(m_selectedUrls); } } @@ -1493,13 +1493,27 @@ void DolphinView::calculateItemCount(int& fileCount, KIO::filesize_t& totalFileSize) const { const int itemCount = m_model->count(); + + bool countFileSize = true; + + // In case we have a precomputed value + const auto job = KIO::statDetails(m_model->rootItem().url(), KIO::StatJob::SourceSide, KIO::StatRecursiveSize); + job->exec(); + const auto entry = job->statResult(); + if (entry.contains(KIO::UDSEntry::UDS_RECURSIVE_SIZE)) { + totalFileSize = static_cast<KIO::filesize_t>(entry.numberValue(KIO::UDSEntry::UDS_RECURSIVE_SIZE)); + countFileSize = false; + } + for (int i = 0; i < itemCount; ++i) { const KFileItem item = m_model->fileItem(i); if (item.isDir()) { ++folderCount; } else { ++fileCount; - totalFileSize += item.size(); + if (countFileSize) { + totalFileSize += item.size(); + } } } } diff --git a/src/views/dolphinviewactionhandler.cpp b/src/views/dolphinviewactionhandler.cpp index c61e1aaa9..e89e2e62c 100644 --- a/src/views/dolphinviewactionhandler.cpp +++ b/src/views/dolphinviewactionhandler.cpp @@ -283,7 +283,7 @@ void DolphinViewActionHandler::createActions() "<para>Hidden items only differ from other ones in that their " "name starts with a \".\". In general there is no need for " "users to access them which is why they are hidden.</para>")); - m_actionCollection->setDefaultShortcuts(showHiddenFiles, {Qt::ALT + Qt::Key_Period, Qt::CTRL + Qt::Key_H, Qt::Key_F8}); + m_actionCollection->setDefaultShortcuts(showHiddenFiles, KStandardShortcut::showHideHiddenFiles()); connect(showHiddenFiles, &KToggleAction::triggered, this, &DolphinViewActionHandler::toggleShowHiddenFiles); QAction* adjustViewProps = m_actionCollection->addAction(QStringLiteral("view_properties")); diff --git a/src/views/versioncontrol/kversioncontrolplugin.h b/src/views/versioncontrol/kversioncontrolplugin.h index 287974534..0d94a3fc8 100644 --- a/src/views/versioncontrol/kversioncontrolplugin.h +++ b/src/views/versioncontrol/kversioncontrolplugin.h @@ -180,14 +180,17 @@ public: virtual ItemVersion itemVersion(const KFileItem& item) const = 0; /** - * @return List of actions that are available for the items \p items. - * It is recommended to keep the number of returned actions small - * in case if an item is an unversioned directory that is not - * inside the hierarchy tree of the version control system. This - * prevents having a cluttered context menu for directories - * outside the version control system. + * @return List of actions that are available for the \p items in a version controlled + * path. */ - virtual QList<QAction*> actions(const KFileItemList& items) const = 0; + virtual QList<QAction*> versionControlActions(const KFileItemList& items) const = 0; + + /** + * @return List of actions that are available for the out of version control + * items \p items. It's opposed to the \p versionedActions. Common usage + * is for clone/checkout actions. + */ + virtual QList<QAction*> outOfVersionControlActions(const KFileItemList& items) const = 0; Q_SIGNALS: /** diff --git a/src/views/versioncontrol/versioncontrolobserver.cpp b/src/views/versioncontrol/versioncontrolobserver.cpp index 2d801686e..f6c74fb5f 100644 --- a/src/views/versioncontrol/versioncontrolobserver.cpp +++ b/src/views/versioncontrol/versioncontrolobserver.cpp @@ -118,11 +118,19 @@ QList<QAction*> VersionControlObserver::actions(const KFileItemList& items) cons } } - if (!m_model || hasNullItems || !isVersioned()) { + if (!m_model || hasNullItems) { return {}; } - return m_plugin->actions(items); + if (isVersionControlled()) { + return m_plugin->versionControlActions(items); + } else { + QList<QAction*> actions; + for (const auto &plugin : qAsConst(m_plugins)) { + actions << plugin.first->outOfVersionControlActions(items); + } + return actions; + } } void VersionControlObserver::delayedDirectoryVerification() @@ -360,7 +368,7 @@ KVersionControlPlugin* VersionControlObserver::searchPlugin(const QUrl& director return bestPlugin; } -bool VersionControlObserver::isVersioned() const +bool VersionControlObserver::isVersionControlled() const { return m_versionedDirectory && m_plugin; } diff --git a/src/views/versioncontrol/versioncontrolobserver.h b/src/views/versioncontrol/versioncontrolobserver.h index 66f992963..648c9d6fd 100644 --- a/src/views/versioncontrol/versioncontrolobserver.h +++ b/src/views/versioncontrol/versioncontrolobserver.h @@ -143,7 +143,7 @@ private: /** * Returns true, if the directory contains a version control information. */ - bool isVersioned() const; + bool isVersionControlled() const; private: bool m_pendingItemStatesUpdate; |
