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
37 GHashTable
* attributes
; // Item attributes.
38 GHashTable
* links
; // Item links.
42 g_lo_menu_struct_item_init (struct item
*menu_item
)
44 menu_item
->attributes
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, reinterpret_cast<GDestroyNotify
>(g_variant_unref
));
45 menu_item
->links
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, g_object_unref
);
48 /* We treat attribute names the same as GSettings keys:
49 * - only lowercase ascii, digits and '-'
50 * - must start with lowercase
51 * - must not end with '-'
52 * - no consecutive '-'
53 * - not longer than 1024 chars
56 valid_attribute_name (const gchar
*name
)
60 if (!g_ascii_islower (name
[0]))
63 for (i
= 1; name
[i
]; i
++)
66 !g_ascii_islower (name
[i
]) &&
67 !g_ascii_isdigit (name
[i
]))
70 if (name
[i
] == '-' && name
[i
+ 1] == '-')
74 if (name
[i
- 1] == '-')
88 g_lo_menu_is_mutable (GMenuModel
*)
90 // Menu is always mutable.
95 g_lo_menu_get_n_items (GMenuModel
*model
)
97 g_return_val_if_fail (model
!= nullptr, 0);
98 GLOMenu
*menu
= G_LO_MENU (model
);
99 g_return_val_if_fail (menu
->items
!= nullptr, 0);
101 return menu
->items
->len
;
105 g_lo_menu_get_n_items_from_section (GLOMenu
*menu
,
108 g_return_val_if_fail (0 <= section
&& section
< static_cast<gint
>(menu
->items
->len
), 0);
110 GLOMenu
*model
= g_lo_menu_get_section (menu
, section
);
112 g_return_val_if_fail (model
!= nullptr, 0);
114 gint length
= model
->items
->len
;
116 g_object_unref (model
);
122 g_lo_menu_get_item_attributes (GMenuModel
*model
,
126 GLOMenu
*menu
= G_LO_MENU (model
);
127 *table
= g_hash_table_ref (g_array_index (menu
->items
, struct item
, position
).attributes
);
131 g_lo_menu_get_item_links (GMenuModel
*model
,
135 GLOMenu
*menu
= G_LO_MENU (model
);
136 *table
= g_hash_table_ref (g_array_index (menu
->items
, struct item
, position
).links
);
140 g_lo_menu_insert (GLOMenu
*menu
,
144 g_lo_menu_insert_section (menu
, position
, label
, nullptr);
148 g_lo_menu_insert_in_section (GLOMenu
*menu
,
153 g_return_if_fail (G_IS_LO_MENU (menu
));
154 g_return_if_fail (0 <= section
&& section
< static_cast<gint
>(menu
->items
->len
));
156 GLOMenu
*model
= g_lo_menu_get_section (menu
, section
);
158 g_return_if_fail (model
!= nullptr);
160 g_lo_menu_insert (model
, position
, label
);
162 g_object_unref (model
);
168 return G_LO_MENU( g_object_new (G_TYPE_LO_MENU
, nullptr) );
172 g_lo_menu_set_attribute_value (GLOMenu
*menu
,
174 const gchar
*attribute
,
177 g_return_if_fail (G_IS_LO_MENU (menu
));
178 g_return_if_fail (attribute
!= nullptr);
179 g_return_if_fail (valid_attribute_name (attribute
));
181 if (position
>= static_cast<gint
>(menu
->items
->len
))
184 struct item menu_item
= g_array_index (menu
->items
, struct item
, position
);
186 if (value
!= nullptr)
187 g_hash_table_insert (menu_item
.attributes
, g_strdup (attribute
), g_variant_ref_sink (value
));
189 g_hash_table_remove (menu_item
.attributes
, attribute
);
193 g_lo_menu_get_attribute_value_from_item_in_section (GLOMenu
*menu
,
196 const gchar
*attribute
,
197 const GVariantType
*type
)
199 GMenuModel
*model
= G_MENU_MODEL (g_lo_menu_get_section (menu
, section
));
201 g_return_val_if_fail (model
!= nullptr, nullptr);
203 GVariant
*value
= g_menu_model_get_item_attribute_value (model
,
208 g_object_unref (model
);
214 g_lo_menu_set_label (GLOMenu
*menu
,
218 g_return_if_fail (G_IS_LO_MENU (menu
));
222 if (label
!= nullptr)
223 value
= g_variant_new_string (label
);
227 g_lo_menu_set_attribute_value (menu
, position
, G_MENU_ATTRIBUTE_LABEL
, value
);
231 g_lo_menu_set_icon (GLOMenu
*menu
,
235 g_return_if_fail (G_IS_LO_MENU (menu
));
241 #if GLIB_CHECK_VERSION(2,38,0)
242 value
= g_icon_serialize (const_cast<GIcon
*>(icon
));
250 #ifndef G_MENU_ATTRIBUTE_ICON
251 # define G_MENU_ATTRIBUTE_ICON "icon"
254 g_lo_menu_set_attribute_value (menu
, position
, G_MENU_ATTRIBUTE_ICON
, value
);
256 g_variant_unref (value
);
260 g_lo_menu_set_label_to_item_in_section (GLOMenu
*menu
,
265 g_return_if_fail (G_IS_LO_MENU (menu
));
267 GLOMenu
*model
= g_lo_menu_get_section (menu
, section
);
269 g_return_if_fail (model
!= nullptr);
271 g_lo_menu_set_label (model
, position
, label
);
273 // Notify the update.
274 g_menu_model_items_changed (G_MENU_MODEL (model
), position
, 1, 1);
276 g_object_unref (model
);
280 g_lo_menu_set_icon_to_item_in_section (GLOMenu
*menu
,
285 g_return_if_fail (G_IS_LO_MENU (menu
));
287 GLOMenu
*model
= g_lo_menu_get_section (menu
, section
);
289 g_return_if_fail (model
!= nullptr);
291 g_lo_menu_set_icon (model
, position
, icon
);
293 // Notify the update.
294 g_menu_model_items_changed (G_MENU_MODEL (model
), position
, 1, 1);
296 g_object_unref (model
);
300 g_lo_menu_get_label_from_item_in_section (GLOMenu
*menu
,
304 g_return_val_if_fail (G_IS_LO_MENU (menu
), nullptr);
306 GVariant
*label_value
= g_lo_menu_get_attribute_value_from_item_in_section (menu
,
309 G_MENU_ATTRIBUTE_LABEL
,
310 G_VARIANT_TYPE_STRING
);
312 gchar
*label
= nullptr;
316 label
= g_variant_dup_string (label_value
, nullptr);
317 g_variant_unref (label_value
);
324 g_lo_menu_set_action_and_target_value (GLOMenu
*menu
,
327 GVariant
*target_value
)
329 g_return_if_fail (G_IS_LO_MENU (menu
));
331 GVariant
*action_value
;
333 if (action
!= nullptr)
335 action_value
= g_variant_new_string (action
);
339 action_value
= nullptr;
340 target_value
= nullptr;
343 g_lo_menu_set_attribute_value (menu
, position
, G_MENU_ATTRIBUTE_ACTION
, action_value
);
344 g_lo_menu_set_attribute_value (menu
, position
, G_MENU_ATTRIBUTE_TARGET
, target_value
);
345 g_lo_menu_set_attribute_value (menu
, position
, G_LO_MENU_ATTRIBUTE_SUBMENU_ACTION
, nullptr);
347 g_menu_model_items_changed (G_MENU_MODEL (menu
), position
, 1, 1);
351 g_lo_menu_set_action_and_target_value_to_item_in_section (GLOMenu
*menu
,
354 const gchar
*command
,
355 GVariant
*target_value
)
357 g_return_if_fail (G_IS_LO_MENU (menu
));
359 GLOMenu
*model
= g_lo_menu_get_section (menu
, section
);
361 g_return_if_fail (model
!= nullptr);
363 g_lo_menu_set_action_and_target_value (model
, position
, command
, target_value
);
365 g_object_unref (model
);
369 g_lo_menu_set_accelerator_to_item_in_section (GLOMenu
*menu
,
372 const gchar
*accelerator
)
374 g_return_if_fail (G_IS_LO_MENU (menu
));
376 GLOMenu
*model
= g_lo_menu_get_section (menu
, section
);
378 g_return_if_fail (model
!= nullptr);
382 if (accelerator
!= nullptr)
383 value
= g_variant_new_string (accelerator
);
387 g_lo_menu_set_attribute_value (model
, position
, G_LO_MENU_ATTRIBUTE_ACCELERATOR
, value
);
389 // Notify the update.
390 g_menu_model_items_changed (G_MENU_MODEL (model
), position
, 1, 1);
392 g_object_unref (model
);
396 g_lo_menu_get_accelerator_from_item_in_section (GLOMenu
*menu
,
400 g_return_val_if_fail (G_IS_LO_MENU (menu
), nullptr);
402 GVariant
*accel_value
= g_lo_menu_get_attribute_value_from_item_in_section (menu
,
405 G_LO_MENU_ATTRIBUTE_ACCELERATOR
,
406 G_VARIANT_TYPE_STRING
);
408 gchar
*accel
= nullptr;
410 if (accel_value
!= nullptr)
412 accel
= g_variant_dup_string (accel_value
, nullptr);
413 g_variant_unref (accel_value
);
420 g_lo_menu_set_command_to_item_in_section (GLOMenu
*menu
,
423 const gchar
*command
)
425 g_return_if_fail (G_IS_LO_MENU (menu
));
427 GLOMenu
*model
= g_lo_menu_get_section (menu
, section
);
429 g_return_if_fail (model
!= nullptr);
433 if (command
!= nullptr)
434 value
= g_variant_new_string (command
);
438 g_lo_menu_set_attribute_value (model
, position
, G_LO_MENU_ATTRIBUTE_COMMAND
, value
);
440 // Notify the update.
441 g_menu_model_items_changed (G_MENU_MODEL (model
), position
, 1, 1);
443 g_object_unref (model
);
447 g_lo_menu_get_command_from_item_in_section (GLOMenu
*menu
,
451 g_return_val_if_fail (G_IS_LO_MENU (menu
), nullptr);
453 GVariant
*command_value
= g_lo_menu_get_attribute_value_from_item_in_section (menu
,
456 G_LO_MENU_ATTRIBUTE_COMMAND
,
457 G_VARIANT_TYPE_STRING
);
459 gchar
*command
= nullptr;
461 if (command_value
!= nullptr)
463 command
= g_variant_dup_string (command_value
, nullptr);
464 g_variant_unref (command_value
);
471 g_lo_menu_set_link (GLOMenu
*menu
,
476 g_return_if_fail (G_IS_LO_MENU (menu
));
477 g_return_if_fail (link
!= nullptr);
478 g_return_if_fail (valid_attribute_name (link
));
480 if (position
< 0 || position
>= static_cast<gint
>(menu
->items
->len
))
481 position
= menu
->items
->len
- 1;
483 struct item menu_item
= g_array_index (menu
->items
, struct item
, position
);
485 if (model
!= nullptr)
486 g_hash_table_insert (menu_item
.links
, g_strdup (link
), g_object_ref (model
));
488 g_hash_table_remove (menu_item
.links
, link
);
492 g_lo_menu_insert_section (GLOMenu
*menu
,
497 g_return_if_fail (G_IS_LO_MENU (menu
));
499 if (position
< 0 || position
> static_cast<gint
>(menu
->items
->len
))
500 position
= menu
->items
->len
;
502 struct item menu_item
;
504 g_lo_menu_struct_item_init(&menu_item
);
506 g_array_insert_val (menu
->items
, position
, menu_item
);
508 g_lo_menu_set_label (menu
, position
, label
);
509 g_lo_menu_set_link (menu
, position
, G_MENU_LINK_SECTION
, section
);
511 g_menu_model_items_changed (G_MENU_MODEL (menu
), position
, 0, 1);
515 g_lo_menu_new_section (GLOMenu
*menu
,
519 GMenuModel
*section
= G_MENU_MODEL (g_lo_menu_new());
521 g_lo_menu_insert_section (menu
, position
, label
, section
);
523 g_object_unref (section
);
527 g_lo_menu_get_section (GLOMenu
*menu
,
530 g_return_val_if_fail (G_IS_LO_MENU (menu
), nullptr);
532 return G_LO_MENU (G_MENU_MODEL_CLASS (g_lo_menu_parent_class
)
533 ->get_item_link (G_MENU_MODEL (menu
), section
, G_MENU_LINK_SECTION
));
537 g_lo_menu_new_submenu_in_item_in_section (GLOMenu
*menu
,
541 g_return_if_fail (G_IS_LO_MENU (menu
));
542 g_return_if_fail (0 <= section
&& section
< static_cast<gint
>(menu
->items
->len
));
544 GLOMenu
* model
= g_lo_menu_get_section (menu
, section
);
546 g_return_if_fail (model
!= nullptr);
548 if (0 <= position
&& position
< static_cast<gint
>(model
->items
->len
)) {
549 GMenuModel
* submenu
= G_MENU_MODEL (g_lo_menu_new());
551 g_lo_menu_set_link (model
, position
, G_MENU_LINK_SUBMENU
, submenu
);
553 g_object_unref (submenu
);
555 g_menu_model_items_changed (G_MENU_MODEL (model
), position
, 1, 1);
557 g_object_unref (model
);
562 g_lo_menu_get_submenu_from_item_in_section (GLOMenu
*menu
,
566 g_return_val_if_fail (G_IS_LO_MENU (menu
), nullptr);
567 g_return_val_if_fail (0 <= section
&& section
< static_cast<gint
>(menu
->items
->len
), nullptr);
569 GLOMenu
*model
= g_lo_menu_get_section (menu
, section
);
571 g_return_val_if_fail (model
!= nullptr, nullptr);
573 GLOMenu
*submenu
= nullptr;
575 if (0 <= position
&& position
< static_cast<gint
>(model
->items
->len
))
576 submenu
= G_LO_MENU (G_MENU_MODEL_CLASS (g_lo_menu_parent_class
)
577 ->get_item_link (G_MENU_MODEL (model
), position
, G_MENU_LINK_SUBMENU
));
578 //submenu = g_menu_model_get_item_link (G_MENU_MODEL (model), position, G_MENU_LINK_SUBMENU);
580 g_object_unref (model
);
586 g_lo_menu_set_submenu_action_to_item_in_section (GLOMenu
*menu
,
591 g_return_if_fail (G_IS_LO_MENU (menu
));
593 GMenuModel
*model
= G_MENU_MODEL (g_lo_menu_get_section (menu
, section
));
595 g_return_if_fail (model
!= nullptr);
599 if (action
!= nullptr)
600 value
= g_variant_new_string (action
);
604 g_lo_menu_set_attribute_value (G_LO_MENU (model
), position
, G_LO_MENU_ATTRIBUTE_SUBMENU_ACTION
, value
);
606 // Notify the update.
607 g_menu_model_items_changed (model
, position
, 1, 1);
609 g_object_unref (model
);
613 g_lo_menu_clear_item (struct item
*menu_item
)
615 if (menu_item
->attributes
!= nullptr)
616 g_hash_table_unref (menu_item
->attributes
);
617 if (menu_item
->links
!= nullptr)
618 g_hash_table_unref (menu_item
->links
);
622 g_lo_menu_remove (GLOMenu
*menu
,
625 g_return_if_fail (G_IS_LO_MENU (menu
));
626 g_return_if_fail (0 <= position
&& position
< static_cast<gint
>(menu
->items
->len
));
628 g_lo_menu_clear_item (&g_array_index (menu
->items
, struct item
, position
));
629 g_array_remove_index (menu
->items
, position
);
630 g_menu_model_items_changed (G_MENU_MODEL (menu
), position
, 1, 0);
634 g_lo_menu_remove_from_section (GLOMenu
*menu
,
638 g_return_if_fail (G_IS_LO_MENU (menu
));
639 g_return_if_fail (0 <= section
&& section
< static_cast<gint
>(menu
->items
->len
));
641 GLOMenu
*model
= g_lo_menu_get_section (menu
, section
);
643 g_return_if_fail (model
!= nullptr);
645 g_lo_menu_remove (model
, position
);
647 g_object_unref (model
);
651 g_lo_menu_finalize (GObject
*object
)
653 GLOMenu
*menu
= G_LO_MENU (object
);
658 n_items
= menu
->items
->len
;
659 items
= reinterpret_cast<struct item
*>(g_array_free (menu
->items
, FALSE
));
660 for (i
= 0; i
< n_items
; i
++)
661 g_lo_menu_clear_item (&items
[i
]);
664 G_OBJECT_CLASS (g_lo_menu_parent_class
)
669 g_lo_menu_init (GLOMenu
*menu
)
671 menu
->items
= g_array_new (FALSE
, FALSE
, sizeof (struct item
));
675 g_lo_menu_class_init (GLOMenuClass
*klass
)
677 GMenuModelClass
*model_class
= G_MENU_MODEL_CLASS (klass
);
678 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
680 object_class
->finalize
= g_lo_menu_finalize
;
682 model_class
->is_mutable
= g_lo_menu_is_mutable
;
683 model_class
->get_n_items
= g_lo_menu_get_n_items
;
684 model_class
->get_item_attributes
= g_lo_menu_get_item_attributes
;
685 model_class
->get_item_links
= g_lo_menu_get_item_links
;
688 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */