Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / unx / gtk3 / gtk3glomenu.cxx
bloba82c6946422c28e4dc76c0f71dbf4a569895211b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include <unx/gtk/glomenu.h>
12 struct GLOMenu
14 GMenuModel const parent_instance;
16 GArray *items;
19 typedef GMenuModelClass GLOMenuClass;
21 #ifdef __GNUC__
22 #pragma GCC diagnostic push
23 #pragma GCC diagnostic ignored "-Wunused-function"
24 #if defined __clang__
25 #if __has_warning("-Wdeprecated-volatile")
26 #pragma clang diagnostic ignored "-Wdeprecated-volatile"
27 #endif
28 #endif
29 #endif
30 G_DEFINE_TYPE (GLOMenu, g_lo_menu, G_TYPE_MENU_MODEL);
31 #ifdef __GNUC__
32 #pragma GCC diagnostic pop
33 #endif
35 struct item
37 GHashTable* attributes; // Item attributes.
38 GHashTable* links; // Item links.
41 static void
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
55 static bool
56 valid_attribute_name (const gchar *name)
58 gint i;
60 if (!g_ascii_islower (name[0]))
61 return FALSE;
63 for (i = 1; name[i]; i++)
65 if (name[i] != '-' &&
66 !g_ascii_islower (name[i]) &&
67 !g_ascii_isdigit (name[i]))
68 return FALSE;
70 if (name[i] == '-' && name[i + 1] == '-')
71 return FALSE;
74 if (name[i - 1] == '-')
75 return FALSE;
77 if (i > 1024)
78 return FALSE;
80 return TRUE;
84 * GLOMenu
87 static gboolean
88 g_lo_menu_is_mutable (GMenuModel*)
90 // Menu is always mutable.
91 return TRUE;
94 static gint
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;
104 gint
105 g_lo_menu_get_n_items_from_section (GLOMenu *menu,
106 gint section)
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);
118 return length;
121 static void
122 g_lo_menu_get_item_attributes (GMenuModel *model,
123 gint position,
124 GHashTable **table)
126 GLOMenu *menu = G_LO_MENU (model);
127 *table = g_hash_table_ref (g_array_index (menu->items, struct item, position).attributes);
130 static void
131 g_lo_menu_get_item_links (GMenuModel *model,
132 gint position,
133 GHashTable **table)
135 GLOMenu *menu = G_LO_MENU (model);
136 *table = g_hash_table_ref (g_array_index (menu->items, struct item, position).links);
139 void
140 g_lo_menu_insert (GLOMenu *menu,
141 gint position,
142 const gchar *label)
144 g_lo_menu_insert_section (menu, position, label, nullptr);
147 void
148 g_lo_menu_insert_in_section (GLOMenu *menu,
149 gint section,
150 gint position,
151 const gchar *label)
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);
165 GLOMenu *
166 g_lo_menu_new()
168 return G_LO_MENU( g_object_new (G_TYPE_LO_MENU, nullptr) );
171 static void
172 g_lo_menu_set_attribute_value (GLOMenu *menu,
173 gint position,
174 const gchar *attribute,
175 GVariant *value)
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))
182 return;
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));
188 else
189 g_hash_table_remove (menu_item.attributes, attribute);
192 static GVariant*
193 g_lo_menu_get_attribute_value_from_item_in_section (GLOMenu *menu,
194 gint section,
195 gint position,
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,
204 position,
205 attribute,
206 type);
208 g_object_unref (model);
210 return value;
213 void
214 g_lo_menu_set_label (GLOMenu *menu,
215 gint position,
216 const gchar *label)
218 g_return_if_fail (G_IS_LO_MENU (menu));
220 GVariant *value;
222 if (label != nullptr)
223 value = g_variant_new_string (label);
224 else
225 value = nullptr;
227 g_lo_menu_set_attribute_value (menu, position, G_MENU_ATTRIBUTE_LABEL, value);
230 void
231 g_lo_menu_set_icon (GLOMenu *menu,
232 gint position,
233 const GIcon *icon)
235 g_return_if_fail (G_IS_LO_MENU (menu));
237 GVariant *value;
239 if (icon != nullptr)
241 #if GLIB_CHECK_VERSION(2,38,0)
242 value = g_icon_serialize (const_cast<GIcon*>(icon));
243 #else
244 value = nullptr;
245 #endif
247 else
248 value = nullptr;
250 #ifndef G_MENU_ATTRIBUTE_ICON
251 # define G_MENU_ATTRIBUTE_ICON "icon"
252 #endif
254 g_lo_menu_set_attribute_value (menu, position, G_MENU_ATTRIBUTE_ICON, value);
255 if (value)
256 g_variant_unref (value);
259 void
260 g_lo_menu_set_label_to_item_in_section (GLOMenu *menu,
261 gint section,
262 gint position,
263 const gchar *label)
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);
279 void
280 g_lo_menu_set_icon_to_item_in_section (GLOMenu *menu,
281 gint section,
282 gint position,
283 const GIcon *icon)
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);
299 gchar *
300 g_lo_menu_get_label_from_item_in_section (GLOMenu *menu,
301 gint section,
302 gint position)
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,
307 section,
308 position,
309 G_MENU_ATTRIBUTE_LABEL,
310 G_VARIANT_TYPE_STRING);
312 gchar *label = nullptr;
314 if (label_value)
316 label = g_variant_dup_string (label_value, nullptr);
317 g_variant_unref (label_value);
320 return label;
323 void
324 g_lo_menu_set_action_and_target_value (GLOMenu *menu,
325 gint position,
326 const gchar *action,
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);
337 else
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);
350 void
351 g_lo_menu_set_action_and_target_value_to_item_in_section (GLOMenu *menu,
352 gint section,
353 gint position,
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);
368 void
369 g_lo_menu_set_accelerator_to_item_in_section (GLOMenu *menu,
370 gint section,
371 gint position,
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);
380 GVariant *value;
382 if (accelerator != nullptr)
383 value = g_variant_new_string (accelerator);
384 else
385 value = nullptr;
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);
395 gchar *
396 g_lo_menu_get_accelerator_from_item_in_section (GLOMenu *menu,
397 gint section,
398 gint position)
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,
403 section,
404 position,
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);
416 return accel;
419 void
420 g_lo_menu_set_command_to_item_in_section (GLOMenu *menu,
421 gint section,
422 gint position,
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);
431 GVariant *value;
433 if (command != nullptr)
434 value = g_variant_new_string (command);
435 else
436 value = nullptr;
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);
446 gchar *
447 g_lo_menu_get_command_from_item_in_section (GLOMenu *menu,
448 gint section,
449 gint position)
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,
454 section,
455 position,
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);
467 return command;
470 static void
471 g_lo_menu_set_link (GLOMenu *menu,
472 gint position,
473 const gchar *link,
474 GMenuModel *model)
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));
487 else
488 g_hash_table_remove (menu_item.links, link);
491 void
492 g_lo_menu_insert_section (GLOMenu *menu,
493 gint position,
494 const gchar *label,
495 GMenuModel *section)
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);
514 void
515 g_lo_menu_new_section (GLOMenu *menu,
516 gint position,
517 const gchar *label)
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);
526 GLOMenu *
527 g_lo_menu_get_section (GLOMenu *menu,
528 gint section)
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));
536 void
537 g_lo_menu_new_submenu_in_item_in_section (GLOMenu *menu,
538 gint section,
539 gint position)
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);
561 GLOMenu *
562 g_lo_menu_get_submenu_from_item_in_section (GLOMenu *menu,
563 gint section,
564 gint position)
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);
582 return submenu;
585 void
586 g_lo_menu_set_submenu_action_to_item_in_section (GLOMenu *menu,
587 gint section,
588 gint position,
589 const gchar *action)
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);
597 GVariant *value;
599 if (action != nullptr)
600 value = g_variant_new_string (action);
601 else
602 value = nullptr;
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);
612 static void
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);
621 void
622 g_lo_menu_remove (GLOMenu *menu,
623 gint position)
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);
633 void
634 g_lo_menu_remove_from_section (GLOMenu *menu,
635 gint section,
636 gint position)
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);
650 static void
651 g_lo_menu_finalize (GObject *object)
653 GLOMenu *menu = G_LO_MENU (object);
654 struct item *items;
655 gint n_items;
656 gint i;
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]);
662 g_free (items);
664 G_OBJECT_CLASS (g_lo_menu_parent_class)
665 ->finalize (object);
668 static void
669 g_lo_menu_init (GLOMenu *menu)
671 menu->items = g_array_new (FALSE, FALSE, sizeof (struct item));
674 static void
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: */