diff options
author | Tom <tomut@yahoo.com> | 2020-07-13 18:58:21 -0600 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-07-15 00:11:30 +0200 |
commit | 50903fd88c14ae8d00a8f16c15364a2c92a0db81 (patch) | |
tree | 26724275bfd52c57f7c36b8810d37c8dbad6bbc0 /Applications/FileManager | |
parent | 535113bac42fb3fd05dd31878ddf01ec5690f1c3 (diff) | |
download | serenity-50903fd88c14ae8d00a8f16c15364a2c92a0db81.zip |
FileManager: Add "Open with" menu if alternative applications are available
Diffstat (limited to 'Applications/FileManager')
-rw-r--r-- | Applications/FileManager/DirectoryView.cpp | 58 | ||||
-rw-r--r-- | Applications/FileManager/DirectoryView.h | 20 | ||||
-rw-r--r-- | Applications/FileManager/main.cpp | 71 |
3 files changed, 128 insertions, 21 deletions
diff --git a/Applications/FileManager/DirectoryView.cpp b/Applications/FileManager/DirectoryView.cpp index e188e96111..bcf49179ab 100644 --- a/Applications/FileManager/DirectoryView.cpp +++ b/Applications/FileManager/DirectoryView.cpp @@ -27,12 +27,56 @@ #include "DirectoryView.h" #include <AK/NumberFormat.h> #include <AK/StringBuilder.h> -#include <AK/URL.h> -#include <LibDesktop/Launcher.h> +#include <LibGUI/MessageBox.h> #include <LibGUI/SortingProxyModel.h> #include <stdio.h> #include <unistd.h> +NonnullRefPtr<GUI::Action> LauncherHandler::create_launch_action(Function<void(const LauncherHandler&)> launch_handler) +{ + RefPtr<Gfx::Bitmap> icon; + auto icon_file = details().icons.get("16x16"); + if (icon_file.has_value()) + icon = Gfx::Bitmap::load_from_file(icon_file.value()); + return GUI::Action::create(details().name, move(icon), [this, launch_handler = move(launch_handler)](auto&) { + launch_handler(*this); + }); +} + +RefPtr<LauncherHandler> DirectoryView::get_default_launch_handler(const NonnullRefPtrVector<LauncherHandler>& handlers) +{ + // If there's a handler preferred by the user, pick this first + for (size_t i = 0; i < handlers.size(); i++) { + if (handlers[i].details().launcher_type == Desktop::Launcher::LauncherType::UserPreferred) + return handlers[i]; + } + // Otherwise, use the user's default, if available + for (size_t i = 0; i < handlers.size(); i++) { + if (handlers[i].details().launcher_type == Desktop::Launcher::LauncherType::UserDefault) + return handlers[i]; + } + // If still no match, use the first one we find + if (!handlers.is_empty()) { + return handlers[0]; + } + + return {}; +} + +NonnullRefPtrVector<LauncherHandler> DirectoryView::get_launch_handlers(const URL& url) +{ + NonnullRefPtrVector<LauncherHandler> handlers; + for (auto& h : Desktop::Launcher::get_handlers_with_details_for_url(url)) { + handlers.append(adopt(*new LauncherHandler(h))); + } + return handlers; +} + +NonnullRefPtrVector<LauncherHandler> DirectoryView::get_launch_handlers(const String& path) +{ + return get_launch_handlers(URL::create_with_file_protocol(path)); +} + void DirectoryView::handle_activation(const GUI::ModelIndex& index) { if (!index.is_valid()) @@ -52,7 +96,15 @@ void DirectoryView::handle_activation(const GUI::ModelIndex& index) return; } - Desktop::Launcher::open(URL::create_with_file_protocol(path)); + auto url = URL::create_with_file_protocol(path); + auto launcher_handlers = get_launch_handlers(url); + auto default_launcher = get_default_launch_handler(launcher_handlers); + if (default_launcher) { + Desktop::Launcher::open(url, default_launcher->details()); + } else { + auto error_message = String::format("Could not open %s", path.characters()); + GUI::MessageBox::show(error_message, "File Manager", GUI::MessageBox::Type::Error); + } } DirectoryView::DirectoryView() diff --git a/Applications/FileManager/DirectoryView.h b/Applications/FileManager/DirectoryView.h index 1bef4e4b2b..88fedefb74 100644 --- a/Applications/FileManager/DirectoryView.h +++ b/Applications/FileManager/DirectoryView.h @@ -26,7 +26,10 @@ #pragma once +#include <AK/URL.h> #include <AK/Vector.h> +#include <LibDesktop/Launcher.h> +#include <LibGUI/Action.h> #include <LibGUI/ColumnsView.h> #include <LibGUI/FileSystemModel.h> #include <LibGUI/IconView.h> @@ -34,6 +37,20 @@ #include <LibGUI/TableView.h> #include <sys/stat.h> +class LauncherHandler: public RefCounted<LauncherHandler> { +public: + LauncherHandler(const NonnullRefPtr<Desktop::Launcher::Details>& details) + : m_details(details) + { + } + + NonnullRefPtr<GUI::Action> create_launch_action(Function<void(const LauncherHandler&)>); + const Desktop::Launcher::Details& details() const { return *m_details; } + +private: + NonnullRefPtr<Desktop::Launcher::Details> m_details; +}; + class DirectoryView final : public GUI::StackWidget , private GUI::ModelClient { C_OBJECT(DirectoryView) @@ -47,6 +64,9 @@ public: void open_next_directory(); int path_history_size() const { return m_path_history.size(); } int path_history_position() const { return m_path_history_position; } + static RefPtr<LauncherHandler> get_default_launch_handler(const NonnullRefPtrVector<LauncherHandler>& handlers); + NonnullRefPtrVector<LauncherHandler> get_launch_handlers(const URL& url); + NonnullRefPtrVector<LauncherHandler> get_launch_handlers(const String& path); void refresh(); diff --git a/Applications/FileManager/main.cpp b/Applications/FileManager/main.cpp index 6d69c7b894..af96c596a2 100644 --- a/Applications/FileManager/main.cpp +++ b/Applications/FileManager/main.cpp @@ -313,7 +313,6 @@ int run_in_windowed_mode(RefPtr<Core::ConfigFile> config, String initial_locatio }; auto directory_context_menu = GUI::Menu::construct("Directory View Directory"); - auto file_context_menu = GUI::Menu::construct("Directory View File"); auto directory_view_context_menu = GUI::Menu::construct("Directory View"); auto tree_view_directory_context_menu = GUI::Menu::construct("Tree View Directory"); auto tree_view_context_menu = GUI::Menu::construct("Tree View"); @@ -719,28 +718,12 @@ int run_in_windowed_mode(RefPtr<Core::ConfigFile> config, String initial_locatio copy_action->set_enabled(!selection.is_empty()); }; - auto open_in_text_editor_action = GUI::Action::create("Open in TextEditor...", Gfx::Bitmap::load_from_file("/res/icons/16x16/app-text-editor.png"), [&](auto&) { - pid_t child; - for (auto& path : selected_file_paths()) { - const char* argv[] = { "TextEditor", path.characters(), nullptr }; - posix_spawn(&child, "/bin/TextEditor", nullptr, nullptr, const_cast<char**>(argv), environ); - } - }); - directory_context_menu->add_action(copy_action); directory_context_menu->add_action(folder_specific_paste_action); directory_context_menu->add_action(delete_action); directory_context_menu->add_separator(); directory_context_menu->add_action(properties_action); - file_context_menu->add_action(copy_action); - file_context_menu->add_action(paste_action); - file_context_menu->add_action(delete_action); - file_context_menu->add_separator(); - file_context_menu->add_action(open_in_text_editor_action); - file_context_menu->add_separator(); - file_context_menu->add_action(properties_action); - directory_view_context_menu->add_action(mkdir_action); directory_view_context_menu->add_action(paste_action); directory_view_context_menu->add_action(open_terminal_action); @@ -755,6 +738,18 @@ int run_in_windowed_mode(RefPtr<Core::ConfigFile> config, String initial_locatio tree_view_directory_context_menu->add_separator(); tree_view_directory_context_menu->add_action(mkdir_action); + RefPtr<GUI::Menu> file_context_menu; + NonnullRefPtrVector<LauncherHandler> current_file_handlers; + RefPtr<GUI::Action> file_context_menu_action_default_action; + + auto file_open_action_handler = [&](const LauncherHandler& launcher_handler) { + pid_t child; + for (auto& path : selected_file_paths()) { + const char* argv[] = { launcher_handler.details().name.characters(), path.characters(), nullptr }; + posix_spawn(&child, launcher_handler.details().executable.characters(), nullptr, nullptr, const_cast<char**>(argv), environ); + } + }; + directory_view.on_context_menu_request = [&](const GUI::AbstractView&, const GUI::ModelIndex& index, const GUI::ContextMenuEvent& event) { if (index.is_valid()) { auto& node = directory_view.model().node(index); @@ -764,7 +759,47 @@ int run_in_windowed_mode(RefPtr<Core::ConfigFile> config, String initial_locatio folder_specific_paste_action->set_enabled(should_get_enabled); directory_context_menu->popup(event.screen_position()); } else { - file_context_menu->popup(event.screen_position(), open_in_text_editor_action); + current_file_handlers = directory_view.get_launch_handlers(node.full_path(directory_view.model())); + + file_context_menu = GUI::Menu::construct("Directory View File"); + file_context_menu->add_action(copy_action); + file_context_menu->add_action(paste_action); + file_context_menu->add_action(delete_action); + + file_context_menu->add_separator(); + bool added_open_menu_items = false; + auto default_file_handler = directory_view.get_default_launch_handler(current_file_handlers); + if (default_file_handler) { + auto file_open_action = default_file_handler->create_launch_action([&](auto& launcher_handler) { + file_open_action_handler(launcher_handler); + }); + file_open_action->set_text(String::format("Open in %s", file_open_action->text().characters())); + + file_context_menu_action_default_action = file_open_action; + + file_context_menu->add_action(move(file_open_action)); + added_open_menu_items = true; + } else { + file_context_menu_action_default_action.clear(); + } + + if (current_file_handlers.size() > 1) { + added_open_menu_items = true; + auto& file_open_with_menu = file_context_menu->add_submenu("Open with"); + for (auto& handler : current_file_handlers) { + if (&handler == default_file_handler.ptr()) + continue; + file_open_with_menu.add_action(handler.create_launch_action([&](auto& launcher_handler) { + file_open_action_handler(launcher_handler); + })); + } + } + + if (added_open_menu_items) + file_context_menu->add_separator(); + + file_context_menu->add_action(properties_action); + file_context_menu->popup(event.screen_position(), file_context_menu_action_default_action); } } else { directory_view_context_menu->popup(event.screen_position()); |