From 7739497e34f7617cb2f6d838cc3710596b91d13e Mon Sep 17 00:00:00 2001 From: Tom Date: Tue, 14 Jul 2020 09:36:00 -0600 Subject: [PATCH] FileManager: Allow double-clicking applications again By adding a special LauncherType::Application we can still get meta data for the application, but also know that we should consider executing that binary as the default action. LaunchServer will not do this for us, as it should probably not be allowed to run arbitrary binaries that haven't been registered as handlers. --- Applications/FileManager/DirectoryView.cpp | 9 ++++++-- Applications/FileManager/DirectoryView.h | 1 + Applications/FileManager/main.cpp | 27 ++++++++++++++-------- Libraries/LibDesktop/Launcher.cpp | 5 +++- Libraries/LibDesktop/Launcher.h | 1 + Services/LaunchServer/Launcher.cpp | 18 +++++++++++---- Services/LaunchServer/Launcher.h | 1 + 7 files changed, 45 insertions(+), 17 deletions(-) diff --git a/Applications/FileManager/DirectoryView.cpp b/Applications/FileManager/DirectoryView.cpp index bcf49179ab..65834ee3ea 100644 --- a/Applications/FileManager/DirectoryView.cpp +++ b/Applications/FileManager/DirectoryView.cpp @@ -45,6 +45,11 @@ NonnullRefPtr LauncherHandler::create_launch_action(Function DirectoryView::get_default_launch_handler(const NonnullRefPtrVector& handlers) { + // If this is an application, pick it first + for (size_t i = 0; i < handlers.size(); i++) { + if (handlers[i].details().launcher_type == Desktop::Launcher::LauncherType::Application) + return handlers[i]; + } // 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) @@ -99,8 +104,8 @@ void DirectoryView::handle_activation(const GUI::ModelIndex& index) 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()); + if (default_launcher && on_launch) { + on_launch(url, *default_launcher); } else { auto error_message = String::format("Could not open %s", path.characters()); GUI::MessageBox::show(error_message, "File Manager", GUI::MessageBox::Type::Error); diff --git a/Applications/FileManager/DirectoryView.h b/Applications/FileManager/DirectoryView.h index 88fedefb74..b5728987ea 100644 --- a/Applications/FileManager/DirectoryView.h +++ b/Applications/FileManager/DirectoryView.h @@ -70,6 +70,7 @@ public: void refresh(); + Function on_launch; Function on_path_change; Function on_selection_change; Function on_context_menu_request; diff --git a/Applications/FileManager/main.cpp b/Applications/FileManager/main.cpp index af96c596a2..0237349b2a 100644 --- a/Applications/FileManager/main.cpp +++ b/Applications/FileManager/main.cpp @@ -742,11 +742,16 @@ int run_in_windowed_mode(RefPtr config, String initial_locatio NonnullRefPtrVector current_file_handlers; RefPtr file_context_menu_action_default_action; - auto file_open_action_handler = [&](const LauncherHandler& launcher_handler) { + directory_view.on_launch = [&](const AK::URL&, const LauncherHandler& launcher_handler) { pid_t child; - for (auto& path : selected_file_paths()) { - const char* argv[] = { launcher_handler.details().name.characters(), path.characters(), nullptr }; + if (launcher_handler.details().launcher_type == Desktop::Launcher::LauncherType::Application) { + const char* argv[] = { launcher_handler.details().name.characters(), nullptr }; posix_spawn(&child, launcher_handler.details().executable.characters(), nullptr, nullptr, const_cast(argv), environ); + } else { + 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(argv), environ); + } } }; @@ -759,7 +764,8 @@ int run_in_windowed_mode(RefPtr config, String initial_locatio folder_specific_paste_action->set_enabled(should_get_enabled); directory_context_menu->popup(event.screen_position()); } else { - current_file_handlers = directory_view.get_launch_handlers(node.full_path(directory_view.model())); + auto full_path = node.full_path(directory_view.model()); + current_file_handlers = directory_view.get_launch_handlers(full_path); file_context_menu = GUI::Menu::construct("Directory View File"); file_context_menu->add_action(copy_action); @@ -770,10 +776,13 @@ int run_in_windowed_mode(RefPtr config, String initial_locatio 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); + auto file_open_action = default_file_handler->create_launch_action([&, full_path = move(full_path)](auto& launcher_handler) { + directory_view.on_launch(URL::create_with_file_protocol(full_path), launcher_handler); }); - file_open_action->set_text(String::format("Open in %s", file_open_action->text().characters())); + if (default_file_handler->details().launcher_type == Desktop::Launcher::LauncherType::Application) + file_open_action->set_text(String::format("Run %s", file_open_action->text().characters())); + else + file_open_action->set_text(String::format("Open in %s", file_open_action->text().characters())); file_context_menu_action_default_action = file_open_action; @@ -789,8 +798,8 @@ int run_in_windowed_mode(RefPtr config, String initial_locatio 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); + file_open_with_menu.add_action(handler.create_launch_action([&, full_path = move(full_path)](auto& launcher_handler) { + directory_view.on_launch(URL::create_with_file_protocol(full_path), launcher_handler); })); } } diff --git a/Libraries/LibDesktop/Launcher.cpp b/Libraries/LibDesktop/Launcher.cpp index 8a42beebc8..39b6888810 100644 --- a/Libraries/LibDesktop/Launcher.cpp +++ b/Libraries/LibDesktop/Launcher.cpp @@ -44,7 +44,9 @@ auto Launcher::Details::from_details_str(const String& details_str) -> NonnullRe details->name = obj.get("name").to_string(); if (auto type_value = obj.get_ptr("type")) { auto type_str = type_value->to_string(); - if (type_str == "userpreferred") + if (type_str == "app") + details->launcher_type = LauncherType::Application; + else if (type_str == "userpreferred") details->launcher_type = LauncherType::UserPreferred; else if (type_str == "userdefault") details->launcher_type = LauncherType::UserDefault; @@ -83,6 +85,7 @@ bool Launcher::open(const URL& url, const String& handler_name) bool Launcher::open(const URL& url, const Details& details) { + ASSERT(details.launcher_type != LauncherType::Application); // Launcher should not be used to execute arbitrary applications return open(url, details.executable); } diff --git a/Libraries/LibDesktop/Launcher.h b/Libraries/LibDesktop/Launcher.h index 0dc0748231..3a42b1ae82 100644 --- a/Libraries/LibDesktop/Launcher.h +++ b/Libraries/LibDesktop/Launcher.h @@ -38,6 +38,7 @@ class Launcher { public: enum class LauncherType { Default = 0, + Application, UserPreferred, UserDefault }; diff --git a/Services/LaunchServer/Launcher.cpp b/Services/LaunchServer/Launcher.cpp index 4715de42a6..ef179ec403 100644 --- a/Services/LaunchServer/Launcher.cpp +++ b/Services/LaunchServer/Launcher.cpp @@ -66,6 +66,9 @@ String Handler::to_details_str() const obj.add("executable", executable); obj.add("name", name); switch (handler_type) { + case Type::Application: + obj.add("type", "app"); + break; case Type::UserDefault: obj.add("type", "userdefault"); break; @@ -75,10 +78,12 @@ String Handler::to_details_str() const default: break; } - JsonObject icons_obj; - for (auto& icon : icons) - icons_obj.set(icon.key, icon.value); - obj.add("icons", move(icons_obj)); + if (!icons.is_empty()) { + JsonObject icons_obj; + for (auto& icon : icons) + icons_obj.set(icon.key, icon.value); + obj.add("icons", move(icons_obj)); + } obj.finish(); return builder.build(); } @@ -282,6 +287,9 @@ void Launcher::for_each_handler_for_path(const String& path, Function bool { @@ -303,7 +311,7 @@ bool Launcher::open_file_url(const URL& url) if (S_ISDIR(st.st_mode)) return spawn("/bin/FileManager", url.path()); - if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) + if ((st.st_mode & S_IFMT) == S_IFREG && st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) return spawn(url.path(), {}); auto extension_parts = url.path().to_lowercase().split('.'); diff --git a/Services/LaunchServer/Launcher.h b/Services/LaunchServer/Launcher.h index 2796cdb960..2108ffd897 100644 --- a/Services/LaunchServer/Launcher.h +++ b/Services/LaunchServer/Launcher.h @@ -36,6 +36,7 @@ namespace LaunchServer { struct Handler { enum class Type { Default = 0, + Application, UserPreferred, UserDefault };