1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
10 #include <unx/gtk/glomenu.h>
14 GMenuModel
const parent_instance
;
19 typedef GMenuModelClass GLOMenuClass
;
22 #pragma GCC diagnostic push
23 #pragma GCC diagnostic ignored "-Wunused-function"
25 #if __has_warning("-Wdeprecated-volatile")
26 #pragma clang diagnostic ignored "-Wdeprecated-volatile"
30 G_DEFINE_TYPE (GLOMenu
, g_lo_menu
, G_TYPE_MENU_MODEL
);
32 #pragma GCC diagnostic pop
39 GHashTable
* attributes
; // Item attributes.
40 GHashTable
* links
; // Item links.
46 g_lo_menu_struct_item_init (struct item
*menu_item
)
48 menu_item
->attributes
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, reinterpret_cast<GDestroyNotify
>(g_variant_unref
));
49 menu_item
->links
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, g_object_unref
);
52 /* We treat attribute names the same as GSettings keys:
53 * - only lowercase ascii, digits and '-'
54 * - must start with lowercase
55 * - must not end with '-'
56 * - no consecutive '-'
57 * - not longer than 1024 chars
60 valid_attribute_name (const gchar
*name
)
64 if (!g_ascii_islower (name
[0]))
67 for (i
= 1; name
[i
]; i
++)
70 !g_ascii_islower (name
[i
]) &&
71 !g_ascii_isdigit (name
[i
]))
74 if (name
[i
] == '-' && name
[i
+ 1] == '-')
78 if (name
[i
- 1] == '-')
92 g_lo_menu_is_mutable (GMenuModel
*)
94 // Menu is always mutable.
99 g_lo_menu_get_n_items (GMenuModel
*model
)
101 g_return_val_if_fail (model
!= nullptr, 0);
102 GLOMenu
*menu
= G_LO_MENU (model
);
103 g_return_val_if_fail (menu
->items
!= nullptr, 0);
105 return menu
->items
->len
;
109 g_lo_menu_get_n_items_from_section (GLOMenu
*menu
,
112 g_return_val_if_fail (0 <= section
&& section
< static_cast<gint
>(menu
->items
->len
), 0);
114 GLOMenu
*model
= g_lo_menu_get_section (menu
, section
);
116 g_return_val_if_fail (model
!= nullptr, 0);
118 gint length
= model
->items
->len
;
120 g_object_unref (model
);
126 g_lo_menu_get_item_attributes (GMenuModel
*model
,
130 GLOMenu
*menu
= G_LO_MENU (model
);
131 *table
= g_hash_table_ref (g_array_index (menu
->items
, struct item
, position
).attributes
);
135 g_lo_menu_get_item_links (GMenuModel
*model
,
139 GLOMenu
*menu
= G_LO_MENU (model
);
140 *table
= g_hash_table_ref (g_array_index (menu
->items
, struct item
, position
).links
);
144 g_lo_menu_insert (GLOMenu
*menu
,
148 g_lo_menu_insert_section (menu
, position
, label
, nullptr);
152 g_lo_menu_insert_in_section (GLOMenu
*menu
,
157 g_return_if_fail (G_IS_LO_MENU (menu
));
158 g_return_if_fail (0 <= section
&& section
< static_cast<gint
>(menu
->items
->len
));
160 GLOMenu
*model
= g_lo_menu_get_section (menu
, section
);
162 g_return_if_fail (model
!= nullptr);
164 g_lo_menu_insert (model
, position
, label
);
166 g_object_unref (model
);
172 return G_LO_MENU( g_object_new (G_TYPE_LO_MENU
, nullptr) );
176 g_lo_menu_set_attribute_value (GLOMenu
*menu
,
178 const gchar
*attribute
,
181 g_return_if_fail (G_IS_LO_MENU (menu
));
182 g_return_if_fail (attribute
!= nullptr);
183 g_return_if_fail (valid_attribute_name (attribute
));
185 if (position
>= static_cast<gint
>(menu
->items
->len
))
188 struct item menu_item
= g_array_index (menu
->items
, struct item
, position
);
190 if (value
!= nullptr)
191 g_hash_table_insert (menu_item
.attributes
, g_strdup (attribute
), g_variant_ref_sink (value
));
193 g_hash_table_remove (menu_item
.attributes
, attribute
);
197 g_lo_menu_get_attribute_value_from_item_in_section (GLOMenu
*menu
,
200 const gchar
*attribute
,
201 const GVariantType
*type
)
203 GMenuModel
*model
= G_MENU_MODEL (g_lo_menu_get_section (menu
, section
));
205 g_return_val_if_fail (model
!= nullptr, nullptr);
207 GVariant
*value
= g_menu_model_get_item_attribute_value (model
,
212 g_object_unref (model
);
218 g_lo_menu_set_label (GLOMenu
*menu
,
222 g_return_if_fail (G_IS_LO_MENU (menu
));
226 if (label
!= nullptr)
227 value
= g_variant_new_string (label
);
231 g_lo_menu_set_attribute_value (menu
, position
, G_MENU_ATTRIBUTE_LABEL
, value
);
235 g_lo_menu_set_icon (GLOMenu
*menu
,
239 g_return_if_fail (G_IS_LO_MENU (menu
));
245 #if GLIB_CHECK_VERSION(2,38,0)
246 value
= g_icon_serialize (const_cast<GIcon
*>(icon
));
254 #ifndef G_MENU_ATTRIBUTE_ICON
255 # define G_MENU_ATTRIBUTE_ICON "icon"
258 g_lo_menu_set_attribute_value (menu
, position
, G_MENU_ATTRIBUTE_ICON
, value
);
260 g_variant_unref (value
);
264 g_lo_menu_set_label_to_item_in_section (GLOMenu
*menu
,
269 g_return_if_fail (G_IS_LO_MENU (menu
));
271 GLOMenu
*model
= g_lo_menu_get_section (menu
, section
);
273 g_return_if_fail (model
!= nullptr);
275 g_lo_menu_set_label (model
, position
, label
);
277 // Notify the update.
278 g_menu_model_items_changed (G_MENU_MODEL (model
), position
, 1, 1);
280 g_object_unref (model
);
284 g_lo_menu_set_icon_to_item_in_section (GLOMenu
*menu
,
289 g_return_if_fail (G_IS_LO_MENU (menu
));
291 GLOMenu
*model
= g_lo_menu_get_section (menu
, section
);
293 g_return_if_fail (model
!= nullptr);
295 g_lo_menu_set_icon (model
, position
, icon
);
297 // Notify the update.
298 g_menu_model_items_changed (G_MENU_MODEL (model
), position
, 1, 1);
300 g_object_unref (model
);
304 g_lo_menu_get_label_from_item_in_section (GLOMenu
*menu
,
308 g_return_val_if_fail (G_IS_LO_MENU (menu
), nullptr);
310 GVariant
*label_value
= g_lo_menu_get_attribute_value_from_item_in_section (menu
,
313 G_MENU_ATTRIBUTE_LABEL
,
314 G_VARIANT_TYPE_STRING
);
316 gchar
*label
= nullptr;
320 label
= g_variant_dup_string (label_value
, nullptr);
321 g_variant_unref (label_value
);
328 g_lo_menu_set_action_and_target_value (GLOMenu
*menu
,
331 GVariant
*target_value
)
333 g_return_if_fail (G_IS_LO_MENU (menu
));
335 GVariant
*action_value
;
337 if (action
!= nullptr)
339 action_value
= g_variant_new_string (action
);
343 action_value
= nullptr;
344 target_value
= nullptr;
347 g_lo_menu_set_attribute_value (menu
, position
, G_MENU_ATTRIBUTE_ACTION
, action_value
);
348 g_lo_menu_set_attribute_value (menu
, position
, G_MENU_ATTRIBUTE_TARGET
, target_value
);
349 g_lo_menu_set_attribute_value (menu
, position
, G_LO_MENU_ATTRIBUTE_SUBMENU_ACTION
, nullptr);
351 g_menu_model_items_changed (G_MENU_MODEL (menu
), position
, 1, 1);
355 g_lo_menu_set_action_and_target_value_to_item_in_section (GLOMenu
*menu
,
358 const gchar
*command
,
359 GVariant
*target_value
)
361 g_return_if_fail (G_IS_LO_MENU (menu
));
363 GLOMenu
*model
= g_lo_menu_get_section (menu
, section
);
365 g_return_if_fail (model
!= nullptr);
367 g_lo_menu_set_action_and_target_value (model
, position
, command
, target_value
);
369 g_object_unref (model
);
373 g_lo_menu_set_accelerator_to_item_in_section (GLOMenu
*menu
,
376 const gchar
*accelerator
)
378 g_return_if_fail (G_IS_LO_MENU (menu
));
380 GLOMenu
*model
= g_lo_menu_get_section (menu
, section
);
382 g_return_if_fail (model
!= nullptr);
386 if (accelerator
!= nullptr)
387 value
= g_variant_new_string (accelerator
);
391 g_lo_menu_set_attribute_value (model
, position
, G_LO_MENU_ATTRIBUTE_ACCELERATOR
, value
);
393 // Notify the update.
394 g_menu_model_items_changed (G_MENU_MODEL (model
), position
, 1, 1);
396 g_object_unref (model
);
400 g_lo_menu_get_accelerator_from_item_in_section (GLOMenu
*menu
,
404 g_return_val_if_fail (G_IS_LO_MENU (menu
), nullptr);
406 GVariant
*accel_value
= g_lo_menu_get_attribute_value_from_item_in_section (menu
,
409 G_LO_MENU_ATTRIBUTE_ACCELERATOR
,
410 G_VARIANT_TYPE_STRING
);
412 gchar
*accel
= nullptr;
414 if (accel_value
!= nullptr)
416 accel
= g_variant_dup_string (accel_value
, nullptr);
417 g_variant_unref (accel_value
);
424 g_lo_menu_set_command_to_item_in_section (GLOMenu
*menu
,
427 const gchar
*command
)
429 g_return_if_fail (G_IS_LO_MENU (menu
));
431 GLOMenu
*model
= g_lo_menu_get_section (menu
, section
);
433 g_return_if_fail (model
!= nullptr);
437 if (command
!= nullptr)
438 value
= g_variant_new_string (command
);
442 g_lo_menu_set_attribute_value (model
, position
, G_LO_MENU_ATTRIBUTE_COMMAND
, value
);
444 // Notify the update.
445 g_menu_model_items_changed (G_MENU_MODEL (model
), position
, 1, 1);
447 g_object_unref (model
);
451 g_lo_menu_get_command_from_item_in_section (GLOMenu
*menu
,
455 g_return_val_if_fail (G_IS_LO_MENU (menu
), nullptr);
457 GVariant
*command_value
= g_lo_menu_get_attribute_value_from_item_in_section (menu
,
460 G_LO_MENU_ATTRIBUTE_COMMAND
,
461 G_VARIANT_TYPE_STRING
);
463 gchar
*command
= nullptr;
465 if (command_value
!= nullptr)
467 command
= g_variant_dup_string (command_value
, nullptr);
468 g_variant_unref (command_value
);
475 g_lo_menu_set_link (GLOMenu
*menu
,
480 g_return_if_fail (G_IS_LO_MENU (menu
));
481 g_return_if_fail (link
!= nullptr);
482 g_return_if_fail (valid_attribute_name (link
));
484 if (position
< 0 || position
>= static_cast<gint
>(menu
->items
->len
))
485 position
= menu
->items
->len
- 1;
487 struct item menu_item
= g_array_index (menu
->items
, struct item
, position
);
489 if (model
!= nullptr)
490 g_hash_table_insert (menu_item
.links
, g_strdup (link
), g_object_ref (model
));
492 g_hash_table_remove (menu_item
.links
, link
);
496 g_lo_menu_insert_section (GLOMenu
*menu
,
501 g_return_if_fail (G_IS_LO_MENU (menu
));
503 if (position
< 0 || position
> static_cast<gint
>(menu
->items
->len
))
504 position
= menu
->items
->len
;
506 struct item menu_item
;
508 g_lo_menu_struct_item_init(&menu_item
);
510 g_array_insert_val (menu
->items
, position
, menu_item
);
512 g_lo_menu_set_label (menu
, position
, label
);
513 g_lo_menu_set_link (menu
, position
, G_MENU_LINK_SECTION
, section
);
515 g_menu_model_items_changed (G_MENU_MODEL (menu
), position
, 0, 1);
519 g_lo_menu_new_section (GLOMenu
*menu
,
523 GMenuModel
*section
= G_MENU_MODEL (g_lo_menu_new());
525 g_lo_menu_insert_section (menu
, position
, label
, section
);
527 g_object_unref (section
);
531 g_lo_menu_get_section (GLOMenu
*menu
,
534 g_return_val_if_fail (G_IS_LO_MENU (menu
), nullptr);
536 return G_LO_MENU (G_MENU_MODEL_CLASS (g_lo_menu_parent_class
)
537 ->get_item_link (G_MENU_MODEL (menu
), section
, G_MENU_LINK_SECTION
));
541 g_lo_menu_new_submenu_in_item_in_section (GLOMenu
*menu
,
545 g_return_if_fail (G_IS_LO_MENU (menu
));
546 g_return_if_fail (0 <= section
&& section
< static_cast<gint
>(menu
->items
->len
));
548 GLOMenu
* model
= g_lo_menu_get_section (menu
, section
);
550 g_return_if_fail (model
!= nullptr);
552 if (0 <= position
&& position
< static_cast<gint
>(model
->items
->len
)) {
553 GMenuModel
* submenu
= G_MENU_MODEL (g_lo_menu_new());
555 g_lo_menu_set_link (model
, position
, G_MENU_LINK_SUBMENU
, submenu
);
557 g_object_unref (submenu
);
559 g_menu_model_items_changed (G_MENU_MODEL (model
), position
, 1, 1);
561 g_object_unref (model
);
566 g_lo_menu_get_submenu_from_item_in_section (GLOMenu
*menu
,
570 g_return_val_if_fail (G_IS_LO_MENU (menu
), nullptr);
571 g_return_val_if_fail (0 <= section
&& section
< static_cast<gint
>(menu
->items
->len
), nullptr);
573 GLOMenu
*model
= g_lo_menu_get_section (menu
, section
);
575 g_return_val_if_fail (model
!= nullptr, nullptr);
577 GLOMenu
*submenu
= nullptr;
579 if (0 <= position
&& position
< static_cast<gint
>(model
->items
->len
))
580 submenu
= G_LO_MENU (G_MENU_MODEL_CLASS (g_lo_menu_parent_class
)
581 ->get_item_link (G_MENU_MODEL (model
), position
, G_MENU_LINK_SUBMENU
));
582 //submenu = g_menu_model_get_item_link (G_MENU_MODEL (model), position, G_MENU_LINK_SUBMENU);
584 g_object_unref (model
);
590 g_lo_menu_set_submenu_action_to_item_in_section (GLOMenu
*menu
,
595 g_return_if_fail (G_IS_LO_MENU (menu
));
597 GMenuModel
*model
= G_MENU_MODEL (g_lo_menu_get_section (menu
, section
));
599 g_return_if_fail (model
!= nullptr);
603 if (action
!= nullptr)
604 value
= g_variant_new_string (action
);
608 g_lo_menu_set_attribute_value (G_LO_MENU (model
), position
, G_LO_MENU_ATTRIBUTE_SUBMENU_ACTION
, value
);
610 // Notify the update.
611 g_menu_model_items_changed (model
, position
, 1, 1);
613 g_object_unref (model
);
617 g_lo_menu_clear_item (struct item
*menu_item
)
619 if (menu_item
->attributes
!= nullptr)
620 g_hash_table_unref (menu_item
->attributes
);
621 if (menu_item
->links
!= nullptr)
622 g_hash_table_unref (menu_item
->links
);
626 g_lo_menu_remove (GLOMenu
*menu
,
629 g_return_if_fail (G_IS_LO_MENU (menu
));
630 g_return_if_fail (0 <= position
&& position
< static_cast<gint
>(menu
->items
->len
));
632 g_lo_menu_clear_item (&g_array_index (menu
->items
, struct item
, position
));
633 g_array_remove_index (menu
->items
, position
);
634 g_menu_model_items_changed (G_MENU_MODEL (menu
), position
, 1, 0);
638 g_lo_menu_remove_from_section (GLOMenu
*menu
,
642 g_return_if_fail (G_IS_LO_MENU (menu
));
643 g_return_if_fail (0 <= section
&& section
< static_cast<gint
>(menu
->items
->len
));
645 GLOMenu
*model
= g_lo_menu_get_section (menu
, section
);
647 g_return_if_fail (model
!= nullptr);
649 g_lo_menu_remove (model
, position
);
651 g_object_unref (model
);
655 g_lo_menu_finalize (GObject
*object
)
657 GLOMenu
*menu
= G_LO_MENU (object
);
662 n_items
= menu
->items
->len
;
663 items
= reinterpret_cast<struct item
*>(g_array_free (menu
->items
, FALSE
));
664 for (i
= 0; i
< n_items
; i
++)
665 g_lo_menu_clear_item (&items
[i
]);
668 G_OBJECT_CLASS (g_lo_menu_parent_class
)
673 g_lo_menu_init (GLOMenu
*menu
)
675 menu
->items
= g_array_new (FALSE
, FALSE
, sizeof (struct item
));
679 g_lo_menu_class_init (GLOMenuClass
*klass
)
681 GMenuModelClass
*model_class
= G_MENU_MODEL_CLASS (klass
);
682 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
684 object_class
->finalize
= g_lo_menu_finalize
;
686 model_class
->is_mutable
= g_lo_menu_is_mutable
;
687 model_class
->get_n_items
= g_lo_menu_get_n_items
;
688 model_class
->get_item_attributes
= g_lo_menu_get_item_attributes
;
689 model_class
->get_item_links
= g_lo_menu_get_item_links
;
692 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */