1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 05:28:11 +00:00
serenity/Userland/Services/Taskbar/QuickLaunchWidget.cpp
DexesTTP 7ceeb74535 AK: Use an enum instead of a bool for String::replace(all_occurences)
This commit has no behavior changes.

In particular, this does not fix any of the wrong uses of the previous
default parameter (which used to be 'false', meaning "only replace the
first occurence in the string"). It simply replaces the default uses by
String::replace(..., ReplaceMode::FirstOnly), leaving them incorrect.
2022-07-06 11:12:45 +02:00

221 lines
7.2 KiB
C++

/*
* Copyright (c) 2021, Fabian Blatz <fabianblatz@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "QuickLaunchWidget.h"
#include <AK/LexicalPath.h>
#include <Kernel/API/InodeWatcherFlags.h>
#include <LibConfig/Client.h>
#include <LibCore/FileWatcher.h>
#include <LibCore/MimeData.h>
#include <LibCore/Process.h>
#include <LibCore/System.h>
#include <LibDesktop/Launcher.h>
#include <LibGUI/BoxLayout.h>
#include <LibGUI/FileIconProvider.h>
#include <LibGUI/Menu.h>
#include <LibGUI/MessageBox.h>
#include <serenity.h>
#include <sys/stat.h>
namespace Taskbar {
constexpr auto quick_launch = "QuickLaunch"sv;
constexpr int quick_launch_button_size = 24;
ErrorOr<void> QuickLaunchEntryAppFile::launch() const
{
auto executable = m_app_file->executable();
pid_t pid = TRY(Core::System::fork());
if (pid == 0) {
if (chdir(Core::StandardPaths::home_directory().characters()) < 0) {
perror("chdir");
exit(1);
}
if (m_app_file->run_in_terminal())
execl("/bin/Terminal", "Terminal", "-e", executable.characters(), nullptr);
else
execl(executable.characters(), executable.characters(), nullptr);
perror("execl");
VERIFY_NOT_REACHED();
} else
TRY(Core::System::disown(pid));
return {};
}
ErrorOr<void> QuickLaunchEntryExecutable::launch() const
{
TRY(Core::Process::spawn(m_path));
return {};
}
GUI::Icon QuickLaunchEntryExecutable::icon() const
{
return GUI::FileIconProvider::icon_for_executable(m_path);
}
String QuickLaunchEntryExecutable::name() const
{
return LexicalPath { m_path }.basename();
}
ErrorOr<void> QuickLaunchEntryFile::launch() const
{
if (!Desktop::Launcher::open(URL::create_with_url_or_path(m_path))) {
// FIXME: LaunchServer doesn't inform us about errors
return Error::from_string_literal("Failed to open file");
}
return {};
}
GUI::Icon QuickLaunchEntryFile::icon() const
{
return GUI::FileIconProvider::icon_for_path(m_path);
}
String QuickLaunchEntryFile::name() const
{
// '=' is a special character in config files
return m_path;
}
QuickLaunchWidget::QuickLaunchWidget()
{
set_shrink_to_fit(true);
set_layout<GUI::HorizontalBoxLayout>();
layout()->set_spacing(0);
set_frame_thickness(0);
set_fixed_height(24);
m_context_menu = GUI::Menu::construct();
m_context_menu_default_action = GUI::Action::create("&Remove", [this](auto&) {
Config::remove_key("Taskbar", quick_launch, m_context_menu_app_name);
auto button = find_child_of_type_named<GUI::Button>(m_context_menu_app_name);
if (button) {
remove_child(*button);
}
});
m_context_menu->add_action(*m_context_menu_default_action);
auto keys = Config::list_keys("Taskbar", quick_launch);
for (auto& name : keys) {
auto value = Config::read_string("Taskbar", quick_launch, name);
auto entry = QuickLaunchEntry::create_from_config_value(value);
if (!entry)
continue;
add_or_adjust_button(name, entry.release_nonnull());
}
}
OwnPtr<QuickLaunchEntry> QuickLaunchEntry::create_from_config_value(StringView value)
{
if (!value.starts_with("/") && value.ends_with(".af")) {
auto af_path = String::formatted("{}/{}", Desktop::AppFile::APP_FILES_DIRECTORY, value);
return make<QuickLaunchEntryAppFile>(Desktop::AppFile::open(af_path));
}
return create_from_path(value);
}
OwnPtr<QuickLaunchEntry> QuickLaunchEntry::create_from_path(StringView path)
{
if (path.ends_with(".af"))
return make<QuickLaunchEntryAppFile>(Desktop::AppFile::open(path));
auto stat_or_error = Core::System::stat(path);
if (stat_or_error.is_error()) {
dbgln("Failed to stat quick launch entry file: {}", stat_or_error.release_error());
return {};
}
auto stat = stat_or_error.release_value();
if (S_ISREG(stat.st_mode) && (stat.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
return make<QuickLaunchEntryExecutable>(path);
return make<QuickLaunchEntryFile>(path);
}
static String sanitize_entry_name(String const& name)
{
return name.replace(" ", "", ReplaceMode::All).replace("=", "", ReplaceMode::All);
}
void QuickLaunchWidget::add_or_adjust_button(String const& button_name, NonnullOwnPtr<QuickLaunchEntry>&& entry)
{
auto file_name_to_watch = entry->file_name_to_watch();
if (!file_name_to_watch.is_null()) {
if (!m_watcher) {
// FIXME: Propagate errors
m_watcher = MUST(Core::FileWatcher::create());
m_watcher->on_change = [this](Core::FileWatcherEvent const& event) {
auto name = sanitize_entry_name(event.event_path);
dbgln("Removing QuickLaunch entry {}", name);
auto button = find_child_of_type_named<GUI::Button>(name);
if (button)
remove_child(*button);
};
}
// FIXME: Propagate errors
MUST(m_watcher->add_watch(file_name_to_watch, Core::FileWatcherEvent::Type::Deleted));
}
auto button = find_child_of_type_named<GUI::Button>(button_name);
if (!button)
button = &add<GUI::Button>();
button->set_fixed_size(quick_launch_button_size, quick_launch_button_size);
button->set_button_style(Gfx::ButtonStyle::Coolbar);
auto icon = entry->icon();
button->set_icon(icon.bitmap_for_size(16));
button->set_tooltip(entry->name());
button->set_name(button_name);
button->on_click = [entry = move(entry), this](auto) {
auto result = entry->launch();
if (result.is_error()) {
// FIXME: This message box is displayed in a weird position
GUI::MessageBox::show_error(window(), String::formatted("Failed to open quick launch entry: {}", result.release_error()));
}
};
button->on_context_menu_request = [this, button_name](auto& context_menu_event) {
m_context_menu_app_name = button_name;
m_context_menu->popup(context_menu_event.screen_position(), m_context_menu_default_action);
};
}
void QuickLaunchWidget::config_key_was_removed(String const& domain, String const& group, String const& key)
{
if (domain == "Taskbar" && group == quick_launch) {
auto button = find_child_of_type_named<GUI::Button>(key);
if (button)
remove_child(*button);
}
}
void QuickLaunchWidget::config_string_did_change(String const& domain, String const& group, String const& key, String const& value)
{
if (domain == "Taskbar" && group == quick_launch) {
auto entry = QuickLaunchEntry::create_from_config_value(value);
if (!entry)
return;
add_or_adjust_button(key, entry.release_nonnull());
}
}
void QuickLaunchWidget::drop_event(GUI::DropEvent& event)
{
event.accept();
if (event.mime_data().has_urls()) {
auto urls = event.mime_data().urls();
for (auto& url : urls) {
auto entry = QuickLaunchEntry::create_from_path(url.path());
if (entry) {
auto item_name = sanitize_entry_name(entry->name());
add_or_adjust_button(item_name, entry.release_nonnull());
Config::write_string("Taskbar", quick_launch, item_name, url.path());
}
}
}
}
}