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