mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-29 14:14:45 +03:00
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:
parent
8ae37bccf1
commit
7739497e34
Notes:
sideshowbarker
2024-07-19 04:49:09 +09:00
Author: https://github.com/tomuta Commit: https://github.com/SerenityOS/serenity/commit/7739497e34f Pull-request: https://github.com/SerenityOS/serenity/pull/2795
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@ class Launcher {
|
||||
public:
|
||||
enum class LauncherType {
|
||||
Default = 0,
|
||||
Application,
|
||||
UserPreferred,
|
||||
UserDefault
|
||||
};
|
||||
|
@ -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('.');
|
||||
|
@ -36,6 +36,7 @@ namespace LaunchServer {
|
||||
struct Handler {
|
||||
enum class Type {
|
||||
Default = 0,
|
||||
Application,
|
||||
UserPreferred,
|
||||
UserDefault
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user