mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 08:47:34 +00:00
LaunchServer+LibDesktop: Open from mime type
Before, LaunchServer would only open files based on their extension. This wouldn't work if the file had the wrong one.
This commit is contained in:
parent
031322fde3
commit
48766449e5
4 changed files with 65 additions and 12 deletions
|
@ -102,6 +102,17 @@ bool AppFile::run_in_terminal() const
|
||||||
return m_config->read_bool_entry("App", "RunInTerminal", false);
|
return m_config->read_bool_entry("App", "RunInTerminal", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector<String> AppFile::launcher_mime_types() const
|
||||||
|
{
|
||||||
|
Vector<String> mime_types;
|
||||||
|
for (auto& entry : m_config->read_entry("Launcher", "MimeTypes").split(',')) {
|
||||||
|
entry = entry.trim_whitespace();
|
||||||
|
if (!entry.is_empty())
|
||||||
|
mime_types.append(entry);
|
||||||
|
}
|
||||||
|
return mime_types;
|
||||||
|
}
|
||||||
|
|
||||||
Vector<String> AppFile::launcher_file_types() const
|
Vector<String> AppFile::launcher_file_types() const
|
||||||
{
|
{
|
||||||
Vector<String> file_types;
|
Vector<String> file_types;
|
||||||
|
|
|
@ -32,6 +32,7 @@ public:
|
||||||
String icon_path() const;
|
String icon_path() const;
|
||||||
GUI::Icon icon() const;
|
GUI::Icon icon() const;
|
||||||
bool run_in_terminal() const;
|
bool run_in_terminal() const;
|
||||||
|
Vector<String> launcher_mime_types() const;
|
||||||
Vector<String> launcher_file_types() const;
|
Vector<String> launcher_file_types() const;
|
||||||
Vector<String> launcher_protocols() const;
|
Vector<String> launcher_protocols() const;
|
||||||
bool spawn() const;
|
bool spawn() const;
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibCore/ConfigFile.h>
|
#include <LibCore/ConfigFile.h>
|
||||||
#include <LibCore/File.h>
|
#include <LibCore/File.h>
|
||||||
|
#include <LibCore/MimeData.h>
|
||||||
#include <LibCore/Process.h>
|
#include <LibCore/Process.h>
|
||||||
#include <LibDesktop/AppFile.h>
|
#include <LibDesktop/AppFile.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
@ -83,6 +84,9 @@ void Launcher::load_handlers(String const& af_dir)
|
||||||
Desktop::AppFile::for_each([&](auto af) {
|
Desktop::AppFile::for_each([&](auto af) {
|
||||||
auto app_name = af->name();
|
auto app_name = af->name();
|
||||||
auto app_executable = af->executable();
|
auto app_executable = af->executable();
|
||||||
|
HashTable<String> mime_types;
|
||||||
|
for (auto& mime_type : af->launcher_mime_types())
|
||||||
|
mime_types.set(mime_type);
|
||||||
HashTable<String> file_types;
|
HashTable<String> file_types;
|
||||||
for (auto& file_type : af->launcher_file_types())
|
for (auto& file_type : af->launcher_file_types())
|
||||||
file_types.set(file_type);
|
file_types.set(file_type);
|
||||||
|
@ -90,13 +94,22 @@ void Launcher::load_handlers(String const& af_dir)
|
||||||
for (auto& protocol : af->launcher_protocols())
|
for (auto& protocol : af->launcher_protocols())
|
||||||
protocols.set(protocol);
|
protocols.set(protocol);
|
||||||
if (access(app_executable.characters(), X_OK) == 0)
|
if (access(app_executable.characters(), X_OK) == 0)
|
||||||
m_handlers.set(app_executable, { Handler::Type::Default, app_name, app_executable, file_types, protocols });
|
m_handlers.set(app_executable, { Handler::Type::Default, app_name, app_executable, mime_types, file_types, protocols });
|
||||||
},
|
},
|
||||||
af_dir);
|
af_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Launcher::load_config(Core::ConfigFile const& cfg)
|
void Launcher::load_config(Core::ConfigFile const& cfg)
|
||||||
{
|
{
|
||||||
|
for (auto key : cfg.keys("MimeType")) {
|
||||||
|
auto handler = cfg.read_entry("MimeType", key).trim_whitespace();
|
||||||
|
if (handler.is_empty())
|
||||||
|
continue;
|
||||||
|
if (access(handler.characters(), X_OK) != 0)
|
||||||
|
continue;
|
||||||
|
m_mime_handlers.set(key.to_lowercase(), handler);
|
||||||
|
}
|
||||||
|
|
||||||
for (auto key : cfg.keys("FileType")) {
|
for (auto key : cfg.keys("FileType")) {
|
||||||
auto handler = cfg.read_entry("FileType", key).trim_whitespace();
|
auto handler = cfg.read_entry("FileType", key).trim_whitespace();
|
||||||
if (handler.is_empty())
|
if (handler.is_empty())
|
||||||
|
@ -156,6 +169,20 @@ Vector<String> Launcher::handlers_with_details_for_url(const URL& url)
|
||||||
return handlers;
|
return handlers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Optional<String> Launcher::mime_type_for_file(String path)
|
||||||
|
{
|
||||||
|
auto file_or_error = Core::File::open(path, Core::OpenMode::ReadOnly);
|
||||||
|
if (file_or_error.is_error()) {
|
||||||
|
return {};
|
||||||
|
} else {
|
||||||
|
auto file = file_or_error.release_value();
|
||||||
|
// Read accounts for longest possible offset + signature we currently match against.
|
||||||
|
auto bytes = file->read(0x9006);
|
||||||
|
|
||||||
|
return Core::guess_mime_type_based_on_sniffed_bytes(bytes.bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Launcher::open_url(const URL& url, String const& handler_name)
|
bool Launcher::open_url(const URL& url, String const& handler_name)
|
||||||
{
|
{
|
||||||
if (!handler_name.is_null())
|
if (!handler_name.is_null())
|
||||||
|
@ -275,12 +302,20 @@ void Launcher::for_each_handler_for_path(String const& path, Function<bool(Handl
|
||||||
f(get_handler_for_executable(Handler::Type::Application, path));
|
f(get_handler_for_executable(Handler::Type::Application, path));
|
||||||
|
|
||||||
auto extension = LexicalPath::extension(path).to_lowercase();
|
auto extension = LexicalPath::extension(path).to_lowercase();
|
||||||
|
auto mime_type = mime_type_for_file(path);
|
||||||
for_each_handler(extension, m_file_handlers, [&](auto const& handler) -> bool {
|
if (mime_type.has_value()) {
|
||||||
if (handler.handler_type != Handler::Type::Default || handler.file_types.contains(extension))
|
for_each_handler(mime_type.value(), m_mime_handlers, [&](auto const& handler) -> bool {
|
||||||
return f(handler);
|
if (handler.handler_type != Handler::Type::Default || handler.mime_types.contains(mime_type.value()))
|
||||||
return false;
|
return f(handler);
|
||||||
});
|
return false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
for_each_handler(extension, m_file_handlers, [&](auto const& handler) -> bool {
|
||||||
|
if (handler.handler_type != Handler::Type::Default || handler.file_types.contains(extension))
|
||||||
|
return f(handler);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Launcher::open_file_url(const URL& url)
|
bool Launcher::open_file_url(const URL& url)
|
||||||
|
@ -312,10 +347,13 @@ bool Launcher::open_file_url(const URL& url)
|
||||||
if ((st.st_mode & S_IFMT) == S_IFREG && 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(), {});
|
return spawn(url.path(), {});
|
||||||
|
|
||||||
auto extension_parts = url.path().to_lowercase().split('.');
|
auto extension = LexicalPath::extension(url.path()).to_lowercase();
|
||||||
String extension = {};
|
auto mime_type = mime_type_for_file(url.path());
|
||||||
if (extension_parts.size() > 1)
|
|
||||||
extension = extension_parts.last();
|
auto mime_type_or_extension = extension;
|
||||||
|
bool should_use_mime_type = mime_type.has_value() && m_mime_handlers.get(mime_type.value()).has_value();
|
||||||
|
if (should_use_mime_type)
|
||||||
|
mime_type_or_extension = mime_type.value();
|
||||||
|
|
||||||
auto handler_optional = m_file_handlers.get("txt");
|
auto handler_optional = m_file_handlers.get("txt");
|
||||||
if (!handler_optional.has_value())
|
if (!handler_optional.has_value())
|
||||||
|
@ -339,6 +377,6 @@ bool Launcher::open_file_url(const URL& url)
|
||||||
|
|
||||||
additional_parameters.append(filepath);
|
additional_parameters.append(filepath);
|
||||||
|
|
||||||
return open_with_user_preferences(m_file_handlers, extension, additional_parameters, default_handler);
|
return open_with_user_preferences(should_use_mime_type ? m_mime_handlers : m_file_handlers, mime_type_or_extension, additional_parameters, default_handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ struct Handler {
|
||||||
Type handler_type;
|
Type handler_type;
|
||||||
String name;
|
String name;
|
||||||
String executable;
|
String executable;
|
||||||
|
HashTable<String> mime_types {};
|
||||||
HashTable<String> file_types {};
|
HashTable<String> file_types {};
|
||||||
HashTable<String> protocols {};
|
HashTable<String> protocols {};
|
||||||
|
|
||||||
|
@ -47,7 +48,9 @@ private:
|
||||||
HashMap<String, Handler> m_handlers;
|
HashMap<String, Handler> m_handlers;
|
||||||
HashMap<String, String> m_protocol_handlers;
|
HashMap<String, String> m_protocol_handlers;
|
||||||
HashMap<String, String> m_file_handlers;
|
HashMap<String, String> m_file_handlers;
|
||||||
|
HashMap<String, String> m_mime_handlers;
|
||||||
|
|
||||||
|
Optional<String> mime_type_for_file(String path);
|
||||||
Handler get_handler_for_executable(Handler::Type, String const&) const;
|
Handler get_handler_for_executable(Handler::Type, String const&) const;
|
||||||
void for_each_handler(String const& key, HashMap<String, String>& user_preferences, Function<bool(Handler const&)> f);
|
void for_each_handler(String const& key, HashMap<String, String>& user_preferences, Function<bool(Handler const&)> f);
|
||||||
void for_each_handler_for_path(String const&, Function<bool(Handler const&)> f);
|
void for_each_handler_for_path(String const&, Function<bool(Handler const&)> f);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue