You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libfm-qt-packaging/src/filemenu.cpp

409 lines
13 KiB

/*
* Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
*
* 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 <libfm/fm-actions.h>
#include "customaction_p.h"
#endif
#include <QMessageBox>
#include <QDebug>
#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<AppInfoAction*>(sender());
openFilesWithApp(action->appInfo());
}
#ifdef CUSTOM_ACTIONS
void FileMenu::onCustomActionTrigerred() {
CustomAction* action = static_cast<CustomAction*>(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