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