/* * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "filemenu.h" #include "createnewmenu.h" #include "icontheme.h" #include "filepropsdialog.h" #include "utilities.h" #include "fileoperation.h" #include "filelauncher.h" #include "appchooserdialog.h" #ifdef CUSTOM_ACTIONS #include #include "customaction_p.h" #endif #include #include #include "filemenu_p.h" namespace Fm { FileMenu::FileMenu(FmFileInfoList* files, FmFileInfo* info, FmPath* cwd, QWidget* parent): QMenu(parent), fileLauncher_(NULL) { createMenu(files, info, cwd); } FileMenu::FileMenu(FmFileInfoList* files, FmFileInfo* info, FmPath* cwd, const QString& title, QWidget* parent): QMenu(title, parent), unTrashAction_(NULL), fileLauncher_(NULL) { createMenu(files, info, cwd); } FileMenu::~FileMenu() { if(files_) fm_file_info_list_unref(files_); if(info_) fm_file_info_unref(info_); if(cwd_) fm_path_unref(cwd_); } void FileMenu::createMenu(FmFileInfoList* files, FmFileInfo* info, FmPath* cwd) { useTrash_ = true; confirmDelete_ = true; confirmTrash_ = false; // Confirm before moving files into "trash can" openAction_ = NULL; openWithMenuAction_ = NULL; openWithAction_ = NULL; separator1_ = NULL; cutAction_ = NULL; copyAction_ = NULL; pasteAction_ = NULL; deleteAction_ = NULL; unTrashAction_ = NULL; renameAction_ = NULL; separator2_ = NULL; propertiesAction_ = NULL; files_ = fm_file_info_list_ref(files); info_ = info ? fm_file_info_ref(info) : NULL; cwd_ = cwd ? fm_path_ref(cwd) : NULL; FmFileInfo* first = fm_file_info_list_peek_head(files); FmMimeType* mime_type = fm_file_info_get_mime_type(first); FmPath* path = fm_file_info_get_path(first); // check if the files are of the same type sameType_ = fm_file_info_list_is_same_type(files); // check if the files are on the same filesystem sameFilesystem_ = fm_file_info_list_is_same_fs(files); // check if the files are all virtual allVirtual_ = sameFilesystem_ && fm_path_is_virtual(path); // check if the files are all in the trash can allTrash_ = sameFilesystem_ && fm_path_is_trash(path); openAction_ = new QAction(QIcon::fromTheme("document-open"), tr("Open"), this); connect(openAction_ , &QAction::triggered, this, &FileMenu::onOpenTriggered); addAction(openAction_); openWithMenuAction_ = new QAction(tr("Open With..."), this); addAction(openWithMenuAction_); // create the "Open with..." sub menu QMenu* menu = new QMenu(this); openWithMenuAction_->setMenu(menu); if(sameType_) { /* add specific menu items for this mime type */ if(mime_type && !allVirtual_) { /* the file has a valid mime-type and its not virtual */ GList* apps = g_app_info_get_all_for_type(fm_mime_type_get_type(mime_type)); GList* l; for(l=apps;l;l=l->next) { GAppInfo* app = G_APP_INFO(l->data); // check if the command really exists gchar * program_path = g_find_program_in_path(g_app_info_get_executable(app)); if (!program_path) continue; g_free(program_path); // create a QAction for the application. AppInfoAction* action = new AppInfoAction(app, menu); connect(action, &QAction::triggered, this, &FileMenu::onApplicationTriggered); menu->addAction(action); } // unref GAppInfos here, they are still ref'ed in the AppInfoActions above g_list_foreach(apps, (GFunc)g_object_unref, NULL); g_list_free(apps); } } menu->addSeparator(); openWithAction_ = new QAction(tr("Other Applications"), this); connect(openWithAction_ , &QAction::triggered, this, &FileMenu::onOpenWithTriggered); menu->addAction(openWithAction_); separator1_ = addSeparator(); createAction_ = new QAction(tr("Create &New"), this); FmPath* dirPath = fm_file_info_list_get_length(files) == 1 && fm_file_info_is_dir(first) ? path : cwd_; createAction_->setMenu(new CreateNewMenu(NULL, dirPath, this)); addAction(createAction_); separator2_ = addSeparator(); if(allTrash_) { // all selected files are in trash:/// bool can_restore = true; /* only immediate children of trash:/// can be restored. */ for(GList* l = fm_file_info_list_peek_head_link(files_); l; l=l->next) { FmPath *trash_path = fm_file_info_get_path(FM_FILE_INFO(l->data)); if(!fm_path_get_parent(trash_path) || !fm_path_is_trash_root(fm_path_get_parent(trash_path))) { can_restore = false; break; } } if(can_restore) { unTrashAction_ = new QAction(tr("&Restore"), this); connect(unTrashAction_, &QAction::triggered, this, &FileMenu::onUnTrashTriggered); addAction(unTrashAction_); } } else { // ordinary files cutAction_ = new QAction(QIcon::fromTheme("edit-cut"), tr("Cut"), this); connect(cutAction_, &QAction::triggered, this, &FileMenu::onCutTriggered); addAction(cutAction_); copyAction_ = new QAction(QIcon::fromTheme("edit-copy"), tr("Copy"), this); connect(copyAction_, &QAction::triggered, this, &FileMenu::onCopyTriggered); addAction(copyAction_); pasteAction_ = new QAction(QIcon::fromTheme("edit-paste"), tr("Paste"), this); connect(pasteAction_, &QAction::triggered, this, &FileMenu::onPasteTriggered); addAction(pasteAction_); deleteAction_ = new QAction(QIcon::fromTheme("user-trash"), tr("&Move to Trash"), this); connect(deleteAction_, &QAction::triggered, this, &FileMenu::onDeleteTriggered); addAction(deleteAction_); renameAction_ = new QAction(tr("Rename"), this); connect(renameAction_, &QAction::triggered, this, &FileMenu::onRenameTriggered); addAction(renameAction_); } #ifdef CUSTOM_ACTIONS // DES-EMA custom actions integration GList* files_list = fm_file_info_list_peek_head_link(files); GList* items = fm_get_actions_for_files(files_list); if(items) { GList* l; for(l=items; l; l=l->next) { FmFileActionItem* item = FM_FILE_ACTION_ITEM(l->data); if(l == items && item && !(fm_file_action_item_is_action(item) && !(fm_file_action_item_get_target(item) & FM_FILE_ACTION_TARGET_CONTEXT))) { addSeparator(); // before all custom actions } addCustomActionItem(this, item); } } g_list_foreach(items, (GFunc)fm_file_action_item_unref, NULL); g_list_free(items); #endif // archiver integration // FIXME: we need to modify upstream libfm to include some Qt-based archiver programs. if(!allVirtual_) { if(sameType_) { FmArchiver* archiver = fm_archiver_get_default(); if(archiver) { if(fm_archiver_is_mime_type_supported(archiver, fm_mime_type_get_type(mime_type))) { if(cwd_ && archiver->extract_to_cmd) { QAction* action = new QAction(tr("Extract to..."), this); connect(action, &QAction::triggered, this, &FileMenu::onExtract); addAction(action); } if(archiver->extract_cmd) { QAction* action = new QAction(tr("Extract Here"), this); connect(action, &QAction::triggered, this, &FileMenu::onExtractHere); addAction(action); } } else { QAction* action = new QAction(tr("Compress"), this); connect(action, &QAction::triggered, this, &FileMenu::onCompress); addAction(action); } } } } separator3_ = addSeparator(); propertiesAction_ = new QAction(QIcon::fromTheme("document-properties"), tr("Properties"), this); connect(propertiesAction_, &QAction::triggered, this, &FileMenu::onFilePropertiesTriggered); addAction(propertiesAction_); } #ifdef CUSTOM_ACTIONS void FileMenu::addCustomActionItem(QMenu* menu, FmFileActionItem* item) { if(!item) { // separator addSeparator(); return; } // this action is not for context menu if(fm_file_action_item_is_action(item) && !(fm_file_action_item_get_target(item) & FM_FILE_ACTION_TARGET_CONTEXT)) return; CustomAction* action = new CustomAction(item, menu); menu->addAction(action); if(fm_file_action_item_is_menu(item)) { GList* subitems = fm_file_action_item_get_sub_items(item); if (subitems != NULL) { QMenu* submenu = new QMenu(menu); for(GList* l = subitems; l; l = l->next) { FmFileActionItem* subitem = FM_FILE_ACTION_ITEM(l->data); addCustomActionItem(submenu, subitem); } action->setMenu(submenu); } } else if(fm_file_action_item_is_action(item)) { connect(action, &QAction::triggered, this, &FileMenu::onCustomActionTrigerred); } } #endif void FileMenu::onOpenTriggered() { if(fileLauncher_) { fileLauncher_->launchFiles(NULL, files_); } else { // use the default launcher Fm::FileLauncher launcher; launcher.launchFiles(NULL, files_); } } void FileMenu::onOpenWithTriggered() { AppChooserDialog dlg(NULL); if(sameType_) { dlg.setMimeType(fm_file_info_get_mime_type(info_)); } else { // we can only set the selected app as default if all files are of the same type dlg.setCanSetDefault(false); } if(execModelessDialog(&dlg) == QDialog::Accepted) { GAppInfo* app = dlg.selectedApp(); if(app) { openFilesWithApp(app); g_object_unref(app); } } } void FileMenu::openFilesWithApp(GAppInfo* app) { FmPathList* paths = fm_path_list_new_from_file_info_list(files_); GList* uris = NULL; for(GList* l = fm_path_list_peek_head_link(paths); l; l = l->next) { FmPath* path = FM_PATH(l->data); char* uri = fm_path_to_uri(path); uris = g_list_prepend(uris, uri); } fm_path_list_unref(paths); fm_app_info_launch_uris(app, uris, NULL, NULL); g_list_foreach(uris, (GFunc)g_free, NULL); g_list_free(uris); } void FileMenu::onApplicationTriggered() { AppInfoAction* action = static_cast(sender()); openFilesWithApp(action->appInfo()); } #ifdef CUSTOM_ACTIONS void FileMenu::onCustomActionTrigerred() { CustomAction* action = static_cast(sender()); FmFileActionItem* item = action->item(); GList* files = fm_file_info_list_peek_head_link(files_); char* output = NULL; /* g_debug("item: %s is activated, id:%s", fm_file_action_item_get_name(item), fm_file_action_item_get_id(item)); */ fm_file_action_item_launch(item, NULL, files, &output); if(output) { QMessageBox::information(this, tr("Output"), QString::fromUtf8(output)); g_free(output); } } #endif void FileMenu::onFilePropertiesTriggered() { FilePropsDialog::showForFiles(files_); } void FileMenu::onCopyTriggered() { FmPathList* paths = fm_path_list_new_from_file_info_list(files_); Fm::copyFilesToClipboard(paths); fm_path_list_unref(paths); } void FileMenu::onCutTriggered() { FmPathList* paths = fm_path_list_new_from_file_info_list(files_); Fm::cutFilesToClipboard(paths); fm_path_list_unref(paths); } void FileMenu::onDeleteTriggered() { FmPathList* paths = fm_path_list_new_from_file_info_list(files_); if(useTrash_) FileOperation::trashFiles(paths, confirmTrash_); else FileOperation::deleteFiles(paths, confirmDelete_); fm_path_list_unref(paths); } void FileMenu::onUnTrashTriggered() { FmPathList* paths = fm_path_list_new_from_file_info_list(files_); FileOperation::unTrashFiles(paths); fm_path_list_unref(paths); } void FileMenu::onPasteTriggered() { Fm::pasteFilesFromClipboard(cwd_); } void FileMenu::onRenameTriggered() { for(GList* l = fm_file_info_list_peek_head_link(files_); l; l = l->next) { FmFileInfo* info = FM_FILE_INFO(l->data); Fm::renameFile(info, NULL); } } void FileMenu::setUseTrash(bool trash) { if(useTrash_ != trash) { useTrash_ = trash; if(deleteAction_) { deleteAction_->setText(useTrash_ ? tr("&Move to Trash") : tr("&Delete")); deleteAction_->setIcon(useTrash_ ? QIcon::fromTheme("user-trash") : QIcon::fromTheme("edit-delete")); } } } void FileMenu::onCompress() { FmArchiver* archiver = fm_archiver_get_default(); if(archiver) { FmPathList* paths = fm_path_list_new_from_file_info_list(files_); fm_archiver_create_archive(archiver, NULL, paths); fm_path_list_unref(paths); } } void FileMenu::onExtract() { FmArchiver* archiver = fm_archiver_get_default(); if(archiver) { FmPathList* paths = fm_path_list_new_from_file_info_list(files_); fm_archiver_extract_archives(archiver, NULL, paths); fm_path_list_unref(paths); } } void FileMenu::onExtractHere() { FmArchiver* archiver = fm_archiver_get_default(); if(archiver) { FmPathList* paths = fm_path_list_new_from_file_info_list(files_); fm_archiver_extract_archives_to(archiver, NULL, paths, cwd_); fm_path_list_unref(paths); } } } // namespace Fm