1
0
Fork 0
libreoffice/vcl/unx/gtk3/glomenu.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

698 lines
20 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <o3tl/safeint.hxx>
#include <unx/gtk/glomenu.h>
struct GLOMenu
{
GMenuModel const parent_instance;
GArray *items;
};
typedef GMenuModelClass GLOMenuClass;
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#if defined __clang__
#if __has_warning("-Wdeprecated-volatile")
#pragma clang diagnostic ignored "-Wdeprecated-volatile"
#endif
#endif
#endif
G_DEFINE_TYPE (GLOMenu, g_lo_menu, G_TYPE_MENU_MODEL);
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
namespace {
struct item
{
GHashTable* attributes; // Item attributes.
GHashTable* links; // Item links.
};
}
static void
g_lo_menu_struct_item_init (struct item *menu_item)
{
menu_item->attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, reinterpret_cast<GDestroyNotify>(g_variant_unref));
menu_item->links = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
}
/* We treat attribute names the same as GSettings keys:
* - only lowercase ascii, digits and '-'
* - must start with lowercase
* - must not end with '-'
* - no consecutive '-'
* - not longer than 1024 chars
*/
static bool
valid_attribute_name (const gchar *name)
{
gint i;
if (!g_ascii_islower (name[0]))
return false;
for (i = 1; name[i]; i++)
{
if (name[i] != '-' &&
!g_ascii_islower (name[i]) &&
!g_ascii_isdigit (name[i]))
return false;
if (name[i] == '-' && name[i + 1] == '-')
return false;
}
if (name[i - 1] == '-')
return false;
if (i > 1024)
return false;
return true;
}
/*
* GLOMenu
*/
static gboolean
g_lo_menu_is_mutable (GMenuModel*)
{
// Menu is always mutable.
return true;
}
static gint
g_lo_menu_get_n_items (GMenuModel *model)
{
g_return_val_if_fail (model != nullptr, 0);
GLOMenu *menu = G_LO_MENU (model);
g_return_val_if_fail (menu->items != nullptr, 0);
return menu->items->len;
}
gint
g_lo_menu_get_n_items_from_section (GLOMenu *menu,
gint section)
{
g_return_val_if_fail (0 <= section && o3tl::make_unsigned(section) < menu->items->len, 0);
GLOMenu *model = g_lo_menu_get_section (menu, section);
g_return_val_if_fail (model != nullptr, 0);
gint length = model->items->len;
g_object_unref (model);
return length;
}
static void
g_lo_menu_get_item_attributes (GMenuModel *model,
gint position,
GHashTable **table)
{
GLOMenu *menu = G_LO_MENU (model);
*table = g_hash_table_ref (g_array_index (menu->items, struct item, position).attributes);
}
static void
g_lo_menu_get_item_links (GMenuModel *model,
gint position,
GHashTable **table)
{
GLOMenu *menu = G_LO_MENU (model);
*table = g_hash_table_ref (g_array_index (menu->items, struct item, position).links);
}
void
g_lo_menu_insert (GLOMenu *menu,
gint position,
const gchar *label)
{
g_lo_menu_insert_section (menu, position, label, nullptr);
}
void
g_lo_menu_insert_in_section (GLOMenu *menu,
gint section,
gint position,
const gchar *label)
{
g_return_if_fail (G_IS_LO_MENU (menu));
g_return_if_fail (0 <= section && o3tl::make_unsigned(section) < menu->items->len);
GLOMenu *model = g_lo_menu_get_section (menu, section);
g_return_if_fail (model != nullptr);
g_lo_menu_insert (model, position, label);
g_object_unref (model);
}
GLOMenu *
g_lo_menu_new()
{
return G_LO_MENU( g_object_new (G_TYPE_LO_MENU, nullptr) );
}
static void
g_lo_menu_set_attribute_value (GLOMenu *menu,
gint position,
const gchar *attribute,
GVariant *value)
{
g_return_if_fail (G_IS_LO_MENU (menu));
g_return_if_fail (attribute != nullptr);
g_return_if_fail (valid_attribute_name (attribute));
if (position >= static_cast<gint>(menu->items->len))
return;
struct item menu_item = g_array_index (menu->items, struct item, position);
if (value != nullptr)
g_hash_table_insert (menu_item.attributes, g_strdup (attribute), g_variant_ref_sink (value));
else
g_hash_table_remove (menu_item.attributes, attribute);
}
static GVariant*
g_lo_menu_get_attribute_value_from_item_in_section (GLOMenu *menu,
gint section,
gint position,
const gchar *attribute,
const GVariantType *type)
{
GMenuModel *model = G_MENU_MODEL (g_lo_menu_get_section (menu, section));
g_return_val_if_fail (model != nullptr, nullptr);
GVariant *value = g_menu_model_get_item_attribute_value (model,
position,
attribute,
type);
g_object_unref (model);
return value;
}
void
g_lo_menu_set_label (GLOMenu *menu,
gint position,
const gchar *label)
{
g_return_if_fail (G_IS_LO_MENU (menu));
GVariant *value;
if (label != nullptr)
value = g_variant_new_string (label);
else
value = nullptr;
g_lo_menu_set_attribute_value (menu, position, G_MENU_ATTRIBUTE_LABEL, value);
}
void
g_lo_menu_set_icon (GLOMenu *menu,
gint position,
const GIcon *icon)
{
g_return_if_fail (G_IS_LO_MENU (menu));
GVariant *value;
if (icon != nullptr)
{
#if GLIB_CHECK_VERSION(2,38,0)
value = g_icon_serialize (const_cast<GIcon*>(icon));
#else
value = nullptr;
#endif
}
else
value = nullptr;
#ifndef G_MENU_ATTRIBUTE_ICON
# define G_MENU_ATTRIBUTE_ICON "icon"
#endif
g_lo_menu_set_attribute_value (menu, position, G_MENU_ATTRIBUTE_ICON, value);
if (value)
g_variant_unref (value);
}
void
g_lo_menu_set_label_to_item_in_section (GLOMenu *menu,
gint section,
gint position,
const gchar *label,
bool fire_event)
{
g_return_if_fail (G_IS_LO_MENU (menu));
GLOMenu *model = g_lo_menu_get_section (menu, section);
g_return_if_fail (model != nullptr);
g_lo_menu_set_label (model, position, label);
// Notify the update.
if (fire_event)
g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1);
g_object_unref (model);
}
void
g_lo_menu_set_icon_to_item_in_section (GLOMenu *menu,
gint section,
gint position,
const GIcon *icon)
{
g_return_if_fail (G_IS_LO_MENU (menu));
GLOMenu *model = g_lo_menu_get_section (menu, section);
g_return_if_fail (model != nullptr);
g_lo_menu_set_icon (model, position, icon);
// Notify the update.
g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1);
g_object_unref (model);
}
gchar *
g_lo_menu_get_label_from_item_in_section (GLOMenu *menu,
gint section,
gint position)
{
g_return_val_if_fail (G_IS_LO_MENU (menu), nullptr);
GVariant *label_value = g_lo_menu_get_attribute_value_from_item_in_section (menu,
section,
position,
G_MENU_ATTRIBUTE_LABEL,
G_VARIANT_TYPE_STRING);
gchar *label = nullptr;
if (label_value)
{
label = g_variant_dup_string (label_value, nullptr);
g_variant_unref (label_value);
}
return label;
}
void
g_lo_menu_set_action_and_target_value (GLOMenu *menu,
gint position,
const gchar *action,
GVariant *target_value)
{
g_return_if_fail (G_IS_LO_MENU (menu));
GVariant *action_value;
if (action != nullptr)
{
action_value = g_variant_new_string (action);
}
else
{
action_value = nullptr;
target_value = nullptr;
}
g_lo_menu_set_attribute_value (menu, position, G_MENU_ATTRIBUTE_ACTION, action_value);
g_lo_menu_set_attribute_value (menu, position, G_MENU_ATTRIBUTE_TARGET, target_value);
g_lo_menu_set_attribute_value (menu, position, G_LO_MENU_ATTRIBUTE_SUBMENU_ACTION, nullptr);
g_menu_model_items_changed (G_MENU_MODEL (menu), position, 1, 1);
}
void
g_lo_menu_set_action_and_target_value_to_item_in_section (GLOMenu *menu,
gint section,
gint position,
const gchar *command,
GVariant *target_value)
{
g_return_if_fail (G_IS_LO_MENU (menu));
GLOMenu *model = g_lo_menu_get_section (menu, section);
g_return_if_fail (model != nullptr);
g_lo_menu_set_action_and_target_value (model, position, command, target_value);
g_object_unref (model);
}
void
g_lo_menu_set_accelerator_to_item_in_section (GLOMenu *menu,
gint section,
gint position,
const gchar *accelerator)
{
g_return_if_fail (G_IS_LO_MENU (menu));
GLOMenu *model = g_lo_menu_get_section (menu, section);
g_return_if_fail (model != nullptr);
GVariant *value;
if (accelerator != nullptr)
value = g_variant_new_string (accelerator);
else
value = nullptr;
g_lo_menu_set_attribute_value (model, position, G_LO_MENU_ATTRIBUTE_ACCELERATOR, value);
// Notify the update.
g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1);
g_object_unref (model);
}
gchar *
g_lo_menu_get_accelerator_from_item_in_section (GLOMenu *menu,
gint section,
gint position)
{
g_return_val_if_fail (G_IS_LO_MENU (menu), nullptr);
GVariant *accel_value = g_lo_menu_get_attribute_value_from_item_in_section (menu,
section,
position,
G_LO_MENU_ATTRIBUTE_ACCELERATOR,
G_VARIANT_TYPE_STRING);
gchar *accel = nullptr;
if (accel_value != nullptr)
{
accel = g_variant_dup_string (accel_value, nullptr);
g_variant_unref (accel_value);
}
return accel;
}
void
g_lo_menu_set_command_to_item_in_section (GLOMenu *menu,
gint section,
gint position,
const gchar *command,
bool fire_event)
{
g_return_if_fail (G_IS_LO_MENU (menu));
GLOMenu *model = g_lo_menu_get_section (menu, section);
g_return_if_fail (model != nullptr);
GVariant *value;
if (command != nullptr)
value = g_variant_new_string (command);
else
value = nullptr;
g_lo_menu_set_attribute_value (model, position, G_LO_MENU_ATTRIBUTE_COMMAND, value);
// Notify the update.
if (fire_event)
g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1);
g_object_unref (model);
}
gchar *
g_lo_menu_get_command_from_item_in_section (GLOMenu *menu,
gint section,
gint position)
{
g_return_val_if_fail (G_IS_LO_MENU (menu), nullptr);
GVariant *command_value = g_lo_menu_get_attribute_value_from_item_in_section (menu,
section,
position,
G_LO_MENU_ATTRIBUTE_COMMAND,
G_VARIANT_TYPE_STRING);
gchar *command = nullptr;
if (command_value != nullptr)
{
command = g_variant_dup_string (command_value, nullptr);
g_variant_unref (command_value);
}
return command;
}
static void
g_lo_menu_set_link (GLOMenu *menu,
gint position,
const gchar *link,
GMenuModel *model)
{
g_return_if_fail (G_IS_LO_MENU (menu));
g_return_if_fail (link != nullptr);
g_return_if_fail (valid_attribute_name (link));
if (position < 0 || o3tl::make_unsigned(position) >= menu->items->len)
position = menu->items->len - 1;
struct item menu_item = g_array_index (menu->items, struct item, position);
if (model != nullptr)
g_hash_table_insert (menu_item.links, g_strdup (link), g_object_ref (model));
else
g_hash_table_remove (menu_item.links, link);
}
void
g_lo_menu_insert_section (GLOMenu *menu,
gint position,
const gchar *label,
GMenuModel *section)
{
g_return_if_fail (G_IS_LO_MENU (menu));
if (position < 0 || o3tl::make_unsigned(position) > menu->items->len)
position = menu->items->len;
struct item menu_item;
g_lo_menu_struct_item_init(&menu_item);
g_array_insert_val (menu->items, position, menu_item);
g_lo_menu_set_label (menu, position, label);
g_lo_menu_set_link (menu, position, G_MENU_LINK_SECTION, section);
g_menu_model_items_changed (G_MENU_MODEL (menu), position, 0, 1);
}
void
g_lo_menu_new_section (GLOMenu *menu,
gint position,
const gchar *label)
{
GMenuModel *section = G_MENU_MODEL (g_lo_menu_new());
g_lo_menu_insert_section (menu, position, label, section);
g_object_unref (section);
}
GLOMenu *
g_lo_menu_get_section (GLOMenu *menu,
gint section)
{
g_return_val_if_fail (G_IS_LO_MENU (menu), nullptr);
return G_LO_MENU (G_MENU_MODEL_CLASS (g_lo_menu_parent_class)
->get_item_link (G_MENU_MODEL (menu), section, G_MENU_LINK_SECTION));
}
void
g_lo_menu_new_submenu_in_item_in_section (GLOMenu *menu,
gint section,
gint position)
{
g_return_if_fail (G_IS_LO_MENU (menu));
g_return_if_fail (0 <= section && o3tl::make_unsigned(section) < menu->items->len);
GLOMenu* model = g_lo_menu_get_section (menu, section);
g_return_if_fail (model != nullptr);
if (0 <= position && o3tl::make_unsigned(position) < model->items->len) {
GMenuModel* submenu = G_MENU_MODEL (g_lo_menu_new());
g_lo_menu_set_link (model, position, G_MENU_LINK_SUBMENU, submenu);
g_object_unref (submenu);
g_menu_model_items_changed (G_MENU_MODEL (model), position, 1, 1);
}
g_object_unref (model);
}
GLOMenu *
g_lo_menu_get_submenu_from_item_in_section (GLOMenu *menu,
gint section,
gint position)
{
g_return_val_if_fail (G_IS_LO_MENU (menu), nullptr);
g_return_val_if_fail (0 <= section && o3tl::make_unsigned(section) < menu->items->len, nullptr);
GLOMenu *model = g_lo_menu_get_section (menu, section);
g_return_val_if_fail (model != nullptr, nullptr);
GLOMenu *submenu = nullptr;
if (0 <= position && o3tl::make_unsigned(position) < model->items->len)
submenu = G_LO_MENU (G_MENU_MODEL_CLASS (g_lo_menu_parent_class)
->get_item_link (G_MENU_MODEL (model), position, G_MENU_LINK_SUBMENU));
//submenu = g_menu_model_get_item_link (G_MENU_MODEL (model), position, G_MENU_LINK_SUBMENU);
g_object_unref (model);
return submenu;
}
void
g_lo_menu_set_submenu_action_to_item_in_section (GLOMenu *menu,
gint section,
gint position,
const gchar *action)
{
g_return_if_fail (G_IS_LO_MENU (menu));
GMenuModel *model = G_MENU_MODEL (g_lo_menu_get_section (menu, section));
g_return_if_fail (model != nullptr);
GVariant *value;
if (action != nullptr)
value = g_variant_new_string (action);
else
value = nullptr;
g_lo_menu_set_attribute_value (G_LO_MENU (model), position, G_LO_MENU_ATTRIBUTE_SUBMENU_ACTION, value);
// Notify the update.
g_menu_model_items_changed (model, position, 1, 1);
g_object_unref (model);
}
static void
g_lo_menu_clear_item (struct item *menu_item)
{
if (menu_item->attributes != nullptr)
g_hash_table_unref (menu_item->attributes);
if (menu_item->links != nullptr)
g_hash_table_unref (menu_item->links);
}
void
g_lo_menu_remove (GLOMenu *menu,
gint position)
{
g_return_if_fail (G_IS_LO_MENU (menu));
g_return_if_fail (0 <= position && o3tl::make_unsigned(position) < menu->items->len);
g_lo_menu_clear_item (&g_array_index (menu->items, struct item, position));
g_array_remove_index (menu->items, position);
g_menu_model_items_changed (G_MENU_MODEL (menu), position, 1, 0);
}
void
g_lo_menu_remove_from_section (GLOMenu *menu,
gint section,
gint position)
{
g_return_if_fail (G_IS_LO_MENU (menu));
g_return_if_fail (0 <= section && o3tl::make_unsigned(section) < menu->items->len);
GLOMenu *model = g_lo_menu_get_section (menu, section);
g_return_if_fail (model != nullptr);
g_lo_menu_remove (model, position);
g_object_unref (model);
}
static void
g_lo_menu_finalize (GObject *object)
{
GLOMenu *menu = G_LO_MENU (object);
struct item *items;
gint n_items;
gint i;
n_items = menu->items->len;
items = reinterpret_cast<struct item *>(g_array_free (menu->items, FALSE));
for (i = 0; i < n_items; i++)
g_lo_menu_clear_item (&items[i]);
g_free (items);
G_OBJECT_CLASS (g_lo_menu_parent_class)
->finalize (object);
}
static void
g_lo_menu_init (GLOMenu *menu)
{
menu->items = g_array_new (FALSE, FALSE, sizeof (struct item));
}
static void
g_lo_menu_class_init (GLOMenuClass *klass)
{
GMenuModelClass *model_class = G_MENU_MODEL_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = g_lo_menu_finalize;
model_class->is_mutable = g_lo_menu_is_mutable;
model_class->get_n_items = g_lo_menu_get_n_items;
model_class->get_item_attributes = g_lo_menu_get_item_attributes;
model_class->get_item_links = g_lo_menu_get_item_links;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */