1 | /* |
2 | * Copyright © 2020 Red Hat, Inc. |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2.1 of the License, or (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 GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | * |
17 | * SPDX-License-Identifier: LGPL-2.1-or-later |
18 | */ |
19 | |
20 | #include "config.h" |
21 | |
22 | #include "gtkjoinedmenuprivate.h" |
23 | |
24 | typedef struct |
25 | { |
26 | GMenuModel *; |
27 | gulong items_changed_handler; |
28 | } ; |
29 | |
30 | struct |
31 | { |
32 | GMenuModel ; |
33 | GArray *; |
34 | }; |
35 | |
36 | G_DEFINE_TYPE (GtkJoinedMenu, gtk_joined_menu, G_TYPE_MENU_MODEL) |
37 | |
38 | static void |
39 | (gpointer data) |
40 | { |
41 | Menu * = data; |
42 | |
43 | g_clear_signal_handler (&menu->items_changed_handler, menu->model); |
44 | g_clear_object (&menu->model); |
45 | } |
46 | |
47 | static gint |
48 | (GtkJoinedMenu *self, |
49 | gint index) |
50 | { |
51 | gint offset = 0; |
52 | |
53 | for (guint i = 0; i < index; i++) |
54 | offset += g_menu_model_get_n_items (g_array_index (self->menus, Menu, i).model); |
55 | |
56 | return offset; |
57 | } |
58 | |
59 | static gint |
60 | (GtkJoinedMenu *self, |
61 | GMenuModel *model) |
62 | { |
63 | gint offset = 0; |
64 | |
65 | for (guint i = 0; i < self->menus->len; i++) |
66 | { |
67 | const Menu * = &g_array_index (self->menus, Menu, i); |
68 | |
69 | if (menu->model == model) |
70 | break; |
71 | |
72 | offset += g_menu_model_get_n_items (model: menu->model); |
73 | } |
74 | |
75 | return offset; |
76 | } |
77 | |
78 | static gboolean |
79 | (GMenuModel *model) |
80 | { |
81 | return TRUE; |
82 | } |
83 | |
84 | static gint |
85 | (GMenuModel *model) |
86 | { |
87 | GtkJoinedMenu *self = (GtkJoinedMenu *)model; |
88 | |
89 | if (self->menus->len == 0) |
90 | return 0; |
91 | |
92 | return gtk_joined_menu_get_offset_at_index (self, index: self->menus->len); |
93 | } |
94 | |
95 | static const Menu * |
96 | (GtkJoinedMenu *self, |
97 | gint *item_index) |
98 | { |
99 | g_assert (GTK_IS_JOINED_MENU (self)); |
100 | |
101 | for (guint i = 0; i < self->menus->len; i++) |
102 | { |
103 | const Menu * = &g_array_index (self->menus, Menu, i); |
104 | gint n_items = g_menu_model_get_n_items (model: menu->model); |
105 | |
106 | if (n_items > *item_index) |
107 | return menu; |
108 | |
109 | (*item_index) -= n_items; |
110 | } |
111 | |
112 | g_return_val_if_reached (NULL); |
113 | } |
114 | |
115 | static void |
116 | (GMenuModel *model, |
117 | gint item_index, |
118 | GHashTable **attributes) |
119 | { |
120 | const Menu * = gtk_joined_menu_get_item (self: GTK_JOINED_MENU (ptr: model), item_index: &item_index); |
121 | G_MENU_MODEL_GET_CLASS (menu->model)->get_item_attributes (menu->model, item_index, attributes); |
122 | } |
123 | |
124 | static GMenuAttributeIter * |
125 | (GMenuModel *model, |
126 | gint item_index) |
127 | { |
128 | const Menu * = gtk_joined_menu_get_item (self: GTK_JOINED_MENU (ptr: model), item_index: &item_index); |
129 | return G_MENU_MODEL_GET_CLASS (menu->model)->iterate_item_attributes (menu->model, item_index); |
130 | } |
131 | |
132 | static GVariant * |
133 | (GMenuModel *model, |
134 | gint item_index, |
135 | const gchar *attribute, |
136 | const GVariantType *expected_type) |
137 | { |
138 | const Menu * = gtk_joined_menu_get_item (self: GTK_JOINED_MENU (ptr: model), item_index: &item_index); |
139 | return G_MENU_MODEL_GET_CLASS (menu->model)->get_item_attribute_value (menu->model, item_index, attribute, expected_type); |
140 | } |
141 | |
142 | static void |
143 | (GMenuModel *model, |
144 | gint item_index, |
145 | GHashTable **links) |
146 | { |
147 | const Menu * = gtk_joined_menu_get_item (self: GTK_JOINED_MENU (ptr: model), item_index: &item_index); |
148 | G_MENU_MODEL_GET_CLASS (menu->model)->get_item_links (menu->model, item_index, links); |
149 | } |
150 | |
151 | static GMenuLinkIter * |
152 | (GMenuModel *model, |
153 | gint item_index) |
154 | { |
155 | const Menu * = gtk_joined_menu_get_item (self: GTK_JOINED_MENU (ptr: model), item_index: &item_index); |
156 | return G_MENU_MODEL_GET_CLASS (menu->model)->iterate_item_links (menu->model, item_index); |
157 | } |
158 | |
159 | static GMenuModel * |
160 | (GMenuModel *model, |
161 | gint item_index, |
162 | const gchar *link) |
163 | { |
164 | const Menu * = gtk_joined_menu_get_item (self: GTK_JOINED_MENU (ptr: model), item_index: &item_index); |
165 | return G_MENU_MODEL_GET_CLASS (menu->model)->get_item_link (menu->model, item_index, link); |
166 | } |
167 | |
168 | static void |
169 | (GObject *object) |
170 | { |
171 | GtkJoinedMenu *self = (GtkJoinedMenu *)object; |
172 | |
173 | g_clear_pointer (&self->menus, g_array_unref); |
174 | |
175 | G_OBJECT_CLASS (gtk_joined_menu_parent_class)->finalize (object); |
176 | } |
177 | |
178 | static void |
179 | (GtkJoinedMenuClass *klass) |
180 | { |
181 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
182 | GMenuModelClass * = G_MENU_MODEL_CLASS (klass); |
183 | |
184 | object_class->finalize = gtk_joined_menu_finalize; |
185 | |
186 | menu_model_class->is_mutable = gtk_joined_menu_is_mutable; |
187 | menu_model_class->get_n_items = gtk_joined_menu_get_n_items; |
188 | menu_model_class->get_item_attributes = gtk_joined_menu_get_item_attributes; |
189 | menu_model_class->iterate_item_attributes = gtk_joined_menu_iterate_item_attributes; |
190 | menu_model_class->get_item_attribute_value = gtk_joined_menu_get_item_attribute_value; |
191 | menu_model_class->get_item_links = gtk_joined_menu_get_item_links; |
192 | menu_model_class->iterate_item_links = gtk_joined_menu_iterate_item_links; |
193 | menu_model_class->get_item_link = gtk_joined_menu_get_item_link; |
194 | } |
195 | |
196 | static void |
197 | (GtkJoinedMenu *self) |
198 | { |
199 | self->menus = g_array_new (FALSE, FALSE, element_size: sizeof (Menu)); |
200 | g_array_set_clear_func (array: self->menus, clear_func: clear_menu); |
201 | } |
202 | |
203 | static void |
204 | (GtkJoinedMenu *self, |
205 | guint offset, |
206 | guint removed, |
207 | guint added, |
208 | GMenuModel *model) |
209 | { |
210 | g_assert (GTK_IS_JOINED_MENU (self)); |
211 | g_assert (G_IS_MENU_MODEL (model)); |
212 | |
213 | offset += gtk_joined_menu_get_offset_at_model (self, model); |
214 | g_menu_model_items_changed (G_MENU_MODEL (self), position: offset, removed, added); |
215 | } |
216 | |
217 | GtkJoinedMenu * |
218 | (void) |
219 | { |
220 | return g_object_new (GTK_TYPE_JOINED_MENU, NULL); |
221 | } |
222 | |
223 | static void |
224 | (GtkJoinedMenu *self, |
225 | GMenuModel *model, |
226 | gint index) |
227 | { |
228 | Menu = { 0 }; |
229 | gint offset; |
230 | gint n_items; |
231 | |
232 | g_assert (GTK_IS_JOINED_MENU (self)); |
233 | g_assert (G_IS_MENU_MODEL (model)); |
234 | g_assert (index >= 0); |
235 | g_assert (index <= self->menus->len); |
236 | |
237 | menu.model = g_object_ref (model); |
238 | menu.items_changed_handler = |
239 | g_signal_connect_swapped (menu.model, |
240 | "items-changed" , |
241 | G_CALLBACK (gtk_joined_menu_on_items_changed), |
242 | self); |
243 | g_array_insert_val (self->menus, index, menu); |
244 | |
245 | n_items = g_menu_model_get_n_items (model); |
246 | offset = gtk_joined_menu_get_offset_at_index (self, index); |
247 | g_menu_model_items_changed (G_MENU_MODEL (self), position: offset, removed: 0, added: n_items); |
248 | } |
249 | |
250 | void |
251 | (GtkJoinedMenu *self, |
252 | GMenuModel *model) |
253 | { |
254 | |
255 | g_return_if_fail (GTK_IS_JOINED_MENU (self)); |
256 | g_return_if_fail (G_MENU_MODEL (model)); |
257 | |
258 | gtk_joined_menu_insert (self, model, index: self->menus->len); |
259 | } |
260 | |
261 | void |
262 | (GtkJoinedMenu *self, |
263 | GMenuModel *model) |
264 | { |
265 | g_return_if_fail (GTK_IS_JOINED_MENU (self)); |
266 | g_return_if_fail (G_MENU_MODEL (model)); |
267 | |
268 | gtk_joined_menu_insert (self, model, index: 0); |
269 | } |
270 | |
271 | void |
272 | (GtkJoinedMenu *self, |
273 | guint index) |
274 | { |
275 | const Menu *; |
276 | gint n_items; |
277 | gint offset; |
278 | |
279 | g_return_if_fail (GTK_IS_JOINED_MENU (self)); |
280 | g_return_if_fail (index < self->menus->len); |
281 | |
282 | menu = &g_array_index (self->menus, Menu, index); |
283 | |
284 | offset = gtk_joined_menu_get_offset_at_index (self, index); |
285 | n_items = g_menu_model_get_n_items (model: menu->model); |
286 | |
287 | g_array_remove_index (array: self->menus, index_: index); |
288 | |
289 | g_menu_model_items_changed (G_MENU_MODEL (self), position: offset, removed: n_items, added: 0); |
290 | } |
291 | |
292 | void |
293 | (GtkJoinedMenu *self, |
294 | GMenuModel *model) |
295 | { |
296 | g_return_if_fail (GTK_IS_JOINED_MENU (self)); |
297 | g_return_if_fail (G_IS_MENU_MODEL (model)); |
298 | |
299 | for (guint i = 0; i < self->menus->len; i++) |
300 | { |
301 | if (g_array_index (self->menus, Menu, i).model == model) |
302 | { |
303 | gtk_joined_menu_remove_index (self, index: i); |
304 | break; |
305 | } |
306 | } |
307 | } |
308 | |
309 | guint |
310 | (GtkJoinedMenu *self) |
311 | { |
312 | g_return_val_if_fail (GTK_IS_JOINED_MENU (self), 0); |
313 | |
314 | return self->menus->len; |
315 | } |
316 | |