QuickLaunchWidget.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /*
  2. * Copyright (c) 2021, Fabian Blatz <fabianblatz@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "QuickLaunchWidget.h"
  7. #include "LibCore/IODevice.h"
  8. #include <AK/LexicalPath.h>
  9. #include <LibConfig/Client.h>
  10. #include <LibCore/MimeData.h>
  11. #include <LibCore/Process.h>
  12. #include <LibCore/System.h>
  13. #include <LibGUI/BoxLayout.h>
  14. #include <LibGUI/FileIconProvider.h>
  15. #include <LibGUI/Menu.h>
  16. #include <LibGUI/MessageBox.h>
  17. #include <serenity.h>
  18. #include <sys/stat.h>
  19. namespace Taskbar {
  20. constexpr auto quick_launch = "QuickLaunch"sv;
  21. constexpr int quick_launch_button_size = 24;
  22. ErrorOr<void> QuickLaunchEntryAppFile::launch() const
  23. {
  24. auto executable = m_app_file->executable();
  25. pid_t pid = TRY(Core::System::fork());
  26. if (pid == 0) {
  27. if (chdir(Core::StandardPaths::home_directory().characters()) < 0) {
  28. perror("chdir");
  29. exit(1);
  30. }
  31. if (m_app_file->run_in_terminal())
  32. execl("/bin/Terminal", "Terminal", "-e", executable.characters(), nullptr);
  33. else
  34. execl(executable.characters(), executable.characters(), nullptr);
  35. perror("execl");
  36. VERIFY_NOT_REACHED();
  37. } else
  38. TRY(Core::System::disown(pid));
  39. return {};
  40. }
  41. ErrorOr<void> QuickLaunchEntryExecutable::launch() const
  42. {
  43. auto pid = Core::Process::spawn(m_path);
  44. if (pid < 0)
  45. return Error::from_syscall("Core::Process::spawn", -errno);
  46. return {};
  47. }
  48. GUI::Icon QuickLaunchEntryExecutable::icon() const
  49. {
  50. return GUI::FileIconProvider::icon_for_executable(m_path);
  51. }
  52. String QuickLaunchEntryExecutable::name() const
  53. {
  54. return LexicalPath { m_path }.basename();
  55. }
  56. QuickLaunchWidget::QuickLaunchWidget()
  57. {
  58. set_shrink_to_fit(true);
  59. set_layout<GUI::HorizontalBoxLayout>();
  60. layout()->set_spacing(0);
  61. set_frame_thickness(0);
  62. set_fixed_height(24);
  63. m_context_menu = GUI::Menu::construct();
  64. m_context_menu_default_action = GUI::Action::create("&Remove", [this](auto&) {
  65. Config::remove_key("Taskbar", quick_launch, m_context_menu_app_name);
  66. auto button = find_child_of_type_named<GUI::Button>(m_context_menu_app_name);
  67. if (button) {
  68. remove_child(*button);
  69. }
  70. });
  71. m_context_menu->add_action(*m_context_menu_default_action);
  72. auto keys = Config::list_keys("Taskbar", quick_launch);
  73. for (auto& name : keys) {
  74. auto value = Config::read_string("Taskbar", quick_launch, name);
  75. auto entry = QuickLaunchEntry::create_from_config_value(value);
  76. if (!entry)
  77. continue;
  78. add_or_adjust_button(name, entry.release_nonnull());
  79. }
  80. }
  81. QuickLaunchWidget::~QuickLaunchWidget()
  82. {
  83. }
  84. OwnPtr<QuickLaunchEntry> QuickLaunchEntry::create_from_config_value(StringView value)
  85. {
  86. if (!value.starts_with("/") && value.ends_with(".af")) {
  87. auto af_path = String::formatted("{}/{}", Desktop::AppFile::APP_FILES_DIRECTORY, value);
  88. return make<QuickLaunchEntryAppFile>(Desktop::AppFile::open(af_path));
  89. } else
  90. return create_from_path(value);
  91. }
  92. OwnPtr<QuickLaunchEntry> QuickLaunchEntry::create_from_path(StringView path)
  93. {
  94. if (path.ends_with(".af"))
  95. return make<QuickLaunchEntryAppFile>(Desktop::AppFile::open(path));
  96. auto stat_or_error = Core::System::stat(path);
  97. if (stat_or_error.is_error()) {
  98. dbgln("Failed to stat quick launch entry file: {}", stat_or_error.release_error());
  99. return {};
  100. }
  101. auto stat = stat_or_error.release_value();
  102. if (stat.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
  103. return make<QuickLaunchEntryExecutable>(path);
  104. dbgln("Config value {} is not a valid quick launch entry", path);
  105. return {};
  106. }
  107. void QuickLaunchWidget::add_or_adjust_button(String const& button_name, NonnullOwnPtr<QuickLaunchEntry>&& entry)
  108. {
  109. auto button = find_child_of_type_named<GUI::Button>(button_name);
  110. if (!button)
  111. button = &add<GUI::Button>();
  112. button->set_fixed_size(quick_launch_button_size, quick_launch_button_size);
  113. button->set_button_style(Gfx::ButtonStyle::Coolbar);
  114. auto icon = entry->icon();
  115. button->set_icon(icon.bitmap_for_size(16));
  116. button->set_tooltip(entry->name());
  117. button->set_name(button_name);
  118. button->on_click = [entry = move(entry), this](auto) {
  119. auto result = entry->launch();
  120. if (result.is_error())
  121. GUI::MessageBox::show_error(window(), String::formatted("Failed to open quick launch entry: {}", result.release_error()));
  122. };
  123. button->on_context_menu_request = [this, button_name](auto& context_menu_event) {
  124. m_context_menu_app_name = button_name;
  125. m_context_menu->popup(context_menu_event.screen_position(), m_context_menu_default_action);
  126. };
  127. }
  128. void QuickLaunchWidget::config_key_was_removed(String const& domain, String const& group, String const& key)
  129. {
  130. if (domain == "Taskbar" && group == quick_launch) {
  131. auto button = find_child_of_type_named<GUI::Button>(key);
  132. if (button)
  133. remove_child(*button);
  134. }
  135. }
  136. void QuickLaunchWidget::config_string_did_change(String const& domain, String const& group, String const& key, String const& value)
  137. {
  138. if (domain == "Taskbar" && group == quick_launch) {
  139. auto entry = QuickLaunchEntry::create_from_config_value(value);
  140. if (!entry)
  141. return;
  142. add_or_adjust_button(key, entry.release_nonnull());
  143. }
  144. }
  145. void QuickLaunchWidget::drop_event(GUI::DropEvent& event)
  146. {
  147. event.accept();
  148. if (event.mime_data().has_urls()) {
  149. auto urls = event.mime_data().urls();
  150. for (auto& url : urls) {
  151. auto entry = QuickLaunchEntry::create_from_path(url.path());
  152. if (entry) {
  153. auto item_name = entry->name().replace(" ", "", true);
  154. add_or_adjust_button(item_name, entry.release_nonnull());
  155. Config::write_string("Taskbar", quick_launch, item_name, url.path());
  156. }
  157. }
  158. }
  159. }
  160. }