From: Alexander Lohnau Date: Tue, 23 Jun 2020 15:42:06 +0000 (+0200) Subject: Merge branch 'release/20.04' X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/commitdiff_plain/d1baf3398e53931735b724672d5ae48649b44a18?hp=37df39b93bf23b89ca760d4dd793788833d9a3e1 Merge branch 'release/20.04' --- diff --git a/.gitignore b/.gitignore index c48f92390..6c1df4de4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ CMakeLists.txt.user .directory *.kdev4 /build*/ +.cmake/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ccd9f8f2..397f3161d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,13 +2,13 @@ cmake_minimum_required(VERSION 3.0) # KDE Application Version, managed by release script set (RELEASE_SERVICE_VERSION_MAJOR "20") -set (RELEASE_SERVICE_VERSION_MINOR "04") -set (RELEASE_SERVICE_VERSION_MICRO "2") +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.71.0") # ECM setup find_package(ECM ${KF5_MIN_VERSION} CONFIG REQUIRED) @@ -77,6 +77,16 @@ set_package_properties(KF5Activities PROPERTIES DESCRIPTION "KActivities librari 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" diff --git a/README b/README.md similarity index 99% rename from README rename to README.md index 970fa1d43..8335d4077 100644 --- a/README +++ b/README.md @@ -14,7 +14,7 @@ To submit a patch to Dolphin, use https://phabricator.kde.org. See https://commu -Development philosophy +Development Philosophy ====================== Dolphin is a file manager focusing on usability. When reading the term Usability people often assume that the focus is on newbies and only basic features are offered. This is not the case; Dolphin is quite full-featured, but the features are carefully chosen so as to not impede any of the users in the target user groups. diff --git a/doc/index.docbook b/doc/index.docbook index d1342d0d4..50fc37d91 100644 --- a/doc/index.docbook +++ b/doc/index.docbook @@ -70,8 +70,8 @@ &FDLNotice; -2020-03-17 -Applications 20.04 +2020-06-10 +Applications 20.08 @@ -1066,7 +1066,7 @@ to close the inactive pane when you are turning off the split view mode, ⪚ pr In this tab, you can configure for which file types previews are shown. -Moreover, the maximum size of remote files for which previews are generated can be chosen. +Moreover, the maximum size of local and remote files for which previews are generated can be chosen. If previews are enabled for folders, previews of some files in the folder will @@ -1137,26 +1137,55 @@ This group contains settings which control the appearance of &dolphin; on startu -The Start in 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 Show on startup option allows choosing the folder which is opened on startup. + + + If the Folders, tabs, and window state from last time item is selected then + + + + When launched from the &GUI; or CLI without any &URL;s, &dolphin; restores session + + + When rebooting with &dolphin; open, it restores session normally after the system comes back + + + When launched with &URL;s, &dolphin; window is opened showing those &URL;s instead of restoring session + + + 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 + + + +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 folder 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. -Split view mode controls if the +Begin in split view mode controls if the &dolphin; view is split on startup -or not. +or not for new windows. + + + +Show filter bar controls if the filter bar is shown on +startup or not. See the section on the filter bar +for details. -Editable location bar controls if the location bar is in +Make location bar editable controls if the location bar is in editable mode on startup. The bread crumb mode of the location bar is used otherwise. See the section about the location bar for details about the two modes. + +Open new folders in tabs 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. + + If Show full path inside location bar is enabled, the full path of the current location is shown in the bread crumb mode of the location bar. @@ -1164,20 +1193,11 @@ Otherwise, a shortened version of the path is shown if it begins with the path o one of the places in the Places panel. - -Show filter bar controls if the filter bar is shown on -startup or not. See the section on the filter bar -for details. - - Show full path in title bar makes it easy to distinguish between files or folders with the same name in different folders. - -Open new folders in tabs 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. - @@ -1272,7 +1292,11 @@ the text of a file item. Expandable folders determines whether any folders that have subfolders are displayed in a tree view, where the sub items can be expanded by &LMB; clicking the > icon and collapsed by clicking the v icon. - + + +Folder size displays allows defining the property to use then sorting folders by their size. It is possible to sort folders by Number of items or Size of contents 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). + + @@ -1705,6 +1729,29 @@ The name of this file has to be entered in a dialog. + + + +&Ctrl;F5 + +Edit +Copy to inactive split view + +Copies the currently selected item(s) from the active split view to the inactive split view. + + + + + +&Ctrl;F6 + +Edit +Move to inactive split view + +Moves the currently selected item(s) from the active split view to the inactive split view. +Is disabled if the current user does not have write permission on the selected item(s). + + @@ -2116,7 +2163,7 @@ project. If you wish to contribute to the documentation please email the The official channel for submitting bug reports is via the &kde; bug tracking system. The &kde; bug tracker can be found at -http://bugs.kde.org. +https://bugs.kde.org. @@ -2129,7 +2176,7 @@ system. The &kde; bug tracker can be found at The official channel for submitting feature requests is via the &kde; bug tracking system. The &kde; bug tracker can be found at -http://bugs.kde.org. +https://bugs.kde.org. diff --git a/doc/preferences-startup.png b/doc/preferences-startup.png index 2b780a4c1..192efe193 100644 Binary files a/doc/preferences-startup.png and b/doc/preferences-startup.png differ diff --git a/logo.png b/logo.png new file mode 100644 index 000000000..de289b26c Binary files /dev/null and b/logo.png differ 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 #include #include #include @@ -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 dd1dcdac5..d45b77676 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -50,9 +50,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -582,6 +584,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(); @@ -647,12 +657,12 @@ void DolphinMainWindow::undo() void DolphinMainWindow::cut() { - m_activeViewContainer->view()->cutSelectedItems(); + m_activeViewContainer->view()->cutSelectedItemsToClipboard(); } void DolphinMainWindow::copy() { - m_activeViewContainer->view()->copySelectedItems(); + m_activeViewContainer->view()->copySelectedItemsToClipboard(); } void DolphinMainWindow::paste() @@ -929,7 +939,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() @@ -1116,6 +1129,8 @@ void DolphinMainWindow::updateControlMenu() // Add "Edit" actions bool added = addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Undo)), menu) | + addActionToMenu(ac->action(QStringLiteral("copy_to_inactive_split_view")), menu) | + addActionToMenu(ac->action(QStringLiteral("move_to_inactive_split_view")), menu) | addActionToMenu(ac->action(KStandardAction::name(KStandardAction::SelectAll)), menu) | addActionToMenu(ac->action(QStringLiteral("invert_selection")), menu); @@ -1244,7 +1259,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); } @@ -1348,6 +1363,24 @@ void DolphinMainWindow::setupActions() "If the items were added to the clipboard by the Cut " "action they are removed from their old location.") + cutCopyPastePara); + QAction* copyToOtherViewAction = actionCollection()->addAction(QStringLiteral("copy_to_inactive_split_view")); + copyToOtherViewAction->setText(i18nc("@action:inmenu", "Copy to inactive split view")); + copyToOtherViewAction->setWhatsThis(xi18nc("@info:whatsthis Copy", "This copies the selected items from " + "the active view to the inactive split view.")); + copyToOtherViewAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy"))); + copyToOtherViewAction->setIconText(i18nc("@action:inmenu Edit", "Copy to inactive split view")); + actionCollection()->setDefaultShortcut(copyToOtherViewAction, Qt::SHIFT + Qt::Key_F5 ); + connect(copyToOtherViewAction, &QAction::triggered, m_tabWidget, &DolphinTabWidget::copyToInactiveSplitView); + + QAction* moveToOtherViewAction = actionCollection()->addAction(QStringLiteral("move_to_inactive_split_view")); + moveToOtherViewAction->setText(i18nc("@action:inmenu", "Move to inactive split view")); + moveToOtherViewAction->setWhatsThis(xi18nc("@info:whatsthis Move", "This moves the selected items from " + "the active view to the inactive split view.")); + moveToOtherViewAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-cut"))); + moveToOtherViewAction->setIconText(i18nc("@action:inmenu Edit", "Move to inactive split view")); + actionCollection()->setDefaultShortcut(moveToOtherViewAction, Qt::SHIFT + Qt::Key_F6 ); + connect(moveToOtherViewAction, &QAction::triggered, m_tabWidget, &DolphinTabWidget::moveToInactiveSplitView); + QAction *searchAction = KStandardAction::find(this, &DolphinMainWindow::find, actionCollection()); searchAction->setText(i18n("Search...")); searchAction->setToolTip(i18nc("@info:tooltip", "Search for files and folders")); @@ -1873,12 +1906,18 @@ void DolphinMainWindow::updateFileAndEditActions() { const KFileItemList list = m_activeViewContainer->view()->selectedItems(); const KActionCollection* col = actionCollection(); + KFileItemListProperties capabilitiesSource(list); + QAction* addToPlacesAction = col->action(QStringLiteral("add_to_places")); + QAction* copyToOtherViewAction = col->action(QStringLiteral("copy_to_inactive_split_view")); + QAction* moveToOtherViewAction = col->action(QStringLiteral("move_to_inactive_split_view")); if (list.isEmpty()) { stateChanged(QStringLiteral("has_no_selection")); addToPlacesAction->setEnabled(true); + copyToOtherViewAction->setEnabled(false); + moveToOtherViewAction->setEnabled(false); } else { stateChanged(QStringLiteral("has_selection")); @@ -1896,16 +1935,32 @@ void DolphinMainWindow::updateFileAndEditActions() addToPlacesAction->setEnabled(false); } - KFileItemListProperties capabilities(list); - const bool enableMoveToTrash = capabilities.isLocal() && capabilities.supportsMoving(); + if (m_tabWidget->currentTabPage()->splitViewEnabled()) { + DolphinTabPage* tabPage = m_tabWidget->currentTabPage(); + KFileItem capabilitiesDestination; + + if (tabPage->primaryViewActive()) { + capabilitiesDestination = tabPage->secondaryViewContainer()->url(); + } else { + capabilitiesDestination = tabPage->primaryViewContainer()->url(); + } + + copyToOtherViewAction->setEnabled(capabilitiesDestination.isWritable()); + moveToOtherViewAction->setEnabled(capabilitiesSource.supportsMoving() && capabilitiesDestination.isWritable()); + } else { + copyToOtherViewAction->setEnabled(false); + moveToOtherViewAction->setEnabled(false); + } + + const bool enableMoveToTrash = capabilitiesSource.isLocal() && capabilitiesSource.supportsMoving(); - renameAction->setEnabled(capabilities.supportsMoving()); + renameAction->setEnabled(capabilitiesSource.supportsMoving()); moveToTrashAction->setEnabled(enableMoveToTrash); - deleteAction->setEnabled(capabilities.supportsDeleting()); - deleteWithTrashShortcut->setEnabled(capabilities.supportsDeleting() && !enableMoveToTrash); - cutAction->setEnabled(capabilities.supportsMoving()); + deleteAction->setEnabled(capabilitiesSource.supportsDeleting()); + deleteWithTrashShortcut->setEnabled(capabilitiesSource.supportsDeleting() && !enableMoveToTrash); + cutAction->setEnabled(capabilitiesSource.supportsMoving()); showTarget->setEnabled(list.length() == 1 && list.at(0).isLink()); - duplicateAction->setEnabled(capabilities.supportsWriting()); + duplicateAction->setEnabled(capabilitiesSource.supportsWriting()); } } diff --git a/src/dolphinpart.cpp b/src/dolphinpart.cpp index 7e7425121..12c361011 100644 --- a/src/dolphinpart.cpp +++ b/src/dolphinpart.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -42,7 +43,7 @@ #include #include #include -#include +#include #include #include @@ -53,6 +54,7 @@ #include #include #include +#include #include #include @@ -506,7 +508,7 @@ void DolphinPart::openSelectionDialog(const QString& title, const QString& text, const QString pattern = QInputDialog::getText(m_view, title, text, QLineEdit::Normal, QStringLiteral("*"), &okClicked); if (okClicked && !pattern.isEmpty()) { - QRegExp patternRegExp(pattern, Qt::CaseSensitive, QRegExp::Wildcard); + const QRegularExpression patternRegExp(QRegularExpression::wildcardToRegularExpression(pattern)); m_view->selectItems(patternRegExp, selectItems); } } @@ -546,7 +548,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/dolphinpart.desktop b/src/dolphinpart.desktop index 13e267ad5..abd206d6a 100644 --- a/src/dolphinpart.desktop +++ b/src/dolphinpart.desktop @@ -3,6 +3,7 @@ Type=Service Name=Dolphin View Name[ar]=منظور دولفين Name[ast]=Vista de Dolphin +Name[az]=Dolphin görünüşü Name[ca]=Vista del Dolphin Name[ca@valencia]=Vista del Dolphin Name[cs]=Pohled Dolphin @@ -63,6 +64,7 @@ Actions=icons;details;compact; Name=Icons Name[ar]=الأيقونات Name[ast]=Iconos +Name[az]=İkonlar Name[ca]=Icones Name[ca@valencia]=Icones Name[cs]=Ikony @@ -115,6 +117,7 @@ Exec=dolphin Name=Compact Name[ar]=متراصّ Name[ast]=Compauta +Name[az]=Yığcam Name[ca]=Compacta Name[ca@valencia]=Compacta Name[cs]=Kompaktní @@ -166,6 +169,7 @@ Exec=dolphin Name=Details Name[ar]=التّفاصيل Name[ast]=Detalles +Name[az]=Ətraflı Name[ca]=Detalls Name[ca@valencia]=Detalls Name[cs]=Podrobnosti diff --git a/src/dolphinpart_ext.cpp b/src/dolphinpart_ext.cpp index 8c6e9641a..83dfddb16 100644 --- a/src/dolphinpart_ext.cpp +++ b/src/dolphinpart_ext.cpp @@ -43,12 +43,12 @@ void DolphinPartBrowserExtension::saveState(QDataStream &stream) void DolphinPartBrowserExtension::cut() { - m_part->view()->cutSelectedItems(); + m_part->view()->cutSelectedItemsToClipboard(); } void DolphinPartBrowserExtension::copy() { - m_part->view()->copySelectedItems(); + m_part->view()->copySelectedItemsToClipboard(); } void DolphinPartBrowserExtension::paste() diff --git a/src/dolphintabwidget.cpp b/src/dolphintabwidget.cpp index 89c54baf5..7a431152e 100644 --- a/src/dolphintabwidget.cpp +++ b/src/dolphintabwidget.cpp @@ -25,9 +25,9 @@ #include "dolphinviewcontainer.h" #include -#include #include #include +#include #include #include @@ -320,6 +320,40 @@ void DolphinTabWidget::restoreClosedTab(const QByteArray& state) currentTabPage()->restoreState(state); } +void DolphinTabWidget::copyToInactiveSplitView() +{ + const DolphinTabPage* tabPage = tabPageAt(currentIndex()); + DolphinViewContainer* activeViewContainer = currentTabPage()->activeViewContainer(); + if (!tabPage->splitViewEnabled() || activeViewContainer->view()->selectedItems().isEmpty()) { + return; + } + + if (tabPage->primaryViewActive()) { + // copy from left panel to right + activeViewContainer->view()->copySelectedItems(activeViewContainer->view()->selectedItems(), tabPage->secondaryViewContainer()->url()); + } else { + // copy from right panel to left + activeViewContainer->view()->copySelectedItems(activeViewContainer->view()->selectedItems(), tabPage->primaryViewContainer()->url()); + } +} + +void DolphinTabWidget::moveToInactiveSplitView() +{ + const DolphinTabPage* tabPage = tabPageAt(currentIndex()); + DolphinViewContainer* activeViewContainer = currentTabPage()->activeViewContainer(); + if (!tabPage->splitViewEnabled() || activeViewContainer->view()->selectedItems().isEmpty()) { + return; + } + + if (tabPage->primaryViewActive()) { + // move from left panel to right + activeViewContainer->view()->moveSelectedItems(activeViewContainer->view()->selectedItems(), tabPage->secondaryViewContainer()->url()); + } else { + // move from right panel to left + activeViewContainer->view()->moveSelectedItems(activeViewContainer->view()->selectedItems(), tabPage->primaryViewContainer()->url()); + } +} + void DolphinTabWidget::detachTab(int index) { Q_ASSERT(index >= 0); @@ -334,8 +368,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/dolphintabwidget.h b/src/dolphintabwidget.h index 746aec6c6..f0ce11ce8 100644 --- a/src/dolphintabwidget.h +++ b/src/dolphintabwidget.h @@ -189,6 +189,12 @@ public slots: */ void restoreClosedTab(const QByteArray& state); + /** Copies all selected items to the inactive view. */ + void copyToInactiveSplitView(); + + /** Moves all selected items to the inactive view. */ + void moveToInactiveSplitView(); + private slots: /** * Opens the tab with the index \a index in a new Dolphin instance and closes diff --git a/src/dolphinui.rc b/src/dolphinui.rc index e717b67ae..acb2f1dcd 100644 --- a/src/dolphinui.rc +++ b/src/dolphinui.rc @@ -1,5 +1,5 @@ - + @@ -20,6 +20,8 @@ + + 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 @@ -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", "This helps you find files and folders. Enter a " "search term 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 @@ -133,6 +133,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 diff --git a/src/filterbar/filterbar.cpp b/src/filterbar/filterbar.cpp index 50af2c6c7..93e929c45 100644 --- a/src/filterbar/filterbar.cpp +++ b/src/filterbar/filterbar.cpp @@ -32,13 +32,6 @@ FilterBar::FilterBar(QWidget* parent) : QWidget(parent) { - // Create close button - QToolButton *closeButton = new QToolButton(this); - closeButton->setAutoRaise(true); - closeButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-close"))); - closeButton->setToolTip(i18nc("@info:tooltip", "Hide Filter Bar")); - connect(closeButton, &QToolButton::clicked, this, &FilterBar::closeRequest); - // Create button to lock text when changing folders m_lockButton = new QToolButton(this); m_lockButton->setAutoRaise(true); @@ -57,12 +50,19 @@ FilterBar::FilterBar(QWidget* parent) : this, &FilterBar::filterChanged); setFocusProxy(m_filterInput); + // Create close button + QToolButton *closeButton = new QToolButton(this); + closeButton->setAutoRaise(true); + closeButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-close"))); + closeButton->setToolTip(i18nc("@info:tooltip", "Hide Filter Bar")); + connect(closeButton, &QToolButton::clicked, this, &FilterBar::closeRequest); + // Apply layout QHBoxLayout* hLayout = new QHBoxLayout(this); hLayout->setContentsMargins(0, 0, 0, 0); - hLayout->addWidget(closeButton); hLayout->addWidget(m_lockButton); hLayout->addWidget(m_filterInput); + hLayout->addWidget(closeButton); } FilterBar::~FilterBar() diff --git a/src/global.cpp b/src/global.cpp index 5236fa4d1..6a702cf43 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -23,7 +23,9 @@ #include "dolphindebug.h" #include "dolphinmainwindowinterface.h" -#include +#include +#include +#include #include #include @@ -60,13 +62,11 @@ void Dolphin::openNewWindow(const QList &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& inputUrls, bool openFiles, bool splitView, const QString& preferredService) @@ -78,36 +78,7 @@ bool Dolphin::attachToExistingInstance(const QList& inputUrls, bool openFi return false; } - QVector, QStringList>> dolphinInterfaces; - if (!preferredService.isEmpty()) { - QSharedPointer 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 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; } @@ -133,7 +104,7 @@ bool Dolphin::attachToExistingInstance(const QList& inputUrls, bool openFi } dolphinInterfaces.front().second << newUrls; - for (const auto& interface: dolphinInterfaces) { + for (const auto& interface: qAsConst(dolphinInterfaces)) { if (!interface.second.isEmpty()) { auto reply = openFiles ? interface.first->openFiles(interface.second, splitView) : interface.first->openDirectories(interface.second, splitView); reply.waitForFinished(); @@ -145,3 +116,38 @@ bool Dolphin::attachToExistingInstance(const QList& inputUrls, bool openFi } return attached; } + +QVector, QStringList>> Dolphin::dolphinGuiInstances(const QString& preferredService) +{ + QVector, QStringList>> dolphinInterfaces; + if (!preferredService.isEmpty()) { + QSharedPointer 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 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 #include +class OrgKdeDolphinMainWindowInterface; + namespace Dolphin { QList validateUris(const QStringList& uriList); @@ -51,6 +53,11 @@ namespace Dolphin { */ bool attachToExistingInstance(const QList& inputUrls, bool openFiles, bool splitView, const QString& preferredService = QString()); + /** + * Returns a QVector with all GUI-capable Dolphin instances + */ + QVector, QStringList>> dolphinGuiInstances(const QString& preferredService); + /** * TODO: Move this somewhere global to all KDE apps, not just Dolphin */ diff --git a/src/kitemviews/kfileitemlistview.cpp b/src/kitemviews/kfileitemlistview.cpp index 80d85aa2e..4fffbcbfd 100644 --- a/src/kitemviews/kfileitemlistview.cpp +++ b/src/kitemviews/kfileitemlistview.cpp @@ -116,6 +116,18 @@ QStringList KFileItemListView::enabledPlugins() const return m_modelRolesUpdater ? m_modelRolesUpdater->enabledPlugins() : QStringList(); } +void KFileItemListView::setLocalFileSizePreviewLimit(const qlonglong size) +{ + if (m_modelRolesUpdater) { + m_modelRolesUpdater->setLocalFileSizePreviewLimit(size); + } +} + +qlonglong KFileItemListView::localFileSizePreviewLimit() const +{ + return m_modelRolesUpdater ? m_modelRolesUpdater->localFileSizePreviewLimit() : 0; +} + QPixmap KFileItemListView::createDragPixmap(const KItemSet& indexes) const { if (!model()) { diff --git a/src/kitemviews/kfileitemlistview.h b/src/kitemviews/kfileitemlistview.h index c13ea5ef2..92005c2df 100644 --- a/src/kitemviews/kfileitemlistview.h +++ b/src/kitemviews/kfileitemlistview.h @@ -71,6 +71,17 @@ public: */ QStringList enabledPlugins() const; + /** + * Sets the maximum file size of local files for which + * previews will be generated (if enabled). A value of 0 + * indicates no file size limit. + * Per default the value from KConfigGroup "PreviewSettings" + * MaximumSize is used, 0 otherwise. + * @param size + */ + void setLocalFileSizePreviewLimit(qlonglong size); + qlonglong localFileSizePreviewLimit() const; + QPixmap createDragPixmap(const KItemSet& indexes) const override; protected: 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 #include @@ -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(); + 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..eb2c1b646 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.cpp +++ b/src/kitemviews/kfileitemmodelrolesupdater.cpp @@ -44,7 +44,6 @@ #include #include - // #define KFILEITEMMODELROLESUPDATER_DEBUG namespace { @@ -86,7 +85,8 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QO m_recentlyChangedItemsTimer(nullptr), m_recentlyChangedItems(), m_changedItems(), - m_directoryContentsCounter(nullptr) + m_directoryContentsCounter(nullptr), + m_localFileSizePreviewLimit(0) #ifdef HAVE_BALOO , m_balooFileMonitor(nullptr) #endif @@ -95,6 +95,7 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QO const KConfigGroup globalConfig(KSharedConfig::openConfig(), "PreviewSettings"); m_enabledPlugins = globalConfig.readEntry("Plugins", KIO::PreviewJob::defaultPlugins()); + m_localFileSizePreviewLimit = static_cast(globalConfig.readEntry("MaximumSize", 0)); connect(m_model, &KFileItemModel::itemsInserted, this, &KFileItemModelRolesUpdater::slotItemsInserted); @@ -108,9 +109,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); @@ -318,6 +319,16 @@ QStringList KFileItemModelRolesUpdater::enabledPlugins() const return m_enabledPlugins; } +void KFileItemModelRolesUpdater::setLocalFileSizePreviewLimit(const qlonglong size) +{ + m_localFileSizePreviewLimit = size; +} + +qlonglong KFileItemModelRolesUpdater::localFileSizePreviewLimit() const +{ + return m_localFileSizePreviewLimit; +} + void KFileItemModelRolesUpdater::slotItemsInserted(const KItemRangeList& itemRanges) { QElapsedTimer timer; @@ -750,7 +761,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 +772,16 @@ void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QStrin QHash 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); } } } @@ -900,7 +910,7 @@ void KFileItemModelRolesUpdater::startPreviewJob() KIO::PreviewJob* job = new KIO::PreviewJob(itemSubSet, cacheSize, &m_enabledPlugins); - job->setIgnoreMaximumSize(itemSubSet.first().isLocalFile()); + job->setIgnoreMaximumSize(itemSubSet.first().isLocalFile() && m_localFileSizePreviewLimit <= 0); if (job->uiDelegate()) { KJobWidgets::setWindow(job, qApp->activeWindow()); } @@ -997,7 +1007,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 +1080,7 @@ QHash 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..b881b73e4 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.h +++ b/src/kitemviews/kfileitemmodelrolesupdater.h @@ -154,6 +154,17 @@ public: */ QStringList enabledPlugins() const; + /** + * Sets the maximum file size of local files for which + * previews will be generated (if enabled). A value of 0 + * indicates no file size limit. + * Per default the value from KConfigGroup "PreviewSettings" + * MaximumSize is used, 0 otherwise. + * @param size + */ + void setLocalFileSizePreviewLimit(qlonglong size); + qlonglong localFileSizePreviewLimit() const; + private slots: void slotItemsInserted(const KItemRangeList& itemRanges); void slotItemsRemoved(const KItemRangeList& itemRanges); @@ -212,7 +223,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: /** @@ -313,6 +324,7 @@ private: QSet m_roles; QSet m_resolvableRoles; QStringList m_enabledPlugins; + qulonglong m_localFileSizePreviewLimit; // Items for which the sort role still has to be determined. QSet m_pendingSortRoleItems; 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/kstandarditemlistwidget.cpp b/src/kitemviews/kstandarditemlistwidget.cpp index 15a618207..4018b3c15 100644 --- a/src/kitemviews/kstandarditemlistwidget.cpp +++ b/src/kitemviews/kstandarditemlistwidget.cpp @@ -88,7 +88,7 @@ qreal KStandardItemListWidgetInformant::preferredRoleColumnWidth(const QByteArra // If current item is a link, we use the customized link font metrics instead of the normal font metrics. const QFontMetrics& fontMetrics = itemIsLink(index, view) ? linkFontMetrics : normalFontMetrics; - width += fontMetrics.width(text); + width += fontMetrics.boundingRect(text).width(); if (role == "text") { if (view->supportsItemExpanding()) { @@ -214,12 +214,12 @@ void KStandardItemListWidgetInformant::calculateCompactLayoutItemSizeHints(QVect qreal maximumRequiredWidth = 0.0; if (showOnlyTextRole) { - maximumRequiredWidth = fontMetrics.width(itemText(index, view)); + maximumRequiredWidth = fontMetrics.boundingRect(itemText(index, view)).width(); } else { const QHash& values = view->model()->data(index); foreach (const QByteArray& role, visibleRoles) { const QString& text = roleText(role, values); - const qreal requiredWidth = fontMetrics.width(text); + const qreal requiredWidth = fontMetrics.boundingRect(text).width(); maximumRequiredWidth = qMax(maximumRequiredWidth, requiredWidth); } } @@ -478,7 +478,7 @@ QRectF KStandardItemListWidget::textFocusRect() const const KItemListStyleOption& option = styleOption(); if (option.extendedSelectionRegion) { const QString text = textInfo->staticText.text(); - rect.setWidth(m_customizedFontMetrics.width(text) + 2 * option.padding); + rect.setWidth(m_customizedFontMetrics.boundingRect(text).width() + 2 * option.padding); } return rect; @@ -1115,11 +1115,11 @@ void KStandardItemListWidget::updateTextsCache() QString KStandardItemListWidget::elideRightKeepExtension(const QString &text, int elidingWidth) const { - auto extensionIndex = text.lastIndexOf('.'); + const auto extensionIndex = text.lastIndexOf('.'); if (extensionIndex != -1) { // has file extension - auto extensionLength = text.length() - extensionIndex; - auto extensionWidth = m_customizedFontMetrics.width(text.right(extensionLength)); + const auto extensionLength = text.length() - extensionIndex; + const auto extensionWidth = m_customizedFontMetrics.boundingRect(text.right(extensionLength)).width(); if (elidingWidth > extensionWidth && extensionLength < 6 && (float(extensionWidth) / float(elidingWidth)) < 0.3) { // if we have room to display the file extension and the extension is not too long QString ret = m_customizedFontMetrics.elidedText(text.chopped(extensionLength), @@ -1241,7 +1241,7 @@ void KStandardItemListWidget::updateIconsLayoutTextCache() if (requiredWidth > maxWidth) { const QString elidedText = elideRightKeepExtension(text, maxWidth); textInfo->staticText.setText(elidedText); - requiredWidth = m_customizedFontMetrics.width(elidedText); + requiredWidth = m_customizedFontMetrics.boundingRect(elidedText).width(); } else if (role == "rating") { // Use the width of the rating pixmap, because the rating text is empty. requiredWidth = m_rating.width(); @@ -1285,7 +1285,7 @@ void KStandardItemListWidget::updateCompactLayoutTextCache() TextInfo* textInfo = m_textInfo.value(role); textInfo->staticText.setText(text); - qreal requiredWidth = m_customizedFontMetrics.width(text); + qreal requiredWidth = m_customizedFontMetrics.boundingRect(text).width(); if (requiredWidth > maxWidth) { requiredWidth = maxWidth; const QString elidedText = elideRightKeepExtension(text, maxWidth); @@ -1335,7 +1335,7 @@ void KStandardItemListWidget::updateDetailsLayoutTextCache() QString text = roleText(role, values); // Elide the text in case it does not fit into the available column-width - qreal requiredWidth = m_customizedFontMetrics.width(text); + qreal requiredWidth = m_customizedFontMetrics.boundingRect(text).width(); const qreal roleWidth = columnWidth(role); qreal availableTextWidth = roleWidth - columnWidthInc; @@ -1346,7 +1346,7 @@ void KStandardItemListWidget::updateDetailsLayoutTextCache() if (requiredWidth > availableTextWidth) { text = elideRightKeepExtension(text, availableTextWidth); - requiredWidth = m_customizedFontMetrics.width(text); + requiredWidth = m_customizedFontMetrics.boundingRect(text).width(); } TextInfo* textInfo = m_textInfo.value(role); @@ -1486,14 +1486,8 @@ QPixmap KStandardItemListWidget::pixmapForIcon(const QString& name, const QStrin const QString key = "KStandardItemListWidget:" % name % ":" % overlays.join(QLatin1Char(':')) % ":" % QString::number(size) % ":" % QString::number(mode); QPixmap pixmap; - if (!QPixmapCache::find(key, pixmap)) { - QIcon icon = QIcon::fromTheme(name); - if (icon.isNull()) { - icon = QIcon(name); - } - if (icon.isNull()) { - icon = fallbackIcon; - } + if (!QPixmapCache::find(key, &pixmap)) { + const QIcon icon = QIcon::fromTheme(name, fallbackIcon); pixmap = icon.pixmap(size / qApp->devicePixelRatio(), size / qApp->devicePixelRatio(), mode); if (pixmap.width() != size || pixmap.height() != size) { diff --git a/src/kitemviews/private/kdirectorycontentscounter.cpp b/src/kitemviews/private/kdirectorycontentscounter.cpp index bd204fe8e..4d6a4861c 100644 --- a/src/kitemviews/private/kdirectorycontentscounter.cpp +++ b/src/kitemviews/private/kdirectorycontentscounter.cpp @@ -24,8 +24,14 @@ #include #include +#include #include +namespace { + /// cache of directory counting result + static QHash> *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>(); + } + 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,49 +86,45 @@ KDirectoryContentsCounter::~KDirectoryContentsCounter() } } -void KDirectoryContentsCounter::addDirectory(const QString& path) +void KDirectoryContentsCounter::scanDirectory(const QString& path) { startWorker(path); } -int KDirectoryContentsCounter::countDirectoryContentsSynchronously(const QString& path) +void KDirectoryContentsCounter::slotResult(const QString& path, int count, long size) { - const QString resolvedPath = QFileInfo(path).canonicalFilePath(); + m_workerIsBusy = false; + + const QFileInfo info = QFileInfo(path); + const QString resolvedPath = info.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; + if (!m_priorityQueue.isEmpty()) { + startWorker(m_priorityQueue.takeFirst()); + } else if (!m_queue.isEmpty()) { + startWorker(m_queue.takeFirst()); } - return KDirectoryContentsCounterWorker::subItemsCount(path, options); -} - -void KDirectoryContentsCounter::slotResult(const QString& path, int count) -{ - m_workerIsBusy = false; - - const QString resolvedPath = QFileInfo(path).canonicalFilePath(); - - if (!m_dirWatcher->contains(resolvedPath)) { - m_dirWatcher->addDir(resolvedPath); - m_watchedDirs.insert(resolvedPath); + 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 (!m_queue.isEmpty()) { - startWorker(m_queue.dequeue()); + 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(count, size)); } - emit result(path, count); + // sends the results + emit result(resolvedPath, count, size); } void KDirectoryContentsCounter::slotDirWatchDirty(const QString& path) @@ -146,7 +149,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,8 +169,23 @@ void KDirectoryContentsCounter::slotItemsRemoved() void KDirectoryContentsCounter::startWorker(const QString& path) { + const bool alreadyInCache = s_cache->contains(path); + if (alreadyInCache) { + // 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); + if (!m_queue.contains(path) && !m_priorityQueue.contains(path)) { + if (alreadyInCache) { + m_queue.append(path); + } else { + // append to priority queue + m_priorityQueue.append(path); + } + } } else { KDirectoryContentsCounterWorker::Options options; @@ -185,4 +203,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..65c4bcb1b 100644 --- a/src/kitemviews/private/kdirectorycontentscounter.h +++ b/src/kitemviews/private/kdirectorycontentscounter.h @@ -23,8 +23,9 @@ #include "kdirectorycontentscounterworker.h" -#include +#include #include +#include 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(); @@ -76,10 +72,11 @@ private: private: KFileItemModel* m_model; - QQueue m_queue; + // Used as FIFO queues. + QLinkedList m_priorityQueue; + QLinkedList 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 +#include #else - #include - #include +#include +#include #endif +#include "dolphin_detailsmodesettings.h" + KDirectoryContentsCounterWorker::KDirectoryContentsCounterWorker(QObject* parent) : QObject(parent) { qRegisterMetaType(); } -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 - 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/kitemviews/private/kfileitemmodelfilter.cpp b/src/kitemviews/private/kfileitemmodelfilter.cpp index fc56cc71d..bcd875132 100644 --- a/src/kitemviews/private/kfileitemmodelfilter.cpp +++ b/src/kitemviews/private/kfileitemmodelfilter.cpp @@ -20,8 +20,9 @@ #include "kfileitemmodelfilter.h" -#include +#include +#include KFileItemModelFilter::KFileItemModelFilter() : m_useRegExp(false), @@ -44,12 +45,10 @@ void KFileItemModelFilter::setPattern(const QString& filter) if (filter.contains('*') || filter.contains('?') || filter.contains('[')) { if (!m_regExp) { - m_regExp = new QRegExp(); - m_regExp->setCaseSensitivity(Qt::CaseInsensitive); - m_regExp->setMinimal(false); - m_regExp->setPatternSyntax(QRegExp::WildcardUnix); + m_regExp = new QRegularExpression(); + m_regExp->setPatternOptions(QRegularExpression::CaseInsensitiveOption); } - m_regExp->setPattern(filter); + m_regExp->setPattern(QRegularExpression::wildcardToRegularExpression(filter)); m_useRegExp = m_regExp->isValid(); } else { m_useRegExp = false; @@ -103,7 +102,7 @@ bool KFileItemModelFilter::matches(const KFileItem& item) const bool KFileItemModelFilter::matchesPattern(const KFileItem& item) const { if (m_useRegExp) { - return m_regExp->exactMatch(item.text()); + return m_regExp->match(item.text()).hasMatch(); } else { return item.text().toLower().contains(m_lowerCasePattern); } diff --git a/src/kitemviews/private/kfileitemmodelfilter.h b/src/kitemviews/private/kfileitemmodelfilter.h index f9f588aba..b56e0ad5b 100644 --- a/src/kitemviews/private/kfileitemmodelfilter.h +++ b/src/kitemviews/private/kfileitemmodelfilter.h @@ -26,7 +26,7 @@ #include class KFileItem; -class QRegExp; +class QRegularExpression; /** * @brief Allows to check whether an item of the KFileItemModel @@ -83,7 +83,7 @@ private: bool m_useRegExp; // If true, m_regExp is used for filtering, // otherwise m_lowerCaseFilter is used. - QRegExp* m_regExp; + QRegularExpression *m_regExp; QString m_lowerCasePattern; // Lowercase version of m_filter for // faster comparison in matches(). QString m_pattern; // Property set by setPattern(). 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 #include #include +#include #include #include @@ -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 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 9f0651993..31c11765a 100644 --- a/src/org.kde.dolphin.appdata.xml +++ b/src/org.kde.dolphin.appdata.xml @@ -6,6 +6,7 @@ Dolphin دولفين Dolphin + Dolphin Dolphin Dolphin Dolphin @@ -52,6 +53,7 @@ File Manager مدير ملفات Xestor de ficheros + Fayl meneceri Upravitelj datoteka Gestor de fitxers Gestor de fitxers @@ -100,6 +102,7 @@

