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