1 /*
2  * Copyright (C) 2013-2020 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 "category.h"
19 
20 #include "category-button.h"
21 #include "launcher-view.h"
22 #include "util.h"
23 
24 #include <algorithm>
25 
26 #include <glib/gi18n-lib.h>
27 
28 using namespace WhiskerMenu;
29 
30 //-----------------------------------------------------------------------------
31 
Category(GarconMenu * menu)32 Category::Category(GarconMenu* menu) :
33 	m_button(nullptr),
34 	m_model(nullptr),
35 	m_has_separators(false),
36 	m_has_subcategories(false),
37 	m_owns_button(true)
38 {
39 	const gchar* icon = nullptr;
40 	const gchar* text = nullptr;
41 	const gchar* tooltip = nullptr;
42 	if (menu)
43 	{
44 		GarconMenuElement* element = GARCON_MENU_ELEMENT(menu);
45 		icon = garcon_menu_element_get_icon_name(element);
46 		text = garcon_menu_element_get_name(element);
47 		tooltip = garcon_menu_element_get_comment(element);
48 	}
49 	else
50 	{
51 		text = _("All Applications");
52 	}
53 	set_icon(!xfce_str_is_empty(icon) ? icon : "applications-other", true);
54 	set_text(text ? text : "");
55 	set_tooltip(tooltip ? tooltip : "");
56 }
57 
58 //-----------------------------------------------------------------------------
59 
~Category()60 Category::~Category()
61 {
62 	unset_model();
63 
64 	if (m_owns_button)
65 	{
66 		delete m_button;
67 	}
68 
69 	for (auto element : m_items)
70 	{
71 		if (Category* category = dynamic_cast<Category*>(element))
72 		{
73 			delete category;
74 		}
75 	}
76 }
77 
78 //-----------------------------------------------------------------------------
79 
get_button()80 CategoryButton* Category::get_button()
81 {
82 	if (!m_button)
83 	{
84 		m_button = new CategoryButton(get_icon(), get_text());
85 	}
86 
87 	return m_button;
88 }
89 
90 //-----------------------------------------------------------------------------
91 
get_model()92 GtkTreeModel* Category::get_model()
93 {
94 	if (!m_model)
95 	{
96 		if (m_has_subcategories)
97 		{
98 			GtkTreeStore* model = gtk_tree_store_new(
99 					LauncherView::N_COLUMNS,
100 					G_TYPE_ICON,
101 					G_TYPE_STRING,
102 					G_TYPE_STRING,
103 					G_TYPE_POINTER);
104 			insert_items(model, nullptr);
105 			m_model = GTK_TREE_MODEL(model);
106 		}
107 		else
108 		{
109 			GtkListStore* model = gtk_list_store_new(
110 					LauncherView::N_COLUMNS,
111 					G_TYPE_ICON,
112 					G_TYPE_STRING,
113 					G_TYPE_STRING,
114 					G_TYPE_POINTER);
115 			insert_items(model);
116 			m_model = GTK_TREE_MODEL(model);
117 		}
118 	}
119 
120 	return m_model;
121 }
122 
123 //-----------------------------------------------------------------------------
124 
append_separator()125 void Category::append_separator()
126 {
127 	if (!m_items.empty() && m_items.back())
128 	{
129 		unset_model();
130 		m_items.push_back(nullptr);
131 		m_has_separators = true;
132 	}
133 }
134 
135 //-----------------------------------------------------------------------------
136 
set_button(CategoryButton * button)137 void Category::set_button(CategoryButton* button)
138 {
139 	if (m_owns_button)
140 	{
141 		delete m_button;
142 	}
143 
144 	m_owns_button = false;
145 
146 	m_button = button;
147 }
148 
149 //-----------------------------------------------------------------------------
150 
sort()151 void Category::sort()
152 {
153 	unset_model();
154 	std::sort(m_items.begin(), m_items.end(), &Element::less_than);
155 }
156 
157 //-----------------------------------------------------------------------------
158 
insert_items(GtkTreeStore * model,GtkTreeIter * parent)159 void Category::insert_items(GtkTreeStore* model, GtkTreeIter* parent)
160 {
161 	if (!m_items.empty() && !m_items.back())
162 	{
163 		m_items.pop_back();
164 	}
165 
166 	for (auto element : m_items)
167 	{
168 		if (Category* category = dynamic_cast<Category*>(element))
169 		{
170 			gchar* text = g_markup_escape_text(category->get_text(), -1);
171 			const gchar* tooltip = category->get_tooltip();
172 
173 			GtkTreeIter iter;
174 			gtk_tree_store_insert_with_values(model,
175 					&iter, parent, G_MAXINT,
176 					LauncherView::COLUMN_ICON, category->get_icon(),
177 					LauncherView::COLUMN_TEXT, text,
178 					LauncherView::COLUMN_TOOLTIP, tooltip,
179 					LauncherView::COLUMN_LAUNCHER, nullptr,
180 					-1);
181 			g_free(text);
182 			category->insert_items(model, &iter);
183 		}
184 		else if (Launcher* launcher = dynamic_cast<Launcher*>(element))
185 		{
186 			gtk_tree_store_insert_with_values(model,
187 					nullptr, parent, G_MAXINT,
188 					LauncherView::COLUMN_ICON, launcher->get_icon(),
189 					LauncherView::COLUMN_TEXT, launcher->get_text(),
190 					LauncherView::COLUMN_TOOLTIP, launcher->get_tooltip(),
191 					LauncherView::COLUMN_LAUNCHER, launcher,
192 					-1);
193 		}
194 		else
195 		{
196 			gtk_tree_store_insert_with_values(model,
197 					nullptr, parent, G_MAXINT,
198 					LauncherView::COLUMN_ICON, nullptr,
199 					LauncherView::COLUMN_TEXT, nullptr,
200 					LauncherView::COLUMN_TOOLTIP, nullptr,
201 					LauncherView::COLUMN_LAUNCHER, nullptr,
202 					-1);
203 		}
204 	}
205 }
206 
207 //-----------------------------------------------------------------------------
208 
insert_items(GtkListStore * model)209 void Category::insert_items(GtkListStore* model)
210 {
211 	if (!m_items.empty() && !m_items.back())
212 	{
213 		m_items.pop_back();
214 	}
215 
216 	for (auto element : m_items)
217 	{
218 		if (Launcher* launcher = dynamic_cast<Launcher*>(element))
219 		{
220 			gtk_list_store_insert_with_values(model,
221 					nullptr, G_MAXINT,
222 					LauncherView::COLUMN_ICON, launcher->get_icon(),
223 					LauncherView::COLUMN_TEXT, launcher->get_text(),
224 					LauncherView::COLUMN_TOOLTIP, launcher->get_tooltip(),
225 					LauncherView::COLUMN_LAUNCHER, launcher,
226 					-1);
227 		}
228 		else
229 		{
230 			gtk_list_store_insert_with_values(model,
231 					nullptr, G_MAXINT,
232 					LauncherView::COLUMN_ICON, nullptr,
233 					LauncherView::COLUMN_TEXT, nullptr,
234 					LauncherView::COLUMN_TOOLTIP, nullptr,
235 					LauncherView::COLUMN_LAUNCHER, nullptr,
236 					-1);
237 		}
238 	}
239 }
240 
241 //-----------------------------------------------------------------------------
242 
unset_model()243 void Category::unset_model()
244 {
245 	if (m_model)
246 	{
247 		g_object_unref(m_model);
248 		m_model = nullptr;
249 	}
250 }
251 
252 //-----------------------------------------------------------------------------
253