diff options
47 files changed, 448 insertions, 274 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 77ccea17d..f0e0f5969 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 3.0) # KDE Application Version, managed by release script set (KDE_APPLICATIONS_VERSION_MAJOR "18") -set (KDE_APPLICATIONS_VERSION_MINOR "04") -set (KDE_APPLICATIONS_VERSION_MICRO "2") +set (KDE_APPLICATIONS_VERSION_MINOR "07") +set (KDE_APPLICATIONS_VERSION_MICRO "70") set (KDE_APPLICATIONS_VERSION "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}.${KDE_APPLICATIONS_VERSION_MICRO}") project(Dolphin VERSION ${KDE_APPLICATIONS_VERSION}) @@ -77,7 +77,7 @@ set_package_properties(KF5Baloo PROPERTIES DESCRIPTION "Baloo Core libraries" PURPOSE "For adding desktop-wide search and tagging support to dolphin" ) -find_package(KF5BalooWidgets 4.97) +find_package(KF5BalooWidgets 18.07.70) set_package_properties(KF5BalooWidgets PROPERTIES DESCRIPTION "Baloos Widgets" URL "http://www.kde.org" TYPE OPTIONAL @@ -94,6 +94,7 @@ if (KF5Baloo_FOUND AND KF5BalooWidgets_FOUND AND KF5FileMetaData_FOUND) message(STATUS "Baloo packages are found") set(HAVE_BALOO TRUE) else() + message(WARNING "Baloo packages not found. They are needed for the metadata features of Dolphin.") find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS KDELibs4Support # for KFileMetaDataWidget ) diff --git a/doc/index.docbook b/doc/index.docbook index 44af99503..c2ed2854f 100644 --- a/doc/index.docbook +++ b/doc/index.docbook @@ -70,8 +70,8 @@ <legalnotice>&FDLNotice;</legalnotice> -<date>2016-06-01</date> -<releaseinfo>Applications 16.04</releaseinfo> +<date>2018-03-26</date> +<releaseinfo>Applications 18.04</releaseinfo> <abstract> <para> @@ -281,6 +281,9 @@ the toolbar can be used to navigate in the history. </mediaobject> </screenshot> +<para>If you click with the &MMB; the item in the history is opened in a new tab +thus keeping the current tab with its content. +</para> </sect2> <sect2 id="dolphin-view-appearance"> @@ -359,8 +362,8 @@ The details view allows you to view the current folder in a tree-like fashion if <link linkend="preferences-dialog-viewmodes-details"> <guilabel>Expandable folders</guilabel></link> are enabled: Each subfolder of the current folder can be -<quote>expanded</quote> or <quote>collapsed</quote> by clicking on the <guiicon>+</guiicon> -or <guiicon>-</guiicon> icon next to it.</para></listitem> +<quote>expanded</quote> or <quote>collapsed</quote> by clicking on the <guiicon>></guiicon> +or <guiicon>v</guiicon> icon next to it.</para></listitem> </itemizedlist> </para> @@ -723,7 +726,7 @@ over with the mouse, including size, type, and date of last modification. It als features a large preview of the selected item and allows you to assign a rating, tags, and comments to it. </para> - +<!--FIXME panel context menu--> </sect2> <sect2 id="folders-panel"> @@ -733,9 +736,10 @@ tags, and comments to it. The <guilabel>Folders</guilabel> panel shows a tree view structure of the file system. It only shows folders. Clicking a folder with the &LMB; opens this folder in the &dolphin; view. - </para> - +<para>Use <guilabel>Limit to Home Directory</guilabel> to hide all folders from +the tree view except your <guilabel>Home</guilabel>. +</para> </sect2> <sect2 id="terminal-panel"> @@ -803,10 +807,6 @@ a KIOSlave is launched to provide the search results.</para> <para>The option from <guilabel>Everywhere</guilabel> with activated Baloo services searches in all indexed folders, without Baloo this option starts the search from the user's <replaceable>Home</replaceable> folder.</para> -<!--FIXME 16.12 -https://git.reviewboard.kde.org/r/123883/ -Add prototype of a "More search tools..." button ---> <screenshot> <screeninfo>Search with More Options</screeninfo> <mediaobject> @@ -832,6 +832,14 @@ the user to shrink the number of search results.</para> together with the <guilabel>Filter</guilabel> bar to find files using Baloo or limit the search to files matching the filter expression.</para> +<para>Use the Save icon to save a search to the <guilabel>Search For</guilabel> +section in the <guilabel>Places</guilabel> panel to quickly access it again in the future. +</para> +<!--FIXME 16.12 +https://git.reviewboard.kde.org/r/123883/ +Add prototype of a "More search tools..." button +--> + </sect2> <sect2 id="mounting-storage-media"> @@ -856,7 +864,7 @@ in the menu, ⪚ <guimenuitem>Undo: Rename</guimenuitem>. </para> </sect2> - +<!-- FIXME https://phabricator.kde.org/D10698--> <sect2 id="batch-rename"> <title>Renaming A Batch Of Files</title> <para> @@ -864,7 +872,7 @@ in the menu, ⪚ <guimenuitem>Undo: Rename</guimenuitem>. will have the file name specified, including a number, ⪚, Image1.jpg, Image2.jpg, Image3.jpg. This can be useful, ⪚, for pictures taken with a digital camera. </para> - +<!--double click--> <para> If you wish to rename a batch of files, first select the files to be renamed. This can be done by pressing the &LMB; and drawing a rectangle around the files @@ -1024,6 +1032,10 @@ startup or not. See the <link linkend="filter-files">section on the filter bar</ for details. </para></listitem> +<listitem><para> +<guilabel>Show full path in title bar</guilabel> makes it easy to distinguish +between files or folders with the same name in different folders. +</para></listitem> </itemizedlist> </para> @@ -1325,6 +1337,10 @@ Enable <guilabel>Rename inline</guilabel> to use this mode if only one item is c If this option is disabled or several items are selected, a dialog will be displayed for renaming. </para></listitem> +<listitem><para><guilabel>Use tab for switching between left and right split view</guilabel> +allows to switch without using the mouse. +</para></listitem> + </itemizedlist> </para> @@ -1353,8 +1369,8 @@ are shown before potentially harmful actions . <warning><para>The confirmation settings for <guilabel>Moving files or folders to trash</guilabel> and <guilabel>Deleting files or folders</guilabel> affect file operations in &dolphin;, &konqueror;, <application>Gwenview</application> and all &kde; applications using the default &kde; file dialog, -whereas <guilabel>Closing Dolphin windows - with multiple tabs</guilabel> is a &dolphin; <!--and &konqueror;--> specific setting.</para></warning> +whereas <guilabel>Closing Dolphin windows +with multiple tabs</guilabel> is a &dolphin; specific setting.</para></warning> </sect3> <sect3 id="preferences-dialog-general-statusbar"> @@ -1577,6 +1593,15 @@ to the trash and cannot be restored.</action></para></listitem> <varlistentry> <term><menuchoice> +<guimenu>File</guimenu> +<guimenuitem>Show Target</guimenuitem> +</menuchoice></term> +<listitem><para>This action highlights a link target in a new &dolphin; window. +</para></listitem> +</varlistentry> + +<varlistentry> +<term><menuchoice> <shortcut> <keycombo action="simul">&Alt;<keycap>Return</keycap></keycombo> </shortcut> @@ -1786,7 +1811,8 @@ current folder grouped by the option selected in <guimenuitem>Sort By</guimenuit <guimenuitem>Hidden Files</guimenuitem> </menuchoice></term> <listitem><para><action>Shows all the hidden files and sub-folders within the current -folder.</action></para></listitem> +folder.</action>There is an alternate shortcut <keycombo action="simul">&Ctrl;<keycap>H</keycap></keycombo> +for this action.</para></listitem> </varlistentry> <varlistentry> @@ -1954,7 +1980,9 @@ be reopened.</action></para></listitem> <guimenu>Tools</guimenu> <guimenuitem>Show Filter Bar</guimenuitem> </menuchoice></term> -<listitem><para><action>Enables and disables the <link linkend="filter-files">filter bar</link>.</action></para></listitem> +<listitem><para><action>Enables and disables the <link linkend="filter-files">filter bar</link>.</action> +You can also use the alternate shortcut <keycombo action="simul">&Shift;<keycap>/</keycap></keycombo> +for this action.</para></listitem> </varlistentry> <varlistentry> diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 263b3ff0c..beaa0ec50 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -207,7 +207,6 @@ set(dolphinstatic_SRCS dolphintabwidget.cpp trash/dolphintrash.cpp filterbar/filterbar.cpp - main.cpp panels/information/filemetadataconfigurationdialog.cpp panels/information/informationpanel.cpp panels/information/informationpanelcontent.cpp @@ -300,7 +299,7 @@ ecm_add_app_icon(dolphin_SRCS ICONS ${ICONS_SRCS}) kf5_add_kdeinit_executable(dolphin ${dolphin_SRCS}) -target_link_libraries(kdeinit_dolphin +target_link_libraries(kdeinit_dolphin PRIVATE dolphinstatic dolphinprivate ) diff --git a/src/dolphincontextmenu.cpp b/src/dolphincontextmenu.cpp index bedc835c2..7d7d7a408 100644 --- a/src/dolphincontextmenu.cpp +++ b/src/dolphincontextmenu.cpp @@ -192,13 +192,29 @@ void DolphinContextMenu::openItemContextMenu() QAction* addToPlacesAction = nullptr; const KFileItemListProperties& selectedItemsProps = selectedItemsProperties(); + KFileItemActions fileItemActions; + fileItemActions.setParentWidget(m_mainWindow); + fileItemActions.setItemListProperties(selectedItemsProps); + if (m_selectedItems.count() == 1) { - if (m_fileInfo.isLink()) { - addAction(m_mainWindow->actionCollection()->action(QStringLiteral("show_target"))); - addSeparator(); - } if (m_fileInfo.isDir()) { - // setup 'Create New' menu + // insert 'Open in new window' and 'Open in new tab' entries + addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_window"))); + addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_tab"))); + + // Insert 'Open With' entries + addOpenWithActions(fileItemActions); + + // insert 'Add to Places' entry + if (!placeExists(m_fileInfo.url())) { + addToPlacesAction = addAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), + i18nc("@action:inmenu Add selected folder to places", + "Add to Places")); + } + + addSeparator(); + + // set up 'Create New' menu DolphinNewFileMenu* newFileMenu = new DolphinNewFileMenu(m_mainWindow->actionCollection(), m_mainWindow); const DolphinView* view = m_mainWindow->activeViewContainer()->view(); newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown()); @@ -212,21 +228,11 @@ void DolphinContextMenu::openItemContextMenu() menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New")); menu->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); addMenu(menu); - addSeparator(); - - // insert 'Open in new window' and 'Open in new tab' entries - addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_window"))); - addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_tab"))); - - // insert 'Add to Places' entry - if (!placeExists(m_fileInfo.url())) { - addToPlacesAction = addAction(QIcon::fromTheme(QStringLiteral("bookmark-new")), - i18nc("@action:inmenu Add selected folder to places", - "Add to Places")); - } addSeparator(); } else if (m_baseUrl.scheme().contains(QStringLiteral("search")) || m_baseUrl.scheme().contains(QStringLiteral("timeline"))) { + addOpenWithActions(fileItemActions); + openParentAction = new QAction(QIcon::fromTheme(QStringLiteral("document-open-folder")), i18nc("@action:inmenu", "Open Path"), @@ -247,11 +253,21 @@ void DolphinContextMenu::openItemContextMenu() addSeparator(); } else if (!DolphinView::openItemAsFolderUrl(m_fileInfo).isEmpty()) { + // Insert 'Open With" entries + addOpenWithActions(fileItemActions); + // insert 'Open in new window' and 'Open in new tab' entries addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_window"))); addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_tab"))); addSeparator(); + } else { + // Insert 'Open With" entries + addOpenWithActions(fileItemActions); + } + if (m_fileInfo.isLink()) { + addAction(m_mainWindow->actionCollection()->action(QStringLiteral("show_target"))); + addSeparator(); } } else { bool selectionHasOnlyDirs = true; @@ -266,18 +282,16 @@ void DolphinContextMenu::openItemContextMenu() if (selectionHasOnlyDirs) { // insert 'Open in new tab' entry addAction(m_mainWindow->actionCollection()->action(QStringLiteral("open_in_new_tabs"))); - addSeparator(); } + // Insert 'Open With" entries + addOpenWithActions(fileItemActions); } insertDefaultItemActions(selectedItemsProps); addSeparator(); - KFileItemActions fileItemActions; - fileItemActions.setItemListProperties(selectedItemsProps); - addServiceActions(fileItemActions); - + fileItemActions.addServiceActionsTo(this); fileItemActions.addPluginActionsTo(this); addVersionControlPluginActions(); @@ -324,9 +338,16 @@ void DolphinContextMenu::openViewportContextMenu() addMenu(newFileMenu->menu()); addSeparator(); + // Insert 'Open With' entries + const KFileItemListProperties baseUrlProperties(KFileItemList() << baseFileItem()); + KFileItemActions fileItemActions; + fileItemActions.setParentWidget(m_mainWindow); + fileItemActions.setItemListProperties(baseUrlProperties); + addOpenWithActions(fileItemActions); + // Insert 'New Window' and 'New Tab' entries. Don't use "open_in_new_window" and // "open_in_new_tab" here, as the current selection should get ignored. - addAction(m_mainWindow->actionCollection()->action(QStringLiteral("new_window"))); + addAction(m_mainWindow->actionCollection()->action(QStringLiteral("file_new"))); addAction(m_mainWindow->actionCollection()->action(QStringLiteral("new_tab"))); // Insert 'Add to Places' entry if exactly one item is selected @@ -342,12 +363,14 @@ void DolphinContextMenu::openViewportContextMenu() addAction(pasteAction); addSeparator(); - // Insert service actions - const KFileItemListProperties baseUrlProperties(KFileItemList() << baseFileItem()); - KFileItemActions fileItemActions; - fileItemActions.setItemListProperties(baseUrlProperties); - addServiceActions(fileItemActions); + // Insert 'Sort By' and 'View Mode' + addAction(m_mainWindow->actionCollection()->action(QStringLiteral("sort"))); + addAction(m_mainWindow->actionCollection()->action(QStringLiteral("view_mode"))); + addSeparator(); + + // Insert service actions + fileItemActions.addServiceActionsTo(this); fileItemActions.addPluginActionsTo(this); addVersionControlPluginActions(); @@ -469,15 +492,10 @@ KFileItem DolphinContextMenu::baseFileItem() return *m_baseFileItem; } -void DolphinContextMenu::addServiceActions(KFileItemActions& fileItemActions) +void DolphinContextMenu::addOpenWithActions(KFileItemActions& fileItemActions) { - fileItemActions.setParentWidget(m_mainWindow); - // insert 'Open With...' action or sub menu fileItemActions.addOpenWithActionsTo(this, QStringLiteral("DesktopEntryName != '%1'").arg(qApp->desktopFileName())); - - // insert 'Actions' sub menu - fileItemActions.addServiceActionsTo(this); } void DolphinContextMenu::addVersionControlPluginActions() diff --git a/src/dolphincontextmenu.h b/src/dolphincontextmenu.h index 9ebdb1a56..bf0cf2c97 100644 --- a/src/dolphincontextmenu.h +++ b/src/dolphincontextmenu.h @@ -116,10 +116,9 @@ private: KFileItem baseFileItem(); /** - * Adds actions that have been installed as service-menu. - * (see http://techbase.kde.org/index.php?title=Development/Tutorials/Creating_Konqueror_Service_Menus) + * Adds "Open With" actions */ - void addServiceActions(KFileItemActions& fileItemActions); + void addOpenWithActions(KFileItemActions& fileItemActions); /** * Adds actions that are provided by a KVersionControlPlugin. diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index d112007bc..f8b35d4a7 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -104,7 +104,7 @@ DolphinMainWindow::DolphinMainWindow() : m_tearDownFromPlacesRequested(false) { Q_INIT_RESOURCE(dolphin); - + setComponentName(QStringLiteral("dolphin"), QGuiApplication::applicationDisplayName()); setObjectName(QStringLiteral("Dolphin#")); connect(&DolphinNewFileMenuObserver::instance(), &DolphinNewFileMenuObserver::errorMessage, @@ -287,13 +287,13 @@ void DolphinMainWindow::updateHistory() const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); const int index = urlNavigator->historyIndex(); - QAction* backAction = actionCollection()->action(QStringLiteral("go_back")); + QAction* backAction = actionCollection()->action(KStandardAction::name(KStandardAction::Back)); if (backAction) { backAction->setToolTip(i18nc("@info", "Go back")); backAction->setEnabled(index < urlNavigator->historySize() - 1); } - QAction* forwardAction = actionCollection()->action(QStringLiteral("go_forward")); + QAction* forwardAction = actionCollection()->action(KStandardAction::name(KStandardAction::Forward)); if (forwardAction) { forwardAction->setToolTip(i18nc("@info", "Go forward")); forwardAction->setEnabled(index > 0); @@ -402,7 +402,7 @@ void DolphinMainWindow::closeEvent(QCloseEvent* event) dialog->setWindowTitle(i18nc("@title:window", "Confirmation")); dialog->setModal(true); QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel); - KGuiItem::assign(buttons->button(QDialogButtonBox::Yes), KStandardGuiItem::quit()); + KGuiItem::assign(buttons->button(QDialogButtonBox::Yes), KGuiItem(i18nc("@action:button 'Quit Dolphin' button", "&Quit %1", QGuiApplication::applicationDisplayName()), QIcon::fromTheme(QStringLiteral("application-exit")))); KGuiItem::assign(buttons->button(QDialogButtonBox::No), KGuiItem(i18n("C&lose Current Tab"), QIcon::fromTheme(QStringLiteral("tab-close")))); KGuiItem::assign(buttons->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); buttons->button(QDialogButtonBox::Yes)->setDefault(true); @@ -457,6 +457,10 @@ void DolphinMainWindow::updateNewMenu() m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown()); m_newFileMenu->checkUpToDate(); m_newFileMenu->setPopupFiles(activeViewContainer()->url()); + + // If we're in the trash, also disable all the 'create new' items + // TODO: remove this once https://phabricator.kde.org/T8234 is implemented + slotWriteStateChanged(m_activeViewContainer->view()->url().scheme() != QLatin1String("trash")); } void DolphinMainWindow::createDirectory() @@ -534,9 +538,9 @@ void DolphinMainWindow::slotDirectoryLoadingCompleted() void DolphinMainWindow::slotToolBarActionMiddleClicked(QAction *action) { - if (action == actionCollection()->action(QStringLiteral("go_back"))) { + if (action == actionCollection()->action(KStandardAction::name(KStandardAction::Back))) { goBackInNewTab(); - } else if (action == actionCollection()->action(QStringLiteral("go_forward"))) { + } else if (action == actionCollection()->action(KStandardAction::name(KStandardAction::Forward))) { goForwardInNewTab(); } else if (action == actionCollection()->action(QStringLiteral("go_up"))) { goUpInNewTab(); @@ -804,7 +808,10 @@ void DolphinMainWindow::slotHandleUrlStatFinished(KJob* job) void DolphinMainWindow::slotWriteStateChanged(bool isFolderWritable) { - newFileMenu()->setEnabled(isFolderWritable); + const auto actions = m_newFileMenu->menu()->actions(); + for (auto menuItem : actions) { + menuItem->setEnabled(isFolderWritable); + } } void DolphinMainWindow::openContextMenu(const QPoint& pos, @@ -819,10 +826,12 @@ void DolphinMainWindow::openContextMenu(const QPoint& pos, switch (command) { case DolphinContextMenu::OpenParentFolder: changeUrl(KIO::upUrl(item.url())); + m_activeViewContainer->view()->markUrlsAsSelected({item.url()}); + m_activeViewContainer->view()->markUrlAsCurrent(item.url()); break; case DolphinContextMenu::OpenParentFolderInNewWindow: - Dolphin::openNewWindow({KIO::upUrl(item.url())}, this); + Dolphin::openNewWindow({item.url()}, this, Dolphin::OpenNewWindowFlag::Select); break; case DolphinContextMenu::OpenParentFolderInNewTab: @@ -854,7 +863,7 @@ void DolphinMainWindow::updateControlMenu() // Add "Edit" actions bool added = addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Undo)), menu) | addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Find)), menu) | - addActionToMenu(ac->action(QStringLiteral("select_all")), menu) | + addActionToMenu(ac->action(KStandardAction::name(KStandardAction::SelectAll)), menu) | addActionToMenu(ac->action(QStringLiteral("invert_selection")), menu); if (added) { @@ -868,8 +877,8 @@ void DolphinMainWindow::updateControlMenu() menu->addSeparator(); } - added = addActionToMenu(ac->action(QStringLiteral("view_mode")), menu) | - addActionToMenu(ac->action(QStringLiteral("sort")), menu) | + added = addActionToMenu(ac->action(QStringLiteral("sort")), menu) | + addActionToMenu(ac->action(QStringLiteral("view_mode")), menu) | addActionToMenu(ac->action(QStringLiteral("additional_info")), menu) | addActionToMenu(ac->action(QStringLiteral("show_preview")), menu) | addActionToMenu(ac->action(QStringLiteral("show_in_groups")), menu) | @@ -880,7 +889,7 @@ void DolphinMainWindow::updateControlMenu() } added = addActionToMenu(ac->action(QStringLiteral("split_view")), menu) | - addActionToMenu(ac->action(QStringLiteral("reload")), menu) | + addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Redisplay)), menu) | addActionToMenu(ac->action(QStringLiteral("view_properties")), menu); if (added) { menu->addSeparator(); @@ -987,7 +996,7 @@ void DolphinMainWindow::activeViewChanged(DolphinViewContainer* viewContainer) void DolphinMainWindow::tabCountChanged(int count) { const bool enableTabActions = (count > 1); - actionCollection()->action(QStringLiteral("close_tab"))->setEnabled(enableTabActions); + actionCollection()->action(KStandardAction::name(KStandardAction::Close))->setEnabled(enableTabActions); actionCollection()->action(QStringLiteral("activate_next_tab"))->setEnabled(enableTabActions); actionCollection()->action(QStringLiteral("activate_prev_tab"))->setEnabled(enableTabActions); } @@ -1064,24 +1073,19 @@ void DolphinMainWindow::setupActions() connect(menu, &QMenu::aboutToShow, this, &DolphinMainWindow::updateNewMenu); - QAction* newWindow = actionCollection()->addAction(QStringLiteral("new_window")); - newWindow->setIcon(QIcon::fromTheme(QStringLiteral("window-new"))); + QAction* newWindow = KStandardAction::openNew(this, &DolphinMainWindow::openNewMainWindow, actionCollection()); newWindow->setText(i18nc("@action:inmenu File", "New &Window")); - actionCollection()->setDefaultShortcut(newWindow, Qt::CTRL + Qt::Key_N); - connect(newWindow, &QAction::triggered, this, &DolphinMainWindow::openNewMainWindow); QAction* newTab = actionCollection()->addAction(QStringLiteral("new_tab")); newTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-new"))); newTab->setText(i18nc("@action:inmenu File", "New Tab")); - actionCollection()->setDefaultShortcuts(newTab, {Qt::CTRL + Qt::Key_T, Qt::CTRL + Qt::SHIFT + Qt::Key_N}); + actionCollection()->setDefaultShortcuts(newTab, {Qt::CTRL + Qt::Key_T, QKeySequence::AddTab}); connect(newTab, &QAction::triggered, this, static_cast<void(DolphinMainWindow::*)()>(&DolphinMainWindow::openNewActivatedTab)); - QAction* closeTab = actionCollection()->addAction(QStringLiteral("close_tab")); - closeTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-close"))); + QAction* closeTab = KStandardAction::close( + m_tabWidget, static_cast<void(DolphinTabWidget::*)()>(&DolphinTabWidget::closeTab), actionCollection()); closeTab->setText(i18nc("@action:inmenu File", "Close Tab")); - actionCollection()->setDefaultShortcut(closeTab, Qt::CTRL + Qt::Key_W); closeTab->setEnabled(false); - connect(closeTab, &QAction::triggered, m_tabWidget, static_cast<void(DolphinTabWidget::*)()>(&DolphinTabWidget::closeTab)); KStandardAction::quit(this, &DolphinMainWindow::quit, actionCollection()); @@ -1101,11 +1105,7 @@ void DolphinMainWindow::setupActions() KStandardAction::find(this, &DolphinMainWindow::find, actionCollection()); - QAction* selectAll = actionCollection()->addAction(QStringLiteral("select_all")); - selectAll->setText(i18nc("@action:inmenu Edit", "Select All")); - selectAll->setIcon(QIcon::fromTheme(QStringLiteral("edit-select-all"))); - actionCollection()->setDefaultShortcut(selectAll, Qt::CTRL + Qt::Key_A); - connect(selectAll, &QAction::triggered, this, &DolphinMainWindow::selectAll); + KStandardAction::selectAll(this, &DolphinMainWindow::selectAll, actionCollection()); QAction* invertSelection = actionCollection()->addAction(QStringLiteral("invert_selection")); invertSelection->setText(i18nc("@action:inmenu Edit", "Invert Selection")); @@ -1129,11 +1129,7 @@ void DolphinMainWindow::setupActions() stashSplit->setVisible(KProtocolInfo::isKnownProtocol("stash")); connect(stashSplit, &QAction::triggered, this, &DolphinMainWindow::toggleSplitStash); - QAction* reload = actionCollection()->addAction(QStringLiteral("reload")); - reload->setText(i18nc("@action:inmenu View", "Reload")); - actionCollection()->setDefaultShortcut(reload, Qt::Key_F5); - reload->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); - connect(reload, &QAction::triggered, this, &DolphinMainWindow::reloadView); + KStandardAction::redisplay(this, &DolphinMainWindow::reloadView, actionCollection()); QAction* stop = actionCollection()->addAction(QStringLiteral("stop")); stop->setText(i18nc("@action:inmenu View", "Stop")); diff --git a/src/dolphinpart.cpp b/src/dolphinpart.cpp index e0f06bc5f..2e7f7639f 100644 --- a/src/dolphinpart.cpp +++ b/src/dolphinpart.cpp @@ -180,7 +180,7 @@ void DolphinPart::createActions() unselectItemsMatching->setText(i18nc("@action:inmenu Edit", "Unselect Items Matching...")); connect(unselectItemsMatching, &QAction::triggered, this, &DolphinPart::slotUnselectItemsMatchingPattern); - actionCollection()->addAction(KStandardAction::SelectAll, QStringLiteral("select_all"), m_view, SLOT(selectAll())); + KStandardAction::selectAll(m_view, &DolphinView::selectAll, actionCollection()); QAction* unselectAll = actionCollection()->addAction(QStringLiteral("unselect_all")); unselectAll->setText(i18nc("@action:inmenu Edit", "Unselect All")); @@ -216,11 +216,8 @@ void DolphinPart::createActions() goActionGroup); // Tools menu - m_findFileAction = actionCollection()->addAction(QStringLiteral("find_file")); + m_findFileAction = KStandardAction::find(this, &DolphinPart::slotFindFile, actionCollection()); m_findFileAction->setText(i18nc("@action:inmenu Tools", "Find File...")); - actionCollection()->setDefaultShortcut(m_findFileAction, Qt::CTRL + Qt::Key_F); - m_findFileAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-find"))); - connect(m_findFileAction, &QAction::triggered, this, &DolphinPart::slotFindFile); #ifndef Q_OS_WIN if (KAuthorized::authorize(QStringLiteral("shell_access"))) { diff --git a/src/dolphinpart.desktop b/src/dolphinpart.desktop index 2344555d5..f0cda29da 100644 --- a/src/dolphinpart.desktop +++ b/src/dolphinpart.desktop @@ -2,6 +2,7 @@ Type=Service Name=Dolphin View Name[ar]=منظور دولفين +Name[ast]=Vista de Dolphin Name[ca]=Vista del Dolphin Name[ca@valencia]=Vista del Dolphin Name[cs]=Pohled Dolphin @@ -109,6 +110,7 @@ Exec=dolphin [Desktop Action compact] Name=Compact Name[ar]=متراصّ +Name[ast]=Compauta Name[ca]=Compacta Name[ca@valencia]=Compacta Name[cs]=Kompaktní diff --git a/src/dolphinpart.rc b/src/dolphinpart.rc index 149fa198a..afd3838e3 100644 --- a/src/dolphinpart.rc +++ b/src/dolphinpart.rc @@ -15,7 +15,7 @@ <Action name="select_items_matching" /> <Action name="unselect_items_matching" /> <Separator/> - <Action name="select_all" /> + <Action name="edit_select_all" /> <Action name="unselect_all" /> <Action name="invert_selection" /> </Menu> diff --git a/src/dolphintabpage.cpp b/src/dolphintabpage.cpp index a96c8b6a3..b2bb5c896 100644 --- a/src/dolphintabpage.cpp +++ b/src/dolphintabpage.cpp @@ -312,6 +312,9 @@ void DolphinTabPage::slotViewActivated() m_primaryViewActive = !m_primaryViewActive; } else { m_primaryViewActive = true; + if (m_secondaryViewContainer) { + m_secondaryViewContainer->setActive(false); + } } } diff --git a/src/dolphintabwidget.cpp b/src/dolphintabwidget.cpp index 0271ed568..a5c2f8c98 100644 --- a/src/dolphintabwidget.cpp +++ b/src/dolphintabwidget.cpp @@ -34,7 +34,7 @@ DolphinTabWidget::DolphinTabWidget(QWidget* parent) : QTabWidget(parent), m_placesSelectorVisible(true), - m_previousTab(0) + m_lastViewedTab(0) { connect(this, &DolphinTabWidget::tabCloseRequested, this, static_cast<void (DolphinTabWidget::*)(int)>(&DolphinTabWidget::closeTab)); @@ -61,6 +61,18 @@ DolphinTabPage* DolphinTabWidget::currentTabPage() const return tabPageAt(currentIndex()); } +DolphinTabPage* DolphinTabWidget::nextTabPage() const +{ + const int index = currentIndex() + 1; + return tabPageAt(index < count() ? index : 0); +} + +DolphinTabPage* DolphinTabWidget::prevTabPage() const +{ + const int index = currentIndex() - 1; + return tabPageAt(index >= 0 ? index : (count() - 1)); +} + DolphinTabPage* DolphinTabWidget::tabPageAt(const int index) const { return static_cast<DolphinTabPage*>(widget(index)); @@ -305,8 +317,8 @@ void DolphinTabWidget::tabUrlChanged(const QUrl& url) void DolphinTabWidget::currentTabChanged(int index) { - // previous tab deactivation - if (DolphinTabPage* tabPage = tabPageAt(m_previousTab)) { + // last-viewed tab deactivation + if (DolphinTabPage* tabPage = tabPageAt(m_lastViewedTab)) { tabPage->setActive(false); } DolphinTabPage* tabPage = tabPageAt(index); @@ -314,7 +326,7 @@ void DolphinTabWidget::currentTabChanged(int index) emit activeViewChanged(viewContainer); emit currentUrlChanged(viewContainer->url()); tabPage->setActive(true); - m_previousTab = index; + m_lastViewedTab = index; } void DolphinTabWidget::tabInserted(int index) diff --git a/src/dolphintabwidget.h b/src/dolphintabwidget.h index ba2fd4867..b4493f7ed 100644 --- a/src/dolphintabwidget.h +++ b/src/dolphintabwidget.h @@ -40,6 +40,18 @@ public: DolphinTabPage* currentTabPage() const; /** + * @return the next tab page. If the current active tab is the last tab, + * it returns the first tab. If there is only one tab, returns nullptr + */ + DolphinTabPage* nextTabPage() const; + + /** + * @return the previous tab page. If the current active tab is the first tab, + * it returns the last tab. If there is only one tab, returns nullptr + */ + DolphinTabPage* prevTabPage() const; + + /** * @return Tab page at the given \a index (can be 0 if the index is out-of-range) */ DolphinTabPage* tabPageAt(const int index) const; @@ -187,7 +199,7 @@ private: /** Caches the (negated) places panel visibility */ bool m_placesSelectorVisible; - int m_previousTab; + int m_lastViewedTab; }; #endif diff --git a/src/dolphinui.rc b/src/dolphinui.rc index 566f50fc8..282ea63e5 100644 --- a/src/dolphinui.rc +++ b/src/dolphinui.rc @@ -1,11 +1,11 @@ <!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> -<kpartgui name="dolphin" version="19"> +<kpartgui name="dolphin" version="21"> <MenuBar> <Menu name="file"> <Action name="new_menu" /> - <Action name="new_window" /> + <Action name="file_new" /> <Action name="new_tab" /> - <Action name="close_tab" /> + <Action name="file_close" /> <Action name="undo_close_tab" /> <Separator/> <Action name="renamefile" /> @@ -17,12 +17,12 @@ <Action name="properties" /> </Menu> <Menu name="edit"> - <Action name="select_all" /> + <Action name="edit_select_all" /> <Action name="invert_selection" /> </Menu> <Menu name="view"> - <Action name="view_mode" /> <Action name="sort" /> + <Action name="view_mode" /> <Action name="additional_info" /> <Action name="show_preview" /> <Action name="show_in_groups" /> @@ -30,7 +30,7 @@ <Separator/> <Action name="split_view" /> <Action name="split_stash" /> - <Action name="reload" /> + <Action name="redisplay" /> <Action name="stop" /> <Separator/> <Action name="panels" /> diff --git a/src/dolphinviewcontainer.cpp b/src/dolphinviewcontainer.cpp index bf8ac8812..f473752dc 100644 --- a/src/dolphinviewcontainer.cpp +++ b/src/dolphinviewcontainer.cpp @@ -108,6 +108,14 @@ DolphinViewContainer::DolphinViewContainer(const QUrl& url, QWidget* parent) : m_messageWidget->setCloseButtonVisible(true); m_messageWidget->hide(); +#ifndef Q_OS_WIN + if (getuid() == 0) { + + // We must be logged in as the root user; show a big scary warning + showMessage(i18n("Running Dolphin as root can be dangerous. Please be careful."), Warning); + } +#endif + m_view = new DolphinView(url, this); connect(m_view, &DolphinView::urlChanged, m_urlNavigator, &KUrlNavigator::setLocationUrl); @@ -153,12 +161,7 @@ DolphinViewContainer::DolphinViewContainer(const QUrl& url, QWidget* parent) : connect(m_urlNavigator, &KUrlNavigator::returnPressed, this, &DolphinViewContainer::slotReturnPressed); connect(m_urlNavigator, &KUrlNavigator::urlsDropped, this, [=](const QUrl &destination, QDropEvent *event) { -#if KIO_VERSION >= QT_VERSION_CHECK(5, 37, 0) m_view->dropUrls(destination, event, m_urlNavigator->dropWidget()); -#else - // TODO: remove as soon as we can hard-depend of KF5 >= 5.37 - m_view->dropUrls(destination, event, m_view); -#endif }); connect(m_view, &DolphinView::directoryLoadingCompleted, this, [this]() { diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index b6a001642..2a9e2ea41 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -1846,16 +1846,14 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b, const break; } - case RatingRole: { - result = a->values.value("rating").toInt() - b->values.value("rating").toInt(); - break; - } - - case ImageSizeRole: { - // Alway use a natural comparing to interpret the numbers of a string like - // "1600 x 1200" for having a correct sorting. - result = collator.compare(a->values.value("imageSize").toString(), - b->values.value("imageSize").toString()); + case RatingRole: + case WidthRole: + case HeightRole: + case WordCountRole: + case LineCountRole: + case TrackRole: + case ReleaseYearRole: { + result = a->values.value(roleForType(m_sortRole)).toInt() - b->values.value(roleForType(m_sortRole)).toInt(); break; } @@ -2305,7 +2303,8 @@ const KFileItemModel::RoleInfoMap* KFileItemModel::rolesInfoMap(int& count) { "wordCount", WordCountRole, I18N_NOOP2_NOSTRIP("@label", "Word Count"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true }, { "lineCount", LineCountRole, I18N_NOOP2_NOSTRIP("@label", "Line Count"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true }, { "imageDateTime", ImageDateTimeRole, I18N_NOOP2_NOSTRIP("@label", "Date Photographed"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, - { "imageSize", ImageSizeRole, I18N_NOOP2_NOSTRIP("@label", "Image Size"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, + { "width", WidthRole, I18N_NOOP2_NOSTRIP("@label", "Width"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, + { "height", HeightRole, I18N_NOOP2_NOSTRIP("@label", "Height"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, { "orientation", OrientationRole, I18N_NOOP2_NOSTRIP("@label", "Orientation"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, { "artist", ArtistRole, I18N_NOOP2_NOSTRIP("@label", "Artist"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, { "genre", GenreRole, I18N_NOOP2_NOSTRIP("@label", "Genre"), I18N_NOOP2_NOSTRIP("@label", "Audio"), true, true }, diff --git a/src/kitemviews/kfileitemmodel.h b/src/kitemviews/kfileitemmodel.h index 75953f0b7..134c50245 100644 --- a/src/kitemviews/kfileitemmodel.h +++ b/src/kitemviews/kfileitemmodel.h @@ -286,7 +286,7 @@ private: NoRole, NameRole, SizeRole, ModificationTimeRole, CreationTimeRole, AccessTimeRole, PermissionsRole, OwnerRole, GroupRole, TypeRole, DestinationRole, PathRole, DeletionTimeRole, // User visible roles available with Baloo: - CommentRole, TagsRole, RatingRole, ImageSizeRole, ImageDateTimeRole, OrientationRole, + CommentRole, TagsRole, RatingRole, WidthRole, HeightRole, ImageDateTimeRole, OrientationRole, WordCountRole, TitleRole, LineCountRole, ArtistRole, GenreRole, AlbumRole, DurationRole, TrackRole, ReleaseYearRole, BitrateRole, OriginUrlRole, // Non-visible roles: diff --git a/src/kitemviews/kfileitemmodelrolesupdater.cpp b/src/kitemviews/kfileitemmodelrolesupdater.cpp index 6c96a08d1..544d88040 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.cpp +++ b/src/kitemviews/kfileitemmodelrolesupdater.cpp @@ -497,7 +497,7 @@ void KFileItemModelRolesUpdater::slotGotPreview(const KFileItem& item, const QPi const QString mimeType = item.mimetype(); const int slashIndex = mimeType.indexOf(QLatin1Char('/')); - const bool isFontPreview = mimeType.right(slashIndex).contains(QLatin1String("font")); + const bool isFontPreview = mimeType.rightRef(slashIndex).contains(QLatin1String("font")); const bool isFolderPreview = item.isDir(); const bool isWindowsExePreview = mimeType == QLatin1String("application/x-ms-dos-executable") || mimeType == QLatin1String("application/x-msdownload"); diff --git a/src/kitemviews/kitemlistgroupheader.cpp b/src/kitemviews/kitemlistgroupheader.cpp index fb9298989..06a32484a 100644 --- a/src/kitemviews/kitemlistgroupheader.cpp +++ b/src/kitemviews/kitemlistgroupheader.cpp @@ -78,6 +78,10 @@ QVariant KItemListGroupHeader::data() const void KItemListGroupHeader::setStyleOption(const KItemListStyleOption& option) { + if (m_styleOption == option) { + return; + } + const KItemListStyleOption previous = m_styleOption; m_styleOption = option; m_dirtyCache = true; diff --git a/src/kitemviews/kitemlistselectionmanager.cpp b/src/kitemviews/kitemlistselectionmanager.cpp index efc256e1e..f5e097c02 100644 --- a/src/kitemviews/kitemlistselectionmanager.cpp +++ b/src/kitemviews/kitemlistselectionmanager.cpp @@ -235,6 +235,9 @@ void KItemListSelectionManager::itemsInserted(const KItemRangeList& itemRanges) // Calling setCurrentItem would trigger the selectionChanged signal, but we want to // emit it only once in this function -> change the current item manually and emit currentChanged m_currentItem += inc; + if (m_currentItem >= m_model->count()) { + m_currentItem = -1; + } emit currentChanged(m_currentItem, previousCurrent); } diff --git a/src/kitemviews/kitemliststyleoption.cpp b/src/kitemviews/kitemliststyleoption.cpp index 1ebcad141..bcfb86064 100644 --- a/src/kitemviews/kitemliststyleoption.cpp +++ b/src/kitemviews/kitemliststyleoption.cpp @@ -53,3 +53,23 @@ KItemListStyleOption::KItemListStyleOption(const KItemListStyleOption& other) : KItemListStyleOption::~KItemListStyleOption() { } + +bool KItemListStyleOption::operator==(const KItemListStyleOption& other) const +{ + return rect == other.rect + && font == other.font + && fontMetrics == other.fontMetrics + && palette == other.palette + && padding == other.padding + && horizontalMargin == other.horizontalMargin + && verticalMargin == other.verticalMargin + && iconSize == other.iconSize + && extendedSelectionRegion == other.extendedSelectionRegion + && maxTextLines == other.maxTextLines + && maxTextWidth == other.maxTextWidth; +} + +bool KItemListStyleOption::operator!=(const KItemListStyleOption& other) const +{ + return !(*this == other); +} diff --git a/src/kitemviews/kitemliststyleoption.h b/src/kitemviews/kitemliststyleoption.h index 09b787c27..93aafac1f 100644 --- a/src/kitemviews/kitemliststyleoption.h +++ b/src/kitemviews/kitemliststyleoption.h @@ -45,6 +45,11 @@ public: bool extendedSelectionRegion; int maxTextLines; int maxTextWidth; + + bool operator==(const KItemListStyleOption& other) const; + bool operator!=(const KItemListStyleOption& other) const; + + }; #endif diff --git a/src/kitemviews/kitemlistview.cpp b/src/kitemviews/kitemlistview.cpp index 489c6f9b6..f0647fb3e 100644 --- a/src/kitemviews/kitemlistview.cpp +++ b/src/kitemviews/kitemlistview.cpp @@ -752,6 +752,10 @@ void KItemListView::setItemSize(const QSizeF& size) void KItemListView::setStyleOption(const KItemListStyleOption& option) { + if (m_styleOption == option) { + return; + } + const KItemListStyleOption previousOption = m_styleOption; m_styleOption = option; diff --git a/src/kitemviews/kitemlistwidget.cpp b/src/kitemviews/kitemlistwidget.cpp index 61dd7256e..28b374620 100644 --- a/src/kitemviews/kitemlistwidget.cpp +++ b/src/kitemviews/kitemlistwidget.cpp @@ -191,10 +191,13 @@ qreal KItemListWidget::columnWidth(const QByteArray& role) const void KItemListWidget::setStyleOption(const KItemListStyleOption& option) { + if (m_styleOption == option) { + return; + } + const KItemListStyleOption previous = m_styleOption; clearHoverCache(); m_styleOption = option; - styleOptionChanged(option, previous); update(); } diff --git a/src/kitemviews/kstandarditemlistview.cpp b/src/kitemviews/kstandarditemlistview.cpp index ee1c06103..929ee1da8 100644 --- a/src/kitemviews/kstandarditemlistview.cpp +++ b/src/kitemviews/kstandarditemlistview.cpp @@ -146,27 +146,20 @@ void KStandardItemListView::applyDefaultStyleOption(int iconSize, { KItemListStyleOption option = styleOption(); - bool changed = false; if (option.iconSize < 0) { option.iconSize = iconSize; - changed = true; } if (option.padding < 0) { option.padding = padding; - changed = true; } if (option.horizontalMargin < 0) { option.horizontalMargin = horizontalMargin; - changed = true; } if (option.verticalMargin < 0) { option.verticalMargin = verticalMargin; - changed = true; } - if (changed) { - setStyleOption(option); - } + setStyleOption(option); } void KStandardItemListView::updateLayoutOfVisibleItems() diff --git a/src/kitemviews/kstandarditemlistwidget.cpp b/src/kitemviews/kstandarditemlistwidget.cpp index 1aca250dd..bc3bb29de 100644 --- a/src/kitemviews/kstandarditemlistwidget.cpp +++ b/src/kitemviews/kstandarditemlistwidget.cpp @@ -1015,15 +1015,21 @@ void KStandardItemListWidget::updatePixmapCache() if (iconOnTop) { // Center horizontally and align on bottom within the icon-area - m_pixmapPos.setX((widgetSize.width() - m_scaledPixmapSize.width()) / 2); + m_pixmapPos.setX((widgetSize.width() - m_scaledPixmapSize.width()) / 2.0); m_pixmapPos.setY(padding + scaledIconSize - m_scaledPixmapSize.height()); } else { // Center horizontally and vertically within the icon-area const TextInfo* textInfo = m_textInfo.value("text"); - m_pixmapPos.setX(textInfo->pos.x() - 2 * padding - - (scaledIconSize + m_scaledPixmapSize.width()) / 2); - m_pixmapPos.setY(padding - + (scaledIconSize - m_scaledPixmapSize.height()) / 2); + m_pixmapPos.setX(textInfo->pos.x() - 2.0 * padding + - (scaledIconSize + m_scaledPixmapSize.width()) / 2.0); + + // Derive icon's vertical center from the center of the text frame, including + // any necessary adjustment if the font's midline is offset from the frame center + const qreal midlineShift = m_customizedFontMetrics.height() / 2.0 + - m_customizedFontMetrics.descent() + - m_customizedFontMetrics.capHeight() / 2.0; + m_pixmapPos.setY(m_textRect.center().y() + midlineShift - m_scaledPixmapSize.height() / 2.0); + } m_iconRect = QRectF(m_pixmapPos, QSizeF(m_scaledPixmapSize)); diff --git a/src/kitemviews/private/kbaloorolesprovider.cpp b/src/kitemviews/private/kbaloorolesprovider.cpp index 6732d08ec..0e9aef71e 100644 --- a/src/kitemviews/private/kbaloorolesprovider.cpp +++ b/src/kitemviews/private/kbaloorolesprovider.cpp @@ -56,9 +56,6 @@ QHash<QByteArray, QVariant> KBalooRolesProvider::roleValues(const Baloo::File& f { QHash<QByteArray, QVariant> values; - int width = -1; - int height = -1; - QMapIterator<KFileMetaData::Property::Property, QVariant> it(file.properties()); while (it.hasNext()) { it.next(); @@ -72,23 +69,7 @@ QHash<QByteArray, QVariant> KBalooRolesProvider::roleValues(const Baloo::File& f const QVariant value = it.value(); - if (role == "imageSize") { - // Merge the two properties for width and height - // as one string into the "imageSize" role - if (property == QLatin1String("width")) { - width = value.toInt(); - } - else if (property == QLatin1String("height")) { - height = value.toInt(); - } - - if (width >= 0 && height >= 0) { - QString widthAndHeight = QString::number(width); - widthAndHeight += QLatin1String(" x "); - widthAndHeight += QString::number(height); - values.insert(role, widthAndHeight); - } - } else if (role == "orientation") { + if (role == "orientation") { const QString orientation = orientationFromValue(value.toInt()); values.insert(role, orientation); } else if (role == "duration") { @@ -137,8 +118,7 @@ KBalooRolesProvider::KBalooRolesProvider() : }; // Mapping from the URIs to the KFileItemModel roles. Note that this must not be - // a 1:1 mapping: One role may contain several URI-values (e.g. the URIs for height and - // width of an image are mapped to the role "imageSize") + // a 1:1 mapping: One role may contain several URI-values static const PropertyInfo propertyInfoList[] = { { "rating", "rating" }, { "tag", "tags" }, @@ -146,8 +126,8 @@ KBalooRolesProvider::KBalooRolesProvider() : { "title", "title" }, { "wordCount", "wordCount" }, { "lineCount", "lineCount" }, - { "width", "imageSize" }, - { "height", "imageSize" }, + { "width", "width" }, + { "height", "height" }, { "imageDateTime", "imageDateTime"}, { "nexif.orientation", "orientation", }, { "artist", "artist" }, diff --git a/src/main.cpp b/src/main.cpp index db52e11be..75bab677d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -43,10 +43,15 @@ extern "C" Q_DECL_EXPORT int kdemain(int argc, char **argv) { #ifndef Q_OS_WIN - // Check whether we are running as root + // Prohibit using sudo or kdesu (but allow using the root user directly) if (getuid() == 0) { - std::cout << "Executing Dolphin as root is not possible." << std::endl; - return EXIT_FAILURE; + if (!qEnvironmentVariableIsEmpty("SUDO_USER")) { + std::cout << "Executing Dolphin with sudo is not possible due to unfixable security vulnerabilities." << std::endl; + return EXIT_FAILURE; + } else if (!qEnvironmentVariableIsEmpty("KDESU_USER")) { + std::cout << "Executing Dolphin with kdesu is not possible due to unfixable security vulnerabilities." << std::endl; + return EXIT_FAILURE; + } } #endif diff --git a/src/panels/folders/folderspanel.cpp b/src/panels/folders/folderspanel.cpp index 61417dc5e..7a0b4b6b2 100644 --- a/src/panels/folders/folderspanel.cpp +++ b/src/panels/folders/folderspanel.cpp @@ -105,7 +105,6 @@ void FoldersPanel::rename(const KFileItem& item) m_controller->view()->editRole(index, "text"); } else { RenameDialog* dialog = new RenameDialog(this, KFileItemList() << item); - dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); dialog->raise(); dialog->activateWindow(); diff --git a/src/panels/information/dolphin_informationpanelsettings.kcfg b/src/panels/information/dolphin_informationpanelsettings.kcfg index 53c756d24..dbc1ab6d2 100644 --- a/src/panels/information/dolphin_informationpanelsettings.kcfg +++ b/src/panels/information/dolphin_informationpanelsettings.kcfg @@ -10,5 +10,13 @@ <label>Previews shown</label> <default>true</default> </entry> + <entry name="dateFormat" type="Enum"> + <label>Date display format</label> + <choices> + <choice name="LongFormat" /> + <choice name="ShortFormat" /> + </choices> + <default>0</default> + </entry> </group> </kcfg> diff --git a/src/panels/information/informationpanelcontent.cpp b/src/panels/information/informationpanelcontent.cpp index f44a3feb7..0cba0cdf0 100644 --- a/src/panels/information/informationpanelcontent.cpp +++ b/src/panels/information/informationpanelcontent.cpp @@ -112,12 +112,13 @@ InformationPanelContent::InformationPanelContent(QWidget* parent) : this, &InformationPanelContent::urlActivated); #else m_metaDataWidget = new Baloo::FileMetaDataWidget(parent); + m_metaDataWidget->setDateFormat(static_cast<Baloo::DateFormats>(InformationPanelSettings::dateFormat())); connect(m_metaDataWidget, &Baloo::FileMetaDataWidget::urlActivated, this, &InformationPanelContent::urlActivated); #endif m_metaDataWidget->setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); m_metaDataWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); - + // Encapsulate the MetaDataWidget inside a container that has a dummy widget // at the bottom. This prevents that the meta data widget gets vertically stretched // in the case where the height of m_metaDataArea > m_metaDataWidget. @@ -160,41 +161,42 @@ void InformationPanelContent::showItem(const KFileItem& item) const QUrl itemUrl = item.url(); const bool isSearchUrl = itemUrl.scheme().contains(QStringLiteral("search")) && item.localPath().isEmpty(); - if (!applyPlace(itemUrl)) { - setNameLabelText(item.text()); - if (isSearchUrl) { - // 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("nepomuk")).pixmap(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous) - ); - } else { - // try to get a preview pixmap from the item... - - // 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 (!item.isDir()) { - m_outdatedPreviewTimer->start(); - } + setNameLabelText(item.text()); + if (isSearchUrl) { + // 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("nepomuk")).pixmap(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous) + ); + } else { + // try to get a preview pixmap from the item... - m_previewJob = new KIO::PreviewJob(KFileItemList() << item, QSize(m_preview->width(), m_preview->height())); - m_previewJob->setScaleType(KIO::PreviewJob::Unscaled); - m_previewJob->setIgnoreMaximumSize(item.isLocalFile()); - if (m_previewJob->uiDelegate()) { - KJobWidgets::setWindow(m_previewJob, this); - } + // 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 (!item.isDir()) { + m_outdatedPreviewTimer->start(); + } - connect(m_previewJob.data(), &KIO::PreviewJob::gotPreview, - this, &InformationPanelContent::showPreview); - connect(m_previewJob.data(), &KIO::PreviewJob::failed, - this, &InformationPanelContent::showIcon); + m_previewJob = new KIO::PreviewJob(KFileItemList() << item, QSize(m_preview->width(), m_preview->height())); + m_previewJob->setScaleType(KIO::PreviewJob::Unscaled); + m_previewJob->setIgnoreMaximumSize(item.isLocalFile()); + if (m_previewJob->uiDelegate()) { + KJobWidgets::setWindow(m_previewJob, this); } + + connect(m_previewJob.data(), &KIO::PreviewJob::gotPreview, + this, &InformationPanelContent::showPreview); + connect(m_previewJob.data(), &KIO::PreviewJob::failed, + this, &InformationPanelContent::showIcon); } if (m_metaDataWidget) { +#ifdef HAVE_BALOO + m_metaDataWidget->setDateFormat(static_cast<Baloo::DateFormats>(InformationPanelSettings::dateFormat())); +#endif m_metaDataWidget->show(); m_metaDataWidget->setItems(KFileItemList() << item); } @@ -283,6 +285,12 @@ void InformationPanelContent::configureSettings(const QList<QAction*>& customCon QAction* configureAction = popup.addAction(i18nc("@action:inmenu", "Configure...")); configureAction->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); +#ifdef HAVE_BALOO + QAction* dateformatAction = popup.addAction(i18nc("@action:inmenu", "Condensed Date")); + dateformatAction->setIcon(QIcon::fromTheme(QStringLiteral("change-date-symbolic"))); + dateformatAction->setCheckable(true); + dateformatAction->setChecked(InformationPanelSettings::dateFormat() == static_cast<int>(Baloo::DateFormats::ShortFormat)); +#endif popup.addSeparator(); foreach (QAction* action, customContextMenuActions) { popup.addAction(action); @@ -308,16 +316,22 @@ void InformationPanelContent::configureSettings(const QList<QAction*>& customCon dialog->show(); connect(dialog, &FileMetaDataConfigurationDialog::destroyed, this, &InformationPanelContent::refreshMetaData); } +#ifdef HAVE_BALOO + if (action == dateformatAction) { + int dateFormat = static_cast<int>(isChecked ? Baloo::DateFormats::ShortFormat : Baloo::DateFormats::LongFormat); + + InformationPanelSettings::setDateFormat(dateFormat); + refreshMetaData(); + } +#endif } void InformationPanelContent::showIcon(const KFileItem& item) { m_outdatedPreviewTimer->stop(); - if (!applyPlace(item.targetUrl())) { - QPixmap pixmap = QIcon::fromTheme(item.iconName()).pixmap(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous); - KIconLoader::global()->drawOverlays(item.overlays(), pixmap, KIconLoader::Desktop); - m_preview->setPixmap(pixmap); - } + QPixmap pixmap = QIcon::fromTheme(item.iconName()).pixmap(KIconLoader::SizeEnormous, KIconLoader::SizeEnormous); + KIconLoader::global()->drawOverlays(item.overlays(), pixmap, KIconLoader::Desktop); + m_preview->setPixmap(pixmap); } void InformationPanelContent::showPreview(const KFileItem& item, @@ -352,21 +366,6 @@ void InformationPanelContent::refreshMetaData() } } -bool InformationPanelContent::applyPlace(const QUrl& url) -{ - const int count = m_placesItemModel->count(); - for (int i = 0; i < count; ++i) { - const PlacesItem* item = m_placesItemModel->placesItem(i); - if (item->url().matches(url, QUrl::StripTrailingSlash)) { - setNameLabelText(item->text()); - m_preview->setPixmap(QIcon::fromTheme(item->icon()).pixmap(128, 128)); - return true; - } - } - - return false; -} - void InformationPanelContent::setNameLabelText(const QString& text) { QTextOption textOption; diff --git a/src/panels/information/informationpanelcontent.h b/src/panels/information/informationpanelcontent.h index 80f3c8e22..9223fcc5a 100644 --- a/src/panels/information/informationpanelcontent.h +++ b/src/panels/information/informationpanelcontent.h @@ -116,14 +116,6 @@ private slots: private: /** - * Checks whether the an URL is repesented by a place. If yes, - * then the place icon and name are shown instead of a preview. - * @return True, if the URL represents exactly a place. - * @param url The url to check. - */ - bool applyPlace(const QUrl& url); - - /** * Sets the text for the label \a m_nameLabel and assures that the * text is split in a way that it can be wrapped within the * label width (QLabel::setWordWrap() does not work if the diff --git a/src/panels/places/placesitemmodel.cpp b/src/panels/places/placesitemmodel.cpp index 3641b3e42..7e313482d 100644 --- a/src/panels/places/placesitemmodel.cpp +++ b/src/panels/places/placesitemmodel.cpp @@ -453,7 +453,7 @@ void PlacesItemModel::initializeDefaultViewProperties() const } else if (path == QLatin1String("/images")) { props.setViewMode(DolphinView::IconsView); props.setPreviewsShown(true); - props.setVisibleRoles({"text", "imageSize"}); + props.setVisibleRoles({"text", "height", "width"}); } else if (path == QLatin1String("/audio")) { props.setViewMode(DolphinView::DetailsView); props.setPreviewsShown(false); @@ -597,7 +597,8 @@ void PlacesItemModel::onSourceModelDataChanged(const QModelIndex &topLeft, const void PlacesItemModel::onSourceModelGroupHiddenChanged(KFilePlacesModel::GroupType group, bool hidden) { - for(const QModelIndex &sourceIndex : m_sourceModel->groupIndexes(group)) { + const auto groupIndexes = m_sourceModel->groupIndexes(group); + for (const QModelIndex &sourceIndex : groupIndexes) { PlacesItem *item = placesItem(mapFromSource(sourceIndex)); if (item) { item->setGroupHidden(hidden); diff --git a/src/panels/places/placespanel.cpp b/src/panels/places/placespanel.cpp index 1f81a1eaa..00d8735c3 100644 --- a/src/panels/places/placespanel.cpp +++ b/src/panels/places/placespanel.cpp @@ -291,7 +291,7 @@ void PlacesPanel::slotViewContextMenuRequested(const QPointF& pos) {KIconLoader::SizeLarge, I18N_NOOP2_NOSTRIP("Huge icon size", "Huge (%1x%2)")} }; - QMap<QAction*, int> iconSizeActionMap; + QHash<QAction*, int> iconSizeActionMap; QActionGroup* iconSizeGroup = new QActionGroup(iconSizeSubMenu); for (int i = 0; i < iconSizeCount; ++i) { diff --git a/src/settings/kcm/kcmdolphingeneral.desktop b/src/settings/kcm/kcmdolphingeneral.desktop index 532f2f7ad..5f569237c 100644 --- a/src/settings/kcm/kcmdolphingeneral.desktop +++ b/src/settings/kcm/kcmdolphingeneral.desktop @@ -1,5 +1,6 @@ Name=Dolphin General Name[ar]=دولفين العامّ +Name[ast]=Axustes xenerales de Dolphin Name[ca]=General del Dolphin Name[ca@valencia]=General del Dolphin Name[cs]=Obecný Dolphin @@ -43,6 +44,7 @@ Name[zh_CN]=Dolphin 常规 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[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. @@ -141,6 +143,7 @@ Name[zh_CN]=常规 Name[zh_TW]=一般 Comment=Configure general file manager settings Comment[ar]=اضبط إعدادات مدير الملفّات العامّة +Comment[ast]=Configura los axustes xenerales del xestor de ficheros 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ů diff --git a/src/settings/kcm/kcmdolphinnavigation.desktop b/src/settings/kcm/kcmdolphinnavigation.desktop index e23594b5e..974ebc3d2 100644 --- a/src/settings/kcm/kcmdolphinnavigation.desktop +++ b/src/settings/kcm/kcmdolphinnavigation.desktop @@ -1,5 +1,6 @@ Name=Dolphin Navigation Name[ar]=التّنقّل في دولفين +Name[ast]=Navegación de Dolphin Name[ca]=Navegació del Dolphin Name[ca@valencia]=Navegació del Dolphin Name[cs]=Navigace Dolphinu @@ -43,6 +44,7 @@ Name[zh_CN]=Dolphin 导航 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[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. @@ -141,6 +143,7 @@ Name[zh_CN]=导航 Name[zh_TW]=導覽 Comment=Configure file manager navigation Comment[ar]=اضبط التّنقّل في مدير الملفّات +Comment[ast]=Configura la navegación del xestor de ficheros 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ů diff --git a/src/settings/kcm/kcmdolphinservices.desktop b/src/settings/kcm/kcmdolphinservices.desktop index 061493028..a2421a848 100644 --- a/src/settings/kcm/kcmdolphinservices.desktop +++ b/src/settings/kcm/kcmdolphinservices.desktop @@ -98,6 +98,7 @@ Name[zh_CN]=服务 Name[zh_TW]=服務 Comment=Configure file manager services Comment[ar]=اضبط خدمات مدير الملفّات +Comment[ast]=Configura los servicios del xestor de ficheros 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ů diff --git a/src/settings/kcm/kcmdolphinviewmodes.desktop b/src/settings/kcm/kcmdolphinviewmodes.desktop index 148d167da..286320eaa 100644 --- a/src/settings/kcm/kcmdolphinviewmodes.desktop +++ b/src/settings/kcm/kcmdolphinviewmodes.desktop @@ -1,5 +1,6 @@ Name=Dolphin View Modes Name[ar]=أوضاع المنظور في دولفين +Name[ast]=Moos de vista de Dolphin Name[ca]=Modes de vista del Dolphin Name[ca@valencia]=Modes de vista del Dolphin Name[cs]=Režimy pohledů Dolphinu @@ -43,6 +44,7 @@ Name[zh_CN]=Dolphin 视图模式 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[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. @@ -96,6 +98,7 @@ X-KDE-ParentApp=kcontrol X-DocPath=dolphin/index.html#preferences-dialog-viewmodes Name=View Modes Name[ar]=أوضاع المنظور +Name[ast]=Moos de vista Name[ca]=Modes de vista Name[ca@valencia]=Modes de vista Name[cs]=Režimy pohledu @@ -139,6 +142,7 @@ Name[zh_CN]=视图模式 Name[zh_TW]=檢視模式 Comment=Configure file manager view modes Comment[ar]=اضبط أوضاع المنظور في مدير الملفّات +Comment[ast]=Configura los moos de vista del xestor de ficheros 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ů diff --git a/src/settings/services/servicemenuinstallation b/src/settings/services/servicemenuinstallation index 36b9debd2..60b699bb6 100755 --- a/src/settings/services/servicemenuinstallation +++ b/src/settings/services/servicemenuinstallation @@ -40,7 +40,6 @@ $archivetypes = { "application/x-tar" => :"tar -xf %s -C %s", "multipart/x-zip" => :"unzip %s -d %s", "application/tgz" => :"tar -zxf %s -C %s", "application/x-compressed-gtar" => :"tar -zxf %s -C %s", - "application/x-gtar" => :"tar -zxf %s -C %s", "file/tgz" => :"tar -zxf %s -C %s", "multipart/x-tar-gz" => :"tar -zxf %s -C %s", "application/x-gunzip" => :"tar -zxf %s -C %s", diff --git a/src/statusbar/statusbarspaceinfo.cpp b/src/statusbar/statusbarspaceinfo.cpp index 9b8d249fb..65af17490 100644 --- a/src/statusbar/statusbarspaceinfo.cpp +++ b/src/statusbar/statusbarspaceinfo.cpp @@ -77,9 +77,7 @@ void StatusBarSpaceInfo::mousePressEvent(QMouseEvent* event) // Note that this object must live long enough in case the user opens // the "Configure..." dialog KMoreToolsMenuFactory menuFactory(QStringLiteral("dolphin/statusbar-diskspace-menu")); -#if KNEWSTUFF_VERSION >= QT_VERSION_CHECK(5, 37, 0) menuFactory.setParentWidget(this); -#endif auto menu = menuFactory.createMenuFromGroupingNames( { "disk-usage", "more:", "disk-partitions" }, m_url); diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index c656173a8..07e4257a0 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -54,10 +54,14 @@ TEST_NAME viewpropertiestest LINK_LIBRARIES dolphinprivate dolphinstatic Qt5::Test) # DolphinMainWindowTest -ecm_add_test(dolphinmainwindowtest.cpp +set(dolphinmainwindowtest_SRCS dolphinmainwindowtest.cpp) +qt5_add_resources(dolphinmainwindowtest_SRCS ${CMAKE_SOURCE_DIR}/src/dolphin.qrc) + +ecm_add_test(${dolphinmainwindowtest_SRCS} TEST_NAME dolphinmainwindowtest LINK_LIBRARIES dolphinprivate dolphinstatic Qt5::Test) +# DragAndDropHelperTest ecm_add_test(draganddrophelpertest.cpp LINK_LIBRARIES dolphinprivate Qt5::Test) # PlacesItemModelTest diff --git a/src/tests/dolphinmainwindowtest.cpp b/src/tests/dolphinmainwindowtest.cpp index a31237f3c..eee387116 100644 --- a/src/tests/dolphinmainwindowtest.cpp +++ b/src/tests/dolphinmainwindowtest.cpp @@ -25,6 +25,7 @@ #include <KActionCollection> #include <QSignalSpy> +#include <QStandardPaths> #include <QTest> class DolphinMainWindowTest : public QObject @@ -32,14 +33,22 @@ class DolphinMainWindowTest : public QObject Q_OBJECT private slots: + void initTestCase(); void init(); void testClosingTabsWithSearchBoxVisible(); + void testActiveViewAfterClosingSplitView_data(); + void testActiveViewAfterClosingSplitView(); void testUpdateWindowTitleAfterClosingSplitView(); private: QScopedPointer<DolphinMainWindow> m_mainWindow; }; +void DolphinMainWindowTest::initTestCase() +{ + QStandardPaths::setTestModeEnabled(true); +} + void DolphinMainWindowTest::init() { m_mainWindow.reset(new DolphinMainWindow()); @@ -68,6 +77,58 @@ void DolphinMainWindowTest::testClosingTabsWithSearchBoxVisible() QCOMPARE(tabWidget->count(), 1); } +void DolphinMainWindowTest::testActiveViewAfterClosingSplitView_data() +{ + QTest::addColumn<bool>("closeLeftView"); + + QTest::newRow("close left view") << true; + QTest::newRow("close right view") << false; +} + +void DolphinMainWindowTest::testActiveViewAfterClosingSplitView() +{ + m_mainWindow->openDirectories({ QUrl::fromLocalFile(QDir::homePath()) }, false); + m_mainWindow->show(); + QVERIFY(QTest::qWaitForWindowExposed(m_mainWindow.data())); + QVERIFY(m_mainWindow->isVisible()); + + auto tabWidget = m_mainWindow->findChild<DolphinTabWidget*>("tabWidget"); + QVERIFY(tabWidget); + QVERIFY(tabWidget->currentTabPage()->primaryViewContainer()); + QVERIFY(!tabWidget->currentTabPage()->secondaryViewContainer()); + + // Open split view. + m_mainWindow->actionCollection()->action(QStringLiteral("split_view"))->trigger(); + QVERIFY(tabWidget->currentTabPage()->splitViewEnabled()); + QVERIFY(tabWidget->currentTabPage()->secondaryViewContainer()); + + // Make sure the right view is the active one. + auto leftViewContainer = tabWidget->currentTabPage()->primaryViewContainer(); + auto rightViewContainer = tabWidget->currentTabPage()->secondaryViewContainer(); + QVERIFY(!leftViewContainer->isActive()); + QVERIFY(rightViewContainer->isActive()); + + QFETCH(bool, closeLeftView); + if (closeLeftView) { + // Activate left view. + leftViewContainer->setActive(true); + QVERIFY(leftViewContainer->isActive()); + QVERIFY(!rightViewContainer->isActive()); + + // Close left view. The secondary view (which was on the right) will become the primary one and must be active. + m_mainWindow->actionCollection()->action(QStringLiteral("split_view"))->trigger(); + QVERIFY(!leftViewContainer->isActive()); + QVERIFY(rightViewContainer->isActive()); + QCOMPARE(rightViewContainer, tabWidget->currentTabPage()->activeViewContainer()); + } else { + // Close right view. The left view will become active. + m_mainWindow->actionCollection()->action(QStringLiteral("split_view"))->trigger(); + QVERIFY(leftViewContainer->isActive()); + QVERIFY(!rightViewContainer->isActive()); + QCOMPARE(leftViewContainer, tabWidget->currentTabPage()->activeViewContainer()); + } +} + // Test case for bug #385111 void DolphinMainWindowTest::testUpdateWindowTitleAfterClosingSplitView() { @@ -99,6 +160,7 @@ void DolphinMainWindowTest::testUpdateWindowTitleAfterClosingSplitView() // Close split view. The secondary view (which was on the right) will become the primary one and must be active. m_mainWindow->actionCollection()->action(QStringLiteral("split_view"))->trigger(); + QVERIFY(!leftViewContainer->isActive()); QVERIFY(rightViewContainer->isActive()); QCOMPARE(rightViewContainer, tabWidget->currentTabPage()->activeViewContainer()); diff --git a/src/tests/placesitemmodeltest.cpp b/src/tests/placesitemmodeltest.cpp index 3263537f9..2e814220a 100644 --- a/src/tests/placesitemmodeltest.cpp +++ b/src/tests/placesitemmodeltest.cpp @@ -161,11 +161,15 @@ QStringList PlacesItemModelTest::initialUrls() const { static QStringList urls; if (urls.isEmpty()) { - urls << QDir::homePath() << QStringLiteral(KDE_ROOT_PATH) << QStringLiteral("trash:/") + urls << QDir::homePath() + << QDir::homePath() + QStringLiteral("/Desktop") + << QDir::homePath() + QStringLiteral("/Downloads") + << QStringLiteral(KDE_ROOT_PATH) << QStringLiteral("trash:/") << QStringLiteral("remote:/") - << QStringLiteral("timeline:/today") << QStringLiteral("timeline:/yesterday") << QStringLiteral("timeline:/thismonth") << QStringLiteral("timeline:/lastmonth") + << QStringLiteral("/media/nfs") + << QStringLiteral("timeline:/today") << QStringLiteral("timeline:/yesterday") << QStringLiteral("search:/documents") << QStringLiteral("search:/images") << QStringLiteral("search:/audio") << QStringLiteral("search:/videos") - << QStringLiteral("/media/nfs") << QStringLiteral("/foreign") + << QStringLiteral("/foreign") << QStringLiteral("/media/floppy0") << QStringLiteral("/media/XO-Y4") << QStringLiteral("/media/cdrom"); } return urls; @@ -228,7 +232,8 @@ void PlacesItemModelTest::init() void PlacesItemModelTest::cleanup() { - for (int i : m_tobeRemoved) { + const auto tobeRemoved = m_tobeRemoved; + for (const int i : tobeRemoved) { int before = m_model->count(); m_model->deleteItem(i); QTRY_COMPARE(m_model->count(), before - 1); @@ -278,21 +283,22 @@ void PlacesItemModelTest::testGroups() { const auto groups = m_model->groups(); + QCOMPARE(groups.size(), 6); QCOMPARE(groups.at(0).first, 0); QCOMPARE(groups.at(0).second.toString(), QStringLiteral("Places")); - QCOMPARE(groups.at(1).first, 3); + QCOMPARE(groups.at(1).first, 5); QCOMPARE(groups.at(1).second.toString(), QStringLiteral("Remote")); - QCOMPARE(groups.at(2).first, 4); + QCOMPARE(groups.at(2).first, 7); QCOMPARE(groups.at(2).second.toString(), QStringLiteral("Recently Saved")); - QCOMPARE(groups.at(3).first, 8); + QCOMPARE(groups.at(3).first, 9); QCOMPARE(groups.at(3).second.toString(), QStringLiteral("Search For")); - QCOMPARE(groups.at(4).first, 12); + QCOMPARE(groups.at(4).first, 13); QCOMPARE(groups.at(4).second.toString(), QStringLiteral("Devices")); QCOMPARE(groups.at(5).first, 14); @@ -314,7 +320,7 @@ void PlacesItemModelTest::testPlaceItem_data() QTest::newRow("Baloo - Documents") << QUrl("search:/documents") << false << true << QStringLiteral("Search For") << false; // baloo - timeline - QTest::newRow("Baloo - Last Month") << QUrl("timeline:/lastmonth") << false << true << QStringLiteral("Recently Saved") << false; + QTest::newRow("Baloo - Today") << QUrl("timeline:/today") << false << true << QStringLiteral("Recently Saved") << false; // devices QTest::newRow("Devices - Floppy") << QUrl("file:///media/floppy0") << false << false << QStringLiteral("Removable Devices") << false; @@ -348,7 +354,7 @@ void PlacesItemModelTest::testDeletePlace() // create a new place createPlaceItem(QStringLiteral("Temporary Dir"), tempUrl, QString()); - urls.insert(3, tempUrl.toLocalFile()); + urls.insert(5, tempUrl.toLocalFile()); // check if the new entry was created QTRY_COMPARE(itemsInsertedSpy.count(), 1); @@ -356,7 +362,7 @@ void PlacesItemModelTest::testDeletePlace() QTRY_COMPARE(model->count(), m_model->count()); // delete item - m_model->deleteItem(3); + m_model->deleteItem(5); // make sure that the new item is removed QTRY_COMPARE(itemsRemovedSpy.count(), 1); @@ -421,7 +427,7 @@ void PlacesItemModelTest::testDefaultViewProperties_data() QTest::newRow("Places - Audio") << QUrl("search:/audio") << DolphinView::DetailsView << false << QList<QByteArray>({"text", "artist", "album"}); // baloo - timeline - QTest::newRow("Baloo - Last Month") << QUrl("timeline:/lastmonth") << DolphinView::DetailsView << true << QList<QByteArray>({"text", "modificationtime"}); + QTest::newRow("Baloo - Today") << QUrl("timeline:/today") << DolphinView::DetailsView << true << QList<QByteArray>({"text", "modificationtime"}); // devices QTest::newRow("Devices - Floppy") << QUrl("file:///media/floppy0") << DolphinView::IconsView << true << QList<QByteArray>({"text"}); @@ -515,28 +521,28 @@ void PlacesItemModelTest::testSystemItems() QTRY_COMPARE(itemsInsertedSpy.count(), 1); // make sure the new place get removed - removePlaceAfter(3); + removePlaceAfter(5); QList<QVariant> args = itemsInsertedSpy.takeFirst(); KItemRangeList range = args.at(0).value<KItemRangeList>(); - QCOMPARE(range.first().index, 3); + QCOMPARE(range.first().index, 5); QCOMPARE(range.first().count, 1); - QVERIFY(!m_model->placesItem(3)->isSystemItem()); + QVERIFY(!m_model->placesItem(5)->isSystemItem()); QCOMPARE(m_model->count(), 18); QTest::qWait(300); // check if the removal signal is correct QSignalSpy itemsRemovedSpy(m_model, &PlacesItemModel::itemsRemoved); - m_model->deleteItem(3); + m_model->deleteItem(5); QTRY_COMPARE(itemsRemovedSpy.count(), 1); args = itemsRemovedSpy.takeFirst(); range = args.at(0).value<KItemRangeList>(); - QCOMPARE(range.first().index, 3); + QCOMPARE(range.first().index, 5); QCOMPARE(range.first().count, 1); QTRY_COMPARE(m_model->count(), 17); //cancel removal (it was removed above) - cancelPlaceRemoval(3); + cancelPlaceRemoval(5); } void PlacesItemModelTest::testEditBookmark() @@ -546,7 +552,7 @@ void PlacesItemModelTest::testEditBookmark() createPlaceItem(QStringLiteral("Temporary Dir"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation)), QString()); // make sure that the new item will be removed later - removePlaceAfter(3); + removePlaceAfter(5); QSignalSpy itemsChangedSply(m_model, &PlacesItemModel::itemsChanged); @@ -582,7 +588,7 @@ void PlacesItemModelTest::testEditAfterCreation() QTRY_COMPARE(model->count(), m_model->count()); // make sure that the new item will be removed later - removePlaceAfter(3); + removePlaceAfter(5); // modify place text PlacesItem *item = m_model->placesItem(3); @@ -612,7 +618,7 @@ void PlacesItemModelTest::testEditMetadata() QTRY_COMPARE(model->count(), m_model->count()); // make sure that the new item will be removed later - removePlaceAfter(3); + removePlaceAfter(5); // modify place metadata PlacesItem *item = m_model->placesItem(3); @@ -642,10 +648,10 @@ void PlacesItemModelTest::testRefresh() QTRY_COMPARE(model->count(), m_model->count()); // make sure that the new item will be removed later - removePlaceAfter(3); + removePlaceAfter(5); - PlacesItem *item = m_model->placesItem(3); - PlacesItem *sameItem = model->placesItem(3); + PlacesItem *item = m_model->placesItem(5); + PlacesItem *sameItem = model->placesItem(5); QCOMPARE(item->text(), sameItem->text()); // modify place text @@ -673,7 +679,7 @@ void PlacesItemModelTest::testIcons_data() QTest::newRow("Baloo - Documents") << QUrl("search:/documents") << QStringLiteral("folder-text"); // baloo - timeline - QTest::newRow("Baloo - Last Month") << QUrl("timeline:/lastmonth") << QStringLiteral("view-calendar-month"); + QTest::newRow("Baloo - Today") << QUrl("timeline:/today") << QStringLiteral("go-jump-today"); // devices QTest::newRow("Devices - Floppy") << QUrl("file:///media/floppy0") << QStringLiteral("blockdevice"); @@ -701,10 +707,10 @@ void PlacesItemModelTest::testDragAndDrop() QSignalSpy itemsRemovedSpy(m_model, &PlacesItemModel::itemsRemoved); CHECK_PLACES_URLS(initialUrls()); - // Move the KDE_ROOT_PATH at the end of the places list will case it to be moved to the end of the places group - QMimeData *dropData = createMimeData(QList<int>() << 1); + // Move the home directory to the end of the places group + QMimeData *dropData = createMimeData(QList<int>() << 0); m_model->dropMimeDataBefore(m_model->count() - 1, dropData); - urls.move(1, 2); + urls.move(0, 4); delete dropData; QTRY_COMPARE(itemsInsertedSpy.count(), 1); @@ -715,24 +721,24 @@ void PlacesItemModelTest::testDragAndDrop() range = args.at(0).value<KItemRangeList>(); QCOMPARE(range.size(), 1); QCOMPARE(range.at(0).count, 1); - QCOMPARE(range.at(0).index, 1); + QCOMPARE(range.at(0).index, 0); // insert intem in his group args = itemsInsertedSpy.takeFirst(); range = args.at(0).value<KItemRangeList>(); QCOMPARE(range.size(), 1); QCOMPARE(range.at(0).count, 1); - QCOMPARE(range.at(0).index, 2); + QCOMPARE(range.at(0).index, 4); CHECK_PLACES_URLS(urls); itemsInsertedSpy.clear(); itemsRemovedSpy.clear(); - // Move the KDE_ROOT_PATH to his original position - dropData = createMimeData(QList<int>() << 2); - m_model->dropMimeDataBefore(1, dropData); - urls.move(2, 1); + // Move home directory item back to its original position + dropData = createMimeData(QList<int>() << 4); + m_model->dropMimeDataBefore(0, dropData); + urls.move(4, 0); delete dropData; QTRY_COMPARE(itemsInsertedSpy.count(), 1); @@ -743,14 +749,14 @@ void PlacesItemModelTest::testDragAndDrop() range = args.at(0).value<KItemRangeList>(); QCOMPARE(range.size(), 1); QCOMPARE(range.at(0).count, 1); - QCOMPARE(range.at(0).index, 2); + QCOMPARE(range.at(0).index, 4); // insert intem in the requested position args = itemsInsertedSpy.takeFirst(); range = args.at(0).value<KItemRangeList>(); QCOMPARE(range.size(), 1); QCOMPARE(range.at(0).count, 1); - QCOMPARE(range.at(0).index, 1); + QCOMPARE(range.at(0).index, 0); CHECK_PLACES_URLS(urls); } @@ -814,10 +820,10 @@ void PlacesItemModelTest::renameAfterCreation() // create a new place createPlaceItem(QStringLiteral("Temporary Dir"), tempUrl, QString()); - urls.insert(3, tempUrl.toLocalFile()); + urls.insert(5, tempUrl.toLocalFile()); // make sure that the new item will be removed later - removePlaceAfter(3); + removePlaceAfter(5); CHECK_PLACES_URLS(urls); QCOMPARE(model->count(), m_model->count()); diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp index acd66eb57..342c22638 100644 --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -634,10 +634,8 @@ void DolphinView::renameSelectedItems() this, &DolphinView::slotRoleEditingFinished); } else { RenameDialog* dialog = new RenameDialog(this, items); - connect(dialog, &RenameDialog::renamingFinished, this, &DolphinView::slotRenameDialogRenamingFinished); - dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); dialog->raise(); dialog->activateWindow(); diff --git a/src/views/dolphinviewactionhandler.cpp b/src/views/dolphinviewactionhandler.cpp index 2b3a241a3..95c140cc5 100644 --- a/src/views/dolphinviewactionhandler.cpp +++ b/src/views/dolphinviewactionhandler.cpp @@ -122,7 +122,7 @@ void DolphinViewActionHandler::createActions() QAction* deleteWithTrashShortcut = m_actionCollection->addAction(QStringLiteral("delete_shortcut")); // The descriptive text is just for the shortcuts editor. deleteWithTrashShortcut->setText(i18nc("@action \"Move to Trash\" for non-local files, etc.", "Delete (using shortcut for Trash)")); - m_actionCollection->setDefaultShortcut(deleteWithTrashShortcut, QKeySequence::Delete); + m_actionCollection->setDefaultShortcuts(deleteWithTrashShortcut, KStandardShortcut::moveToTrash()); deleteWithTrashShortcut->setEnabled(false); connect(deleteWithTrashShortcut, &QAction::triggered, this, &DolphinViewActionHandler::slotDeleteItems); diff --git a/src/views/renamedialog.cpp b/src/views/renamedialog.cpp index e49f56e2d..c9f9c177b 100644 --- a/src/views/renamedialog.cpp +++ b/src/views/renamedialog.cpp @@ -62,6 +62,7 @@ RenameDialog::RenameDialog(QWidget *parent, const KFileItemList& items) : m_okButton->setShortcut(Qt::CTRL + Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &RenameDialog::slotAccepted); connect(buttonBox, &QDialogButtonBox::rejected, this, &RenameDialog::reject); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QObject::deleteLater); m_okButton->setDefault(true); KGuiItem::assign(m_okButton, KGuiItem(i18nc("@action:button", "&Rename"), QStringLiteral("dialog-ok-apply"))); @@ -178,6 +179,7 @@ void RenameDialog::slotAccepted() KIO::FileUndoManager::self()->recordJob(cmdType, srcList, parentUrl, job); connect(job, &KJob::result, this, &RenameDialog::slotResult); + connect(job, &KJob::result, this, &QObject::deleteLater); job->uiDelegate()->setAutoErrorHandlingEnabled(true); diff --git a/src/views/renamedialog.h b/src/views/renamedialog.h index 5b04f857c..08571cd9d 100644 --- a/src/views/renamedialog.h +++ b/src/views/renamedialog.h @@ -33,6 +33,8 @@ class QPushButton; class KJob; /** * @brief Dialog for renaming a variable number of files. + * + * The dialog deletes itself when accepted or rejected. */ class DOLPHIN_EXPORT RenameDialog : public QDialog { diff --git a/src/views/versioncontrol/fileviewversioncontrolplugin.desktop b/src/views/versioncontrol/fileviewversioncontrolplugin.desktop index b42ed91a6..71d1603e6 100644 --- a/src/views/versioncontrol/fileviewversioncontrolplugin.desktop +++ b/src/views/versioncontrol/fileviewversioncontrolplugin.desktop @@ -3,6 +3,7 @@ Type=ServiceType 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[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 |
