Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : */
9 :
10 : #include <string.h>
11 :
12 : #include <unx/gtk/gtksalmenu.hxx>
13 :
14 : #ifdef ENABLE_GMENU_INTEGRATION
15 :
16 : #include <unx/gtk/glomenu.h>
17 :
18 : struct _GLOMenu
19 : {
20 : GMenuModel parent_instance;
21 :
22 : GArray *items;
23 : };
24 :
25 : typedef GMenuModelClass GLOMenuClass;
26 :
27 : #ifdef __GNUC__
28 : #pragma GCC diagnostic push
29 : #pragma GCC diagnostic ignored "-Wunused-function"
30 : #endif
31 51 : G_DEFINE_TYPE (GLOMenu, g_lo_menu, G_TYPE_MENU_MODEL);
32 : #ifdef __GNUC__
33 : #pragma GCC diagnostic pop
34 : #endif
35 :
36 : struct item
37 : {
38 : GHashTable* attributes; // Item attributes.
39 : GHashTable* links; // Item links.
40 : };
41 :
42 : static void
43 2 : g_lo_menu_struct_item_init (struct item *menu_item)
44 : {
45 2 : menu_item->attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, reinterpret_cast<GDestroyNotify>(g_variant_unref));
46 2 : menu_item->links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
47 2 : }
48 :
49 : /* We treat attribute names the same as GSettings keys:
50 : * - only lowercase ascii, digits and '-'
51 : * - must start with lowercase
52 : * - must not end with '-'
53 : * - no consecutive '-'
54 : * - not longer than 1024 chars
55 : */
56 : static gboolean
57 4 : valid_attribute_name (const gchar *name)
58 : {
59 : gint i;
60 :
61 4 : if (!g_ascii_islower (name[0]))
62 0 : return FALSE;
63 :
64 24 : for (i = 1; name[i]; i++)
65 : {
66 40 : if (name[i] != '-' &&
67 20 : !g_ascii_islower (name[i]) &&
68 0 : !g_ascii_isdigit (name[i]))
69 0 : return FALSE;
70 :
71 20 : if (name[i] == '-' && name[i + 1] == '-')
72 0 : return FALSE;
73 : }
74 :
75 4 : if (name[i - 1] == '-')
76 0 : return FALSE;
77 :
78 4 : if (i > 1024)
79 0 : return FALSE;
80 :
81 4 : return TRUE;
82 : }
83 :
84 : /*
85 : * GLOMenu
86 : */
87 :
88 : static gboolean
89 0 : g_lo_menu_is_mutable (GMenuModel*)
90 : {
91 : // Menu is always mutable.
92 0 : return TRUE;
93 : }
94 :
95 : static gint
96 2 : g_lo_menu_get_n_items (GMenuModel *model)
97 : {
98 2 : g_return_val_if_fail (model != NULL, 0);
99 2 : GLOMenu *menu = G_LO_MENU (model);
100 2 : g_return_val_if_fail (menu->items != NULL, 0);
101 :
102 2 : return menu->items->len;
103 : }
104 :
105 : gint
106 0 : g_lo_menu_get_n_items_from_section (GLOMenu *menu,
107 : gint section)
108 : {
109 0 : g_return_val_if_fail (0 <= section && section < (gint) menu->items->len, 0);
110 :
111 0 : GLOMenu *model = g_lo_menu_get_section (menu, section);
112 :
113 0 : g_return_val_if_fail (model != NULL, 0);
114 :
115 0 : gint length = model->items->len;
116 :
117 0 : g_object_unref (model);
118 :
119 0 : return length;
120 : }
121 :
122 : static void
123 0 : g_lo_menu_get_item_attributes (GMenuModel *model,
124 : gint position,
125 : GHashTable **table)
126 : {
127 0 : GLOMenu *menu = G_LO_MENU (model);
128 0 : *table = g_hash_table_ref (g_array_index (menu->items, struct item, position).attributes);
129 0 : }
130 :
131 : static void
132 0 : g_lo_menu_get_item_links (GMenuModel *model,
133 : gint position,
134 : GHashTable **table)
135 : {
136 0 : GLOMenu *menu = G_LO_MENU (model);
137 0 : *table = g_hash_table_ref (g_array_index (menu->items, struct item, position).links);
138 0 : }
139 :
140 : void
141 0 : g_lo_menu_insert (GLOMenu *menu,
142 : gint position,
143 : const gchar *label)
144 : {
145 0 : g_lo_menu_insert_section (menu, position, label, NULL);
146 0 : }
147 :
148 : void
149 0 : g_lo_menu_insert_in_section (GLOMenu *menu,
150 : gint section,
151 : gint position,
152 : const gchar *label)
153 : {
154 0 : g_return_if_fail (G_IS_LO_MENU (menu));
155 0 : g_return_if_fail (0 <= section && section < (gint) menu->items->len);
156 :
157 0 : GLOMenu *model = g_lo_menu_get_section (menu, section);
158 :
159 0 : g_return_if_fail (model != NULL);
160 :
161 0 : g_lo_menu_insert (model, position, label);
162 :
163 0 : g_object_unref (model);
164 : }
165 :
166 : GLOMenu *
167 18 : g_lo_menu_new()
168 : {
169 18 : return G_LO_MENU( g_object_new (G_TYPE_LO_MENU, NULL) );
170 : }
171 :
172 : void
173 2 : g_lo_menu_set_attribute_value (GLOMenu *menu,
174 : gint position,
175 : const gchar *attribute,
176 : GVariant *value)
177 : {
178 2 : g_return_if_fail (G_IS_LO_MENU (menu));
179 2 : g_return_if_fail (attribute != NULL);
180 2 : g_return_if_fail (valid_attribute_name (attribute));
181 :
182 2 : if (position >= (gint) menu->items->len)
183 0 : return;
184 :
185 2 : struct item menu_item = g_array_index (menu->items, struct item, position);
186 :
187 2 : if (value != NULL)
188 0 : g_hash_table_insert (menu_item.attributes, g_strdup (attribute), g_variant_ref_sink (value));
189 : else
190 2 : g_hash_table_remove (menu_item.attributes, attribute);
191 : }
192 :
193 : GVariant*
194 0 : g_lo_menu_get_attribute_value_from_item_in_section (GLOMenu *menu,
195 : gint section,
196 : gint position,
197 : const gchar *attribute,
198 : const GVariantType *type)
199 : {
200 0 : GMenuModel *model = G_MENU_MODEL (g_lo_menu_get_section (menu, section));
201 :
202 0 : g_return_val_if_fail (model != NULL, NULL);
203 :
204 : GVariant *value = g_menu_model_get_item_attribute_value (model,
205 : position,
206 : attribute,
207 0 : type);
208 :
209 0 : g_object_unref (model);
210 :
211 0 : return value;
212 : }
213 :
214 : void
215 2 : g_lo_menu_set_label (GLOMenu *menu,
216 : gint position,
217 : const gchar *label)
218 : {
219 4 : g_return_if_fail (G_IS_LO_MENU (menu));
220 :
221 : GVariant *value;
222 :
223 2 : if (label != NULL)
224 0 : value = g_variant_new_string (label);
225 : else
226 2 : value = NULL;
227 :
228 2 : g_lo_menu_set_attribute_value (menu, position, G_MENU_ATTRIBUTE_LABEL, value);
229 : }
230 :
231 : void
232 0 : g_lo_menu_set_label_to_item_in_section (GLOMenu *menu,
233 : gint section,
234 : gint position,
235 : const gchar *label)
236 : {
237 0 : g_return_if_fail (G_IS_LO_MENU (menu));
238 :
239 0 : GLOMenu *model = g_lo_menu_get_section (menu, section);
240 :
241 0 : g_return_if_fail (model != NULL);
242 :
243 0 : g_lo_menu_set_label (model, position, label);
244 :
245 : // Notify the update.
246 0 : g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1);
247 :
248 0 : g_object_unref (model);
249 : }
250 :
251 : gchar *
252 0 : g_lo_menu_get_label_from_item_in_section (GLOMenu *menu,
253 : gint section,
254 : gint position)
255 : {
256 0 : g_return_val_if_fail (G_IS_LO_MENU (menu), NULL);
257 :
258 : GVariant *label_value = g_lo_menu_get_attribute_value_from_item_in_section (menu,
259 : section,
260 : position,
261 : G_MENU_ATTRIBUTE_LABEL,
262 0 : G_VARIANT_TYPE_STRING);
263 :
264 0 : gchar *label = NULL;
265 :
266 0 : if (label_value)
267 : {
268 0 : label = g_variant_dup_string (label_value, NULL);
269 0 : g_variant_unref (label_value);
270 : }
271 :
272 0 : return label;
273 : }
274 :
275 : void
276 0 : g_lo_menu_set_action_and_target_value (GLOMenu *menu,
277 : gint position,
278 : const gchar *action,
279 : GVariant *target_value)
280 : {
281 0 : g_return_if_fail (G_IS_LO_MENU (menu));
282 :
283 : GVariant *action_value;
284 :
285 0 : if (action != NULL)
286 : {
287 0 : action_value = g_variant_new_string (action);
288 : }
289 : else
290 : {
291 0 : action_value = NULL;
292 0 : target_value = NULL;
293 : }
294 :
295 0 : g_lo_menu_set_attribute_value (menu, position, G_MENU_ATTRIBUTE_ACTION, action_value);
296 0 : g_lo_menu_set_attribute_value (menu, position, G_MENU_ATTRIBUTE_TARGET, target_value);
297 :
298 0 : g_menu_model_items_changed (G_MENU_MODEL (menu), position, 1, 1);
299 : }
300 :
301 : void
302 0 : g_lo_menu_set_action_and_target_value_to_item_in_section (GLOMenu *menu,
303 : gint section,
304 : gint position,
305 : const gchar *command,
306 : GVariant *target_value)
307 : {
308 0 : g_return_if_fail (G_IS_LO_MENU (menu));
309 :
310 0 : GLOMenu *model = g_lo_menu_get_section (menu, section);
311 :
312 0 : g_return_if_fail (model != NULL);
313 :
314 0 : g_lo_menu_set_action_and_target_value (model, position, command, target_value);
315 :
316 0 : g_object_unref (model);
317 : }
318 :
319 : void
320 0 : g_lo_menu_set_accelerator_to_item_in_section (GLOMenu *menu,
321 : gint section,
322 : gint position,
323 : const gchar *accelerator)
324 : {
325 0 : g_return_if_fail (G_IS_LO_MENU (menu));
326 :
327 0 : GLOMenu *model = g_lo_menu_get_section (menu, section);
328 :
329 0 : g_return_if_fail (model != NULL);
330 :
331 : GVariant *value;
332 :
333 0 : if (accelerator != NULL)
334 0 : value = g_variant_new_string (accelerator);
335 : else
336 0 : value = NULL;
337 :
338 0 : g_lo_menu_set_attribute_value (model, position, G_LO_MENU_ATTRIBUTE_ACCELERATOR, value);
339 :
340 : // Notify the update.
341 0 : g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1);
342 :
343 0 : g_object_unref (model);
344 : }
345 :
346 : gchar *
347 0 : g_lo_menu_get_accelerator_from_item_in_section (GLOMenu *menu,
348 : gint section,
349 : gint position)
350 : {
351 0 : g_return_val_if_fail (G_IS_LO_MENU (menu), NULL);
352 :
353 : GVariant *accel_value = g_lo_menu_get_attribute_value_from_item_in_section (menu,
354 : section,
355 : position,
356 : G_LO_MENU_ATTRIBUTE_ACCELERATOR,
357 0 : G_VARIANT_TYPE_STRING);
358 :
359 0 : gchar *accel = NULL;
360 :
361 0 : if (accel_value != NULL)
362 : {
363 0 : accel = g_variant_dup_string (accel_value, NULL);
364 0 : g_variant_unref (accel_value);
365 : }
366 :
367 0 : return accel;
368 : }
369 :
370 : void
371 0 : g_lo_menu_set_command_to_item_in_section (GLOMenu *menu,
372 : gint section,
373 : gint position,
374 : const gchar *command)
375 : {
376 0 : g_return_if_fail (G_IS_LO_MENU (menu));
377 :
378 0 : GLOMenu *model = g_lo_menu_get_section (menu, section);
379 :
380 0 : g_return_if_fail (model != NULL);
381 :
382 : GVariant *value;
383 :
384 0 : if (command != NULL)
385 0 : value = g_variant_new_string (command);
386 : else
387 0 : value = NULL;
388 :
389 0 : g_lo_menu_set_attribute_value (model, position, G_LO_MENU_ATTRIBUTE_COMMAND, value);
390 :
391 : // Notify the update.
392 0 : g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1);
393 :
394 0 : g_object_unref (model);
395 : }
396 :
397 : gchar *
398 0 : g_lo_menu_get_command_from_item_in_section (GLOMenu *menu,
399 : gint section,
400 : gint position)
401 : {
402 0 : g_return_val_if_fail (G_IS_LO_MENU (menu), NULL);
403 :
404 : GVariant *command_value = g_lo_menu_get_attribute_value_from_item_in_section (menu,
405 : section,
406 : position,
407 : G_LO_MENU_ATTRIBUTE_COMMAND,
408 0 : G_VARIANT_TYPE_STRING);
409 :
410 0 : gchar *command = NULL;
411 :
412 0 : if (command_value != NULL)
413 : {
414 0 : command = g_variant_dup_string (command_value, NULL);
415 0 : g_variant_unref (command_value);
416 : }
417 :
418 0 : return command;
419 : }
420 :
421 : void
422 2 : g_lo_menu_set_link (GLOMenu *menu,
423 : gint position,
424 : const gchar *link,
425 : GMenuModel *model)
426 : {
427 2 : g_return_if_fail (G_IS_LO_MENU (menu));
428 2 : g_return_if_fail (link != NULL);
429 2 : g_return_if_fail (valid_attribute_name (link));
430 :
431 2 : if (position < 0 || position >= (gint) menu->items->len)
432 0 : position = menu->items->len - 1;
433 :
434 2 : struct item menu_item = g_array_index (menu->items, struct item, position);
435 :
436 2 : if (model != NULL)
437 2 : g_hash_table_insert (menu_item.links, g_strdup (link), g_object_ref (model));
438 : else
439 0 : g_hash_table_remove (menu_item.links, link);
440 : }
441 :
442 : void
443 2 : g_lo_menu_insert_section (GLOMenu *menu,
444 : gint position,
445 : const gchar *label,
446 : GMenuModel *section)
447 : {
448 2 : g_return_if_fail (G_IS_LO_MENU (menu));
449 :
450 2 : if (position < 0 || position > (gint) menu->items->len)
451 0 : position = menu->items->len;
452 :
453 : struct item menu_item;
454 :
455 2 : g_lo_menu_struct_item_init(&menu_item);
456 :
457 2 : g_array_insert_val (menu->items, position, menu_item);
458 :
459 2 : g_lo_menu_set_label (menu, position, label);
460 2 : g_lo_menu_set_link (menu, position, G_MENU_LINK_SECTION, section);
461 :
462 2 : g_menu_model_items_changed (G_MENU_MODEL (menu), position, 0, 1);
463 : }
464 :
465 : void
466 0 : g_lo_menu_new_section (GLOMenu *menu,
467 : gint position,
468 : const gchar *label)
469 : {
470 0 : GMenuModel *section = G_MENU_MODEL (g_lo_menu_new());
471 :
472 0 : g_lo_menu_insert_section (menu, position, label, section);
473 :
474 0 : g_object_unref (section);
475 0 : }
476 :
477 : GLOMenu *
478 0 : g_lo_menu_get_section (GLOMenu *menu,
479 : gint section)
480 : {
481 0 : g_return_val_if_fail (G_IS_LO_MENU (menu), NULL);
482 :
483 0 : return G_LO_MENU (G_MENU_MODEL_CLASS (g_lo_menu_parent_class)
484 : ->get_item_link (G_MENU_MODEL (menu), section, G_MENU_LINK_SECTION));
485 : }
486 :
487 : void
488 0 : g_lo_menu_new_submenu_in_item_in_section (GLOMenu *menu,
489 : gint section,
490 : gint position)
491 : {
492 0 : g_return_if_fail (G_IS_LO_MENU (menu));
493 0 : g_return_if_fail (0 <= section && section < (gint) menu->items->len);
494 :
495 0 : GLOMenu* model = g_lo_menu_get_section (menu, section);
496 :
497 0 : g_return_if_fail (model != NULL);
498 :
499 0 : if (0 <= position && position < (gint) model->items->len) {
500 0 : GMenuModel* submenu = G_MENU_MODEL (g_lo_menu_new());
501 :
502 0 : g_lo_menu_set_link (model, position, G_MENU_LINK_SUBMENU, submenu);
503 :
504 0 : g_object_unref (submenu);
505 :
506 0 : g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1);
507 :
508 0 : g_object_unref (model);
509 : }
510 : }
511 :
512 : void
513 0 : g_lo_menu_set_submenu_to_item_in_section (GLOMenu *menu,
514 : gint section,
515 : gint position,
516 : GMenuModel *submenu)
517 : {
518 0 : g_return_if_fail (G_IS_LO_MENU (menu));
519 0 : g_return_if_fail (0 <= section && section < (gint) menu->items->len);
520 :
521 0 : GLOMenu *model = g_lo_menu_get_section (menu, section);
522 :
523 0 : g_return_if_fail (model != NULL);
524 :
525 0 : g_lo_menu_set_link (model, position, G_MENU_LINK_SUBMENU, submenu);
526 :
527 : // Notify the update.
528 0 : g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1);
529 :
530 0 : g_object_unref (model);
531 : }
532 :
533 : GLOMenu *
534 0 : g_lo_menu_get_submenu_from_item_in_section (GLOMenu *menu,
535 : gint section,
536 : gint position)
537 : {
538 0 : g_return_val_if_fail (G_IS_LO_MENU (menu), NULL);
539 0 : g_return_val_if_fail (0 <= section && section < (gint) menu->items->len, NULL);
540 :
541 0 : GLOMenu *model = g_lo_menu_get_section (menu, section);
542 :
543 0 : g_return_val_if_fail (model != NULL, NULL);
544 :
545 0 : GLOMenu *submenu = NULL;
546 :
547 0 : if (0 <= position && position < (gint) model->items->len)
548 0 : submenu = G_LO_MENU (G_MENU_MODEL_CLASS (g_lo_menu_parent_class)
549 0 : ->get_item_link (G_MENU_MODEL (model), position, G_MENU_LINK_SUBMENU));
550 : //submenu = g_menu_model_get_item_link (G_MENU_MODEL (model), position, G_MENU_LINK_SUBMENU);
551 :
552 0 : g_object_unref (model);
553 :
554 0 : return submenu;
555 : }
556 :
557 : void
558 0 : g_lo_menu_set_submenu_action_to_item_in_section (GLOMenu *menu,
559 : gint section,
560 : gint position,
561 : const gchar *action)
562 : {
563 0 : g_return_if_fail (G_IS_LO_MENU (menu));
564 :
565 0 : GMenuModel *model = G_MENU_MODEL (g_lo_menu_get_section (menu, section));
566 :
567 0 : g_return_if_fail (model != NULL);
568 :
569 : GVariant *value;
570 :
571 0 : if (action != NULL)
572 0 : value = g_variant_new_string (action);
573 : else
574 0 : value = NULL;
575 :
576 0 : g_lo_menu_set_attribute_value (G_LO_MENU (model), position, G_LO_MENU_ATTRIBUTE_SUBMENU_ACTION, value);
577 :
578 : // Notify the update.
579 0 : g_menu_model_items_changed (model, position, 1, 1);
580 :
581 0 : g_object_unref (model);
582 : }
583 :
584 : static void
585 0 : g_lo_menu_clear_item (struct item *menu_item)
586 : {
587 0 : if (menu_item->attributes != NULL)
588 0 : g_hash_table_unref (menu_item->attributes);
589 0 : if (menu_item->links != NULL)
590 0 : g_hash_table_unref (menu_item->links);
591 0 : }
592 :
593 : void
594 0 : g_lo_menu_remove (GLOMenu *menu,
595 : gint position)
596 : {
597 0 : g_return_if_fail (G_IS_LO_MENU (menu));
598 0 : g_return_if_fail (0 <= position && position < (gint) menu->items->len);
599 :
600 0 : g_lo_menu_clear_item (&g_array_index (menu->items, struct item, position));
601 0 : g_array_remove_index (menu->items, position);
602 0 : g_menu_model_items_changed (G_MENU_MODEL (menu), position, 1, 0);
603 : }
604 :
605 : void
606 0 : g_lo_menu_remove_from_section (GLOMenu *menu,
607 : gint section,
608 : gint position)
609 : {
610 0 : g_return_if_fail (G_IS_LO_MENU (menu));
611 0 : g_return_if_fail (0 <= section && section < (gint) menu->items->len);
612 :
613 0 : GLOMenu *model = g_lo_menu_get_section (menu, section);
614 :
615 0 : g_return_if_fail (model != NULL);
616 :
617 0 : g_lo_menu_remove (model, position);
618 :
619 0 : g_object_unref (model);
620 : }
621 :
622 : static void
623 0 : g_lo_menu_finalize (GObject *object)
624 : {
625 0 : GLOMenu *menu = G_LO_MENU (object);
626 : struct item *items;
627 : gint n_items;
628 : gint i;
629 :
630 0 : n_items = menu->items->len;
631 0 : items = reinterpret_cast<struct item *>(g_array_free (menu->items, FALSE));
632 0 : for (i = 0; i < n_items; i++)
633 0 : g_lo_menu_clear_item (&items[i]);
634 0 : g_free (items);
635 :
636 0 : G_OBJECT_CLASS (g_lo_menu_parent_class)
637 0 : ->finalize (object);
638 0 : }
639 :
640 : static void
641 18 : g_lo_menu_init (GLOMenu *menu)
642 : {
643 18 : menu->items = g_array_new (FALSE, FALSE, sizeof (struct item));
644 18 : }
645 :
646 : static void
647 3 : g_lo_menu_class_init (GLOMenuClass *klass)
648 : {
649 3 : GMenuModelClass *model_class = G_MENU_MODEL_CLASS (klass);
650 3 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
651 :
652 3 : object_class->finalize = g_lo_menu_finalize;
653 :
654 3 : model_class->is_mutable = g_lo_menu_is_mutable;
655 3 : model_class->get_n_items = g_lo_menu_get_n_items;
656 3 : model_class->get_item_attributes = g_lo_menu_get_item_attributes;
657 3 : model_class->get_item_links = g_lo_menu_get_item_links;
658 3 : }
659 :
660 : #endif
661 :
662 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|