Dolphin is a lightweight file manager. It has been designed with ease of use and simplicity in mind, while still allowing flexibility and customisation. This means that you can do your file management exactly the way you want to do it.

دولفين هو مدير ملفات خفيف. صُمِّم دولفين مع أخذ سهولة الاستخدام والبساطة بعين الاعتبار، مع السماح بالمرونة والتخصيص. يعني هذا أنه يمكنك إدارة ملفاتك كما تريد تمامًا.

Dolphin ye un xestor de ficheros llixeru. Diseñóse cola cenciellez y facilidá d'usu en mente, al empar que permite flexibilidá y personalización. Esto quier dicir que pues facer la xestión de ficheros del mou exautu que quieras.

+

Dolphin yüngül bir fayl meneceridir. Bu tətbiq istifadə rahatlığı və sadələiyi ilə bərabər çevik və fərdi ayarlana bilmə üstünlükləri nəzərə alınaraq hazırlanmışdır. Bu o deməkdir ki, siz faylları istədiyiniz kimi idarə edə bilərsiniz.

Dolphinje lagan file manager. On je bio dizajniran sa lakoćom korišćenja i jednostavnosti u vidu, još omogućavajući fleksibilnost i prilagođavanje. To znači da možete da radite svoje upravljanje datotekama onako kako želite da to uradi.

