.directory
*.kdev4
/build*/
+.cmake/
# KDE Application Version, managed by release script
set (RELEASE_SERVICE_VERSION_MAJOR "20")
-set (RELEASE_SERVICE_VERSION_MINOR "04")
-set (RELEASE_SERVICE_VERSION_MICRO "0")
+set (RELEASE_SERVICE_VERSION_MINOR "07")
+set (RELEASE_SERVICE_VERSION_MICRO "70")
set (RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
project(Dolphin VERSION ${RELEASE_SERVICE_VERSION})
set(QT_MIN_VERSION "5.11.0")
-set(KF5_MIN_VERSION "5.67.0")
+set(KF5_MIN_VERSION "5.70.0")
# ECM setup
find_package(ECM ${KF5_MIN_VERSION} CONFIG REQUIRED)
find_package(Phonon4Qt5 CONFIG REQUIRED)
+find_package(PackageKitQt5)
+set_package_properties(PackageKitQt5
+ PROPERTIES DESCRIPTION "Software Manager integration"
+ TYPE OPTIONAL
+ PURPOSE "Used in the service menu installer"
+ )
+if(PackageKitQt5_FOUND)
+ set(HAVE_PACKAGEKIT TRUE)
+endif()
+
find_package(KF5Baloo ${KF5_MIN_VERSION})
set_package_properties(KF5Baloo PROPERTIES DESCRIPTION "Baloo Core libraries"
URL "https://www.kde.org"
<legalnotice>&FDLNotice;</legalnotice>
-<date>2020-03-17</date>
-<releaseinfo>Applications 20.04</releaseinfo>
+<date>2020-05-05</date>
+<releaseinfo>Applications 20.08</releaseinfo>
<abstract>
<para>
<itemizedlist>
<listitem><para>
-The <guilabel>Start in</guilabel> folder is the folder which is opened on startup. The
-location of the folder can be entered directly or chosen in a dialog which can
+The <guilabel>Show on startup</guilabel> option allows choosing the folder which is opened on startup.
+</para>
+<para>
+ If the <guimenuitem>Folders, tabs, and window state from last time</guimenuitem> item is selected then
+</para>
+<itemizedlist>
+ <listitem>
+ <para>When launched from the &GUI; or CLI without any &URL;s, &dolphin; restores session</para>
+ </listitem>
+ <listitem>
+ <para>When rebooting with &dolphin; open, it restores session normally after the system comes back</para>
+ </listitem>
+ <listitem>
+ <para>When launched with &URL;s, &dolphin; window is opened showing those &URL;s instead of restoring session</para>
+ </listitem>
+ <listitem>
+ <para>When &dolphin; is already running and a new window is opened, that new window shows a single tab with the same &URL; as was visible in the previously-open &dolphin; instance</para>
+ </listitem>
+</itemizedlist>
+<para>
+The location of the folder can also be entered directly or chosen in a dialog which can
be opened by clicking the button showing a <quote>folder</quote> icon. Moreover,
the current location or the default location (which is the user's home folder)
can be used as the startup folder by clicking the corresponding button.
</para></listitem>
<listitem><para>
-<guilabel>Split view mode</guilabel> controls if the
+<guilabel>Begin in split view mode</guilabel> controls if the
<link linkend="dolphin-view">&dolphin; view</link> is split on startup
-or not.
+or not for new windows.
</para></listitem>
<listitem><para>
-<guilabel>Editable location bar</guilabel> controls if the location bar is in
+<guilabel>Show filter bar</guilabel> controls if the filter bar is shown on
+startup or not. See the <link linkend="filter-files">section on the filter bar</link>
+for details.
+</para></listitem>
+
+<listitem><para>
+<guilabel>Make location bar editable</guilabel> controls if the location bar is in
editable mode on startup. The bread crumb mode of the location bar is used
otherwise. See the <link linkend="location-bar">section about the location bar</link>
for details about the two modes.
</para></listitem>
+<listitem><para>
+<guilabel>Open new folders in tabs</guilabel> controls whether &dolphin; should open a new folder in a new tab of the current instance when called externally. If not enabled, the new folders will be opened in new instances of &dolphin;. By default this option is enabled.
+</para></listitem>
+
<listitem><para>
If <guilabel>Show full path inside location bar</guilabel> is enabled, the full
path of the current location is shown in the bread crumb mode of the location bar.
one of the places in the <guilabel>Places</guilabel> panel.
</para></listitem>
-<listitem><para>
-<guilabel>Show filter bar</guilabel> controls if the filter bar is shown on
-startup or not. See the <link linkend="filter-files">section on the filter bar</link>
-for details.
-</para></listitem>
-
<listitem><para>
<guilabel>Show full path in title bar</guilabel> makes it easy to distinguish
between files or folders with the same name in different folders.
</para></listitem>
-<listitem><para>
-<guilabel>Open new folders in tabs</guilabel> controls whether &dolphin; should open a new folder in a new tab of the current instance when called externally. If not enabled, the new folders will be opened in new instances of &dolphin;. By default this option is enabled.
-</para></listitem>
</itemizedlist>
</para>
<guilabel>Expandable folders</guilabel> determines whether any folders that have subfolders
are displayed in a tree view, where the sub items can be expanded by &LMB; clicking the
<guiicon>></guiicon> icon and collapsed by clicking the <guiicon>v</guiicon> icon.
-</para></listitem>
+</para>
+<para>
+<guilabel>Folder size displays</guilabel> allows defining the property to use then sorting folders by their size. It is possible to sort folders by <guilabel>Number of items</guilabel> or <guilabel>Size of contents</guilabel> and choose a limit to the recursive level (can be useful to constrain unneeded iterations in the deep folder structures or on the slow file systems).
+</para>
+</listitem>
</itemizedlist>
</para>
</sect3>
<para>
The official channel for submitting bug reports is via the &kde; bug tracking
system. The &kde; bug tracker can be found at
-<ulink url="http://bugs.kde.org">http://bugs.kde.org</ulink>.
+<ulink url="https://bugs.kde.org">https://bugs.kde.org</ulink>.
</para>
</answer>
</qandaentry>
<para>
The official channel for submitting feature requests is via the &kde; bug
tracking system. The &kde; bug tracker can be found at
-<ulink url="http://bugs.kde.org">http://bugs.kde.org</ulink>.
+<ulink url="https://bugs.kde.org">https://bugs.kde.org</ulink>.
</para>
</answer>
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(
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})
--- /dev/null
+#cmakedefine HAVE_PACKAGEKIT
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;
#include "dolphinmainwindow.h"
#include "dolphinviewcontainer.h"
#include "global.h"
+#include <KActionCollection>
#include <KBookmarkMenu>
#include <KIO/Global>
#include <QDebug>
}
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()
#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>
}
}
+ 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();
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()
void DolphinMainWindow::updateWindowTitle()
{
- const QString newTitle = m_activeViewContainer->caption();
+ const QString newTitle = m_activeViewContainer->captionWindowTitle();
if (windowTitle() != newTitle) {
setWindowTitle(newTitle);
}
#include <KActionCollection>
#include <KAuthorized>
#include <KConfigGroup>
+#include <KDialogJobUiDelegate>
#include <KFileItemListProperties>
#include <KIconLoader>
#include <KJobWidgets>
#include <KMimeTypeEditor>
#include <KNS3/KMoreToolsMenuFactory>
#include <KPluginFactory>
-#include <KRun>
+#include <KIO/CommandLauncherJob>
#include <KSharedConfig>
#include <KToolInvocation>
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();
}
}
#include "dolphinviewcontainer.h"
#include <KConfigGroup>
-#include <KRun>
#include <KShell>
#include <kio/global.h>
+#include <KIO/CommandLauncherJob>
#include <KAcceleratorManager>
#include <QApplication>
}
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);
}
#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>
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 "
setSearchModeEnabled(isSearchUrl(url));
+ connect(DetailsModeSettings::self(), &KCoreConfigSkeleton::configChanged, this, [=]() {
+ if (view()->mode() == DolphinView::Mode::DetailsView) {
+ view()->reload();
+ }
+ });
+
// Initialize kactivities resource instance
#ifdef HAVE_KACTIVITIES
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()) {
}
}
- 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);
*/
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
#include "dolphindebug.h"
#include "dolphinmainwindowinterface.h"
-#include <KRun>
+#include <KDialogJobUiDelegate>
+#include <KIO/ApplicationLauncherJob>
+#include <KService>
#include <KWindowSystem>
#include <QApplication>
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)
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;
}
}
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;
+}
#include <QUrl>
#include <QWidget>
+class OrgKdeDolphinMainWindowInterface;
+
namespace Dolphin {
QList<QUrl> validateUris(const QStringList& uriList);
*/
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
*/
#include "kfileitemmodel.h"
#include "kitemlistview.h"
+#include "dolphin_detailsmodesettings.h"
+
#include <KFormat>
#include <KLocalizedString>
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 {
#include "kfileitemmodel.h"
#include "dolphin_generalsettings.h"
+#include "dolphin_detailsmodesettings.h"
#include "dolphindebug.h"
#include "private/kfileitemmodeldirlister.h"
#include "private/kfileitemmodelsortalgorithm.h"
// 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()) {
} 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():
#include <QElapsedTimer>
#include <QTimer>
-
// #define KFILEITEMMODELROLESUPDATER_DEBUG
namespace {
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);
#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");
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);
}
}
}
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);
// 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
}
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:
/**
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);
const bool wasHovered = m_hovered;
setAlternateBackground(false);
- setSelected(false);
setHovered(false);
paint(&painter, option, widget);
#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),
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);
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.
}
}
-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);
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)
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();
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 {
}
QThread* KDirectoryContentsCounter::m_workerThread = nullptr;
-int KDirectoryContentsCounter::m_workersCount = 0;
#include <QQueue>
#include <QSet>
+#include <QHash>
class KDirWatch;
class KFileItemModel;
*
* 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();
QQueue<QString> m_queue;
static QThread* m_workerThread;
- static int m_workersCount;
KDirectoryContentsCounterWorker* m_worker;
bool m_workerIsBusy;
// 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 <faure@kde.org>
-
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) {
// 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);
}
};
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);
/**
*
* @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:
/**
#include <KDBusService>
#include <KLocalizedString>
#include <Kdelibs4ConfigMigrator>
+#include <KConfigGui>
#include <QApplication>
#include <QCommandLineParser>
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;
}
}
- if (urls.isEmpty()) {
+ if (!startedWithURLs) {
// We need at least one URL to open Dolphin
urls.append(Dolphin::homeUrl());
}
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!";
+ }
}
}
<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>
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);
}
// 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);
// 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,
}
m_preview->setCursor(Qt::ArrowCursor);
- bool usePhonon = false;
setNameLabelText(m_item.text());
if (InformationPanelSettings::previewsShown()) {
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 {
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
m_preview->show();
}
- m_phononWidget->show();
m_phononWidget->setUrl(m_item.targetUrl(), m_isVideo ? PhononWidget::MediaKind::Video : PhononWidget::MediaKind::Audio);
adjustWidgetSizes(parentWidget()->width());
}
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()));
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);
}
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()
--- /dev/null
+<!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>
***************************************************************************/
#include "terminalpanel.h"
+#include "kiofuse_interface.h"
#include <KIO/DesktopExecParser>
#include <KIO/Job>
#include <KJobWidgets>
#include <KLocalizedString>
#include <KMessageWidget>
+#include <KMountPoint>
#include <KParts/ReadOnlyPart>
#include <KPluginFactory>
#include <KPluginLoader>
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);
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;
}
}
+ // 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
#define TERMINALPANEL_H
#include "panels/panel.h"
+#include "kiofuse_interface.h"
#include <QQueue>
KParts::ReadOnlyPart* m_konsolePart;
QString m_konsolePartCurrentDirectory;
QQueue<QString> m_sendCdToTerminalHistory;
+ org::kde::KIOFuse::VFS m_kiofuseInterface;
};
#endif // TERMINALPANEL_H
}
query.addQueryItem(QStringLiteral("url"), searchPath().url());
+ query.addQueryItem(QStringLiteral("title"), queryTitle(m_searchInput->text()));
url.setQuery(query);
}
m_searchInput->clear();
}
}
+ else if (event->key() == Qt::Key_Down) {
+ emit focusViewRequest();
+ }
}
bool DolphinSearchBox::eventFilter(QObject* obj, QEvent* event)
void DolphinSearchBox::slotReturnPressed()
{
emitSearchRequest();
- emit returnPressed();
+ emit focusViewRequest();
}
void DolphinSearchBox::slotFacetChanged()
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
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
{
#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
*/
void searchTextChanged(const QString& text);
- void returnPressed();
-
/**
* Emitted as soon as the search box should get closed.
*/
* @see DolphinSearchBox::setActive()
*/
void activated();
+ void focusViewRequest();
private slots:
void emitSearchRequest();
bool isIndexingEnabled() const;
private:
+ QString queryTitle(const QString& text) const;
+
bool m_startedSearching;
bool m_active;
<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>
<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>
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);
// 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);
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();
}
}
Q_OBJECT
public:
- DolphinGeneralConfigModule(QWidget* parent, const QVariantList& args);
+ DolphinGeneralConfigModule(QWidget *parent, const QVariantList &args);
~DolphinGeneralConfigModule() override;
void save() override;
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, {});
}
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
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, {});
}
void defaults() override;
private:
- ServicesSettingsPage* m_services;
+ ServicesSettingsPage *m_services;
};
#endif
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);
void DolphinViewModesConfigModule::save()
{
- foreach (ViewSettingsTab* tab, m_tabs) {
+ for (ViewSettingsTab *tab : qAsConst(m_tabs)) {
tab->applySettings();
}
reparseConfiguration();
void DolphinViewModesConfigModule::defaults()
{
- foreach (ViewSettingsTab* tab, m_tabs) {
+ for (ViewSettingsTab *tab : qAsConst(m_tabs)) {
tab->restoreDefaultSettings();
}
reparseConfiguration();
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"
Q_OBJECT
public:
- DolphinViewModesConfigModule(QWidget* parent, const QVariantList& args);
+ DolphinViewModesConfigModule(QWidget *parent, const QVariantList &args);
~DolphinViewModesConfigModule() override;
void save() override;
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})
#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);
}
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;
QStringList args2;
};
+enum ScriptExecution{
+ Process,
+ Konsole
+};
+
void runUncompress(const QString &inputPath, const QString &outputPath)
{
QVector<QPair<QStringList, UncompressCommand>> mimeTypeToCommand;
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));
}
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;
}
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)) {
}
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));
return false;
}
} else {
+ if (binaryPackages.contains(QMimeDatabase().mimeTypeForFile(archive).name())) {
+ packageKit(PackageOperation::Uninstall, archive);
+ }
const QString dir = generateDirPath(archive);
// Try "deinstall" first
#include <QListWidget>
#include <QShowEvent>
#include <QSortFilterProxyModel>
+#include <QLineEdit>
namespace
{
"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);
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);
connect(downloadButton, &KNS3::Button::dialogFinished, this, &ServicesSettingsPage::loadServices);
topLayout->addWidget(label);
+ topLayout->addWidget(m_searchLineEdit);
topLayout->addWidget(m_listView);
topLayout->addWidget(downloadButton);
}
m_sortModel->sort(Qt::DisplayRole);
+ m_searchLineEdit->setFocus(Qt::OtherFocusReason);
}
void ServicesSettingsPage::loadVersionControlSystems()
class QListView;
class QSortFilterProxyModel;
class ServiceModel;
+class QLineEdit;
/**
* @brief Page for the 'Services' settings of the Dolphin settings dialog.
ServiceModel* m_serviceModel;
QSortFilterProxyModel* m_sortModel;
QListView* m_listView;
+ QLineEdit *m_searchLineEdit;
QStringList m_enabledVcsPlugins;
};
#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),
{
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();
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"));
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()
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();
}
// 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));
{
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());
#include <QUrl>
-class QLineEdit;
class QCheckBox;
+class QLineEdit;
+class QRadioButton;
/**
* @brief Page for the 'Startup' settings of the Dolphin settings dialog.
private slots:
void slotSettingsChanged();
+ void updateInitialViewOptions();
void selectHomeUrl();
void useCurrentLocation();
void useDefaultLocation();
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;
#include <QComboBox>
#include <QHelpEvent>
#include <QFormLayout>
+#include <QSpinBox>
+#include <QRadioButton>
+#include <QButtonGroup>
+#include <QLabel>
ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget* parent) :
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();
m_fontRequester = new DolphinFontRequester(this);
topLayout->addRow(i18nc("@label:listbox", "Label font:"), m_fontRequester);
-
switch (m_mode) {
case IconsMode: {
m_widthBox = new QComboBox();
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;
}
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;
break;
case DetailsMode:
DetailsModeSettings::setExpandableFolders(m_expandableFolders->isChecked());
+ DetailsModeSettings::setDirectorySizeCount(m_numberOfItems->isChecked());
+ DetailsModeSettings::setRecursiveDirectorySizeLimit(m_recursiveDirectorySizeLimit->value());
break;
default:
break;
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;
class QComboBox;
class QCheckBox;
class QSlider;
+class QSpinBox;
+class QRadioButton;
/**
* @brief Represents one tab of the view-settings page.
QComboBox* m_widthBox;
QComboBox* m_maxLinesBox;
QCheckBox* m_expandableFolders;
+ QRadioButton* m_numberOfItems;
+ QRadioButton* m_sizeOfContents;
+ QSpinBox* m_recursiveDirectorySizeLimit;
};
#endif
emit errorMessage(job->errorString());
}
if (!m_selectedUrls.isEmpty()) {
- m_selectedUrls << KDirModel::simplifiedUrlList(m_selectedUrls);
+ m_selectedUrls = KDirModel::simplifiedUrlList(m_selectedUrls);
}
}
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();
+ }
}
}
}
"<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"));
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:
/**
}
}
- 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()
return bestPlugin;
}
-bool VersionControlObserver::isVersioned() const
+bool VersionControlObserver::isVersionControlled() const
{
return m_versionedDirectory && m_plugin;
}
/**
* Returns true, if the directory contains a version control information.
*/
- bool isVersioned() const;
+ bool isVersionControlled() const;
private:
bool m_pendingItemStatesUpdate;