1 /*
2 * SPDX-FileCopyrightText: 2006-2010 Peter Penz <peter.penz19@gmail.com>
3 * SPDX-FileCopyrightText: 2006 Cvetoslav Ludmiloff <ludmiloff@gmail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8 #include "treeviewcontextmenu.h"
9
10 #include "folderspanel.h"
11 #include "global.h"
12
13 #include <KConfigGroup>
14 #include <KFileItemListProperties>
15 #include <KIO/CopyJob>
16 #include <KIO/DeleteJob>
17 #include <KIO/FileUndoManager>
18 #include <KIO/JobUiDelegate>
19 #include <KIO/Paste>
20 #include <KIO/PasteJob>
21 #include <KJobWidgets>
22 #include <KLocalizedString>
23 #include <KPropertiesDialog>
24 #include <KSharedConfig>
25 #include <KUrlMimeData>
26
27 #include <QApplication>
28 #include <QClipboard>
29 #include <QMenu>
30 #include <QMimeData>
31 #include <QPointer>
32
TreeViewContextMenu(FoldersPanel * parent,const KFileItem & fileInfo)33 TreeViewContextMenu::TreeViewContextMenu(FoldersPanel* parent,
34 const KFileItem& fileInfo) :
35 QObject(parent),
36 m_parent(parent),
37 m_fileItem(fileInfo)
38 {
39 }
40
~TreeViewContextMenu()41 TreeViewContextMenu::~TreeViewContextMenu()
42 {
43 }
44
open(const QPoint & pos)45 void TreeViewContextMenu::open(const QPoint& pos)
46 {
47 QMenu* popup = new QMenu(m_parent);
48
49 if (!m_fileItem.isNull()) {
50 KFileItemListProperties capabilities(KFileItemList() << m_fileItem);
51
52 // insert 'Cut', 'Copy' and 'Paste'
53 QAction* cutAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-cut")), i18nc("@action:inmenu", "Cut"), this);
54 cutAction->setEnabled(capabilities.supportsMoving());
55 connect(cutAction, &QAction::triggered, this, &TreeViewContextMenu::cut);
56
57 QAction* copyAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18nc("@action:inmenu", "Copy"), this);
58 connect(copyAction, &QAction::triggered, this, &TreeViewContextMenu::copy);
59
60 const QMimeData *mimeData = QApplication::clipboard()->mimeData();
61 bool canPaste;
62 const QString text = KIO::pasteActionText(mimeData, &canPaste, m_fileItem);
63 QAction* pasteAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-paste")), text, this);
64 connect(pasteAction, &QAction::triggered, this, &TreeViewContextMenu::paste);
65 pasteAction->setEnabled(canPaste);
66
67 popup->addAction(cutAction);
68 popup->addAction(copyAction);
69 popup->addAction(pasteAction);
70 popup->addSeparator();
71
72 // insert 'Rename'
73 QAction* renameAction = new QAction(i18nc("@action:inmenu", "Rename..."), this);
74 renameAction->setEnabled(capabilities.supportsMoving());
75 renameAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename")));
76 connect(renameAction, &QAction::triggered, this, &TreeViewContextMenu::rename);
77 popup->addAction(renameAction);
78
79 // insert 'Move to Trash' and (optionally) 'Delete'
80 KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::IncludeGlobals);
81 KConfigGroup configGroup(globalConfig, "KDE");
82 bool showDeleteCommand = configGroup.readEntry("ShowDeleteCommand", false);
83
84 const QUrl url = m_fileItem.url();
85 if (url.isLocalFile()) {
86 QAction* moveToTrashAction = new QAction(QIcon::fromTheme(QStringLiteral("user-trash")),
87 i18nc("@action:inmenu", "Move to Trash"), this);
88 const bool enableMoveToTrash = capabilities.isLocal() && capabilities.supportsMoving();
89 moveToTrashAction->setEnabled(enableMoveToTrash);
90 connect(moveToTrashAction, &QAction::triggered, this, &TreeViewContextMenu::moveToTrash);
91 popup->addAction(moveToTrashAction);
92 } else {
93 showDeleteCommand = true;
94 }
95
96 if (showDeleteCommand) {
97 QAction* deleteAction = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18nc("@action:inmenu", "Delete"), this);
98 deleteAction->setEnabled(capabilities.supportsDeleting());
99 connect(deleteAction, &QAction::triggered, this, &TreeViewContextMenu::deleteItem);
100 popup->addAction(deleteAction);
101 }
102
103 popup->addSeparator();
104 }
105
106 // insert 'Show Hidden Files'
107 QAction* showHiddenFilesAction = new QAction(i18nc("@action:inmenu", "Show Hidden Files"), this);
108 showHiddenFilesAction->setCheckable(true);
109 showHiddenFilesAction->setChecked(m_parent->showHiddenFiles());
110 popup->addAction(showHiddenFilesAction);
111 connect(showHiddenFilesAction, &QAction::toggled, this, &TreeViewContextMenu::setShowHiddenFiles);
112
113 if (!m_fileItem.isNull()) {
114 // insert 'Limit to Home Directory'
115 const QUrl url = m_fileItem.url();
116 const bool enableLimitToHomeDirectory = url.isLocalFile();
117 QAction* limitFoldersPanelToHomeAction = new QAction(i18nc("@action:inmenu", "Limit to Home Directory"), this);
118 limitFoldersPanelToHomeAction->setCheckable(true);
119 limitFoldersPanelToHomeAction->setEnabled(enableLimitToHomeDirectory);
120 limitFoldersPanelToHomeAction->setChecked(m_parent->limitFoldersPanelToHome());
121 popup->addAction(limitFoldersPanelToHomeAction);
122 connect(limitFoldersPanelToHomeAction, &QAction::toggled, this, &TreeViewContextMenu::setLimitFoldersPanelToHome);
123 }
124
125 // insert 'Automatic Scrolling'
126 QAction* autoScrollingAction = new QAction(i18nc("@action:inmenu", "Automatic Scrolling"), this);
127 autoScrollingAction->setCheckable(true);
128 autoScrollingAction->setChecked(m_parent->autoScrolling());
129 // TODO: Temporary disabled. Horizontal autoscrolling will be implemented later either
130 // in KItemViews or manually as part of the FoldersPanel
131 //popup->addAction(autoScrollingAction);
132 connect(autoScrollingAction, &QAction::toggled, this, &TreeViewContextMenu::setAutoScrolling);
133
134 if (!m_fileItem.isNull()) {
135 // insert 'Properties' entry
136 QAction* propertiesAction = new QAction(i18nc("@action:inmenu", "Properties"), this);
137 propertiesAction->setIcon(QIcon::fromTheme(QStringLiteral("document-properties")));
138 connect(propertiesAction, &QAction::triggered, this, &TreeViewContextMenu::showProperties);
139 popup->addAction(propertiesAction);
140 }
141
142 const QList<QAction*> customActions = m_parent->customContextMenuActions();
143 if (!customActions.isEmpty()) {
144 popup->addSeparator();
145 for (QAction* action : customActions) {
146 popup->addAction(action);
147 }
148 }
149
150 QPointer<QMenu> popupPtr = popup;
151 popup->exec(pos);
152 if (popupPtr.data()) {
153 popupPtr.data()->deleteLater();
154 }
155 }
156
populateMimeData(QMimeData * mimeData,bool cut)157 void TreeViewContextMenu::populateMimeData(QMimeData* mimeData, bool cut)
158 {
159 QList<QUrl> kdeUrls;
160 kdeUrls.append(m_fileItem.url());
161 QList<QUrl> mostLocalUrls;
162 bool dummy;
163 mostLocalUrls.append(m_fileItem.mostLocalUrl(&dummy));
164 KIO::setClipboardDataCut(mimeData, cut);
165 KUrlMimeData::setUrls(kdeUrls, mostLocalUrls, mimeData);
166 }
167
cut()168 void TreeViewContextMenu::cut()
169 {
170 QMimeData* mimeData = new QMimeData();
171 populateMimeData(mimeData, true);
172 QApplication::clipboard()->setMimeData(mimeData);
173 }
174
copy()175 void TreeViewContextMenu::copy()
176 {
177 QMimeData* mimeData = new QMimeData();
178 populateMimeData(mimeData, false);
179 QApplication::clipboard()->setMimeData(mimeData);
180 }
181
paste()182 void TreeViewContextMenu::paste()
183 {
184 KIO::PasteJob *job = KIO::paste(QApplication::clipboard()->mimeData(), m_fileItem.url());
185 KJobWidgets::setWindow(job, m_parent);
186 }
187
rename()188 void TreeViewContextMenu::rename()
189 {
190 m_parent->rename(m_fileItem);
191 }
192
moveToTrash()193 void TreeViewContextMenu::moveToTrash()
194 {
195 const QList<QUrl> list{m_fileItem.url()};
196 KIO::JobUiDelegate uiDelegate;
197 uiDelegate.setWindow(m_parent);
198 if (uiDelegate.askDeleteConfirmation(list, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation)) {
199 KIO::Job* job = KIO::trash(list);
200 KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Trash, list, QUrl(QStringLiteral("trash:/")), job);
201 KJobWidgets::setWindow(job, m_parent);
202 job->uiDelegate()->setAutoErrorHandlingEnabled(true);
203 }
204 }
205
deleteItem()206 void TreeViewContextMenu::deleteItem()
207 {
208 const QList<QUrl> list{m_fileItem.url()};
209 KIO::JobUiDelegate uiDelegate;
210 uiDelegate.setWindow(m_parent);
211 if (uiDelegate.askDeleteConfirmation(list, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::DefaultConfirmation)) {
212 KIO::Job* job = KIO::del(list);
213 KJobWidgets::setWindow(job, m_parent);
214 job->uiDelegate()->setAutoErrorHandlingEnabled(true);
215 }
216 }
217
showProperties()218 void TreeViewContextMenu::showProperties()
219 {
220 KPropertiesDialog* dialog = new KPropertiesDialog(m_fileItem.url(), m_parent);
221 dialog->setAttribute(Qt::WA_DeleteOnClose);
222 dialog->show();
223 }
224
setShowHiddenFiles(bool show)225 void TreeViewContextMenu::setShowHiddenFiles(bool show)
226 {
227 m_parent->setShowHiddenFiles(show);
228 }
229
setLimitFoldersPanelToHome(bool enable)230 void TreeViewContextMenu::setLimitFoldersPanelToHome(bool enable)
231 {
232 m_parent->setLimitFoldersPanelToHome(enable);
233 }
234
setAutoScrolling(bool enable)235 void TreeViewContextMenu::setAutoScrolling(bool enable)
236 {
237 m_parent->setAutoScrolling(enable);
238 }
239
240