El Dolphin és un gestor de fitxers lleuger. S'ha dissenyat pensant a facilitar el seu ús i que sigui simple, permetent la flexibilitat i la personalització. Això vol dir que podeu fer la gestió dels vostres fitxers de la manera exacta com ho vulgueu fer.

El Dolphin és un gestor de fitxers lleuger. S'ha dissenyat pensant a facilitar el seu ús i que siga simple, permetent la flexibilitat i la personalització. Això vol dir que podeu fer la gestió dels vostres fitxers de la manera exacta com ho vulgueu fer.

@@ -144,6 +147,7 @@

Features:

المزايا:

Carauterístiques:

+

Xüsusiyyətləri:

Svojstva:

Característiques:

Característiques:

@@ -190,6 +194,7 @@

功能:

  • Navigation (or breadcrumb) bar for URLs, allowing you to quickly navigate through the hierarchy of files and folders.
  • +
  • Ünvan sətri qovluqlar üzrə cəld hərəkət etməyə imkan verir.
  • Navigacijska (ili mrvična) traka za URL, dopuÅ¡ta vam da se brzo krećete kroz hijerarhiju datoteka i direktorija.
  • Barra de navegació (o fil d'Ariadna) pels URL, permetent una navegació ràpida per la jerarquia dels fitxers i carpetes.
  • Barra de navegació (o fil d'Ariadna) pels URL, permetent una navegació ràpida per la jerarquia dels fitxers i carpetes.
  • @@ -233,6 +238,7 @@
  • Supports several different kinds of view styles and properties and allows you to configure the view exactly how you want it.
  • يدعم العديد من الأنواع المختلفة من الخصائص وأنماط العرض ويسمح لك بضبط العرض كما تريد تمامًا.
  • Sofita estilos y propiedaes de vista diferentes, y permítete configurar la vista exautamente como quieras.
  • +
  • Bir neçə fərqli görünüş tərzi və xüsusiyyətlərini dəstəkləyir və görünüşü tam olaraq istədiyiniz kimi tənzimləməyə imkan verir.
  • DopuÅ¡ta viÅ¡te vrsta stilova pogleda i svojstava i dopÅ¡ta vam da konfiguriÅ¡ete pogled baÅ¡ kako želite.
  • Accepta diferents classes diverses d'estils de visualització i propietats i us permet configurar la visualització exactament com la vulgueu.
  • Accepta diferents classes diverses d'estils de visualització i propietats i vos permet configurar la visualització exactament com la vulgueu.
  • @@ -276,6 +282,7 @@
  • Split view, allowing you to easily copy or move files between locations.
  • العرض المقسوم، يسمح لك بنسخ ونقل الملفات بين مكانين بسهولة.
  • La vista dixebrada permítete copiar o mover ficheros de mou fácil ente allugamientos.
  • +
  • İkipanelli rejimdə faylları müxtəlif qovluqlar arasında cəld kopyalamq və köçürmək daha rahatdır.
  • Razdvaja pogled, dopuÅ¡tajući lako kopiranje ili pomijeranje datoteka između lokacija
  • Divisió de visualització, permetent copiar o moure fitxers fàcilment entre les ubicacions.
  • Divisió de visualització, permetent copiar o moure fitxers fàcilment entre les ubicacions.
  • @@ -319,6 +326,7 @@
  • Additional information and shortcuts are available as dock-able panels, allowing you to move them around freely and display exactly what you want.
  • تتوفر معلومات واختصارات إضافية كلوحات قابلة للرصف، مما يسمح لك بنقلها بحريّة وعرضها بالضبط كما تريد.
  • La información adicional y los atayos tán disponibles como paneles anclables que pues mover ande quieras y amosar como exautamente quieras.
  • +
  • Əlavə məlumatlar və yarlıqlar yeri dəyişdirilə bilən panellər kimidir və bu sizə onları istədiyiniz yerə daşımağa və görünüşünü istədiyiniz kimi dəyişməyə imkan verir.
  • Dodatne informacije i kratice su dostupne kao usidreni paneli, dopuÅ¡tajući vam da se krećete slobodno i prikažete Å¡ta želite.
  • Hi ha informació addicional i dreceres disponibles com a plafons que es poden acoblar, permetent moure'ls lliurement i mostrar exactament el què vulgueu.
  • Hi ha informació addicional i dreceres disponibles com a plafons que es poden acoblar, permetent moure'ls lliurement i mostrar exactament el què vulgueu.
  • @@ -362,6 +370,7 @@
  • Multiple tab support
  • دعم تعدّد الألسنة
  • Sofitu pa munches llingüetes
  • +
  • Birdən çox vərəqi dəstəkləyir
  • PodrÅ¡ka za viÅ¡e kartica
  • Admet pestanyes múltiples
  • Admet pestanyes múltiples
  • @@ -409,6 +418,7 @@
  • Informational dialogues are displayed in an unobtrusive way.
  • حواريات المعلومات تُعرَض بطريقة غير مُزعجة.
  • Los diálogos informativos amuésense d'un mou non intrusivu.
  • +
  • İnformasiya pəncərələri maneə olmadan görünür.
  • Informativni dijalozi su prikazani na nenametljiv način.
  • Els diàlegs informatius es mostren d'una manera no molesta.
  • Els diàlegs informatius es mostren d'una manera no molesta.
  • @@ -452,6 +462,7 @@
  • Undo/redo support
  • دعم التراجع والإعادة
  • Sofitu pa la desfechura/refechura
  • +
  • Geri qaytarmaq və təkrarlamaq dəstəyi
  • PodrÅ¡ka za poniÅ¡tavanje/ponavljanje akcija
  • Admet desfer/refer
  • Admet desfer/refer
  • @@ -499,6 +510,7 @@
  • Transparent network access through the KIO system.
  • اتصال شبكيّ مباشر باستخدام نظام KIO.
  • Accesu tresparente a la rede pente'l sistema KIO.
  • +
  • KİO vasitəsi ilə şəbəkə fayl sisteminə şəffaf giriş.
  • Transparentni mrežni pristup kroz KIO sistem.
  • Accés transparent a la xarxa a través del sistema KIO.
  • Accés transparent a la xarxa a través del sistema KIO.
  • @@ -551,6 +563,7 @@ File management in Dolphin Xestión de ficheros en Dolphin + Dolphində faylların idarə edilməsi Gestió de fitxers al Dolphin Gestió de fitxers al Dolphin Správa souborů v Dolphinu @@ -576,6 +589,7 @@ Gerenciamento de arquivos no Dolphin Управление файлами Správa súborov v Dolphin + Upravljanje datotek v Dolphinu Filhantering i Dolphin Керування файлами у Dolphin xxFile management in Dolphinxx diff --git a/src/org.kde.dolphin.desktop b/src/org.kde.dolphin.desktop index 6404808ee..94682ce6d 100755 --- a/src/org.kde.dolphin.desktop +++ b/src/org.kde.dolphin.desktop @@ -2,6 +2,7 @@ Name=Dolphin Name[ar]=دولفين Name[ast]=Dolphin +Name[az]=Dolphin Name[ca]=Dolphin Name[ca@valencia]=Dolphin Name[cs]=Dolphin @@ -53,6 +54,7 @@ Categories=Qt;KDE;System;FileTools;FileManager; GenericName=File Manager GenericName[ar]=مدير ملفّات GenericName[ast]=Xestor de ficheros +GenericName[az]=Fayl meneceri GenericName[ca]=Gestor de fitxers GenericName[ca@valencia]=Gestor de fitxers GenericName[cs]=Správce souborů 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..9db7f8bb7 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,25 +224,27 @@ 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 { refreshPixmapView(); const QString mimeType = m_item.mimetype(); - const bool isAnimatedImage = m_preview->isAnimatedImage(itemUrl.toLocalFile()); + const bool isAnimatedImage = m_preview->isAnimatedMimeType(mimeType); 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/information/pixmapviewer.cpp b/src/panels/information/pixmapviewer.cpp index 2601e82ae..39fedb1a1 100644 --- a/src/panels/information/pixmapviewer.cpp +++ b/src/panels/information/pixmapviewer.cpp @@ -186,8 +186,9 @@ void PixmapViewer::stopAnimatedImage() } } -bool PixmapViewer::isAnimatedImage(const QString &fileName) +bool PixmapViewer::isAnimatedMimeType(const QString &mimeType) { - const QByteArray imageFormat = QImageReader::imageFormat(fileName); - return !imageFormat.isEmpty() && QMovie::supportedFormats().contains(imageFormat); + const QList imageFormats = QImageReader::imageFormatsForMimeType(mimeType.toUtf8()); + return std::any_of(imageFormats.begin(), imageFormats.end(), + [](const QByteArray &format){ return QMovie::supportedFormats().contains(format); }); } diff --git a/src/panels/information/pixmapviewer.h b/src/panels/information/pixmapviewer.h index 37071045f..c231b590d 100644 --- a/src/panels/information/pixmapviewer.h +++ b/src/panels/information/pixmapviewer.h @@ -80,9 +80,9 @@ public: void stopAnimatedImage(); /** - * Checks if \a fileName contains an animated image supported by QMovie. + * Checks if \a mimeType has a format supported by QMovie. */ - static bool isAnimatedImage(const QString &fileName); + static bool isAnimatedMimeType(const QString &mimeType); protected: void paintEvent(QPaintEvent* event) override; diff --git a/src/panels/places/placesitem.cpp b/src/panels/places/placesitem.cpp index ea2270020..b1f24e401 100644 --- a/src/panels/places/placesitem.cpp +++ b/src/panels/places/placesitem.cpp @@ -209,7 +209,7 @@ void PlacesItem::initializeDevice(const QString& udi) m_disc = m_device.as(); m_player = m_device.as(); - setText(m_device.description()); + setText(m_device.displayName()); setIcon(m_device.icon()); setIconOverlays(m_device.emblems()); setUdi(udi); 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 @@ + + + + + + + + + + + + + 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 #include @@ -25,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -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 @@ -101,6 +102,7 @@ private: KParts::ReadOnlyPart* m_konsolePart; QString m_konsolePartCurrentDirectory; QQueue m_sendCdToTerminalHistory; + org::kde::KIOFuse::VFS m_kiofuseInterface; }; #endif // TERMINALPANEL_H diff --git a/src/search/dolphinquery.cpp b/src/search/dolphinquery.cpp index ab107f43f..0581a02ec 100644 --- a/src/search/dolphinquery.cpp +++ b/src/search/dolphinquery.cpp @@ -27,6 +27,7 @@ #endif namespace { +#ifdef HAVE_BALOO /** Checks if a given term in the Baloo::Query::searchString() is a special search term * @return: the specific search token of the term, or an empty QString() if none is found */ @@ -67,20 +68,40 @@ namespace { } return textParts; } +#endif } -DolphinQuery DolphinQuery::fromBalooSearchUrl(const QUrl& searchUrl) + +DolphinQuery DolphinQuery::fromSearchUrl(const QUrl& searchUrl) { DolphinQuery model; model.m_searchUrl = searchUrl; + if (searchUrl.scheme() == QLatin1String("baloosearch")) { + model.parseBalooQuery(); + } + + return model; +} + +bool DolphinQuery::supportsScheme(const QString& urlScheme) +{ + static const QStringList supportedSchemes = { + QStringLiteral("baloosearch"), + }; + + return supportedSchemes.contains(urlScheme); +} + +void DolphinQuery::parseBalooQuery() +{ #ifdef HAVE_BALOO - const Baloo::Query query = Baloo::Query::fromSearchUrl(searchUrl); + const Baloo::Query query = Baloo::Query::fromSearchUrl(m_searchUrl); - model.m_includeFolder = query.includeFolder(); + m_includeFolder = query.includeFolder(); const QStringList types = query.types(); - model.m_fileType = types.isEmpty() ? QString() : types.first(); + m_fileType = types.isEmpty() ? QString() : types.first(); QStringList textParts; QString fileName; @@ -93,34 +114,33 @@ DolphinQuery DolphinQuery::fromBalooSearchUrl(const QUrl& searchUrl) if (token == QLatin1String("filename:")) { if (!value.isEmpty()) { fileName = value; - model.m_hasFileName = true; + m_hasFileName = true; } continue; } else if (!token.isEmpty()) { - model.m_searchTerms << token + value; + m_searchTerms << token + value; continue; } else if (subTerm == QLatin1String("AND") && subTerm != subTerms.at(0) && subTerm != subTerms.back()) { continue; } else if (!value.isEmpty()) { textParts << value; - model.m_hasContentSearch = true; + m_hasContentSearch = true; } } - if (model.m_hasFileName) { - if (model.m_hasContentSearch) { + if (m_hasFileName) { + if (m_hasContentSearch) { textParts << QStringLiteral("filename:\"%1\"").arg(fileName); } else { textParts << fileName; } } - model.m_searchText = textParts.join(QLatin1Char(' ')); - + m_searchText = textParts.join(QLatin1Char(' ')); #endif - return model; } + QUrl DolphinQuery::searchUrl() const { return m_searchUrl; diff --git a/src/search/dolphinquery.h b/src/search/dolphinquery.h index 544f246bc..5032621a9 100644 --- a/src/search/dolphinquery.h +++ b/src/search/dolphinquery.h @@ -32,9 +32,10 @@ class DolphinQuery { public: - /** Calls Baloo::Query::fromSearchUrl() with the given @p searchUrl - * and parses the result to extract its separate components */ - static DolphinQuery fromBalooSearchUrl(const QUrl& searchUrl); + /** Parses the components of @p searchUrl for the supported schemes */ + static DolphinQuery fromSearchUrl(const QUrl& searchUrl); + /** Checks whether the DolphinQuery supports the given @p urlScheme */ + static bool supportsScheme(const QString& urlScheme); /** @return the \a searchUrl passed to Baloo::Query::fromSearchUrl() */ QUrl searchUrl() const; @@ -53,6 +54,11 @@ public: /** @return whether the query includes a filter by fileName */ bool hasFileName() const; +private: + /** Calls Baloo::Query::fromSearchUrl() on the current searchUrl + * and parses the result to extract its separate components */ + void parseBalooQuery(); + private: QUrl m_searchUrl; QString m_searchText; diff --git a/src/search/dolphinsearchbox.cpp b/src/search/dolphinsearchbox.cpp index 23f520de1..239280280 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); } @@ -145,8 +146,8 @@ QUrl DolphinSearchBox::urlForSearching() const void DolphinSearchBox::fromSearchUrl(const QUrl& url) { - if (url.scheme() == QLatin1String("baloosearch")) { - const DolphinQuery query = DolphinQuery::fromBalooSearchUrl(url); + if (DolphinQuery::supportsScheme(url.scheme())) { + const DolphinQuery query = DolphinQuery::fromSearchUrl(url); updateFromQuery(query); } else if (url.scheme() == QLatin1String("filenamesearch")) { const QUrlQuery query(url); @@ -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() @@ -340,13 +344,6 @@ void DolphinSearchBox::saveSettings() void DolphinSearchBox::init() { - // Create close button - QToolButton* closeButton = new QToolButton(this); - closeButton->setAutoRaise(true); - closeButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-close"))); - closeButton->setToolTip(i18nc("@info:tooltip", "Quit searching")); - connect(closeButton, &QToolButton::clicked, this, &DolphinSearchBox::emitCloseRequest); - // Create search box m_searchInput = new QLineEdit(this); m_searchInput->setPlaceholderText(i18n("Search...")); @@ -367,11 +364,18 @@ void DolphinSearchBox::init() m_searchInput->addAction(m_saveSearchAction, QLineEdit::TrailingPosition); connect(m_saveSearchAction, &QAction::triggered, this, &DolphinSearchBox::slotSearchSaved); + // Create close button + QToolButton* closeButton = new QToolButton(this); + closeButton->setAutoRaise(true); + closeButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-close"))); + closeButton->setToolTip(i18nc("@info:tooltip", "Quit searching")); + connect(closeButton, &QToolButton::clicked, this, &DolphinSearchBox::emitCloseRequest); + // Apply layout for the search input QHBoxLayout* searchInputLayout = new QHBoxLayout(); searchInputLayout->setContentsMargins(0, 0, 0, 0); - searchInputLayout->addWidget(closeButton); searchInputLayout->addWidget(m_searchInput); + searchInputLayout->addWidget(closeButton); // Create "Filename" and "Content" button m_fileNameButton = new QToolButton(this); @@ -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..9d05a8ab0 100644 --- a/src/settings/dolphin_detailsmodesettings.kcfg +++ b/src/settings/dolphin_detailsmodesettings.kcfg @@ -44,5 +44,13 @@ true + + + true + + + + 10 + 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 @@ QUrl::fromLocalFile(QDir::homePath()).toDisplayString(QUrl::PreferLocalFile) + + + true + false diff --git a/src/settings/general/previewssettingspage.cpp b/src/settings/general/previewssettingspage.cpp index 90a4211c0..3e435b53c 100644 --- a/src/settings/general/previewssettingspage.cpp +++ b/src/settings/general/previewssettingspage.cpp @@ -38,7 +38,8 @@ // default settings namespace { - const int MaxRemotePreviewSize = 0; // 0 MB + const int DefaultMaxLocalPreviewSize = 0; // 0 MB + const int DefaultMaxRemotePreviewSize = 0; // 0 MB } PreviewsSettingsPage::PreviewsSettingsPage(QWidget* parent) : @@ -46,6 +47,7 @@ PreviewsSettingsPage::PreviewsSettingsPage(QWidget* parent) : m_initialized(false), m_listView(nullptr), m_enabledPreviewPlugins(), + m_localFileSizeBox(nullptr), m_remoteFileSizeBox(nullptr) { QVBoxLayout* topLayout = new QVBoxLayout(this); @@ -68,24 +70,41 @@ PreviewsSettingsPage::PreviewsSettingsPage(QWidget* parent) : m_listView->setItemDelegate(delegate); m_listView->setVerticalScrollMode(QListView::ScrollPerPixel); + QLabel* localFileSizeLabel = new QLabel(i18n("Skip previews for local files above:"), this); + + m_localFileSizeBox = new QSpinBox(this); + m_localFileSizeBox->setSingleStep(1); + m_localFileSizeBox->setSuffix(QStringLiteral(" MB")); + m_localFileSizeBox->setRange(0, 9999999); /* MB */ + m_localFileSizeBox->setSpecialValueText(tr("No limit")); + + QHBoxLayout* localFileSizeBoxLayout = new QHBoxLayout(); + localFileSizeBoxLayout->addWidget(localFileSizeLabel); + localFileSizeBoxLayout->addStretch(0); + localFileSizeBoxLayout->addWidget(m_localFileSizeBox); + QLabel* remoteFileSizeLabel = new QLabel(i18nc("@label", "Skip previews for remote files above:"), this); m_remoteFileSizeBox = new QSpinBox(this); m_remoteFileSizeBox->setSingleStep(1); m_remoteFileSizeBox->setSuffix(QStringLiteral(" MB")); m_remoteFileSizeBox->setRange(0, 9999999); /* MB */ + m_remoteFileSizeBox->setSpecialValueText(tr("No previews")); - QHBoxLayout* fileSizeBoxLayout = new QHBoxLayout(); - fileSizeBoxLayout->addWidget(remoteFileSizeLabel, 0, Qt::AlignRight); - fileSizeBoxLayout->addWidget(m_remoteFileSizeBox); + QHBoxLayout* remoteFileSizeBoxLayout = new QHBoxLayout(); + remoteFileSizeBoxLayout->addWidget(remoteFileSizeLabel); + remoteFileSizeBoxLayout->addStretch(0); + remoteFileSizeBoxLayout->addWidget(m_remoteFileSizeBox); topLayout->addWidget(showPreviewsLabel); topLayout->addWidget(m_listView); - topLayout->addLayout(fileSizeBoxLayout); + topLayout->addLayout(localFileSizeBoxLayout); + topLayout->addLayout(remoteFileSizeBoxLayout); loadSettings(); connect(m_listView, &QListView::clicked, this, &PreviewsSettingsPage::changed); + connect(m_localFileSizeBox, QOverload::of(&QSpinBox::valueChanged), this, &PreviewsSettingsPage::changed); connect(m_remoteFileSizeBox, QOverload::of(&QSpinBox::valueChanged), this, &PreviewsSettingsPage::changed); } @@ -112,6 +131,11 @@ void PreviewsSettingsPage::applySettings() KConfigGroup globalConfig(KSharedConfig::openConfig(), QStringLiteral("PreviewSettings")); globalConfig.writeEntry("Plugins", m_enabledPreviewPlugins); + const qulonglong maximumLocalSize = static_cast(m_localFileSizeBox->value()) * 1024 * 1024; + globalConfig.writeEntry("MaximumSize", + maximumLocalSize, + KConfigBase::Normal | KConfigBase::Global); + const qulonglong maximumRemoteSize = static_cast(m_remoteFileSizeBox->value()) * 1024 * 1024; globalConfig.writeEntry("MaximumRemoteSize", maximumRemoteSize, @@ -121,7 +145,8 @@ void PreviewsSettingsPage::applySettings() void PreviewsSettingsPage::restoreDefaults() { - m_remoteFileSizeBox->setValue(MaxRemotePreviewSize); + m_localFileSizeBox->setValue(DefaultMaxLocalPreviewSize); + m_remoteFileSizeBox->setValue(DefaultMaxRemotePreviewSize); } void PreviewsSettingsPage::showEvent(QShowEvent* event) @@ -169,9 +194,13 @@ void PreviewsSettingsPage::loadSettings() const KConfigGroup globalConfig(KSharedConfig::openConfig(), QStringLiteral("PreviewSettings")); m_enabledPreviewPlugins = globalConfig.readEntry("Plugins", KIO::PreviewJob::defaultPlugins()); - const qulonglong defaultRemotePreview = static_cast(MaxRemotePreviewSize) * 1024 * 1024; + const qulonglong defaultLocalPreview = static_cast(DefaultMaxLocalPreviewSize) * 1024 * 1024; + const qulonglong maxLocalByteSize = globalConfig.readEntry("MaximumSize", defaultLocalPreview); + const int maxLocalMByteSize = maxLocalByteSize / (1024 * 1024); + m_localFileSizeBox->setValue(maxLocalMByteSize); + + const qulonglong defaultRemotePreview = static_cast(DefaultMaxRemotePreviewSize) * 1024 * 1024; const qulonglong maxRemoteByteSize = globalConfig.readEntry("MaximumRemoteSize", defaultRemotePreview); const int maxRemoteMByteSize = maxRemoteByteSize / (1024 * 1024); m_remoteFileSizeBox->setValue(maxRemoteMByteSize); } - diff --git a/src/settings/general/previewssettingspage.h b/src/settings/general/previewssettingspage.h index 957523710..a7f54f601 100644 --- a/src/settings/general/previewssettingspage.h +++ b/src/settings/general/previewssettingspage.h @@ -61,6 +61,7 @@ private: bool m_initialized; QListView *m_listView; QStringList m_enabledPreviewPlugins; + QSpinBox* m_localFileSizeBox; QSpinBox* m_remoteFileSizeBox; }; 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(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.desktop b/src/settings/kcm/kcmdolphingeneral.desktop index f46d5118b..ba8cdefde 100644 --- a/src/settings/kcm/kcmdolphingeneral.desktop +++ b/src/settings/kcm/kcmdolphingeneral.desktop @@ -1,6 +1,7 @@ Name=Dolphin General Name[ar]=دولفين العامّ Name[ast]=Axustes xenerales de Dolphin +Name[az]=Əsas Dolphin parametrləri Name[ca]=General del Dolphin Name[ca@valencia]=General del Dolphin Name[cs]=Obecný Dolphin @@ -47,6 +48,7 @@ Name[zh_TW]=Dolphin 一般 Comment=This service allows configuration of general Dolphin settings. Comment[ar]=تسمح هذه الخدمة بضبط إعدادات دولفين العامّة. Comment[ast]=Esti serviciu permite la configuración de los axustes xenerales de Dolphin. +Comment[az]=Bu xidmət əsas Dolphin parametrlərini ayarlamağa imkan verir. Comment[ca]=Aquest servei permet la configuració de l'arranjament general del Dolphin. Comment[ca@valencia]=Aquest servei permet la configuració de l'arranjament general del Dolphin. Comment[cs]=Tato služba umožňuje obecné nastavení Dolphinu. @@ -104,6 +106,7 @@ X-DocPath=dolphin/index.html#preferences-dialog-general Name=General Name[ar]=عامّ Name[ast]=Xeneral +Name[az]=Əsas Name[ca]=General Name[ca@valencia]=General Name[cs]=Obecné @@ -150,6 +153,7 @@ Name[zh_TW]=一般 Comment=Configure general file manager settings Comment[ar]=اضبط إعدادات مدير الملفّات العامّة Comment[ast]=Configura los axustes xenerales del xestor de ficheros +Comment[az]=Fayl menecerinin ayarları Comment[ca]=Configura les opcions generals del gestor de fitxers Comment[ca@valencia]=Configura les opcions generals del gestor de fitxers Comment[cs]=Obecné nastavení správce souborů @@ -195,6 +199,7 @@ Comment[zh_TW]=設定一般檔案管理員 X-KDE-Keywords=file manager X-KDE-Keywords[ar]=مدير ملفّات ملفات الملفّات الملفات X-KDE-Keywords[ast]=xestor de ficheros +X-KDE-Keywords[az]=fayl meneceri X-KDE-Keywords[ca]=gestor de fitxers X-KDE-Keywords[ca@valencia]=gestor de fitxers X-KDE-Keywords[cs]=správce souborů 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(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.desktop b/src/settings/kcm/kcmdolphinnavigation.desktop index 503f163d1..fd649b548 100644 --- a/src/settings/kcm/kcmdolphinnavigation.desktop +++ b/src/settings/kcm/kcmdolphinnavigation.desktop @@ -1,6 +1,7 @@ Name=Dolphin Navigation Name[ar]=التّنقّل في دولفين Name[ast]=Navegación de Dolphin +Name[az]=Dolphində hərəkət Name[ca]=Navegació del Dolphin Name[ca@valencia]=Navegació del Dolphin Name[cs]=Navigace Dolphinu @@ -47,6 +48,7 @@ Name[zh_TW]=Dolphin 導覽 Comment=This service allows configuration of the Dolphin navigation. Comment[ar]=تسمح هذه الخدمة بضبط التّنقّل في دولفين. Comment[ast]=Esti serviciu permite la configuración de la navegación del Dolphin. +Comment[az]=Bu xidmət Dolphin üzrə hərəkəti tənzimləməyə imkan verir. Comment[ca]=Aquest servei permet la configuració de la navegació del Dolphin. Comment[ca@valencia]=Aquest servei permet la configuració de la navegació del Dolphin. Comment[cs]=Tato služba umožňuje nastavení navigace v Dolphinu. @@ -103,6 +105,7 @@ X-DocPath=dolphin/index.html#preferences-dialog-navigation Name=Navigation Name[ar]=التّنقّل Name[ast]=Navegación +Name[az]=Naviqasiy Name[ca]=Navegació Name[ca@valencia]=Navegació Name[cs]=Navigace @@ -150,6 +153,7 @@ Name[zh_TW]=導覽 Comment=Configure file manager navigation Comment[ar]=اضبط التّنقّل في مدير الملفّات Comment[ast]=Configura la navegación del xestor de ficheros +Comment[az]=Fayl meneceri naviqasiyasını tənzimləmək Comment[ca]=Configura la navegació del gestor de fitxers Comment[ca@valencia]=Configura la navegació del gestor de fitxers Comment[cs]=Nastavení navigace správce souborů @@ -196,6 +200,7 @@ Comment[zh_TW]=設定檔案管理員導覽 X-KDE-Keywords=file manager X-KDE-Keywords[ar]=مدير ملفّات ملفات الملفّات الملفات X-KDE-Keywords[ast]=xestor de ficheros +X-KDE-Keywords[az]=fayl meneceri X-KDE-Keywords[ca]=gestor de fitxers X-KDE-Keywords[ca@valencia]=gestor de fitxers X-KDE-Keywords[cs]=správce souborů 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(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.desktop b/src/settings/kcm/kcmdolphinservices.desktop index 1187733a4..10e806a81 100644 --- a/src/settings/kcm/kcmdolphinservices.desktop +++ b/src/settings/kcm/kcmdolphinservices.desktop @@ -1,6 +1,7 @@ Name=Dolphin Services Name[ar]=خدمات دولفين Name[ast]=Servicios de Dolphin +Name[az]=Dolphin xidmətləri Name[ca]=Serveis del Dolphin Name[ca@valencia]=Serveis del Dolphin Name[cs]=Služby Dolphinu @@ -57,6 +58,7 @@ X-DocPath=dolphin/index.html#preferences-dialog-services Name=Services Name[ar]=الخدمات Name[ast]=Servicios +Name[az]=Xidmətlər Name[ca]=Serveis Name[ca@valencia]=Serveis Name[cs]=Služby @@ -103,6 +105,7 @@ Name[zh_TW]=服務 Comment=Configure file manager services Comment[ar]=اضبط خدمات مدير الملفّات Comment[ast]=Configura los servicios del xestor de ficheros +Comment[az]=Fayl meneceri xidmətlərini tənzimləmək Comment[ca]=Configura els serveis del gestor de fitxers Comment[ca@valencia]=Configura els serveis del gestor de fitxers Comment[cs]=Nastavení služeb správce souborů @@ -148,6 +151,7 @@ Comment[zh_TW]=設定檔案管理員服務 X-KDE-Keywords=file manager X-KDE-Keywords[ar]=مدير ملفّات ملفات الملفّات الملفات X-KDE-Keywords[ast]=xestor de ficheros +X-KDE-Keywords[az]=fayl meneceri X-KDE-Keywords[ca]=gestor de fitxers X-KDE-Keywords[ca@valencia]=gestor de fitxers X-KDE-Keywords[cs]=správce souborů 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..7077bac8a 100644 --- a/src/settings/kcm/kcmdolphinviewmodes.cpp +++ b/src/settings/kcm/kcmdolphinviewmodes.cpp @@ -33,12 +33,10 @@ K_PLUGIN_FACTORY(KCMDolphinViewModesConfigFactory, registerPlugin(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); + markAsChanged(); } #include "kcmdolphinviewmodes.moc" diff --git a/src/settings/kcm/kcmdolphinviewmodes.desktop b/src/settings/kcm/kcmdolphinviewmodes.desktop index 16b3fd568..b5ea5d047 100644 --- a/src/settings/kcm/kcmdolphinviewmodes.desktop +++ b/src/settings/kcm/kcmdolphinviewmodes.desktop @@ -1,6 +1,7 @@ Name=Dolphin View Modes Name[ar]=أوضاع المنظور في دولفين Name[ast]=Moos de vista de Dolphin +Name[az]=Dolphin baxış rejimi Name[ca]=Modes de vista del Dolphin Name[ca@valencia]=Modes de vista del Dolphin Name[cs]=Režimy pohledů Dolphinu @@ -47,6 +48,7 @@ Name[zh_TW]=設定檔案管理員服務 Comment=This service allows configuration of the Dolphin view modes. Comment[ar]=تسمح هذه الخدمة بضبط أوضاع المنظور في دولفين. Comment[ast]=Esti serviciu permite la configuración de los moos de vista de Dolphin. +Comment[az]=Bu xidmət Dolphin baxış rejimini tənzimləməyə imkan verir Comment[ca]=Aquest servei permet la configuració dels modes de vista del Dolphin. Comment[ca@valencia]=Aquest servei permet la configuració dels modes de vista del Dolphin. Comment[cs]=Tato služba umožňuje nastavení režimů pohledu Dolphinu. @@ -103,6 +105,7 @@ X-DocPath=dolphin/index.html#preferences-dialog-viewmodes Name=View Modes Name[ar]=أوضاع المنظور Name[ast]=Moos de vista +Name[az]=Baxış rejimləri Name[ca]=Modes de vista Name[ca@valencia]=Modes de vista Name[cs]=Režimy pohledu @@ -149,6 +152,7 @@ Name[zh_TW]=檢視模式 Comment=Configure file manager view modes Comment[ar]=اضبط أوضاع المنظور في مدير الملفّات Comment[ast]=Configura los moos de vista del xestor de ficheros +Comment[az]=Fayl meneceri baxış rejimlərini tənzimləmək Comment[ca]=Configura els modes de vista del gestor de fitxers Comment[ca@valencia]=Configura els modes de vista del gestor de fitxers Comment[cs]=Nastavení režimů pohledu správce souborů @@ -195,6 +199,7 @@ Comment[zh_TW]=設定檔案管理員檢視模式 X-KDE-Keywords=file manager X-KDE-Keywords[ar]=مدير ملفّات ملفات الملفّات الملفات X-KDE-Keywords[ast]=xestor de ficheros +X-KDE-Keywords[az]=fayl meneceri X-KDE-Keywords[ca]=gestor de fitxers X-KDE-Keywords[ca@valencia]=gestor de fitxers X-KDE-Keywords[cs]=správce souborů 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 #include +#include #include #include #include #include #include #include -#include #include - #include +#include + +#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 +#include +#include +#else +#include +#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> 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..e18bfb09e 100644 --- a/src/settings/services/servicessettingspage.cpp +++ b/src/settings/services/servicessettingspage.cpp @@ -38,6 +38,7 @@ #include #include #include +#include namespace { @@ -61,25 +62,33 @@ 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, [this](const QString &filter){ + m_sortModel->setFilterFixedString(filter); + }); m_listView = new QListView(this); - ServiceItemDelegate* delegate = new ServiceItemDelegate(m_listView, m_listView); + auto *delegate = new ServiceItemDelegate(m_listView, m_listView); m_serviceModel = new ServiceModel(this); m_sortModel = new QSortFilterProxyModel(this); 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(m_listView, &QListView::clicked, this, &ServicesSettingsPage::changed); - KNS3::Button* downloadButton = new KNS3::Button(i18nc("@action:button", "Download New Services..."), - QStringLiteral("servicemenu.knsrc"), - this); + auto *downloadButton = new KNS3::Button(i18nc("@action:button", "Download New Services..."), + QStringLiteral("servicemenu.knsrc"), + this); connect(downloadButton, &KNS3::Button::dialogFinished, this, &ServicesSettingsPage::loadServices); topLayout->addWidget(label); + topLayout->addWidget(m_searchLineEdit); topLayout->addWidget(m_listView); topLayout->addWidget(downloadButton); @@ -87,9 +96,7 @@ ServicesSettingsPage::ServicesSettingsPage(QWidget* parent) : std::sort(m_enabledVcsPlugins.begin(), m_enabledVcsPlugins.end()); } -ServicesSettingsPage::~ServicesSettingsPage() -{ -} +ServicesSettingsPage::~ServicesSettingsPage() = default; void ServicesSettingsPage::applySettings() { @@ -102,7 +109,7 @@ void ServicesSettingsPage::applySettings() QStringList enabledPlugins; - const QAbstractItemModel* model = m_listView->model(); + const QAbstractItemModel *model = m_listView->model(); for (int i = 0; i < model->rowCount(); ++i) { const QModelIndex index = model->index(i, 0); const QString service = model->data(index, ServiceModel::DesktopEntryNameRole).toString(); @@ -188,19 +195,16 @@ void ServicesSettingsPage::loadServices() // Load generic services const KService::List entries = KServiceTypeTrader::self()->query(QStringLiteral("KonqPopupMenu/Plugin")); - foreach (const KService::Ptr& service, entries) { + for (const KService::Ptr &service : entries) { const QString file = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kservices5/" % service->entryPath()); - const QList serviceActions = - KDesktopFileActions::userDefinedServices(file, true); + const QList serviceActions = KDesktopFileActions::userDefinedServices(file, true); - KDesktopFile desktopFile(file); + const KDesktopFile desktopFile(file); const QString subMenuName = desktopFile.desktopGroup().readEntry("X-KDE-Submenu"); - foreach (const KServiceAction& action, serviceActions) { + for (const KServiceAction &action : serviceActions) { const QString serviceName = action.name(); - const bool addService = !action.noDisplay() - && !action.isSeparator() - && !isInServicesList(serviceName); + const bool addService = !action.noDisplay() && !action.isSeparator() && !isInServicesList(serviceName); if (addService) { const QString itemName = subMenuName.isEmpty() @@ -214,7 +218,7 @@ void ServicesSettingsPage::loadServices() // Load service plugins that implement the KFileItemActionPlugin interface const KService::List pluginServices = KServiceTypeTrader::self()->query(QStringLiteral("KFileItemAction/Plugin")); - foreach (const KService::Ptr& service, pluginServices) { + for (const KService::Ptr &service : pluginServices) { const QString desktopEntryName = service->desktopEntryName(); if (!isInServicesList(desktopEntryName)) { const bool checked = showGroup.readEntry(desktopEntryName, true); @@ -227,7 +231,7 @@ void ServicesSettingsPage::loadServices() return metaData.serviceTypes().contains(QLatin1String("KFileItemAction/Plugin")); }); - foreach (const auto& jsonMetadata, jsonPlugins) { + for (const auto &jsonMetadata : jsonPlugins) { const QString desktopEntryName = jsonMetadata.pluginId(); if (!isInServicesList(desktopEntryName)) { const bool checked = showGroup.readEntry(desktopEntryName, true); @@ -236,6 +240,7 @@ void ServicesSettingsPage::loadServices() } m_sortModel->sort(Qt::DisplayRole); + m_searchLineEdit->setFocus(Qt::OtherFocusReason); } void ServicesSettingsPage::loadVersionControlSystems() @@ -244,8 +249,8 @@ void ServicesSettingsPage::loadVersionControlSystems() // Create a checkbox for each available version control plugin const KService::List pluginServices = KServiceTypeTrader::self()->query(QStringLiteral("FileViewVersionControlPlugin")); - for (KService::List::ConstIterator it = pluginServices.constBegin(); it != pluginServices.constEnd(); ++it) { - const QString pluginName = (*it)->name(); + for (const auto &plugin : pluginServices) { + const QString pluginName = plugin->name(); addRow(QStringLiteral("code-class"), pluginName, VersionControlServicePrefix + pluginName, @@ -255,7 +260,7 @@ void ServicesSettingsPage::loadVersionControlSystems() m_sortModel->sort(Qt::DisplayRole); } -bool ServicesSettingsPage::isInServicesList(const QString& service) const +bool ServicesSettingsPage::isInServicesList(const QString &service) const { for (int i = 0; i < m_serviceModel->rowCount(); ++i) { const QModelIndex index = m_serviceModel->index(i, 0); @@ -266,9 +271,9 @@ bool ServicesSettingsPage::isInServicesList(const QString& service) const return false; } -void ServicesSettingsPage::addRow(const QString& icon, - const QString& text, - const QString& value, +void ServicesSettingsPage::addRow(const QString &icon, + const QString &text, + const QString &value, bool checked) { m_serviceModel->insertRow(0); diff --git a/src/settings/services/servicessettingspage.h b/src/settings/services/servicessettingspage.h index cd4cbe52f..8f507407b 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. @@ -59,21 +60,22 @@ private: */ void loadVersionControlSystems(); - bool isInServicesList(const QString& service) const; + bool isInServicesList(const QString &service) const; /** * Adds a row to the model of m_listView. */ - void addRow(const QString& icon, - const QString& text, - const QString& value, + void addRow(const QString &icon, + const QString &text, + const QString &value, bool checked); private: bool m_initialized; - ServiceModel* m_serviceModel; - QSortFilterProxyModel* m_sortModel; + 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 #include +#include #include #include #include #include +#include #include +#include #include -#include 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 -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..3e3f96ffc 100644 --- a/src/settings/viewmodes/viewsettingstab.cpp +++ b/src/settings/viewmodes/viewsettingstab.cpp @@ -33,6 +33,10 @@ #include #include #include +#include +#include +#include +#include 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::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,13 @@ ViewSettingsTab::ViewSettingsTab(Mode mode, QWidget* parent) : break; case DetailsMode: connect(m_expandableFolders, &QCheckBox::toggled, this, &ViewSettingsTab::changed); +#ifndef Q_OS_WIN + connect(m_recursiveDirectorySizeLimit, QOverload::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()); + }); +#endif break; default: break; @@ -153,6 +185,10 @@ void ViewSettingsTab::applySettings() break; case DetailsMode: DetailsModeSettings::setExpandableFolders(m_expandableFolders->isChecked()); +#ifndef Q_OS_WIN + DetailsModeSettings::setDirectorySizeCount(m_numberOfItems->isChecked()); + DetailsModeSettings::setRecursiveDirectorySizeLimit(m_recursiveDirectorySizeLimit->value()); +#endif break; default: break; @@ -201,6 +237,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/statusbar/dolphinstatusbar.cpp b/src/statusbar/dolphinstatusbar.cpp index 8a81960af..6cd76bb60 100644 --- a/src/statusbar/dolphinstatusbar.cpp +++ b/src/statusbar/dolphinstatusbar.cpp @@ -115,7 +115,7 @@ DolphinStatusBar::DolphinStatusBar(QWidget* parent) : m_zoomSlider->setMaximumWidth(fontMetrics.averageCharWidth() * 25); - m_spaceInfo->setFixedHeight(zoomSliderHeight); + m_spaceInfo->setFixedHeight(contentHeight); m_spaceInfo->setMaximumWidth(fontMetrics.averageCharWidth() * 25); m_spaceInfo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); @@ -229,7 +229,9 @@ QString DolphinStatusBar::defaultText() const void DolphinStatusBar::setUrl(const QUrl& url) { - m_spaceInfo->setUrl(url); + if (GeneralSettings::showSpaceInfo()) { + m_spaceInfo->setUrl(url); + } } QUrl DolphinStatusBar::url() const @@ -332,7 +334,7 @@ void DolphinStatusBar::setExtensionsVisible(bool visible) showSpaceInfo = GeneralSettings::showSpaceInfo(); showZoomSlider = GeneralSettings::showZoomSlider(); } - m_spaceInfo->setVisible(showSpaceInfo); + m_spaceInfo->setShown(showSpaceInfo); m_zoomSlider->setVisible(showZoomSlider); } diff --git a/src/statusbar/spaceinfoobserver.cpp b/src/statusbar/spaceinfoobserver.cpp index 692eba7e7..0d8f5f2fe 100644 --- a/src/statusbar/spaceinfoobserver.cpp +++ b/src/statusbar/spaceinfoobserver.cpp @@ -24,6 +24,7 @@ SpaceInfoObserver::SpaceInfoObserver(const QUrl& url, QObject* parent) : QObject(parent), m_mountPointObserver(nullptr), + m_hasData(false), m_dataSize(0), m_dataAvailable(0) { @@ -81,7 +82,8 @@ void SpaceInfoObserver::update() void SpaceInfoObserver::spaceInfoChanged(quint64 size, quint64 available) { // Make sure that the size has actually changed - if (m_dataSize != size || m_dataAvailable != available) { + if (m_dataSize != size || m_dataAvailable != available || !m_hasData) { + m_hasData = true; m_dataSize = size; m_dataAvailable = available; diff --git a/src/statusbar/spaceinfoobserver.h b/src/statusbar/spaceinfoobserver.h index 93f4c8c91..67dbf15ec 100644 --- a/src/statusbar/spaceinfoobserver.h +++ b/src/statusbar/spaceinfoobserver.h @@ -55,6 +55,7 @@ private slots: private: MountPointObserver* m_mountPointObserver; + bool m_hasData; quint64 m_dataSize; quint64 m_dataAvailable; }; diff --git a/src/statusbar/statusbarspaceinfo.cpp b/src/statusbar/statusbarspaceinfo.cpp index 3ac87925f..acffcf69d 100644 --- a/src/statusbar/statusbarspaceinfo.cpp +++ b/src/statusbar/statusbarspaceinfo.cpp @@ -38,12 +38,23 @@ StatusBarSpaceInfo::~StatusBarSpaceInfo() { } +void StatusBarSpaceInfo::setShown(bool shown) +{ + m_shown = shown; + if (!m_shown) { + hide(); + m_ready = false; + } +} + void StatusBarSpaceInfo::setUrl(const QUrl& url) { if (m_url != url) { m_url = url; + m_ready = false; if (m_observer) { - m_observer->setUrl(url); + m_observer.reset(new SpaceInfoObserver(m_url, this)); + connect(m_observer.data(), &SpaceInfoObserver::valuesChanged, this, &StatusBarSpaceInfo::slotValuesChanged); } } } @@ -62,15 +73,24 @@ void StatusBarSpaceInfo::update() void StatusBarSpaceInfo::showEvent(QShowEvent* event) { - KCapacityBar::showEvent(event); - m_observer.reset(new SpaceInfoObserver(m_url, this)); - slotValuesChanged(); - connect(m_observer.data(), &SpaceInfoObserver::valuesChanged, this, &StatusBarSpaceInfo::slotValuesChanged); + if (m_shown) { + if (m_ready) { + KCapacityBar::showEvent(event); + } + + if (m_observer.isNull()) { + m_observer.reset(new SpaceInfoObserver(m_url, this)); + connect(m_observer.data(), &SpaceInfoObserver::valuesChanged, this, &StatusBarSpaceInfo::slotValuesChanged); + } + } } void StatusBarSpaceInfo::hideEvent(QHideEvent* event) { - m_observer.reset(); + if (m_ready) { + m_observer.reset(); + m_ready = false; + } KCapacityBar::hideEvent(event); } @@ -95,19 +115,26 @@ void StatusBarSpaceInfo::slotValuesChanged() { Q_ASSERT(m_observer); const quint64 size = m_observer->size(); - if (size == 0) { - setText(i18nc("@info:status", "Unknown size")); - setValue(0); - update(); + + if (!m_shown || size == 0) { + hide(); + return; + } + + m_ready = true; + + const quint64 available = m_observer->available(); + const quint64 used = size - available; + const int percentUsed = qRound(100.0 * qreal(used) / qreal(size)); + + setText(i18nc("@info:status Free disk space", "%1 free", KIO::convertSize(available))); + setUpdatesEnabled(false); + setValue(percentUsed); + setUpdatesEnabled(true); + + if (!isVisible()) { + show(); } else { - const quint64 available = m_observer->available(); - const quint64 used = size - available; - const int percentUsed = qRound(100.0 * qreal(used) / qreal(size)); - - setText(i18nc("@info:status Free disk space", "%1 free", KIO::convertSize(available))); - setUpdatesEnabled(false); - setValue(percentUsed); - setUpdatesEnabled(true); update(); } } diff --git a/src/statusbar/statusbarspaceinfo.h b/src/statusbar/statusbarspaceinfo.h index 24f8b7f29..0b0d787dd 100644 --- a/src/statusbar/statusbarspaceinfo.h +++ b/src/statusbar/statusbarspaceinfo.h @@ -42,6 +42,10 @@ public: explicit StatusBarSpaceInfo(QWidget* parent = nullptr); ~StatusBarSpaceInfo() override; + /** + * Use this to set the widget visibility as it can hide itself + */ + void setShown(bool); void setUrl(const QUrl& url); QUrl url() const; @@ -58,6 +62,8 @@ private slots: private: QScopedPointer m_observer; QUrl m_url; + bool m_ready; + bool m_shown; }; #endif diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 8d4498675..a6fbf7845 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -3,11 +3,7 @@ set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) find_package(Qt5Test CONFIG REQUIRED) include(ECMAddTests) -include(FindGem) - -find_gem(test-unit REQUIRED) -set_package_properties(Gem:test-unit PROPERTIES - DESCRIPTION "Ruby gem 'test-unit' required for testing of servicemenu helpers.") +include(FindGem) # For servicemenutest, see bottom of this file # KItemSetTest ecm_add_test(kitemsettest.cpp LINK_LIBRARIES dolphinprivate Qt5::Test) @@ -44,16 +40,16 @@ ecm_add_test(kitemlistkeyboardsearchmanagertest.cpp LINK_LIBRARIES dolphinprivat # DolphinSearchBox if (KF5Baloo_FOUND) - ecm_add_test(dolphinsearchboxtest.cpp - TEST_NAME dolphinsearchboxtest - LINK_LIBRARIES dolphinprivate dolphinstatic Qt5::Test) + ecm_add_test(dolphinsearchboxtest.cpp + TEST_NAME dolphinsearchboxtest + LINK_LIBRARIES dolphinprivate dolphinstatic Qt5::Test) endif() # DolphinQuery if (KF5Baloo_FOUND) - ecm_add_test(dolphinquerytest.cpp - TEST_NAME dolphinquerytest - LINK_LIBRARIES dolphinprivate dolphinstatic Qt5::Test) + ecm_add_test(dolphinquerytest.cpp + TEST_NAME dolphinquerytest + LINK_LIBRARIES dolphinprivate dolphinstatic Qt5::Test) endif() # KStandardItemModelTest @@ -84,5 +80,11 @@ if (KF5_VERSION VERSION_GREATER_EQUAL 5.63.0) LINK_LIBRARIES dolphinprivate dolphinstatic Qt5::Test) endif() -add_test(NAME servicemenutest - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/../settings/services/test/test_run.rb) +find_gem(test-unit) +set_package_properties(Gem:test-unit PROPERTIES + TYPE RECOMMENDED + DESCRIPTION "Ruby gem 'test-unit' required for testing of servicemenu helpers.") +if (Gem:test-unit_FOUND) + add_test(NAME servicemenutest + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/../settings/services/test/test_run.rb) +endif() diff --git a/src/tests/dolphinquerytest.cpp b/src/tests/dolphinquerytest.cpp index b65ee037f..a0774e88c 100644 --- a/src/tests/dolphinquerytest.cpp +++ b/src/tests/dolphinquerytest.cpp @@ -36,11 +36,40 @@ private slots: void testBalooSearchParsing(); }; +/** + * Helper function to compose the baloo query URL used for searching + */ +QUrl balooQueryUrl(const QString& searchString) +{ + const QJsonObject jsonObject { + {"searchString", searchString} + }; + + const QJsonDocument doc(jsonObject); + const QString queryString = QString::fromUtf8(doc.toJson(QJsonDocument::Compact)); + + QUrlQuery urlQuery; + urlQuery.addQueryItem(QStringLiteral("json"), queryString); + + QUrl searchUrl; + searchUrl.setScheme(QLatin1String("baloosearch")); + searchUrl.setQuery(urlQuery); + + return searchUrl; +} + /** * Defines the parameters for the test cases in testBalooSearchParsing() */ void DolphinSearchBoxTest::testBalooSearchParsing_data() { + + QTest::addColumn("searchUrl"); + QTest::addColumn("expectedText"); + QTest::addColumn("expectedTerms"); + QTest::addColumn("hasContent"); + QTest::addColumn("hasFileName"); + const QString text = QStringLiteral("abc"); const QString textS = QStringLiteral("abc xyz"); const QString filename = QStringLiteral("filename:\"%1\"").arg(text); @@ -53,115 +82,89 @@ void DolphinSearchBoxTest::testBalooSearchParsing_data() const QString tagS = QStringLiteral("tag:\"tagB with spaces\""); // in search url const QString tagR = QStringLiteral("tag:tagB with spaces"); // in result term - QTest::addColumn("searchString"); - QTest::addColumn("expectedText"); - QTest::addColumn("expectedTerms"); - QTest::addColumn("hasContent"); - QTest::addColumn("hasFileName"); - // Test for "Content" - QTest::newRow("content") << text << text << QStringList() << true << false; - QTest::newRow("content/space") << textS << textS << QStringList() << true << false; - QTest::newRow("content/empty") << "" << "" << QStringList() << false << false; - QTest::newRow("content/single_quote") << "\"" << "\"" << QStringList() << true << false; - QTest::newRow("content/double_quote") << "\"\"" << "" << QStringList() << false << false; + QTest::newRow("content") << balooQueryUrl(text) << text << QStringList() << true << false; + QTest::newRow("content/space") << balooQueryUrl(textS) << textS << QStringList() << true << false; + QTest::newRow("content/empty") << balooQueryUrl("") << "" << QStringList() << false << false; + QTest::newRow("content/single_quote") << balooQueryUrl("\"") << "\"" << QStringList() << true << false; + QTest::newRow("content/double_quote") << balooQueryUrl("\"\"") << "" << QStringList() << false << false; // Test for "FileName" - QTest::newRow("filename") << filename << text << QStringList() << false << true; - QTest::newRow("filename/space") << filenameS << textS << QStringList() << false << true; - QTest::newRow("filename/empty") << "filename:" << "" << QStringList() << false << false; - QTest::newRow("filename/single_quote") << "filename:\"" << "\"" << QStringList() << false << true; - QTest::newRow("filename/double_quote") << "filename:\"\"" << "" << QStringList() << false << false; + QTest::newRow("filename") << balooQueryUrl(filename) << text << QStringList() << false << true; + QTest::newRow("filename/space") << balooQueryUrl(filenameS) << textS << QStringList() << false << true; + QTest::newRow("filename/empty") << balooQueryUrl("filename:") << "" << QStringList() << false << false; + QTest::newRow("filename/single_quote") << balooQueryUrl("filename:\"") << "\"" << QStringList() << false << true; + QTest::newRow("filename/double_quote") << balooQueryUrl("filename:\"\"") << "" << QStringList() << false << false; // Combined content and filename search QTest::newRow("content+filename") - << text + " " + filename + << balooQueryUrl(text + " " + filename) << text + " " + filename << QStringList() << true << true; // Test for rating - QTest::newRow("rating") << rating << "" << QStringList({rating}) << false << false; - QTest::newRow("rating+content") << rating + " " + text << text << QStringList({rating}) << true << false; - QTest::newRow("rating+filename") << rating + " " + filename << text << QStringList({rating}) << false << true; + QTest::newRow("rating") << balooQueryUrl(rating) << "" << QStringList({rating}) << false << false; + QTest::newRow("rating+content") << balooQueryUrl(rating + " " + text) << text << QStringList({rating}) << true << false; + QTest::newRow("rating+filename") << balooQueryUrl(rating + " " + filename) << text << QStringList({rating}) << false << true; // Test for modified date - QTest::newRow("modified") << modified << "" << QStringList({modified}) << false << false; - QTest::newRow("modified+content") << modified + " " + text << text << QStringList({modified}) << true << false; - QTest::newRow("modified+filename") << modified + " " + filename << text << QStringList({modified}) << false << true; + QTest::newRow("modified") << balooQueryUrl(modified) << "" << QStringList({modified}) << false << false; + QTest::newRow("modified+content") << balooQueryUrl(modified + " " + text) << text << QStringList({modified}) << true << false; + QTest::newRow("modified+filename") << balooQueryUrl(modified + " " + filename) << text << QStringList({modified}) << false << true; // Test for tags - QTest::newRow("tag") << tag << "" << QStringList({tag}) << false << false; - QTest::newRow("tag/space" ) << tagS << "" << QStringList({tagR}) << false << false; - QTest::newRow("tag/double") << tag + " " + tagS << "" << QStringList({tag, tagR}) << false << false; - QTest::newRow("tag+content") << tag + " " + text << text << QStringList({tag}) << true << false; - QTest::newRow("tag+filename") << tag + " " + filename << text << QStringList({tag}) << false << true; + QTest::newRow("tag") << balooQueryUrl(tag) << "" << QStringList({tag}) << false << false; + QTest::newRow("tag/space" ) << balooQueryUrl(tagS) << "" << QStringList({tagR}) << false << false; + QTest::newRow("tag/double") << balooQueryUrl(tag + " " + tagS) << "" << QStringList({tag, tagR}) << false << false; + QTest::newRow("tag+content") << balooQueryUrl(tag + " " + text) << text << QStringList({tag}) << true << false; + QTest::newRow("tag+filename") << balooQueryUrl(tag + " " + filename) << text << QStringList({tag}) << false << true; // Combined search terms QTest::newRow("searchTerms") - << rating + " AND " + modified + " AND " + tag + " AND " + tagS + << balooQueryUrl(rating + " AND " + modified + " AND " + tag + " AND " + tagS) << "" << QStringList({modified, rating, tag, tagR}) << false << false; QTest::newRow("searchTerms+content") - << rating + " AND " + modified + " " + text + " " + tag + " AND " + tagS + << balooQueryUrl(rating + " AND " + modified + " " + text + " " + tag + " AND " + tagS) << text << QStringList({modified, rating, tag, tagR}) << true << false; QTest::newRow("searchTerms+filename") - << rating + " AND " + modified + " " + filename + " " + tag + " AND " + tagS + << balooQueryUrl(rating + " AND " + modified + " " + filename + " " + tag + " AND " + tagS) << text << QStringList({modified, rating, tag, tagR}) << false << true; QTest::newRow("allTerms") - << text + " " + filename + " " + rating + " AND " + modified + " AND " + tag + << balooQueryUrl(text + " " + filename + " " + rating + " AND " + modified + " AND " + tag) << text + " " + filename << QStringList({modified, rating, tag}) << true << true; QTest::newRow("allTerms/space") - << textS + " " + filenameS + " " + rating + " AND " + modified + " AND " + tagS + << balooQueryUrl(textS + " " + filenameS + " " + rating + " AND " + modified + " AND " + tagS) << textS + " " + filenameS << QStringList({modified, rating, tagR}) << true << true; } /** - * Helper function to compose the baloo query URL used for searching - */ -QUrl composeQueryUrl(const QString& searchString) -{ - const QJsonObject jsonObject { - {"searchString", searchString} - }; - - const QJsonDocument doc(jsonObject); - const QString queryString = QString::fromUtf8(doc.toJson(QJsonDocument::Compact)); - - QUrlQuery urlQuery; - urlQuery.addQueryItem(QStringLiteral("json"), queryString); - - QUrl searchUrl; - searchUrl.setScheme(QLatin1String("baloosearch")); - searchUrl.setQuery(urlQuery); - - return searchUrl; -} - -/** - * The test verifies whether the different terms of a Baloo search URL ("baloosearch:") are + * The test verifies whether the different terms search URL (e.g. "baloosearch:/") are * properly handled by the searchbox, and only "user" or filename terms are added to the * text bar of the searchbox. */ void DolphinSearchBoxTest::testBalooSearchParsing() { - QFETCH(QString, searchString); + QFETCH(QUrl, searchUrl); QFETCH(QString, expectedText); QFETCH(QStringList, expectedTerms); QFETCH(bool, hasContent); QFETCH(bool, hasFileName); - const QUrl testUrl = composeQueryUrl(searchString); - const DolphinQuery query = DolphinQuery::fromBalooSearchUrl(testUrl); + const DolphinQuery query = DolphinQuery::fromSearchUrl(searchUrl); - QStringList searchTerms = query.searchTerms(); - searchTerms.sort(); + // Checkt that the URL is supported + QVERIFY(DolphinQuery::supportsScheme(searchUrl.scheme())); // Check for parsed text (would be displayed on the input search bar) QCOMPARE(query.text(), expectedText); // Check for parsed search terms (would be displayed by the facetsWidget) + QStringList searchTerms = query.searchTerms(); + searchTerms.sort(); + QCOMPARE(searchTerms.count(), expectedTerms.count()); for (int i = 0; i < expectedTerms.count(); i++) { QCOMPARE(searchTerms.at(i), expectedTerms.at(i)); diff --git a/src/tests/kfileitemmodeltest.cpp b/src/tests/kfileitemmodeltest.cpp index 0c38f02bf..7e949c34f 100644 --- a/src/tests/kfileitemmodeltest.cpp +++ b/src/tests/kfileitemmodeltest.cpp @@ -18,6 +18,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ +#include #include #include #include @@ -450,9 +451,9 @@ void KFileItemModelTest::testModelConsistencyWhenInsertingItems() itemsInsertedSpy.clear(); for (int j = 0; j < 10; ++j) { - int itemName = qrand(); + int itemName = QRandomGenerator::global()->generate(); while (insertedItems.contains(itemName)) { - itemName = qrand(); + itemName = QRandomGenerator::global()->generate(); } insertedItems.insert(itemName); diff --git a/src/views/dolphinitemlistview.cpp b/src/views/dolphinitemlistview.cpp index 6397a48b7..9e8fda650 100644 --- a/src/views/dolphinitemlistview.cpp +++ b/src/views/dolphinitemlistview.cpp @@ -91,7 +91,7 @@ void DolphinItemListView::readSettings() const KConfigGroup globalConfig(KSharedConfig::openConfig(), "PreviewSettings"); setEnabledPlugins(globalConfig.readEntry("Plugins", KIO::PreviewJob::defaultPlugins())); - + setLocalFileSizePreviewLimit(globalConfig.readEntry("MaximumSize", 0)); endTransaction(); } diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp index 2caa8ec68..9af691927 100644 --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -364,7 +364,7 @@ void DolphinView::markUrlAsCurrent(const QUrl &url) m_scrollToCurrentItem = true; } -void DolphinView::selectItems(const QRegExp& pattern, bool enabled) +void DolphinView::selectItems(const QRegularExpression ®exp, bool enabled) { const KItemListSelectionManager::SelectionMode mode = enabled ? KItemListSelectionManager::Select @@ -373,7 +373,7 @@ void DolphinView::selectItems(const QRegExp& pattern, bool enabled) for (int index = 0; index < m_model->count(); index++) { const KFileItem item = m_model->fileItem(index); - if (pattern.exactMatch(item.text())) { + if (regexp.match(item.text()).hasMatch()) { // An alternative approach would be to store the matching items in a KItemSet and // select them in one go after the loop, but we'd need a new function // KItemListSelectionManager::setSelected(KItemSet, SelectionMode mode) @@ -679,19 +679,40 @@ void DolphinView::deleteSelectedItems() } } -void DolphinView::cutSelectedItems() +void DolphinView::cutSelectedItemsToClipboard() { QMimeData* mimeData = selectionMimeData(); KIO::setClipboardDataCut(mimeData, true); QApplication::clipboard()->setMimeData(mimeData); } -void DolphinView::copySelectedItems() +void DolphinView::copySelectedItemsToClipboard() { QMimeData* mimeData = selectionMimeData(); QApplication::clipboard()->setMimeData(mimeData); } +void DolphinView::copySelectedItems(const KFileItemList &selection, const QUrl &destinationUrl) +{ + KIO::CopyJob* job = KIO::copy(selection.urlList(), destinationUrl, KIO::DefaultFlags); + KJobWidgets::setWindow(job, this); + + connect(job, &KIO::DropJob::result, this, &DolphinView::slotJobResult); + connect(job, &KIO::CopyJob::copyingDone, this, &DolphinView::slotCopyingDone); + KIO::FileUndoManager::self()->recordCopyJob(job); +} + +void DolphinView::moveSelectedItems(const KFileItemList &selection, const QUrl &destinationUrl) +{ + KIO::CopyJob* job = KIO::move(selection.urlList(), destinationUrl, KIO::DefaultFlags); + KJobWidgets::setWindow(job, this); + + connect(job, &KIO::DropJob::result, this, &DolphinView::slotJobResult); + connect(job, &KIO::CopyJob::copyingDone, this, &DolphinView::slotCopyingDone); + KIO::FileUndoManager::self()->recordCopyJob(job); + +} + void DolphinView::paste() { pasteToUrl(url()); @@ -1132,7 +1153,7 @@ void DolphinView::dropUrls(const QUrl &destUrl, QDropEvent *dropEvent, QWidget * KIO::DropJob* job = DragAndDropHelper::dropUrls(destUrl, dropEvent, dropWidget); if (job) { - connect(job, &KIO::DropJob::result, this, &DolphinView::slotPasteJobResult); + connect(job, &KIO::DropJob::result, this, &DolphinView::slotJobResult); if (destUrl == url()) { // Mark the dropped urls as selected. @@ -1185,6 +1206,11 @@ void DolphinView::slotSelectedItemTextPressed(int index) } } +void DolphinView::slotCopyingDone(KIO::Job *, const QUrl &, const QUrl &to) +{ + slotItemCreated(to); +} + void DolphinView::slotItemCreated(const QUrl& url) { if (m_markFirstNewlySelectedItemAsCurrent) { @@ -1194,13 +1220,13 @@ void DolphinView::slotItemCreated(const QUrl& url) m_selectedUrls << url; } -void DolphinView::slotPasteJobResult(KJob *job) +void DolphinView::slotJobResult(KJob *job) { if (job->error()) { emit errorMessage(job->errorString()); } if (!m_selectedUrls.isEmpty()) { - m_selectedUrls << KDirModel::simplifiedUrlList(m_selectedUrls); + m_selectedUrls = KDirModel::simplifiedUrlList(m_selectedUrls); } } @@ -1493,13 +1519,31 @@ void DolphinView::calculateItemCount(int& fileCount, KIO::filesize_t& totalFileSize) const { const int itemCount = m_model->count(); + + bool countFileSize = true; + + if (!m_model->rootItem().url().isValid()) { + return; + } + + // In case we have a precomputed value + const auto job = KIO::statDetails(m_model->rootItem().url(), KIO::StatJob::SourceSide, KIO::StatRecursiveSize, KIO::HideProgressInfo); + job->exec(); + const auto entry = job->statResult(); + if (entry.contains(KIO::UDSEntry::UDS_RECURSIVE_SIZE)) { + totalFileSize = static_cast(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(); + } } } } @@ -1831,7 +1875,7 @@ void DolphinView::pasteToUrl(const QUrl& url) m_clearSelectionBeforeSelectingNewItems = true; m_markFirstNewlySelectedItemAsCurrent = true; connect(job, &KIO::PasteJob::itemCreated, this, &DolphinView::slotItemCreated); - connect(job, &KIO::PasteJob::result, this, &DolphinView::slotPasteJobResult); + connect(job, &KIO::PasteJob::result, this, &DolphinView::slotJobResult); } QList DolphinView::simplifiedSelectedUrls() const diff --git a/src/views/dolphinview.h b/src/views/dolphinview.h index 83c5f92a4..4306b3eb7 100644 --- a/src/views/dolphinview.h +++ b/src/views/dolphinview.h @@ -46,7 +46,7 @@ class ToolTipManager; class VersionControlObserver; class ViewProperties; class QGraphicsSceneDragDropEvent; -class QRegExp; +class QRegularExpression; /** * @short Represents a view for the directory content. @@ -183,10 +183,16 @@ public: void markUrlAsCurrent(const QUrl& url); /** - * All items that match to the pattern \a pattern will get selected - * if \a enabled is true and deselected if \a enabled is false. + * All items that match the regular expression \a regexp will get selected + * if \a enabled is true and deselected if \a enabled is false. + * + * Note that to match the whole string the pattern should be anchored: + * - you can anchor the pattern with QRegularExpression::anchoredPattern() + * - if you use QRegularExpresssion::wildcardToRegularExpression(), don't use + * QRegularExpression::anchoredPattern() as the former already returns an + * anchored pattern */ - void selectItems(const QRegExp& pattern, bool enabled); + void selectItems(const QRegularExpression ®exp, bool enabled); /** * Sets the zoom level to \a level. It is assured that the used @@ -359,10 +365,20 @@ public slots: * Copies all selected items to the clipboard and marks * the items as cut. */ - void cutSelectedItems(); + void cutSelectedItemsToClipboard(); /** Copies all selected items to the clipboard. */ - void copySelectedItems(); + void copySelectedItemsToClipboard(); + + /** + * Copies all selected items to @p destinationUrl. + */ + void copySelectedItems(const KFileItemList &selection, const QUrl &destinationUrl); + + /** + * Moves all selected items to @p destinationUrl. + */ + void moveSelectedItems(const KFileItemList &selection, const QUrl &destinationUrl); /** Pastes the clipboard data to this view. */ void paste(); @@ -602,6 +618,7 @@ private slots: void slotMouseButtonPressed(int itemIndex, Qt::MouseButtons buttons); void slotRenameDialogRenamingFinished(const QList& urls); void slotSelectedItemTextPressed(int index); + void slotCopyingDone(KIO::Job *, const QUrl &, const QUrl &to); /* * Is called when new items get pasted or dropped. @@ -610,7 +627,7 @@ private slots: /* * Is called after all pasted or dropped items have been copied to destination. */ - void slotPasteJobResult(KJob *job); + void slotJobResult(KJob *job); /** * Emits the signal \a selectionChanged() with a small delay. This is 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() "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.")); - 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/fileviewversioncontrolplugin.desktop b/src/views/versioncontrol/fileviewversioncontrolplugin.desktop index c3d4d1837..ec0bbbcd3 100644 --- a/src/views/versioncontrol/fileviewversioncontrolplugin.desktop +++ b/src/views/versioncontrol/fileviewversioncontrolplugin.desktop @@ -4,6 +4,7 @@ X-KDE-ServiceType=FileViewVersionControlPlugin Comment=Version Control Plugin for File Views Comment[ar]=ملحقة تحكّم بالإصدارات لمناظير الملفّات Comment[ast]=Complementu de control de versiones pa les vistes de ficheros +Comment[az]=Fayl meneceri üçün versiyaya nəzarət əlavəsi Comment[ca]=Connector de control de versions per a les vistes de fitxers Comment[ca@valencia]=Connector de control de versions per a les vistes de fitxers Comment[cs]=Modul pro správu verzí pro pohledy na soubory 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 actions(const KFileItemList& items) const = 0; + virtual QList 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 outOfVersionControlActions(const KFileItemList& items) const = 0; Q_SIGNALS: /** diff --git a/src/views/versioncontrol/versioncontrolobserver.cpp b/src/views/versioncontrol/versioncontrolobserver.cpp index 2d801686e..2f0632243 100644 --- a/src/views/versioncontrol/versioncontrolobserver.cpp +++ b/src/views/versioncontrol/versioncontrolobserver.cpp @@ -118,11 +118,19 @@ QList 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 actions; + for (const auto &plugin : qAsConst(m_plugins)) { + actions << plugin.first->outOfVersionControlActions(items); + } + return actions; + } } void VersionControlObserver::delayedDirectoryVerification() @@ -160,21 +168,8 @@ void VersionControlObserver::verifyDirectory() return; } - if (m_plugin) { - m_plugin->disconnect(this); - } - m_plugin = searchPlugin(rootItem.url()); if (m_plugin) { - connect(m_plugin, &KVersionControlPlugin::itemVersionsChanged, - this, &VersionControlObserver::silentDirectoryVerification); - connect(m_plugin, &KVersionControlPlugin::infoMessage, - this, &VersionControlObserver::infoMessage); - connect(m_plugin, &KVersionControlPlugin::errorMessage, - this, &VersionControlObserver::errorMessage); - connect(m_plugin, &KVersionControlPlugin::operationCompletedMessage, - this, &VersionControlObserver::operationCompletedMessage); - if (!m_versionedDirectory) { m_versionedDirectory = true; @@ -303,6 +298,15 @@ KVersionControlPlugin* VersionControlObserver::searchPlugin(const QUrl& director if (enabledPlugins.contains((*it)->name())) { KVersionControlPlugin* plugin = (*it)->createInstance(this); if (plugin) { + connect(plugin, &KVersionControlPlugin::itemVersionsChanged, + this, &VersionControlObserver::silentDirectoryVerification); + connect(plugin, &KVersionControlPlugin::infoMessage, + this, &VersionControlObserver::infoMessage); + connect(plugin, &KVersionControlPlugin::errorMessage, + this, &VersionControlObserver::errorMessage); + connect(plugin, &KVersionControlPlugin::operationCompletedMessage, + this, &VersionControlObserver::operationCompletedMessage); + m_plugins.append( qMakePair(plugin, plugin->fileName()) ); } } @@ -360,7 +364,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;