1 /*
2 * Copyright (C) 2013-2021 Graeme Gott <graeme@gottcode.org>
3 *
4 * This library is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include "settings.h"
19
20 #include "command.h"
21 #include "plugin.h"
22 #include "search-action.h"
23
24 #include <algorithm>
25
26 #include <exo/exo.h>
27
28 #include <unistd.h>
29
30 using namespace WhiskerMenu;
31
32 //-----------------------------------------------------------------------------
33
34 Settings* WhiskerMenu::wm_settings = nullptr;
35
36 //-----------------------------------------------------------------------------
37
Settings()38 Settings::Settings() :
39 m_button_title_default(_("Applications")),
40 m_modified(false),
41
42 favorites("favorites", {
43 #if EXO_CHECK_VERSION(4,15,0)
44 "xfce4-web-browser.desktop",
45 "xfce4-mail-reader.desktop",
46 "xfce4-file-manager.desktop",
47 "xfce4-terminal-emulator.desktop"
48 #else
49 "exo-web-browser.desktop",
50 "exo-mail-reader.desktop",
51 "exo-file-manager.desktop",
52 "exo-terminal-emulator.desktop"
53 #endif
54 }),
55 recent("recent", { }),
56
57 custom_menu_file("custom-menu-file"),
58
59 button_title("button-title", m_button_title_default),
60 button_icon_name("button-icon", "org.xfce.panel.whiskermenu"),
61 button_title_visible("show-button-title", false),
62 button_icon_visible("show-button-icon", true),
63 button_single_row("button-single-row", false),
64
65 launcher_show_name("launcher-show-name", true),
66 launcher_show_description("launcher-show-description", true),
67 launcher_show_tooltip("launcher-show-tooltip", true),
68 launcher_icon_size("launcher-icon-size", IconSize::Small),
69
70 category_hover_activate("hover-switch-category", false),
71 category_show_name("category-show-name", true),
72 sort_categories("sort-categories", true),
73 category_icon_size("category-icon-size", IconSize::Smaller),
74
75 view_mode("view-mode", ViewAsList, ViewAsIcons, ViewAsTree),
76
77 default_category("default-category", CategoryFavorites, CategoryFavorites, CategoryAll),
78
79 recent_items_max("recent-items-max", 10, 0, 100),
80 favorites_in_recent("favorites-in-recent", true),
81
82 position_search_alternate("position-search-alternate", false),
83 position_commands_alternate("position-commands-alternate", false),
84 position_categories_alternate("position-categories-alternate", false),
85 position_categories_horizontal("position-categories-horizontal", false),
86 stay_on_focus_out("stay-on-focus-out", false),
87
88 profile_shape("profile-shape", ProfileRound, ProfileRound, ProfileHidden),
89
90 confirm_session_command("confirm-session-command", true),
91
92 search_actions {
93 new SearchAction(_("Man Pages"), "#", "exo-open --launch TerminalEmulator man %s", false, true),
94 new SearchAction(_("Search the Web"), "?", "exo-open --launch WebBrowser https://duckduckgo.com/?q=%u", false, true),
95 new SearchAction(_("Search for Files"), "-", "catfish --path=~ --start %s", false, true),
96 new SearchAction(_("Wikipedia"), "!w", "exo-open --launch WebBrowser https://en.wikipedia.org/wiki/%u", false, true),
97 new SearchAction(_("Run in Terminal"), "!", "exo-open --launch TerminalEmulator %s", false, true),
98 new SearchAction(_("Open URI"), "^(file|http|https):\\/\\/(.*)$", "exo-open \\0", true, true)
99 },
100
101 menu_width("menu-width", 450, 10, INT_MAX),
102 menu_height("menu-height", 500, 10, INT_MAX),
103 menu_opacity("menu-opacity", 100, 0, 100)
104 {
105 command[CommandSettings] = new Command("command-settings", "show-command-settings",
106 "org.xfce.settings.manager", "preferences-desktop",
107 _("_Settings Manager"),
108 "xfce4-settings-manager", true,
109 _("Failed to open settings manager."));
110 command[CommandLockScreen] = new Command("command-lockscreen", "show-command-lockscreen",
111 "xfsm-lock", "system-lock-screen",
112 _("_Lock Screen"),
113 "xflock4", true,
114 _("Failed to lock screen."));
115 command[CommandSwitchUser] = new Command("command-switchuser", "show-command-switchuser",
116 "xfsm-switch-user", "system-users",
117 _("Switch _User"),
118 "dm-tool switch-to-greeter", false,
119 _("Failed to switch user."));
120 command[CommandLogOutUser] = new Command("command-logoutuser", "show-command-logoutuser",
121 "xfsm-logout", "system-log-out",
122 _("Log _Out"),
123 "xfce4-session-logout --logout --fast", false,
124 _("Failed to log out."),
125 _("Are you sure you want to log out?"),
126 _("Logging out in %d seconds."));
127 command[CommandRestart] = new Command("command-restart", "show-command-restart",
128 "xfsm-reboot", "system-reboot",
129 _("_Restart"),
130 "xfce4-session-logout --reboot --fast", false,
131 _("Failed to restart."),
132 _("Are you sure you want to restart?"),
133 _("Restarting computer in %d seconds."));
134 command[CommandShutDown] = new Command("command-shutdown", "show-command-shutdown",
135 "xfsm-shutdown", "system-shutdown",
136 _("Shut _Down"),
137 "xfce4-session-logout --halt --fast", false,
138 _("Failed to shut down."),
139 _("Are you sure you want to shut down?"),
140 _("Turning off computer in %d seconds."));
141 command[CommandSuspend] = new Command("command-suspend", "show-command-suspend",
142 "xfsm-suspend", "system-suspend",
143 _("Suspe_nd"),
144 "xfce4-session-logout --suspend", false,
145 _("Failed to suspend."),
146 _("Do you want to suspend to RAM?"),
147 _("Suspending computer in %d seconds."));
148 command[CommandHibernate] = new Command("command-hibernate", "show-command-hibernate",
149 "xfsm-hibernate", "system-hibernate",
150 _("_Hibernate"),
151 "xfce4-session-logout --hibernate", false,
152 _("Failed to hibernate."),
153 _("Do you want to suspend to disk?"),
154 _("Hibernating computer in %d seconds."));
155 command[CommandLogOut] = new Command("command-logout", "show-command-logout",
156 "xfsm-logout", "system-log-out",
157 _("Log Ou_t..."),
158 "xfce4-session-logout", true,
159 _("Failed to log out."));
160 command[CommandMenuEditor] = new Command("command-menueditor", "show-command-menueditor",
161 "menu-editor", "xfce4-menueditor",
162 _("_Edit Applications"),
163 "menulibre", true,
164 _("Failed to launch menu editor."));
165 command[CommandProfile] = new Command("command-profile", "show-command-profile",
166 "avatar-default", "preferences-desktop-user",
167 _("Edit _Profile"),
168 "mugshot", true,
169 _("Failed to edit profile."));
170 }
171
172 //-----------------------------------------------------------------------------
173
~Settings()174 Settings::~Settings()
175 {
176 for (auto i : command)
177 {
178 delete i;
179 }
180 }
181
182 //-----------------------------------------------------------------------------
183
load(gchar * file)184 void Settings::load(gchar* file)
185 {
186 if (!file)
187 {
188 command[CommandProfile]->set_shown(false);
189 command[CommandMenuEditor]->set_shown(false);
190 return;
191 }
192
193 XfceRc* rc = xfce_rc_simple_open(file, true);
194 g_free(file);
195 if (!rc)
196 {
197 return;
198 }
199 xfce_rc_set_group(rc, nullptr);
200
201 favorites.load(rc);
202 recent.load(rc);
203
204 custom_menu_file.load(rc);
205
206 button_title.load(rc);
207 button_icon_name.load(rc);
208 button_single_row.load(rc);
209 button_title_visible.load(rc);
210 button_icon_visible.load(rc);
211
212 launcher_show_name.load(rc);
213 launcher_show_description.load(rc);
214 launcher_show_tooltip.load(rc);
215 if (xfce_rc_has_entry(rc, "item-icon-size"))
216 {
217 launcher_icon_size = xfce_rc_read_int_entry(rc, "item-icon-size", launcher_icon_size);
218 }
219 launcher_icon_size.load(rc);
220
221 category_hover_activate.load(rc);
222 category_show_name.load(rc);
223 category_icon_size.load(rc);
224 if (!category_show_name && (category_icon_size == -1))
225 {
226 category_show_name = true;
227 }
228
229 if (!xfce_rc_has_entry(rc, "view-mode"))
230 {
231 if (xfce_rc_read_bool_entry(rc, "load-hierarchy", view_mode == ViewAsTree))
232 {
233 view_mode = ViewAsTree;
234 if (!xfce_rc_has_entry(rc, "sort-categories"))
235 {
236 sort_categories = false;
237 }
238 }
239 else if (xfce_rc_read_bool_entry(rc, "view-as-icons", view_mode == ViewAsIcons))
240 {
241 view_mode = ViewAsIcons;
242 }
243 }
244 view_mode.load(rc);
245 sort_categories.load(rc);
246
247 if (xfce_rc_has_entry(rc, "display-recent-default"))
248 {
249 default_category = xfce_rc_read_bool_entry(rc, "display-recent-default", default_category);
250 }
251 default_category.load(rc);
252
253 recent_items_max.load(rc);
254 favorites_in_recent.load(rc);
255 if (!recent_items_max && (default_category == CategoryRecent))
256 {
257 default_category = CategoryFavorites;
258 }
259
260 position_search_alternate.load(rc);
261 position_commands_alternate.load(rc);
262 position_categories_alternate.load(rc);
263 position_categories_horizontal.load(rc);
264 stay_on_focus_out.load(rc);
265
266 profile_shape.load(rc);
267
268 confirm_session_command.load(rc);
269
270 menu_width.load(rc);
271 menu_height.load(rc);
272 menu_opacity.load(rc);
273
274 for (auto i : command)
275 {
276 i->load(rc);
277 }
278
279 search_actions.load(rc);
280
281 xfce_rc_close(rc);
282
283 m_modified = false;
284 }
285
286 //-----------------------------------------------------------------------------
287
save(gchar * file)288 void Settings::save(gchar* file)
289 {
290 if (!file)
291 {
292 return;
293 }
294
295 // Start with fresh settings
296 unlink(file);
297
298 XfceRc* rc = xfce_rc_simple_open(file, false);
299 g_free(file);
300 if (!rc)
301 {
302 return;
303 }
304 xfce_rc_set_group(rc, nullptr);
305
306 favorites.save(rc);
307 recent.save(rc);
308
309 if (!custom_menu_file.empty())
310 {
311 custom_menu_file.save(rc);
312 }
313
314 if (button_title != Plugin::get_button_title_default())
315 {
316 button_title.save(rc);
317 }
318 button_icon_name.save(rc);
319 button_single_row.save(rc);
320 button_title_visible.save(rc);
321 button_icon_visible.save(rc);
322
323 launcher_show_name.save(rc);
324 launcher_show_description.save(rc);
325 launcher_show_tooltip.save(rc);
326 launcher_icon_size.save(rc);
327
328 category_hover_activate.save(rc);
329 category_show_name.save(rc);
330 category_icon_size.save(rc);
331 sort_categories.save(rc);
332
333 view_mode.save(rc);
334
335 default_category.save(rc);
336
337 recent_items_max.save(rc);
338 favorites_in_recent.save(rc);
339
340 position_search_alternate.save(rc);
341 position_commands_alternate.save(rc);
342 position_categories_alternate.save(rc);
343 position_categories_horizontal.save(rc);
344 stay_on_focus_out.save(rc);
345
346 profile_shape.save(rc);
347
348 confirm_session_command.save(rc);
349
350 menu_width.save(rc);
351 menu_height.save(rc);
352 menu_opacity.save(rc);
353
354 for (auto i : command)
355 {
356 i->save(rc);
357 }
358
359 search_actions.save(rc);
360
361 xfce_rc_close(rc);
362
363 m_modified = false;
364 }
365
366 //-----------------------------------------------------------------------------
367
Boolean(const gchar * property,bool data)368 Boolean::Boolean(const gchar* property, bool data) :
369 m_property(property),
370 m_data(data)
371 {
372 }
373
374 //-----------------------------------------------------------------------------
375
load(XfceRc * rc)376 void Boolean::load(XfceRc* rc)
377 {
378 set(xfce_rc_read_bool_entry(rc, m_property, m_data));
379 }
380
381 //-----------------------------------------------------------------------------
382
save(XfceRc * rc)383 void Boolean::save(XfceRc* rc)
384 {
385 xfce_rc_write_bool_entry(rc, m_property, m_data);
386 }
387
388 //-----------------------------------------------------------------------------
389
set(bool data)390 void Boolean::set(bool data)
391 {
392 if (m_data == data)
393 {
394 return;
395 }
396
397 m_data = data;
398 wm_settings->set_modified();
399 }
400
401 //-----------------------------------------------------------------------------
402
Integer(const gchar * property,int data,int min,int max)403 Integer::Integer(const gchar* property, int data, int min, int max) :
404 m_property(property),
405 m_min(min),
406 m_max(max),
407 m_data(CLAMP(data, min, max))
408 {
409 }
410
411 //-----------------------------------------------------------------------------
412
load(XfceRc * rc)413 void Integer::load(XfceRc* rc)
414 {
415 set(xfce_rc_read_int_entry(rc, m_property, m_data));
416 }
417
418 //-----------------------------------------------------------------------------
419
save(XfceRc * rc)420 void Integer::save(XfceRc* rc)
421 {
422 xfce_rc_write_int_entry(rc, m_property, m_data);
423 }
424
425 //-----------------------------------------------------------------------------
426
set(int data)427 void Integer::set(int data)
428 {
429 data = CLAMP(data, m_min, m_max);
430 if (m_data == data)
431 {
432 return;
433 }
434
435 m_data = data;
436 wm_settings->set_modified();
437 }
438
439 //-----------------------------------------------------------------------------
440
String(const gchar * property,const std::string & data)441 String::String(const gchar* property, const std::string& data) :
442 m_property(property),
443 m_data(data)
444 {
445 }
446
447 //-----------------------------------------------------------------------------
448
load(XfceRc * rc)449 void String::load(XfceRc* rc)
450 {
451 set(xfce_rc_read_entry(rc, m_property, m_data.c_str()));
452 }
453
454 //-----------------------------------------------------------------------------
455
save(XfceRc * rc)456 void String::save(XfceRc* rc)
457 {
458 xfce_rc_write_entry(rc, m_property, m_data.c_str());
459 }
460
461 //-----------------------------------------------------------------------------
462
set(const std::string & data)463 void String::set(const std::string& data)
464 {
465 if (m_data == data)
466 {
467 return;
468 }
469
470 m_data = data;
471 wm_settings->set_modified();
472 }
473
474 //-----------------------------------------------------------------------------
475
StringList(const gchar * property,std::initializer_list<std::string> data)476 StringList::StringList(const gchar* property, std::initializer_list<std::string> data) :
477 m_property(property),
478 m_data(data)
479 {
480 }
481
482 //-----------------------------------------------------------------------------
483
clear()484 void StringList::clear()
485 {
486 m_data.clear();
487 wm_settings->set_modified();
488 }
489
490 //-----------------------------------------------------------------------------
491
erase(int pos)492 void StringList::erase(int pos)
493 {
494 m_data.erase(m_data.begin() + pos);
495 wm_settings->set_modified();
496 }
497
498 //-----------------------------------------------------------------------------
499
insert(int pos,const std::string & value)500 void StringList::insert(int pos, const std::string& value)
501 {
502 m_data.insert(m_data.begin() + pos, value);
503 wm_settings->set_modified();
504 }
505
506 //-----------------------------------------------------------------------------
507
push_back(const std::string & value)508 void StringList::push_back(const std::string& value)
509 {
510 m_data.push_back(value);
511 wm_settings->set_modified();
512 }
513
514 //-----------------------------------------------------------------------------
515
resize(int count)516 void StringList::resize(int count)
517 {
518 m_data.resize(count);
519 wm_settings->set_modified();
520 }
521
522 //-----------------------------------------------------------------------------
523
set(int pos,const std::string & value)524 void StringList::set(int pos, const std::string& value)
525 {
526 m_data[pos] = value;
527 wm_settings->set_modified();
528 }
529
530 //-----------------------------------------------------------------------------
531
load(XfceRc * rc)532 void StringList::load(XfceRc* rc)
533 {
534 if (!xfce_rc_has_entry(rc, m_property))
535 {
536 return;
537 }
538
539 m_data.clear();
540
541 gchar** data = xfce_rc_read_list_entry(rc, m_property, ",");
542 if (!data)
543 {
544 return;
545 }
546
547 for (int i = 0; data[i]; ++i)
548 {
549 std::string desktop_id(data[i]);
550 #if EXO_CHECK_VERSION(4,15,0)
551 if (desktop_id == "exo-web-browser.desktop")
552 {
553 desktop_id = "xfce4-web-browser.desktop";
554 }
555 else if (desktop_id == "exo-mail-reader.desktop")
556 {
557 desktop_id = "xfce4-mail-reader.desktop";
558 }
559 else if (desktop_id == "exo-file-manager.desktop")
560 {
561 desktop_id = "xfce4-file-manager.desktop";
562 }
563 else if (desktop_id == "exo-terminal-emulator.desktop")
564 {
565 desktop_id = "xfce4-terminal-emulator.desktop";
566 }
567 #endif
568 if (std::find(m_data.begin(), m_data.end(), desktop_id) == m_data.end())
569 {
570 m_data.push_back(std::move(desktop_id));
571 }
572 }
573
574 g_strfreev(data);
575 }
576
577 //-----------------------------------------------------------------------------
578
save(XfceRc * rc)579 void StringList::save(XfceRc* rc)
580 {
581 const int size = m_data.size();
582 gchar** values = g_new0(gchar*, size + 1);
583 for (int i = 0; i < size; ++i)
584 {
585 values[i] = g_strdup(m_data[i].c_str());
586 }
587 xfce_rc_write_list_entry(rc, m_property, values, ",");
588 g_strfreev(values);
589 }
590
591 //-----------------------------------------------------------------------------
592
SearchActionList(std::initializer_list<SearchAction * > data)593 SearchActionList::SearchActionList(std::initializer_list<SearchAction*> data) :
594 m_data(data)
595 {
596 }
597
598 //-----------------------------------------------------------------------------
599
~SearchActionList()600 SearchActionList::~SearchActionList()
601 {
602 for (auto action : m_data)
603 {
604 delete action;
605 }
606 }
607
608 //-----------------------------------------------------------------------------
609
erase(SearchAction * value)610 void SearchActionList::erase(SearchAction* value)
611 {
612 m_data.erase(std::find(m_data.begin(), m_data.end(), value));
613 wm_settings->set_modified();
614 }
615
616 //-----------------------------------------------------------------------------
617
push_back(SearchAction * value)618 void SearchActionList::push_back(SearchAction* value)
619 {
620 m_data.push_back(value);
621 wm_settings->set_modified();
622 }
623
624 //-----------------------------------------------------------------------------
625
load(XfceRc * rc)626 void SearchActionList::load(XfceRc* rc)
627 {
628 const int size = xfce_rc_read_int_entry(rc, "search-actions", -1);
629 if (size < 0)
630 {
631 return;
632 }
633
634 for (auto action : m_data)
635 {
636 delete action;
637 }
638 m_data.clear();
639
640 for (int i = 0; i < size; ++i)
641 {
642 gchar* key = g_strdup_printf("action%i", i);
643 if (!xfce_rc_has_group(rc, key))
644 {
645 g_free(key);
646 continue;
647 }
648 xfce_rc_set_group(rc, key);
649 g_free(key);
650
651 m_data.push_back(new SearchAction(
652 xfce_rc_read_entry(rc, "name", ""),
653 xfce_rc_read_entry(rc, "pattern", ""),
654 xfce_rc_read_entry(rc, "command", ""),
655 xfce_rc_read_bool_entry(rc, "regex", false),
656 wm_settings->launcher_show_description));
657 }
658
659 wm_settings->set_modified();
660 }
661
662 //-----------------------------------------------------------------------------
663
save(XfceRc * rc)664 void SearchActionList::save(XfceRc* rc)
665 {
666 const int size = m_data.size();
667 xfce_rc_write_int_entry(rc, "search-actions", size);
668
669 for (int i = 0; i < size; ++i)
670 {
671 gchar* key = g_strdup_printf("action%i", i);
672 xfce_rc_set_group(rc, key);
673 g_free(key);
674
675 const SearchAction* action = m_data[i];
676 xfce_rc_write_entry(rc, "name", action->get_name());
677 xfce_rc_write_entry(rc, "pattern", action->get_pattern());
678 xfce_rc_write_entry(rc, "command", action->get_command());
679 xfce_rc_write_bool_entry(rc, "regex", action->get_is_regex());
680 }
681 }
682
683 //-----------------------------------------------------------------------------
684