1 /*
2 * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
3 * Copyright (C) 2012 - 2014 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 */
20
21
22 #include "foldermenu.h"
23 #include "createnewmenu.h"
24 #include "filepropsdialog.h"
25 #include "folderview.h"
26 #include "utilities.h"
27 #include "fileoperation.h"
28 #include <cstring> // for memset
29 #include <QDebug>
30 #include "customaction_p.h"
31 #include "customactions/fileaction.h"
32 #include <QMessageBox>
33
34 namespace Fm {
35
FolderMenu(FolderView * view,QWidget * parent)36 FolderMenu::FolderMenu(FolderView* view, QWidget* parent):
37 QMenu(parent),
38 view_(view) {
39
40 createAction_ = nullptr;
41 pasteAction_ = nullptr;
42 separator1_ = nullptr;
43 separator2_ = nullptr;
44
45 ProxyFolderModel* model = view_->model();
46
47 bool insideTrash(view_->path() && view_->path().hasUriScheme("trash"));
48 if(insideTrash) {
49 if(auto folder = view_->folder()) {
50 if(!folder->isEmpty()) {
51 auto trashAction = new QAction(tr("Empty Trash"), this);
52 addAction(trashAction);
53 connect(trashAction, &QAction::triggered, []() {
54 Fm::FilePathList files;
55 files.push_back(Fm::FilePath::fromUri("trash:///"));
56 Fm::FileOperation::deleteFiles(std::move(files), true);
57 });
58 addSeparator();
59 }
60 }
61 }
62 else {
63 createAction_ = new QAction(tr("Create &New"), this);
64 addAction(createAction_);
65 createAction_->setMenu(new CreateNewMenu(view_, view_->path(), this));
66 separator1_ = addSeparator();
67
68 pasteAction_ = new QAction(QIcon::fromTheme(QStringLiteral("edit-paste")), tr("&Paste"), this);
69 addAction(pasteAction_);
70 connect(pasteAction_, &QAction::triggered, this, &FolderMenu::onPasteActionTriggered);
71 separator2_ = addSeparator();
72 }
73
74 auto selMode = view_->childView()->selectionMode();
75 if(selMode == QAbstractItemView::SingleSelection || selMode == QAbstractItemView::NoSelection) {
76 selectAllAction_ = nullptr;
77 invertSelectionAction_ = nullptr;
78 separator3_ = nullptr;
79 }
80 else {
81 selectAllAction_ = new QAction(tr("Select &All"), this);
82 addAction(selectAllAction_);
83 connect(selectAllAction_, &QAction::triggered, this, &FolderMenu::onSelectAllActionTriggered);
84
85 invertSelectionAction_ = new QAction(tr("Invert Selection"), this);
86 addAction(invertSelectionAction_);
87 connect(invertSelectionAction_, &QAction::triggered, this, &FolderMenu::onInvertSelectionActionTriggered);
88
89 separator3_ = addSeparator();
90 }
91
92 sortAction_ = new QAction(tr("Sorting"), this);
93 addAction(sortAction_);
94 createSortMenu();
95 sortAction_->setMenu(sortMenu_);
96
97 showHiddenAction_ = new QAction(tr("Show Hidden"), this);
98 addAction(showHiddenAction_);
99 showHiddenAction_->setCheckable(true);
100 showHiddenAction_->setChecked(model->showHidden());
101 connect(showHiddenAction_, &QAction::triggered, this, &FolderMenu::onShowHiddenActionTriggered);
102
103 auto folderInfo = view_->folderInfo();
104 if(folderInfo) { // should never be null (see FolderView::onFileClicked)
105 // DES-EMA custom actions integration
106 FileInfoList files;
107 files.push_back(folderInfo);
108 auto custom_actions = FileActionItem::get_actions_for_files(files);
109 bool firstAction = true;
110 for(auto& item: custom_actions) {
111 if(item && !(item->get_target() & FILE_ACTION_TARGET_CONTEXT)) {
112 continue; // this item is not for context menu
113 }
114 if(firstAction) {
115 addSeparator(); // before all custom actions
116 firstAction = false;
117 }
118 addCustomActionItem(this, item);
119 }
120
121 // disable actons that can't be used
122 if(pasteAction_) {
123 pasteAction_->setEnabled(folderInfo->isWritable());
124 }
125 if(createAction_) {
126 createAction_->setEnabled(folderInfo->isWritable());
127 }
128 }
129
130 separator4_ = addSeparator();
131
132 propertiesAction_ = new QAction(tr("Folder Pr&operties"), this);
133 addAction(propertiesAction_);
134 connect(propertiesAction_, &QAction::triggered, this, &FolderMenu::onPropertiesActionTriggered);
135 }
136
~FolderMenu()137 FolderMenu::~FolderMenu() {
138 }
139
addCustomActionItem(QMenu * menu,std::shared_ptr<const FileActionItem> item)140 void FolderMenu::addCustomActionItem(QMenu* menu, std::shared_ptr<const FileActionItem> item) {
141 if(!item) {
142 return;
143 }
144 if(item->is_action() && !(item->get_target() & FILE_ACTION_TARGET_CONTEXT)) {
145 return;
146 }
147
148 CustomAction* action = new CustomAction(item, menu);
149 menu->addAction(action);
150 if(item->is_menu()) {
151 auto& subitems = item->get_sub_items();
152 if(!subitems.empty()) {
153 QMenu* submenu = new QMenu(menu);
154 for(auto& subitem: subitems) {
155 addCustomActionItem(submenu, subitem);
156 }
157 action->setMenu(submenu);
158 }
159 }
160 else if(item->is_action()) {
161 connect(action, &QAction::triggered, this, &FolderMenu::onCustomActionTrigerred);
162 }
163 }
164
onCustomActionTrigerred()165 void FolderMenu::onCustomActionTrigerred() {
166 CustomAction* action = static_cast<CustomAction*>(sender());
167 auto& item = action->item();
168 auto folderInfo = view_->folderInfo();
169 if(folderInfo) {
170 CStrPtr output;
171 FileInfoList file_list;
172 file_list.push_back(folderInfo);
173 item->launch(nullptr, file_list, output);
174 if(output) {
175 QMessageBox::information(this, tr("Output"), QString::fromUtf8(output.get()));
176 }
177 }
178 }
179
addSortMenuItem(const QString & title,int id)180 void FolderMenu::addSortMenuItem(const QString &title, int id) {
181 QAction* action = new QAction(title, this);
182 action->setData(QVariant(id));
183 sortMenu_->addAction(action);
184 action->setCheckable(true);
185 action->setChecked(id == view_->model()->sortColumn());
186 sortActionGroup_->addAction(action);
187 connect(action, &QAction::triggered, this, &FolderMenu::onSortActionTriggered);
188 }
189
createSortMenu()190 void FolderMenu::createSortMenu() {
191 ProxyFolderModel* model = view_->model();
192
193 sortMenu_ = new QMenu(this);
194 sortActionGroup_ = new QActionGroup(sortMenu_);
195 sortActionGroup_->setExclusive(true);
196
197 addSortMenuItem(tr("By File Name"), FolderModel::ColumnFileName);
198 addSortMenuItem(tr("By Modification Time"), FolderModel::ColumnFileMTime);
199 addSortMenuItem(tr("By Creation Time"), FolderModel::ColumnFileCrTime);
200 if(auto folderPath = view_->path()) {
201 if(strcmp(folderPath.toString().get(), "trash:///") == 0) {
202 addSortMenuItem(tr("By Deletion Time"), FolderModel::ColumnFileDTime);
203 }
204 }
205 addSortMenuItem(tr("By File Size"), FolderModel::ColumnFileSize);
206 addSortMenuItem(tr("By File Type"), FolderModel::ColumnFileType);
207 addSortMenuItem(tr("By File Owner"), FolderModel::ColumnFileOwner);
208 addSortMenuItem(tr("By File Group"), FolderModel::ColumnFileGroup);
209
210 sortMenu_->addSeparator();
211
212 QActionGroup* group = new QActionGroup(this);
213 group->setExclusive(true);
214 actionAscending_ = new QAction(tr("Ascending"), this);
215 actionAscending_->setCheckable(true);
216 sortMenu_->addAction(actionAscending_);
217 group->addAction(actionAscending_);
218
219 actionDescending_ = new QAction(tr("Descending"), this);
220 actionDescending_->setCheckable(true);
221 sortMenu_->addAction(actionDescending_);
222 group->addAction(actionDescending_);
223
224 if(model->sortOrder() == Qt::AscendingOrder) {
225 actionAscending_->setChecked(true);
226 }
227 else {
228 actionDescending_->setChecked(true);
229 }
230
231 connect(actionAscending_, &QAction::triggered, this, &FolderMenu::onSortOrderActionTriggered);
232 connect(actionDescending_, &QAction::triggered, this, &FolderMenu::onSortOrderActionTriggered);
233
234 sortMenu_->addSeparator();
235
236 QAction* actionFolderFirst = new QAction(tr("Folder First"), this);
237 sortMenu_->addAction(actionFolderFirst);
238 actionFolderFirst->setCheckable(true);
239 if(model->folderFirst()) {
240 actionFolderFirst->setChecked(true);
241 }
242 connect(actionFolderFirst, &QAction::triggered, this, &FolderMenu::onFolderFirstActionTriggered);
243
244 QAction* actionHiddenLast = new QAction(tr("Hidden Last"), this);
245 sortMenu_->addAction(actionHiddenLast);
246 actionHiddenLast->setCheckable(true);
247 if(model->hiddenLast()) {
248 actionHiddenLast->setChecked(true);
249 }
250 connect(actionHiddenLast, &QAction::triggered, this, &FolderMenu::onHiddenLastActionTriggered);
251
252 QAction* actionCaseSensitive = new QAction(tr("Case Sensitive"), this);
253 sortMenu_->addAction(actionCaseSensitive);
254 actionCaseSensitive->setCheckable(true);
255
256 if(model->sortCaseSensitivity() == Qt::CaseSensitive) {
257 actionCaseSensitive->setChecked(true);
258 }
259
260 connect(actionCaseSensitive, &QAction::triggered, this, &FolderMenu::onCaseSensitiveActionTriggered);
261 }
262
onPasteActionTriggered()263 void FolderMenu::onPasteActionTriggered() {
264 auto folderPath = view_->path();
265 if(folderPath) {
266 pasteFilesFromClipboard(folderPath);
267 }
268 }
269
onSelectAllActionTriggered()270 void FolderMenu::onSelectAllActionTriggered() {
271 view_->selectAll();
272 }
273
onInvertSelectionActionTriggered()274 void FolderMenu::onInvertSelectionActionTriggered() {
275 view_->invertSelection();
276 }
277
onSortActionTriggered(bool)278 void FolderMenu::onSortActionTriggered(bool /*checked*/) {
279 ProxyFolderModel* model = view_->model();
280
281 if(model && sortActionGroup_) {
282 QAction* action = static_cast<QAction*>(sender());
283
284 const auto actions = sortActionGroup_->actions();
285 for(auto a : actions) {
286 if(a == action) {
287 int col = a->data().toInt();
288 if(col >= 0 && col < FolderModel::NumOfColumns) {
289 model->sort(col, model->sortOrder());
290 }
291 break;
292 }
293 }
294 }
295 }
296
onSortOrderActionTriggered(bool)297 void FolderMenu::onSortOrderActionTriggered(bool /*checked*/) {
298 ProxyFolderModel* model = view_->model();
299
300 if(model) {
301 QAction* action = static_cast<QAction*>(sender());
302 Qt::SortOrder order;
303
304 if(action == actionAscending_) {
305 order = Qt::AscendingOrder;
306 }
307 else {
308 order = Qt::DescendingOrder;
309 }
310
311 model->sort(model->sortColumn(), order);
312 }
313 }
314
onShowHiddenActionTriggered(bool checked)315 void FolderMenu::onShowHiddenActionTriggered(bool checked) {
316 ProxyFolderModel* model = view_->model();
317
318 if(model) {
319 qDebug("show hidden: %d", checked);
320 model->setShowHidden(checked);
321 }
322 }
323
onCaseSensitiveActionTriggered(bool checked)324 void FolderMenu::onCaseSensitiveActionTriggered(bool checked) {
325 ProxyFolderModel* model = view_->model();
326
327 if(model) {
328 model->setSortCaseSensitivity(checked ? Qt::CaseSensitive : Qt::CaseInsensitive);
329 }
330 }
331
onFolderFirstActionTriggered(bool checked)332 void FolderMenu::onFolderFirstActionTriggered(bool checked) {
333 ProxyFolderModel* model = view_->model();
334
335 if(model) {
336 model->setFolderFirst(checked);
337 }
338 }
339
onHiddenLastActionTriggered(bool checked)340 void FolderMenu::onHiddenLastActionTriggered(bool checked) {
341 ProxyFolderModel* model = view_->model();
342
343 if(model) {
344 model->setHiddenLast(checked);
345 }
346 }
347
onPropertiesActionTriggered()348 void FolderMenu::onPropertiesActionTriggered() {
349 auto folderInfo = view_->folderInfo();
350 if(folderInfo) {
351 FilePropsDialog::showForFile(folderInfo);
352 }
353 }
354
355 } // namespace Fm
356