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.
This commit is contained in:
Tom 2020-07-14 09:36:00 -06:00 committed by Andreas Kling
parent 8ae37bccf1
commit 7739497e34
Notes: sideshowbarker 2024-07-19 04:49:09 +09:00
7 changed files with 45 additions and 17 deletions

View File

@ -45,6 +45,11 @@ NonnullRefPtr<GUI::Action> LauncherHandler::create_launch_action(Function<void(c
RefPtr<LauncherHandler> DirectoryView::get_default_launch_handler(const NonnullRefPtrVector<LauncherHandler>& 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);

View File

@ -70,6 +70,7 @@ public:
void refresh();
Function<void(const AK::URL&, const LauncherHandler&)> on_launch;
Function<void(const StringView&)> on_path_change;
Function<void(GUI::AbstractView&)> on_selection_change;
Function<void(const GUI::AbstractView&, const GUI::ModelIndex&, const GUI::ContextMenuEvent&)> on_context_menu_request;

View File

@ -742,11 +742,16 @@ int run_in_windowed_mode(RefPtr<Core::ConfigFile> config, String initial_locatio
NonnullRefPtrVector<LauncherHandler> current_file_handlers;
RefPtr<GUI::Action> 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<char**>(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<char**>(argv), environ);
}
}
};
@ -759,7 +764,8 @@ 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 {
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<Core::ConfigFile> 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<Core::ConfigFile> 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);
}));
}
}

View File

@ -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);
}

View File

@ -38,6 +38,7 @@ class Launcher {
public:
enum class LauncherType {
Default = 0,
Application,
UserPreferred,
UserDefault
};

View File

@ -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(const
return;
}
if ((st.st_mode & S_IFMT) == S_IFREG && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
f(get_handler_for_executable(Handler::Type::Application, path));
auto extension = LexicalPath(path).extension().to_lowercase();
for_each_handler(extension, m_file_handlers, [&](const auto& handler) -> 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('.');

View File

@ -36,6 +36,7 @@ namespace LaunchServer {
struct Handler {
enum class Type {
Default = 0,
Application,
UserPreferred,
UserDefault
};