mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 12:38:12 +00:00
FileManager: Add "Open with" menu if alternative applications are available
This commit is contained in:
parent
535113bac4
commit
50903fd88c
4 changed files with 129 additions and 21 deletions
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -106,6 +106,7 @@ public:
|
|||
virtual ~Action() override;
|
||||
|
||||
String text() const { return m_text; }
|
||||
void set_text(String text) { m_text = move(text); }
|
||||
Shortcut shortcut() const { return m_shortcut; }
|
||||
const Gfx::Bitmap* icon() const { return m_icon.ptr(); }
|
||||
void set_icon(const Gfx::Bitmap*);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue