diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -211,7 +211,6 @@ panels/places/placesview.cpp panels/panel.cpp panels/folders/foldersitemlistwidget.cpp - panels/folders/treeviewcontextmenu.cpp panels/folders/folderspanel.cpp panels/terminal/terminalpanel.cpp search/dolphinfacetswidget.cpp diff --git a/src/dolphincontextmenu.h b/src/dolphincontextmenu.h --- a/src/dolphincontextmenu.h +++ b/src/dolphincontextmenu.h @@ -70,7 +70,8 @@ DolphinContextMenu(DolphinMainWindow* parent, const QPoint& pos, const KFileItem& fileInfo, - const QUrl& baseUrl); + const QUrl& baseUrl, + const KFileItemList selection); ~DolphinContextMenu() override; diff --git a/src/dolphincontextmenu.cpp b/src/dolphincontextmenu.cpp --- a/src/dolphincontextmenu.cpp +++ b/src/dolphincontextmenu.cpp @@ -57,25 +57,22 @@ DolphinContextMenu::DolphinContextMenu(DolphinMainWindow* parent, const QPoint& pos, const KFileItem& fileInfo, - const QUrl& baseUrl) : + const QUrl& baseUrl, + const KFileItemList selection) : QMenu(parent), m_pos(pos), m_mainWindow(parent), m_fileInfo(fileInfo), m_baseUrl(baseUrl), m_baseFileItem(nullptr), - m_selectedItems(), + m_selectedItems(selection), m_selectedItemsProperties(nullptr), m_context(NoContext), m_copyToMenu(parent), m_customActions(), m_command(None), m_removeAction(nullptr) { - // The context menu either accesses the URLs of the selected items - // or the items itself. To increase the performance both lists are cached. - const DolphinView* view = m_mainWindow->activeViewContainer()->view(); - m_selectedItems = view->selectedItems(); } DolphinContextMenu::~DolphinContextMenu() diff --git a/src/dolphinmainwindow.h b/src/dolphinmainwindow.h --- a/src/dolphinmainwindow.h +++ b/src/dolphinmainwindow.h @@ -27,6 +27,8 @@ #include #include #include +#include +#include "kitemviews/kitemlistselectionmanager.h" #include #include @@ -43,16 +45,15 @@ class DolphinViewContainer; class DolphinRemoteEncoding; class DolphinTabWidget; -class KFileItem; -class KFileItemList; class KJob; class KNewFileMenu; class KHelpMenu; class KToolBarPopupAction; class QToolButton; class QIcon; class PlacesPanel; class TerminalPanel; +class KFileItemListView; /** * @short Main window for Dolphin. @@ -136,14 +137,6 @@ */ bool isUrlOpen(const QString &url); - - /** - * Pastes the clipboard data into the currently selected folder - * of the active view. If not exactly one folder is selected, - * no pasting is done at all. - */ - void pasteIntoFolder(); - /** * Implementation of the MainWindowAdaptor/QDBusAbstractAdaptor interface. * Inform all affected dolphin components (panels, views) of an URL @@ -171,6 +164,13 @@ */ void openNewTabAfterLastTab(const QUrl& url); + /** + * Pastes the clipboard data into the currently selected folder + * of the active view. If not exactly one folder is selected, + * no pasting is done at all. + */ + void pasteIntoFolder(); + signals: /** * Is sent if the selection of the currently active view has @@ -242,18 +242,6 @@ /** Performs the current undo operation. */ void undo(); - /** - * Copies all selected items to the clipboard and marks - * the items as cut. - */ - void cut(); - - /** Copies all selected items to the clipboard. */ - void copy(); - - /** Pastes the clipboard data to the active view. */ - void paste(); - /** Replaces the URL navigator by a search box to find files. */ void find(); @@ -460,6 +448,23 @@ const KFileItem& item, const QUrl& url, const QList& customActions); + /** + * Opens the context menu on the current mouse position. + * @pos Position in screen coordinates. + * @item File item context. If item is null, the context menu + * should be applied to \a url. + * @url URL which contains \a item. + * @selection The selection manager for this action + * @view The optional view the selection belongs to + * @customActions Actions that should be added to the context menu, + * if the file item is null. + */ + void openContextMenuWithSelection(const QPoint& pos, + const KFileItem& item, + const QUrl& url, + const QList& customActions, + KItemListSelectionManager* selectionManager, + KFileItemListView* view = nullptr); void updateControlMenu(); void updateToolBar(); @@ -619,6 +624,9 @@ /** Returns preferred search tool as configured in "More Search Tools" menu. */ QPointer preferredSearchTool(); + KFileItemList selectedItems(); + KItemSet selectedItemSet(); + private: /** * Implements a custom error handling for the undo manager. This @@ -658,6 +666,7 @@ QMenu m_searchTools; + KItemListSelectionManager* m_currentSelectionManager = nullptr; }; inline DolphinViewContainer* DolphinMainWindow::activeViewContainer() const diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -45,12 +45,16 @@ #include "views/viewproperties.h" #include "views/dolphinnewfilemenuobserver.h" #include "dolphin_generalsettings.h" +#include "kitemviews/kitemlistselectionmanager.h" +#include "kitemviews/kfileitemmodel.h" +#include "kitemviews/kfileitemlistview.h" #include #include #include #include #include +#include #include #include #include @@ -73,6 +77,7 @@ #include #include #include +#include #include #include @@ -162,12 +167,12 @@ this, &DolphinMainWindow::updateWindowTitle); setCentralWidget(m_tabWidget); - setupActions(); - m_actionHandler = new DolphinViewActionHandler(actionCollection(), this); connect(m_actionHandler, &DolphinViewActionHandler::actionBeingHandled, this, &DolphinMainWindow::clearStatusBar); connect(m_actionHandler, &DolphinViewActionHandler::createDirectoryTriggered, this, &DolphinMainWindow::createDirectory); + setupActions(); + m_remoteEncoding = new DolphinRemoteEncoding(this, m_actionHandler); connect(this, &DolphinMainWindow::urlChanged, m_remoteEncoding, &DolphinRemoteEncoding::slotAboutToOpenUrl); @@ -279,7 +284,7 @@ void DolphinMainWindow::pasteIntoFolder() { - m_activeViewContainer->view()->pasteIntoFolder(); + m_actionHandler->pasteIntoFolder(); } void DolphinMainWindow::changeUrl(const QUrl &url) @@ -378,11 +383,11 @@ QString name; // If nothing is selected, act on the current dir - if (m_activeViewContainer->view()->selectedItems().isEmpty()) { + if (selectedItems().isEmpty()) { url = m_activeViewContainer->url(); name = m_activeViewContainer->placesText(); } else { - const auto dirToAdd = m_activeViewContainer->view()->selectedItems().first(); + const auto dirToAdd = selectedItems().first(); url = dirToAdd.url(); name = dirToAdd.name(); } @@ -415,10 +420,10 @@ void DolphinMainWindow::openInNewTab() { - const KFileItemList& list = m_activeViewContainer->view()->selectedItems(); + const KFileItemList& list = selectedItems(); bool tabCreated = false; - foreach (const KFileItem& item, list) { + for (const KFileItem& item : list) { const QUrl& url = DolphinView::openItemAsFolderUrl(item); if (!url.isEmpty()) { openNewTabAfterCurrentTab(url); @@ -437,7 +442,7 @@ { QUrl newWindowUrl; - const KFileItemList list = m_activeViewContainer->view()->selectedItems(); + const KFileItemList list = selectedItems(); if (list.isEmpty()) { newWindowUrl = m_activeViewContainer->url(); } else if (list.count() == 1) { @@ -452,7 +457,7 @@ void DolphinMainWindow::showTarget() { - const auto link = m_activeViewContainer->view()->selectedItems().at(0); + const auto link = selectedItems().at(0); const auto linkLocationDir = QFileInfo(link.localPath()).absoluteDir(); auto linkDestination = link.linkDest(); if (QFileInfo(linkDestination).isRelative()) { @@ -643,19 +648,23 @@ KIO::FileUndoManager::self()->undo(); } -void DolphinMainWindow::cut() +KItemSet DolphinMainWindow::selectedItemSet() { - m_activeViewContainer->view()->cutSelectedItems(); + return m_currentSelectionManager->selectedItems(); } -void DolphinMainWindow::copy() -{ - m_activeViewContainer->view()->copySelectedItems(); -} -void DolphinMainWindow::paste() +KFileItemList DolphinMainWindow::selectedItems() { - m_activeViewContainer->view()->paste(); + KFileItemList selectedItems; + const auto items = selectedItemSet(); + const auto model = qobject_cast(m_currentSelectionManager->model()); + + selectedItems.reserve(items.count()); + for (int index : items) { + selectedItems.append(model->fileItem(index)); + } + return selectedItems; } void DolphinMainWindow::find() @@ -1090,7 +1099,28 @@ const QUrl& url, const QList& customActions) { - QPointer contextMenu = new DolphinContextMenu(this, pos, item, url); + openContextMenuWithSelection(pos, item, url, customActions, activeViewContainer()->view()->selectionManager()); +} + +void DolphinMainWindow::openContextMenuWithSelection(const QPoint& pos, + const KFileItem& item, + const QUrl& url, + const QList& customActions, + KItemListSelectionManager* selectionManager, + KFileItemListView* view) +{ + m_currentSelectionManager = selectionManager; + updateFileAndEditActions(); + m_actionHandler->setCurrentSelectionManager(m_currentSelectionManager); + m_actionHandler->setCurrentItemListView(view); + + auto* model = qobject_cast(m_currentSelectionManager->model()); + KFileItemList list; + for (auto i : selectionManager->selectedItems()) { + list.append(model->fileItem(i)); + } + + QPointer contextMenu = new DolphinContextMenu(this, pos, item, url, list); contextMenu.data()->setCustomActions(customActions); const DolphinContextMenu::Command command = contextMenu.data()->open(); @@ -1118,6 +1148,10 @@ if (contextMenu) { contextMenu->deleteLater(); } + + m_currentSelectionManager = activeViewContainer()->view()->selectionManager(); + m_actionHandler->setCurrentSelectionManager(m_currentSelectionManager); + m_actionHandler->setCurrentItemListView(nullptr); } void DolphinMainWindow::updateControlMenu() @@ -1225,6 +1259,7 @@ Q_ASSERT(viewContainer); m_activeViewContainer = viewContainer; + m_currentSelectionManager = viewContainer->view()->selectionManager(); if (oldViewContainer) { const QAction* toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search")); @@ -1352,18 +1387,18 @@ "keyboard shortcuts are prominently placed right " "next to each other on the keyboard: Ctrl+X, " "Ctrl+C and Ctrl+V."); - QAction* cutAction = KStandardAction::cut(this, &DolphinMainWindow::cut, actionCollection()); + QAction* cutAction = KStandardAction::cut(m_actionHandler, &DolphinViewActionHandler::cut, actionCollection()); cutAction->setWhatsThis(xi18nc("@info:whatsthis cut", "This copies the items " "in your current selection to the clipboard." "Use the Paste action afterwards to copy them from " "the clipboard to a new location. The items will be removed from their " "initial location.") + cutCopyPastePara); - QAction* copyAction = KStandardAction::copy(this, &DolphinMainWindow::copy, actionCollection()); + QAction* copyAction = KStandardAction::copy(m_actionHandler, &DolphinViewActionHandler::copy, actionCollection()); copyAction->setWhatsThis(xi18nc("@info:whatsthis copy", "This copies the " "items in your current selection to the clipboard." "Use the Paste action afterwards to copy them " "from the clipboard to a new location.") + cutCopyPastePara); - QAction* paste = KStandardAction::paste(this, &DolphinMainWindow::paste, actionCollection()); + QAction* paste = KStandardAction::paste(m_actionHandler, &DolphinViewActionHandler::paste, actionCollection()); // The text of the paste-action is modified dynamically by Dolphin // (e. g. to "Paste One Folder"). To prevent that the size of the toolbar changes // due to the long text, the text "Paste" is used: @@ -1741,6 +1776,20 @@ this, &DolphinMainWindow::openNewTabAfterCurrentTab); connect(foldersPanel, &FoldersPanel::errorMessage, this, &DolphinMainWindow::showErrorMessage); + connect(foldersPanel, &FoldersPanel::requestOpenContextMenu, + this, &DolphinMainWindow::openContextMenuWithSelection); + connect(foldersPanel, &FoldersPanel::focused, + this, [=] (){ + m_currentSelectionManager = foldersPanel->selectionManager(); + m_actionHandler->setCurrentSelectionManager(m_currentSelectionManager); + m_actionHandler->setCurrentItemListView(foldersPanel->view()); + }); + connect(foldersPanel, &FoldersPanel::focusedOut, + this, [=] (){ + m_currentSelectionManager = m_activeViewContainer->view()->selectionManager(); + m_actionHandler->setCurrentSelectionManager(m_currentSelectionManager); + m_actionHandler->setCurrentItemListView(nullptr); + }); actionCollection()->action(QStringLiteral("show_folders_panel")) ->setWhatsThis(xi18nc("@info:whatsthis", "This toggles the " @@ -1896,7 +1945,7 @@ void DolphinMainWindow::updateFileAndEditActions() { - const KFileItemList list = m_activeViewContainer->view()->selectedItems(); + const KFileItemList list = selectedItems(); const KActionCollection* col = actionCollection(); QAction* addToPlacesAction = col->action(QStringLiteral("add_to_places")); diff --git a/src/dolphinpart.h b/src/dolphinpart.h --- a/src/dolphinpart.h +++ b/src/dolphinpart.h @@ -84,6 +84,8 @@ /// Returns the view owned by this part; used by DolphinPartBrowserExtension DolphinView* view() { return m_view; } + DolphinViewActionHandler* actionHandler() { return m_actionHandler; } + /** * Sets a name filter, like *.diff */ diff --git a/src/dolphinpart_ext.cpp b/src/dolphinpart_ext.cpp --- a/src/dolphinpart_ext.cpp +++ b/src/dolphinpart_ext.cpp @@ -21,6 +21,7 @@ #include "dolphinpart.h" #include "views/dolphinview.h" +#include "views/dolphinviewactionhandler.h" DolphinPartBrowserExtension::DolphinPartBrowserExtension(DolphinPart* part) :KParts::BrowserExtension( part ) @@ -43,22 +44,22 @@ void DolphinPartBrowserExtension::cut() { - m_part->view()->cutSelectedItems(); + m_part->actionHandler()->cut(); } void DolphinPartBrowserExtension::copy() { - m_part->view()->copySelectedItems(); + m_part->actionHandler()->copy(); } void DolphinPartBrowserExtension::paste() { - m_part->view()->paste(); + m_part->actionHandler()->paste(); } void DolphinPartBrowserExtension::pasteTo(const QUrl&) { - m_part->view()->pasteIntoFolder(); + m_part->actionHandler()->pasteIntoFolder(); } void DolphinPartBrowserExtension::reparseConfiguration() diff --git a/src/panels/folders/folderspanel.h b/src/panels/folders/folderspanel.h --- a/src/panels/folders/folderspanel.h +++ b/src/panels/folders/folderspanel.h @@ -28,6 +28,9 @@ class KItemListController; class QGraphicsSceneDragDropEvent; class KFileItem; +class KItemListSelectionManager; +class KFileItemListView; + /** * @brief Shows a tree view of the directories starting from * the currently selected place. @@ -51,13 +54,34 @@ void setAutoScrolling(bool enable); bool autoScrolling() const; + KItemListSelectionManager* selectionManager(); + KFileItemListView* view(); + void rename(const KFileItem& item); signals: void folderActivated(const QUrl& url); void folderMiddleClicked(const QUrl& url); void errorMessage(const QString& error); + /** + * Use it to ask the main view to open a context menu + */ + void requestOpenContextMenu(const QPoint& pos, + const KFileItem& item, + const QUrl& url, + const QList& customActions, + KItemListSelectionManager* selection, + KFileItemListView* view); + + void focused(); + void focusedOut(); + +public slots: + + /** Sends focused/focusedOut if the item list container gets focus/unfocused. */ + bool eventFilter(QObject* watched, QEvent* event) override; + protected: /** @see Panel::urlChanged() */ bool urlChanged() override; @@ -84,7 +108,6 @@ */ void startFadeInAnimation(); - private: /** * Indicate if it is allowed to leave current location. @@ -110,6 +133,8 @@ void updateCurrentItem(int index); private: + QList folderPanelContextMenuActions(); + bool m_updateCurrentItem; KItemListController* m_controller; KFileItemModel* m_model; diff --git a/src/panels/folders/folderspanel.cpp b/src/panels/folders/folderspanel.cpp --- a/src/panels/folders/folderspanel.cpp +++ b/src/panels/folders/folderspanel.cpp @@ -28,15 +28,17 @@ #include "kitemviews/kitemlistcontainer.h" #include "kitemviews/kitemlistcontroller.h" #include "kitemviews/kitemlistselectionmanager.h" -#include "treeviewcontextmenu.h" #include "views/draganddrophelper.h" #include #include #include #include #include #include +#include +#include +#include #include #include @@ -180,6 +182,7 @@ QVBoxLayout* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(container); + container->installEventFilter(this); } loadTree(url()); @@ -196,6 +199,33 @@ } } +/** Activates the view if the item list container gets focus. */ +bool FoldersPanel::eventFilter(QObject* watched, QEvent* event) +{ + Q_UNUSED(watched) + switch (event->type()) { + case QEvent::FocusOut: + focusedOut(); + break; + case QEvent::FocusIn: + focused(); + break; + default: + break; + } + return false; +} + +KItemListSelectionManager* FoldersPanel::selectionManager() +{ + return m_controller->selectionManager(); +} + +KFileItemListView* FoldersPanel::view() +{ + return qobject_cast(m_controller->view()); +} + void FoldersPanel::slotItemActivated(int index) { const KFileItem item = m_model->fileItem(index); @@ -212,23 +242,64 @@ } } +QList FoldersPanel::folderPanelContextMenuActions() +{ + QList actions; + + QAction* showHiddenFilesAction = new QAction(i18nc("@action:inmenu", "Show Hidden Files"), this); + showHiddenFilesAction->setCheckable(true); + showHiddenFilesAction->setChecked(showHiddenFiles()); + actions.append(showHiddenFilesAction); + connect(showHiddenFilesAction, &QAction::toggled, this, &FoldersPanel::setShowHiddenFiles); + + // insert 'Limit to Home Directory' + QAction* limitFoldersPanelToHomeAction = new QAction(i18nc("@action:inmenu", "Limit to Home Directory"), this); + limitFoldersPanelToHomeAction->setCheckable(true); + limitFoldersPanelToHomeAction->setChecked(limitFoldersPanelToHome()); + actions.append(limitFoldersPanelToHomeAction); + connect(limitFoldersPanelToHomeAction, &QAction::toggled, this, &FoldersPanel::setLimitFoldersPanelToHome); + + // TODO: Temporary disabled. Horizontal autoscrolling will be implemented later either + // in KItemViews or manually as part of the FoldersPanel + /* + // insert 'Automatic Scrolling' + QAction* autoScrollingAction = new QAction(i18nc("@action:inmenu", "Automatic Scrolling"), this); + autoScrollingAction->setCheckable(true); + autoScrollingAction->setChecked(autoScrolling()); + connect(autoScrollingAction, &QAction::toggled, this, &FoldersPanel::setAutoScrolling); + actions->addAction(autoScrollingAction); + */ + + const QList customActions = customContextMenuActions(); + if (!customActions.isEmpty()) { + QAction *separatorAction = new QAction(); + separatorAction->setSeparator(true); + actions.append(separatorAction); + for (QAction* action: customActions) { + actions.append(action); + } + } + + return actions; +} + void FoldersPanel::slotItemContextMenuRequested(int index, const QPointF& pos) { const KFileItem fileItem = m_model->fileItem(index); - QPointer contextMenu = new TreeViewContextMenu(this, fileItem); - contextMenu.data()->open(pos.toPoint()); - if (contextMenu.data()) { - delete contextMenu.data(); - } + const auto view = qobject_cast(m_controller->view()); + requestOpenContextMenu(pos.toPoint(), fileItem, fileItem.url(), folderPanelContextMenuActions(), m_controller->selectionManager(), view); } void FoldersPanel::slotViewContextMenuRequested(const QPointF& pos) { - QPointer contextMenu = new TreeViewContextMenu(this, KFileItem()); - contextMenu.data()->open(pos.toPoint()); - if (contextMenu.data()) { - delete contextMenu.data(); + QMenu* popup = new QMenu(this); + popup->addActions(folderPanelContextMenuActions()); + + QPointer popupPtr = popup; + popup->exec(pos.toPoint()); + if (popupPtr.data()) { + popupPtr.data()->deleteLater(); } } diff --git a/src/panels/folders/treeviewcontextmenu.h b/src/panels/folders/treeviewcontextmenu.h deleted file mode 100644 --- a/src/panels/folders/treeviewcontextmenu.h +++ /dev/null @@ -1,102 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006 by Peter Penz * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - ***************************************************************************/ - -#ifndef TREEVIEWCONTEXTMENU_H -#define TREEVIEWCONTEXTMENU_H - -#include - -#include - -class QMimeData; -class FoldersPanel; - -/** - * @brief Represents the context menu which appears when doing a right - * click on an item of the treeview. - */ -class TreeViewContextMenu : public QObject -{ - Q_OBJECT - -public: - /** - * @parent Pointer to the folders panel the context menu - * belongs to. - * @fileInfo Pointer to the file item the context menu - * is applied. If 0 is passed, the context menu - * is above the viewport. - */ - TreeViewContextMenu(FoldersPanel* parent, - const KFileItem& fileInfo); - - ~TreeViewContextMenu() override; - - /** Opens the context menu modal. */ - void open(const QPoint& pos); - -private slots: - /** Cuts the item m_fileItem. */ - void cut(); - - /** Copies the item m_fileItem. */ - void copy(); - - /** Paste the clipboard to m_fileItem. */ - void paste(); - - /** Renames the item m_fileItem. */ - void rename(); - - /** Moves the item m_fileItem to the trash. */ - void moveToTrash(); - - /** Deletes the item m_fileItem. */ - void deleteItem(); - - /** Shows the properties of the item m_fileItem. */ - void showProperties(); - - /** - * Sets the 'Show Hidden Files' setting for the - * folders panel to \a show. - */ - void setShowHiddenFiles(bool show); - - /** - * Sets the 'Limit folders panel to home' setting for the - * folders panel to \a enable. - */ - void setLimitFoldersPanelToHome(bool enable); - - /** - * Sets the 'Automatic Scrolling' setting for the - * folders panel to \a enable. - */ - void setAutoScrolling(bool enable); - -private: - void populateMimeData(QMimeData* mimeData, bool cut); - -private: - FoldersPanel* m_parent; - KFileItem m_fileItem; -}; - -#endif diff --git a/src/panels/folders/treeviewcontextmenu.cpp b/src/panels/folders/treeviewcontextmenu.cpp deleted file mode 100644 --- a/src/panels/folders/treeviewcontextmenu.cpp +++ /dev/null @@ -1,252 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006-2010 by Peter Penz * - * Copyright (C) 2006 by Cvetoslav Ludmiloff * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - ***************************************************************************/ - -#include "treeviewcontextmenu.h" - -#include "folderspanel.h" -#include "global.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -TreeViewContextMenu::TreeViewContextMenu(FoldersPanel* parent, - const KFileItem& fileInfo) : - QObject(parent), - m_parent(parent), - m_fileItem(fileInfo) -{ -} - -TreeViewContextMenu::~TreeViewContextMenu() -{ -} - -void TreeViewContextMenu::open(const QPoint& pos) -{ - QMenu* popup = new QMenu(m_parent); - - if (!m_fileItem.isNull()) { - KFileItemListProperties capabilities(KFileItemList() << m_fileItem); - - // insert 'Cut', 'Copy' and 'Paste' - QAction* cutAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-cut")), i18nc("@action:inmenu", "Cut"), this); - cutAction->setEnabled(capabilities.supportsMoving()); - connect(cutAction, &QAction::triggered, this, &TreeViewContextMenu::cut); - - QAction* copyAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18nc("@action:inmenu", "Copy"), this); - connect(copyAction, &QAction::triggered, this, &TreeViewContextMenu::copy); - - const QMimeData *mimeData = QApplication::clipboard()->mimeData(); - bool canPaste; - const QString text = KIO::pasteActionText(mimeData, &canPaste, m_fileItem); - QAction* pasteAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-paste")), text, this); - connect(pasteAction, &QAction::triggered, this, &TreeViewContextMenu::paste); - pasteAction->setEnabled(canPaste); - - popup->addAction(cutAction); - popup->addAction(copyAction); - popup->addAction(pasteAction); - popup->addSeparator(); - - // insert 'Rename' - QAction* renameAction = new QAction(i18nc("@action:inmenu", "Rename..."), this); - renameAction->setEnabled(capabilities.supportsMoving()); - renameAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename"))); - connect(renameAction, &QAction::triggered, this, &TreeViewContextMenu::rename); - popup->addAction(renameAction); - - // insert 'Move to Trash' and (optionally) 'Delete' - KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::IncludeGlobals); - KConfigGroup configGroup(globalConfig, "KDE"); - bool showDeleteCommand = configGroup.readEntry("ShowDeleteCommand", false); - - const QUrl url = m_fileItem.url(); - if (url.isLocalFile()) { - QAction* moveToTrashAction = new QAction(QIcon::fromTheme(QStringLiteral("user-trash")), - i18nc("@action:inmenu", "Move to Trash"), this); - const bool enableMoveToTrash = capabilities.isLocal() && capabilities.supportsMoving(); - moveToTrashAction->setEnabled(enableMoveToTrash); - connect(moveToTrashAction, &QAction::triggered, this, &TreeViewContextMenu::moveToTrash); - popup->addAction(moveToTrashAction); - } else { - showDeleteCommand = true; - } - - if (showDeleteCommand) { - QAction* deleteAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@action:inmenu", "Delete"), this); - deleteAction->setEnabled(capabilities.supportsDeleting()); - connect(deleteAction, &QAction::triggered, this, &TreeViewContextMenu::deleteItem); - popup->addAction(deleteAction); - } - - popup->addSeparator(); - } - - // insert 'Show Hidden Files' - QAction* showHiddenFilesAction = new QAction(i18nc("@action:inmenu", "Show Hidden Files"), this); - showHiddenFilesAction->setCheckable(true); - showHiddenFilesAction->setChecked(m_parent->showHiddenFiles()); - popup->addAction(showHiddenFilesAction); - connect(showHiddenFilesAction, &QAction::toggled, this, &TreeViewContextMenu::setShowHiddenFiles); - - if (!m_fileItem.isNull()) { - // insert 'Limit to Home Directory' - const QUrl url = m_fileItem.url(); - const bool enableLimitToHomeDirectory = url.isLocalFile(); - QAction* limitFoldersPanelToHomeAction = new QAction(i18nc("@action:inmenu", "Limit to Home Directory"), this); - limitFoldersPanelToHomeAction->setCheckable(true); - limitFoldersPanelToHomeAction->setEnabled(enableLimitToHomeDirectory); - limitFoldersPanelToHomeAction->setChecked(m_parent->limitFoldersPanelToHome()); - popup->addAction(limitFoldersPanelToHomeAction); - connect(limitFoldersPanelToHomeAction, &QAction::toggled, this, &TreeViewContextMenu::setLimitFoldersPanelToHome); - } - - // insert 'Automatic Scrolling' - QAction* autoScrollingAction = new QAction(i18nc("@action:inmenu", "Automatic Scrolling"), this); - autoScrollingAction->setCheckable(true); - autoScrollingAction->setChecked(m_parent->autoScrolling()); - // TODO: Temporary disabled. Horizontal autoscrolling will be implemented later either - // in KItemViews or manually as part of the FoldersPanel - //popup->addAction(autoScrollingAction); - connect(autoScrollingAction, &QAction::toggled, this, &TreeViewContextMenu::setAutoScrolling); - - if (!m_fileItem.isNull()) { - // insert 'Properties' entry - QAction* propertiesAction = new QAction(i18nc("@action:inmenu", "Properties"), this); - propertiesAction->setIcon(QIcon::fromTheme(QStringLiteral("document-properties"))); - connect(propertiesAction, &QAction::triggered, this, &TreeViewContextMenu::showProperties); - popup->addAction(propertiesAction); - } - - QList customActions = m_parent->customContextMenuActions(); - if (!customActions.isEmpty()) { - popup->addSeparator(); - foreach (QAction* action, customActions) { - popup->addAction(action); - } - } - - QPointer popupPtr = popup; - popup->exec(pos); - if (popupPtr.data()) { - popupPtr.data()->deleteLater(); - } -} - -void TreeViewContextMenu::populateMimeData(QMimeData* mimeData, bool cut) -{ - QList kdeUrls; - kdeUrls.append(m_fileItem.url()); - QList mostLocalUrls; - bool dummy; - mostLocalUrls.append(m_fileItem.mostLocalUrl(&dummy)); - KIO::setClipboardDataCut(mimeData, cut); - KUrlMimeData::setUrls(kdeUrls, mostLocalUrls, mimeData); -} - -void TreeViewContextMenu::cut() -{ - QMimeData* mimeData = new QMimeData(); - populateMimeData(mimeData, true); - QApplication::clipboard()->setMimeData(mimeData); -} - -void TreeViewContextMenu::copy() -{ - QMimeData* mimeData = new QMimeData(); - populateMimeData(mimeData, false); - QApplication::clipboard()->setMimeData(mimeData); -} - -void TreeViewContextMenu::paste() -{ - KIO::PasteJob *job = KIO::paste(QApplication::clipboard()->mimeData(), m_fileItem.url()); - KJobWidgets::setWindow(job, m_parent); -} - -void TreeViewContextMenu::rename() -{ - m_parent->rename(m_fileItem); -} - -void TreeViewContextMenu::moveToTrash() -{ - const QList list{m_fileItem.url()}; - KIO::JobUiDelegate uiDelegate; - uiDelegate.setWindow(m_parent); - if (uiDelegate.askDeleteConfirmation(list, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation)) { - KIO::Job* job = KIO::trash(list); - KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Trash, list, QUrl(QStringLiteral("trash:/")), job); - KJobWidgets::setWindow(job, m_parent); - job->uiDelegate()->setAutoErrorHandlingEnabled(true); - } -} - -void TreeViewContextMenu::deleteItem() -{ - const QList list{m_fileItem.url()}; - KIO::JobUiDelegate uiDelegate; - uiDelegate.setWindow(m_parent); - if (uiDelegate.askDeleteConfirmation(list, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::DefaultConfirmation)) { - KIO::Job* job = KIO::del(list); - KJobWidgets::setWindow(job, m_parent); - job->uiDelegate()->setAutoErrorHandlingEnabled(true); - } -} - -void TreeViewContextMenu::showProperties() -{ - KPropertiesDialog* dialog = new KPropertiesDialog(m_fileItem.url(), m_parent); - dialog->setAttribute(Qt::WA_DeleteOnClose); - dialog->show(); -} - -void TreeViewContextMenu::setShowHiddenFiles(bool show) -{ - m_parent->setShowHiddenFiles(show); -} - -void TreeViewContextMenu::setLimitFoldersPanelToHome(bool enable) -{ - m_parent->setLimitFoldersPanelToHome(enable); -} - -void TreeViewContextMenu::setAutoScrolling(bool enable) -{ - m_parent->setAutoScrolling(enable); -} - diff --git a/src/panels/panel.h b/src/panels/panel.h --- a/src/panels/panel.h +++ b/src/panels/panel.h @@ -24,6 +24,8 @@ #include #include +class KFileItem; + /** * @brief Base widget for all panels that can be docked on the window borders. * diff --git a/src/views/dolphinview.h b/src/views/dolphinview.h --- a/src/views/dolphinview.h +++ b/src/views/dolphinview.h @@ -47,6 +47,7 @@ class ViewProperties; class QGraphicsSceneDragDropEvent; class QRegExp; +class KItemListSelectionManager; /** * @short Represents a view for the directory content. @@ -168,6 +169,10 @@ */ int selectedItemsCount() const; + KItemListSelectionManager* selectionManager() const; + + DolphinItemListView* listView() const; + /** * Marks the items indicated by \p urls to get selected after the * directory DolphinView::url() has been loaded. Note that nothing @@ -317,6 +322,19 @@ */ void hideToolTip(const ToolTipManager::HideBehavior behavior = ToolTipManager::HideBehavior::Later); + /** + * Call this before pasting into the view + */ + void prepareToPaste(); + + /** + * Clears the selection and updates current item and selection according to the parameters + * + * @param current URL to be set as current + * @param selected list of selected items + */ + void forceUrlsSelection(const QUrl& current, const QList& selected); + public slots: /** * Changes the directory to \a url. If the current directory is equal to @@ -340,61 +358,45 @@ void clearSelection(); /** - * Triggers the renaming of the currently selected items, where - * the user must input a new name for the items. - */ - void renameSelectedItems(); - - /** - * Moves all selected items to the trash. + * Handles a drop of @p dropEvent onto widget @p dropWidget and destination @p destUrl */ - void trashSelectedItems(); + void dropUrls(const QUrl &destUrl, QDropEvent *dropEvent, QWidget *dropWidget); - /** - * Deletes all selected items. - */ - void deleteSelectedItems(); + void stopLoading(); /** - * Copies all selected items to the clipboard and marks - * the items as cut. + * Applies the state that has been restored by restoreViewState() + * to the view. */ - void cutSelectedItems(); - - /** Copies all selected items to the clipboard. */ - void copySelectedItems(); + void updateViewState(); - /** Pastes the clipboard data to this view. */ - void paste(); + /** Activates the view if the item list container gets focus. */ + bool eventFilter(QObject* watched, QEvent* event) override; /** - * Pastes the clipboard data into the currently selected - * folder. If the current selection is not exactly one folder, no - * paste operation is done. + * Indicates in the status bar that the delete operation + * of the job \a job has been finished. */ - void pasteIntoFolder(); + void slotDeleteFileFinished(KJob* job); /** - * Creates duplicates of selected items, appending "copy" - * to the end. + * Indicates in the status bar that the trash operation + * of the job \a job has been finished. */ - void duplicateSelectedItems(); + void slotTrashFileFinished(KJob* job); - /** - * Handles a drop of @p dropEvent onto widget @p dropWidget and destination @p destUrl + /* + * Is called when new items get pasted or dropped. */ - void dropUrls(const QUrl &destUrl, QDropEvent *dropEvent, QWidget *dropWidget); - - void stopLoading(); - - /** - * Applies the state that has been restored by restoreViewState() - * to the view. + void slotItemCreated(const QUrl &url); + /* + * Is called after all pasted or dropped items have been copied to destination. */ - void updateViewState(); + void slotPasteJobResult(KJob *job); - /** Activates the view if the item list container gets focus. */ - bool eventFilter(QObject* watched, QEvent* event) override; + void slotRoleEditingCanceled(); + void slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value); + void slotRenameDialogRenamingFinished(const QList& urls); signals: /** @@ -574,6 +576,11 @@ */ void urlActivated(const QUrl& url); + /** + * Request that the selected items renaming process is launched + */ + void requestRenameSelectedItems(); + protected: /** Changes the zoom level if Control is pressed during a wheel event. */ void wheelEvent(QWheelEvent* event) override; @@ -600,18 +607,8 @@ void slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event); void slotModelChanged(KItemModelBase* current, KItemModelBase* previous); void slotMouseButtonPressed(int itemIndex, Qt::MouseButtons buttons); - void slotRenameDialogRenamingFinished(const QList& urls); void slotSelectedItemTextPressed(int index); - /* - * Is called when new items get pasted or dropped. - */ - void slotItemCreated(const QUrl &url); - /* - * Is called after all pasted or dropped items have been copied to destination. - */ - void slotPasteJobResult(KJob *job); - /** * Emits the signal \a selectionChanged() with a small delay. This is * because getting all file items for the selection can be an expensive @@ -645,18 +642,6 @@ */ void updateSortFoldersFirst(bool foldersFirst); - /** - * Indicates in the status bar that the delete operation - * of the job \a job has been finished. - */ - void slotDeleteFileFinished(KJob* job); - - /** - * Indicates in the status bar that the trash operation - * of the job \a job has been finished. - */ - void slotTrashFileFinished(KJob* job); - /** * Invoked when the rename job is done, for error handling. */ @@ -674,11 +659,6 @@ */ void slotDirectoryLoadingCompleted(); - /** - * Is invoked when items of KFileItemModel have been changed. - */ - void slotItemsChanged(); - /** * Is invoked when the sort order has been changed by the user by clicking * on a header item. The view properties of the directory will get updated. @@ -698,9 +678,6 @@ void slotVisibleRolesChangedByHeader(const QList& current, const QList& previous); - void slotRoleEditingCanceled(); - void slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value); - /** * Observes the item with the URL \a url. As soon as the directory * model indicates that the item is available, the item will @@ -780,22 +757,13 @@ */ QUrl viewPropertiesUrl() const; - /** - * Clears the selection and updates current item and selection according to the parameters - * - * @param current URL to be set as current - * @param selected list of selected items - */ - void forceUrlsSelection(const QUrl& current, const QList& selected); - void abortTwoClicksRenaming(); private: void updatePalette(); bool m_active; bool m_tabsForFiles; - bool m_assureVisibleCurrentIndex; bool m_isFolderWritable; bool m_dragging; // True if a dragging is done. Required to be able to decide whether a // tooltip may be shown when hovering an item. diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -75,7 +75,6 @@ QWidget(parent), m_active(true), m_tabsForFiles(false), - m_assureVisibleCurrentIndex(false), m_isFolderWritable(true), m_dragging(false), m_url(url), @@ -153,8 +152,6 @@ connect(m_model, &KFileItemModel::directoryLoadingCanceled, this, &DolphinView::directoryLoadingCanceled); connect(m_model, &KFileItemModel::directoryLoadingProgress, this, &DolphinView::directoryLoadingProgress); connect(m_model, &KFileItemModel::directorySortingProgress, this, &DolphinView::directorySortingProgress); - connect(m_model, &KFileItemModel::itemsChanged, - this, &DolphinView::slotItemsChanged); connect(m_model, &KFileItemModel::itemsRemoved, this, &DolphinView::itemCountChanged); connect(m_model, &KFileItemModel::itemsInserted, this, &DolphinView::itemCountChanged); connect(m_model, &KFileItemModel::infoMessage, this, &DolphinView::infoMessage); @@ -353,6 +350,16 @@ return selectionManager->selectedItems().count(); } +KItemListSelectionManager* DolphinView::selectionManager() const +{ + return m_container->controller()->selectionManager(); +} + +DolphinItemListView* DolphinView::listView() const +{ + return m_view; +} + void DolphinView::markUrlsAsSelected(const QList& urls) { m_selectedUrls = urls; @@ -622,136 +629,6 @@ m_container->controller()->selectionManager()->clearSelection(); } -void DolphinView::renameSelectedItems() -{ - const KFileItemList items = selectedItems(); - if (items.isEmpty()) { - return; - } - - if (items.count() == 1 && GeneralSettings::renameInline()) { - const int index = m_model->index(items.first()); - m_view->editRole(index, "text"); - - hideToolTip(); - - connect(m_view, &DolphinItemListView::roleEditingFinished, - this, &DolphinView::slotRoleEditingFinished); - } else { - KIO::RenameFileDialog* dialog = new KIO::RenameFileDialog(items, this); - connect(dialog, &KIO::RenameFileDialog::renamingFinished, - this, &DolphinView::slotRenameDialogRenamingFinished); - - dialog->open(); - } - - // Assure that the current index remains visible when KFileItemModel - // will notify the view about changed items (which might result in - // a changed sorting). - m_assureVisibleCurrentIndex = true; -} - -void DolphinView::trashSelectedItems() -{ - const QList list = simplifiedSelectedUrls(); - KIO::JobUiDelegate uiDelegate; - uiDelegate.setWindow(window()); - if (uiDelegate.askDeleteConfirmation(list, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation)) { - KIO::Job* job = KIO::trash(list); - KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Trash, list, QUrl(QStringLiteral("trash:/")), job); - KJobWidgets::setWindow(job, this); - connect(job, &KIO::Job::result, - this, &DolphinView::slotTrashFileFinished); - } -} - -void DolphinView::deleteSelectedItems() -{ - const QList list = simplifiedSelectedUrls(); - - KIO::JobUiDelegate uiDelegate; - uiDelegate.setWindow(window()); - if (uiDelegate.askDeleteConfirmation(list, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::DefaultConfirmation)) { - KIO::Job* job = KIO::del(list); - KJobWidgets::setWindow(job, this); - connect(job, &KIO::Job::result, - this, &DolphinView::slotDeleteFileFinished); - } -} - -void DolphinView::cutSelectedItems() -{ - QMimeData* mimeData = selectionMimeData(); - KIO::setClipboardDataCut(mimeData, true); - QApplication::clipboard()->setMimeData(mimeData); -} - -void DolphinView::copySelectedItems() -{ - QMimeData* mimeData = selectionMimeData(); - QApplication::clipboard()->setMimeData(mimeData); -} - -void DolphinView::paste() -{ - pasteToUrl(url()); -} - -void DolphinView::pasteIntoFolder() -{ - const KFileItemList items = selectedItems(); - if ((items.count() == 1) && items.first().isDir()) { - pasteToUrl(items.first().url()); - } -} - -void DolphinView::duplicateSelectedItems() -{ - const KFileItemList itemList = selectedItems(); - if (itemList.isEmpty()) { - return; - } - - const QMimeDatabase db; - - // Duplicate all selected items and append "copy" to the end of the file name - // but before the filename extension, if present - QList newSelection; - for (const auto &item : itemList) { - const QUrl originalURL = item.url(); - const QString originalDirectoryPath = originalURL.adjusted(QUrl::RemoveFilename).path(); - const QString originalFileName = item.name(); - - QString extension = db.suffixForFileName(originalFileName); - - QUrl duplicateURL = originalURL; - - // No extension; new filename is " copy" - if (extension.isEmpty()) { - duplicateURL.setPath(originalDirectoryPath + i18nc(" copy", "%1 copy", originalFileName)); - // There's an extension; new filename is " copy." - } else { - // Need to add a dot since QMimeDatabase::suffixForFileName() doesn't include it - extension = QLatin1String(".") + extension; - const QString originalFilenameWithoutExtension = originalFileName.chopped(extension.size()); - // Preserve file's original filename extension in case the casing differs - // from what QMimeDatabase::suffixForFileName() returned - const QString originalExtension = originalFileName.right(extension.size()); - duplicateURL.setPath(originalDirectoryPath + i18nc(" copy", "%1 copy", originalFilenameWithoutExtension) + originalExtension); - } - - KIO::CopyJob* job = KIO::copyAs(originalURL, duplicateURL, KIO::HideProgressInfo); - KJobWidgets::setWindow(job, this); - - if (job) { - newSelection << duplicateURL; - KIO::FileUndoManager::self()->recordCopyJob(job); - } - } - - forceUrlsSelection(newSelection.first(), newSelection); -} - void DolphinView::stopLoading() { m_model->cancelDirectoryLoading(); @@ -1194,6 +1071,12 @@ m_selectedUrls << url; } +void DolphinView::prepareToPaste() +{ + m_clearSelectionBeforeSelectingNewItems = true; + m_markFirstNewlySelectedItemAsCurrent = true; +} + void DolphinView::slotPasteJobResult(KJob *job) { if (job->error()) { @@ -1529,7 +1412,7 @@ // check if the selected item was the same item that started the twoClicksRenaming if (fileItemUrl.isValid() && m_twoClicksRenamingItemUrl == fileItemUrl) { - renameSelectedItems(); + emit requestRenameSelectedItems(); } } } @@ -1591,11 +1474,6 @@ updateWritableState(); } -void DolphinView::slotItemsChanged() -{ - m_assureVisibleCurrentIndex = false; -} - void DolphinView::slotSortOrderChangedByHeader(Qt::SortOrder current, Qt::SortOrder previous) { Q_UNUSED(previous) diff --git a/src/views/dolphinviewactionhandler.h b/src/views/dolphinviewactionhandler.h --- a/src/views/dolphinviewactionhandler.h +++ b/src/views/dolphinviewactionhandler.h @@ -32,6 +32,9 @@ class QActionGroup; class DolphinView; class KActionCollection; +class KItemListSelectionManager; +class KFileItemListView; +class QMimeData; /** * @short Handles all actions for DolphinView @@ -59,6 +62,17 @@ */ void setCurrentView(DolphinView* view); + /** + * Sets the selection manager that this action handler should work on. + */ + void setCurrentSelectionManager(KItemListSelectionManager* selectionManager); + + /** + * Sets the current KFileItemListView to use for the actions + * Used for renaming + */ + void setCurrentItemListView(KFileItemListView* view); + /** * Returns the view that this action handler should work on. */ @@ -75,6 +89,25 @@ KActionCollection* actionCollection(); public Q_SLOTS: + + /** + * Copies all selected items to the clipboard and marks + * the items as cut. + */ + void cut(); + + /** Copies all selected items to the clipboard. */ + void copy(); + + /** Pastes the clipboard data to the active view. */ + void paste(); + /** + * Pastes the clipboard data into the currently selected folder + * of the active view. If not exactly one folder is selected, + * no pasting is done at all. + */ + void pasteIntoFolder(); + /** * Update all actions in the 'View' menu, i.e. those that depend on the * settings in the current view. @@ -254,8 +287,15 @@ */ KToggleAction* detailsModeAction(); + KItemSet selectedItemSet(); + QMimeData* selectionMimeData(); + KFileItemList selectedItems(); + QList simplifiedSelectedUrls(); + KActionCollection* m_actionCollection; DolphinView* m_currentView; + KItemListSelectionManager* m_currentSelectionManager; + KFileItemListView* m_currentItemListView; QHash m_sortByActions; QHash m_visibleRoles; diff --git a/src/views/dolphinviewactionhandler.cpp b/src/views/dolphinviewactionhandler.cpp --- a/src/views/dolphinviewactionhandler.cpp +++ b/src/views/dolphinviewactionhandler.cpp @@ -35,13 +35,32 @@ #include #include +#include "kitemviews/kitemlistselectionmanager.h" +#include "dolphin_generalsettings.h" +#include "dolphinitemlistview.h" + +#include +#include +#include +#include +#include +#include +#include +#include + #include #include +#include +#include +#include +#include DolphinViewActionHandler::DolphinViewActionHandler(KActionCollection* collection, QObject* parent) : QObject(parent), m_actionCollection(collection), m_currentView(nullptr), + m_currentSelectionManager(nullptr), + m_currentItemListView(nullptr), m_sortByActions(), m_visibleRoles() { @@ -79,13 +98,27 @@ this, &DolphinViewActionHandler::slotZoomLevelChanged); connect(view, &DolphinView::writeStateChanged, this, &DolphinViewActionHandler::slotWriteStateChanged); + connect(view, &DolphinView::requestRenameSelectedItems, + this, &DolphinViewActionHandler::slotRename); + + m_currentSelectionManager = view->selectionManager(); } DolphinView* DolphinViewActionHandler::currentView() { return m_currentView; } +void DolphinViewActionHandler::setCurrentSelectionManager(KItemListSelectionManager* selectionManager) +{ + m_currentSelectionManager = selectionManager; +} + +void DolphinViewActionHandler::setCurrentItemListView(KFileItemListView* view) +{ + m_currentItemListView = view; +} + void DolphinViewActionHandler::createActions() { // This action doesn't appear in the GUI, it's for the shortcut only. @@ -371,6 +404,93 @@ return rolesActionGroup; } + +QMimeData* DolphinViewActionHandler::selectionMimeData() +{ + return m_currentSelectionManager->model()->createMimeData(selectedItemSet()); +} + +KItemSet DolphinViewActionHandler::selectedItemSet() +{ + return m_currentSelectionManager->selectedItems(); +} + +KFileItemList DolphinViewActionHandler::selectedItems() +{ + KFileItemList selectedItems; + const auto items = selectedItemSet(); + const auto model = qobject_cast(m_currentSelectionManager->model()); + + selectedItems.reserve(items.count()); + for (int index : items) { + selectedItems.append(model->fileItem(index)); + } + return selectedItems; +} + +QList DolphinViewActionHandler::simplifiedSelectedUrls() +{ + QList urls; + + const KFileItemList items = selectedItems(); + urls.reserve(items.count()); + for (const KFileItem& item: items) { + urls.append(item.url()); + } + + if (m_currentView->itemsExpandable()) { + urls = KDirModel::simplifiedUrlList(urls); + } + + return urls; +} + +void DolphinViewActionHandler::cut() +{ + QMimeData* mimeData = selectionMimeData(); + KIO::setClipboardDataCut(mimeData, true); + QApplication::clipboard()->setMimeData(mimeData); +} + +void DolphinViewActionHandler::copy() +{ + QMimeData* mimeData = selectionMimeData(); + QApplication::clipboard()->setMimeData(mimeData); +} + +void DolphinViewActionHandler::paste() +{ + auto* model = qobject_cast(m_currentSelectionManager->model()); + // first selected item + const KFileItem& item = model->fileItem(*m_currentSelectionManager->selectedItems().constBegin()); + + KIO::PasteJob *job = KIO::paste(QApplication::clipboard()->mimeData(), item.url()); + KJobWidgets::setWindow(job, m_currentView->window()); + + // issue with m_markFirstNewlySelectedItemAsCurrent + m_markFirstNewlySelectedItemAsCurrent + connect(job, &KIO::PasteJob::itemCreated, m_currentView, &DolphinView::slotItemCreated); + connect(job, &KIO::PasteJob::result, m_currentView, &DolphinView::slotPasteJobResult); +} + +void DolphinViewActionHandler::pasteIntoFolder() +{ + const KFileItemList items = selectedItems(); + if (items.count() == 1) { + + const KFileItem& item = items.at(0); + if (item.isDir()) { + + m_currentView->prepareToPaste(); + + KIO::PasteJob *job = KIO::paste(QApplication::clipboard()->mimeData(), item.url()); + KJobWidgets::setWindow(job, m_currentView->window()); + + connect(job, &KIO::PasteJob::itemCreated, m_currentView, &DolphinView::slotItemCreated); + connect(job, &KIO::PasteJob::result, m_currentView, &DolphinView::slotPasteJobResult); + } + } +} + void DolphinViewActionHandler::slotViewModeActionTriggered(QAction* action) { const DolphinView::Mode mode = action->data().value(); @@ -383,19 +503,68 @@ void DolphinViewActionHandler::slotRename() { emit actionBeingHandled(); - m_currentView->renameSelectedItems(); + const KFileItemList items = selectedItems(); + if (items.isEmpty()) { + return; + } + + bool listEditInview = items.count() == 1 && GeneralSettings::renameInline(); + int index; + if (listEditInview) { + const auto model = qobject_cast(m_currentSelectionManager->model()); + index = model->index(items.first()); + listEditInview = index != -1; + } + if (listEditInview) { + if (m_currentSelectionManager == m_currentView->selectionManager()) { + m_currentView->listView()->editRole(index, "text"); + } else if (m_currentItemListView != nullptr) { + m_currentItemListView->editRole(index, "text"); + } + m_currentView->hideToolTip(); + + connect(m_currentView->listView(), &DolphinItemListView::roleEditingFinished, + m_currentView, &DolphinView::slotRoleEditingFinished); + + } else { + KIO::RenameFileDialog* dialog = new KIO::RenameFileDialog(items, m_currentView); + connect(dialog, &KIO::RenameFileDialog::renamingFinished, + m_currentView, &DolphinView::slotRenameDialogRenamingFinished); + + dialog->open(); + } } void DolphinViewActionHandler::slotTrashActivated() { emit actionBeingHandled(); - m_currentView->trashSelectedItems(); + const QList list = simplifiedSelectedUrls(); + KIO::JobUiDelegate uiDelegate; + uiDelegate.setWindow(m_currentView->window()); + if (uiDelegate.askDeleteConfirmation(list, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation)) { + KIO::Job* job = KIO::trash(list); + KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Trash, list, QUrl(QStringLiteral("trash:/")), job); + KJobWidgets::setWindow(job, m_currentView->window()); + + // signal to view + connect(job, &KIO::Job::result, + m_currentView, &DolphinView::slotTrashFileFinished); + } } void DolphinViewActionHandler::slotDeleteItems() { emit actionBeingHandled(); - m_currentView->deleteSelectedItems(); + const QList list = simplifiedSelectedUrls(); + KIO::JobUiDelegate uiDelegate; + uiDelegate.setWindow(m_currentView->window()); + if (uiDelegate.askDeleteConfirmation(list, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::DefaultConfirmation)) { + KIO::Job* job = KIO::del(list); + KJobWidgets::setWindow(job, m_currentView->window()); + + // signal to view + connect(job, &KIO::Job::result, m_currentView, &DolphinView::slotDeleteFileFinished); + } } void DolphinViewActionHandler::togglePreview(bool show) @@ -690,13 +859,55 @@ void DolphinViewActionHandler::slotDuplicate() { emit actionBeingHandled(); - m_currentView->duplicateSelectedItems(); + const KFileItemList itemList = selectedItems(); + if (itemList.isEmpty()) { + return; + } + + const QMimeDatabase db; + + // Duplicate all selected items and append "copy" to the end of the file name + // but before the filename extension, if present + QList newSelection; + for (const auto &item : itemList) { + const QUrl originalURL = item.url(); + const QString originalDirectoryPath = originalURL.adjusted(QUrl::RemoveFilename).path(); + const QString originalFileName = item.name(); + + QString extension = db.suffixForFileName(originalFileName); + + QUrl duplicateURL = originalURL; + + // No extension; new filename is " copy" + if (extension.isEmpty()) { + duplicateURL.setPath(originalDirectoryPath + i18nc(" copy", "%1 copy", originalFileName)); + // There's an extension; new filename is " copy." + } else { + // Need to add a dot since QMimeDatabase::suffixForFileName() doesn't include it + extension = QLatin1String(".") + extension; + const QString originalFilenameWithoutExtension = originalFileName.chopped(extension.size()); + // Preserve file's original filename extension in case the casing differs + // from what QMimeDatabase::suffixForFileName() returned + const QString originalExtension = originalFileName.right(extension.size()); + duplicateURL.setPath(originalDirectoryPath + i18nc(" copy", "%1 copy", originalFilenameWithoutExtension) + originalExtension); + } + + KIO::CopyJob* job = KIO::copyAs(originalURL, duplicateURL, KIO::HideProgressInfo); + KJobWidgets::setWindow(job, m_currentView); + + if (job) { + newSelection << duplicateURL; + KIO::FileUndoManager::self()->recordCopyJob(job); + } + } + + m_currentView->forceUrlsSelection(newSelection.first(), newSelection); } void DolphinViewActionHandler::slotProperties() { KPropertiesDialog* dialog = nullptr; - const KFileItemList list = m_currentView->selectedItems(); + const KFileItemList list = selectedItems(); if (list.isEmpty()) { const QUrl url = m_currentView->url(); dialog = new KPropertiesDialog(url, m_currentView);