diff options
-rw-r--r-- | gtk/gtkjoinedmenu.c | 315 | ||||
-rw-r--r-- | gtk/gtkjoinedmenuprivate.h | 41 | ||||
-rw-r--r-- | gtk/gtklabel.c | 10 | ||||
-rw-r--r-- | gtk/gtkpasswordentry.c | 12 | ||||
-rw-r--r-- | gtk/gtktext.c | 10 | ||||
-rw-r--r-- | gtk/gtktextview.c | 11 | ||||
-rw-r--r-- | gtk/meson.build | 1 |
7 files changed, 391 insertions, 9 deletions
diff --git a/gtk/gtkjoinedmenu.c b/gtk/gtkjoinedmenu.c new file mode 100644 index 0000000000..bd1be741b2 --- /dev/null +++ b/gtk/gtkjoinedmenu.c @@ -0,0 +1,315 @@ +/* + * Copyright © 2020 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "gtkjoinedmenuprivate.h" + +typedef struct +{ + GMenuModel *model; + gulong items_changed_handler; +} Menu; + +struct _GtkJoinedMenu +{ + GMenuModel parent_instance; + GArray *menus; +}; + +G_DEFINE_TYPE (GtkJoinedMenu, gtk_joined_menu, G_TYPE_MENU_MODEL) + +static void +clear_menu (gpointer data) +{ + Menu *menu = data; + + g_clear_signal_handler (&menu->items_changed_handler, menu->model); + g_clear_object (&menu->model); +} + +static gint +gtk_joined_menu_get_offset_at_index (GtkJoinedMenu *self, + gint index) +{ + gint offset = 0; + + for (guint i = 0; i < index; i++) + offset += g_menu_model_get_n_items (g_array_index (self->menus, Menu, i).model); + + return offset; +} + +static gint +gtk_joined_menu_get_offset_at_model (GtkJoinedMenu *self, + GMenuModel *model) +{ + gint offset = 0; + + for (guint i = 0; i < self->menus->len; i++) + { + const Menu *menu = &g_array_index (self->menus, Menu, i); + + if (menu->model == model) + break; + + offset += g_menu_model_get_n_items (menu->model); + } + + return offset; +} + +static gboolean +gtk_joined_menu_is_mutable (GMenuModel *model) +{ + return TRUE; +} + +static gint +gtk_joined_menu_get_n_items (GMenuModel *model) +{ + GtkJoinedMenu *self = (GtkJoinedMenu *)model; + + if (self->menus->len == 0) + return 0; + + return gtk_joined_menu_get_offset_at_index (self, self->menus->len); +} + +static const Menu * +gtk_joined_menu_get_item (GtkJoinedMenu *self, + gint *item_index) +{ + g_assert (GTK_IS_JOINED_MENU (self)); + + for (guint i = 0; i < self->menus->len; i++) + { + const Menu *menu = &g_array_index (self->menus, Menu, i); + gint n_items = g_menu_model_get_n_items (menu->model); + + if (n_items > *item_index) + return menu; + + (*item_index) -= n_items; + } + + g_return_val_if_reached (NULL); +} + +static void +gtk_joined_menu_get_item_attributes (GMenuModel *model, + gint item_index, + GHashTable **attributes) +{ + const Menu *menu = gtk_joined_menu_get_item (GTK_JOINED_MENU (model), &item_index); + G_MENU_MODEL_GET_CLASS (menu->model)->get_item_attributes (menu->model, item_index, attributes); +} + +static GMenuAttributeIter * +gtk_joined_menu_iterate_item_attributes (GMenuModel *model, + gint item_index) +{ + const Menu *menu = gtk_joined_menu_get_item (GTK_JOINED_MENU (model), &item_index); + return G_MENU_MODEL_GET_CLASS (menu->model)->iterate_item_attributes (menu->model, item_index); +} + +static GVariant * +gtk_joined_menu_get_item_attribute_value (GMenuModel *model, + gint item_index, + const gchar *attribute, + const GVariantType *expected_type) +{ + const Menu *menu = gtk_joined_menu_get_item (GTK_JOINED_MENU (model), &item_index); + return G_MENU_MODEL_GET_CLASS (menu->model)->get_item_attribute_value (menu->model, item_index, attribute, expected_type); +} + +static void +gtk_joined_menu_get_item_links (GMenuModel *model, + gint item_index, + GHashTable **links) +{ + const Menu *menu = gtk_joined_menu_get_item (GTK_JOINED_MENU (model), &item_index); + G_MENU_MODEL_GET_CLASS (menu->model)->get_item_links (menu->model, item_index, links); +} + +static GMenuLinkIter * +gtk_joined_menu_iterate_item_links (GMenuModel *model, + gint item_index) +{ + const Menu *menu = gtk_joined_menu_get_item (GTK_JOINED_MENU (model), &item_index); + return G_MENU_MODEL_GET_CLASS (menu->model)->iterate_item_links (menu->model, item_index); +} + +static GMenuModel * +gtk_joined_menu_get_item_link (GMenuModel *model, + gint item_index, + const gchar *link) +{ + const Menu *menu = gtk_joined_menu_get_item (GTK_JOINED_MENU (model), &item_index); + return G_MENU_MODEL_GET_CLASS (menu->model)->get_item_link (menu->model, item_index, link); +} + +static void +gtk_joined_menu_finalize (GObject *object) +{ + GtkJoinedMenu *self = (GtkJoinedMenu *)object; + + g_clear_pointer (&self->menus, g_array_unref); + + G_OBJECT_CLASS (gtk_joined_menu_parent_class)->finalize (object); +} + +static void +gtk_joined_menu_class_init (GtkJoinedMenuClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GMenuModelClass *menu_model_class = G_MENU_MODEL_CLASS (klass); + + object_class->finalize = gtk_joined_menu_finalize; + + menu_model_class->is_mutable = gtk_joined_menu_is_mutable; + menu_model_class->get_n_items = gtk_joined_menu_get_n_items; + menu_model_class->get_item_attributes = gtk_joined_menu_get_item_attributes; + menu_model_class->iterate_item_attributes = gtk_joined_menu_iterate_item_attributes; + menu_model_class->get_item_attribute_value = gtk_joined_menu_get_item_attribute_value; + menu_model_class->get_item_links = gtk_joined_menu_get_item_links; + menu_model_class->iterate_item_links = gtk_joined_menu_iterate_item_links; + menu_model_class->get_item_link = gtk_joined_menu_get_item_link; +} + +static void +gtk_joined_menu_init (GtkJoinedMenu *self) +{ + self->menus = g_array_new (FALSE, FALSE, sizeof (Menu)); + g_array_set_clear_func (self->menus, clear_menu); +} + +static void +gtk_joined_menu_on_items_changed (GtkJoinedMenu *self, + guint offset, + guint removed, + guint added, + GMenuModel *model) +{ + g_assert (GTK_IS_JOINED_MENU (self)); + g_assert (G_IS_MENU_MODEL (model)); + + offset += gtk_joined_menu_get_offset_at_model (self, model); + g_menu_model_items_changed (G_MENU_MODEL (self), offset, removed, added); +} + +GtkJoinedMenu * +gtk_joined_menu_new (void) +{ + return g_object_new (GTK_TYPE_JOINED_MENU, NULL); +} + +static void +gtk_joined_menu_insert (GtkJoinedMenu *self, + GMenuModel *model, + gint index) +{ + Menu menu = { 0 }; + gint offset; + gint n_items; + + g_assert (GTK_IS_JOINED_MENU (self)); + g_assert (G_IS_MENU_MODEL (model)); + g_assert (index >= 0); + g_assert (index <= self->menus->len); + + menu.model = g_object_ref (model); + menu.items_changed_handler = + g_signal_connect_swapped (menu.model, + "items-changed", + G_CALLBACK (gtk_joined_menu_on_items_changed), + self); + g_array_insert_val (self->menus, index, menu); + + n_items = g_menu_model_get_n_items (model); + offset = gtk_joined_menu_get_offset_at_index (self, index); + g_menu_model_items_changed (G_MENU_MODEL (self), offset, 0, n_items); +} + +void +gtk_joined_menu_append_menu (GtkJoinedMenu *self, + GMenuModel *model) +{ + + g_return_if_fail (GTK_IS_JOINED_MENU (self)); + g_return_if_fail (G_MENU_MODEL (model)); + + gtk_joined_menu_insert (self, model, self->menus->len); +} + +void +gtk_joined_menu_prepend_menu (GtkJoinedMenu *self, + GMenuModel *model) +{ + g_return_if_fail (GTK_IS_JOINED_MENU (self)); + g_return_if_fail (G_MENU_MODEL (model)); + + gtk_joined_menu_insert (self, model, 0); +} + +void +gtk_joined_menu_remove_index (GtkJoinedMenu *self, + guint index) +{ + const Menu *menu; + gint n_items; + gint offset; + + g_return_if_fail (GTK_IS_JOINED_MENU (self)); + g_return_if_fail (index < self->menus->len); + + menu = &g_array_index (self->menus, Menu, index); + + offset = gtk_joined_menu_get_offset_at_index (self, index); + n_items = g_menu_model_get_n_items (menu->model); + + g_array_remove_index (self->menus, index); + + g_menu_model_items_changed (G_MENU_MODEL (self), offset, n_items, 0); +} + +void +gtk_joined_menu_remove_menu (GtkJoinedMenu *self, + GMenuModel *model) +{ + g_return_if_fail (GTK_IS_JOINED_MENU (self)); + g_return_if_fail (G_IS_MENU_MODEL (model)); + + for (guint i = 0; i < self->menus->len; i++) + { + if (g_array_index (self->menus, Menu, i).model == model) + { + gtk_joined_menu_remove_index (self, i); + break; + } + } +} + +guint +gtk_joined_menu_get_n_joined (GtkJoinedMenu *self) +{ + g_return_val_if_fail (GTK_IS_JOINED_MENU (self), 0); + + return self->menus->len; +} diff --git a/gtk/gtkjoinedmenuprivate.h b/gtk/gtkjoinedmenuprivate.h new file mode 100644 index 0000000000..8e4b61e6cb --- /dev/null +++ b/gtk/gtkjoinedmenuprivate.h @@ -0,0 +1,41 @@ +/* + * Copyright © 2020 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define GTK_TYPE_JOINED_MENU (gtk_joined_menu_get_type()) + +G_DECLARE_FINAL_TYPE (GtkJoinedMenu, gtk_joined_menu, GTK, JOINED_MENU, GMenuModel) + +GtkJoinedMenu *gtk_joined_menu_new (void); +guint gtk_joined_menu_get_n_joined (GtkJoinedMenu *self); +void gtk_joined_menu_append_menu (GtkJoinedMenu *self, + GMenuModel *model); +void gtk_joined_menu_prepend_menu (GtkJoinedMenu *self, + GMenuModel *model); +void gtk_joined_menu_remove_menu (GtkJoinedMenu *self, + GMenuModel *model); +void gtk_joined_menu_remove_index (GtkJoinedMenu *self, + guint index); + +G_END_DECLS diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index bfa231b9d9..ced834f980 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -52,6 +52,7 @@ #include "gtkdragsourceprivate.h" #include "gtkdragicon.h" #include "gtkcsscolorvalueprivate.h" +#include "gtkjoinedmenuprivate.h" #include <math.h> #include <string.h> @@ -5468,9 +5469,11 @@ gtk_label_move_cursor (GtkLabel *self, static GMenuModel * gtk_label_get_menu_model (GtkLabel *self) { + GtkJoinedMenu *joined; GMenu *menu, *section; GMenuItem *item; + joined = gtk_joined_menu_new (); menu = g_menu_new (); section = g_menu_new (); @@ -5498,10 +5501,13 @@ gtk_label_get_menu_model (GtkLabel *self) g_menu_append_section (menu, NULL, G_MENU_MODEL (section)); g_object_unref (section); + gtk_joined_menu_append_menu (joined, G_MENU_MODEL (menu)); + g_object_unref (menu); + if (self->extra_menu) - g_menu_append_section (menu, NULL, self->extra_menu); + gtk_joined_menu_append_menu (joined, self->extra_menu); - return G_MENU_MODEL (menu); + return G_MENU_MODEL (joined); } static void diff --git a/gtk/gtkpasswordentry.c b/gtk/gtkpasswordentry.c index 7e6e470dd7..4975ee7312 100644 --- a/gtk/gtkpasswordentry.c +++ b/gtk/gtkpasswordentry.c @@ -36,6 +36,7 @@ #include "gtkwidgetprivate.h" #include "gtkcsspositionvalueprivate.h" #include "gtkcssnodeprivate.h" +#include "gtkjoinedmenuprivate.h" /** @@ -658,6 +659,7 @@ void gtk_password_entry_set_extra_menu (GtkPasswordEntry *entry, GMenuModel *model) { + GtkJoinedMenu *joined; GMenu *menu; GMenu *section; GMenuItem *item; @@ -671,6 +673,7 @@ gtk_password_entry_set_extra_menu (GtkPasswordEntry *entry, return; } + joined = gtk_joined_menu_new (); menu = g_menu_new (); section = g_menu_new (); @@ -682,12 +685,15 @@ gtk_password_entry_set_extra_menu (GtkPasswordEntry *entry, g_menu_append_section (menu, NULL, G_MENU_MODEL (section)); g_object_unref (section); + gtk_joined_menu_append_menu (joined, G_MENU_MODEL (menu)); + g_object_unref (menu); + if (model) - g_menu_append_section (menu, NULL, model); + gtk_joined_menu_append_menu (joined, model); - gtk_text_set_extra_menu (GTK_TEXT (entry->entry), G_MENU_MODEL (menu)); + gtk_text_set_extra_menu (GTK_TEXT (entry->entry), G_MENU_MODEL (joined)); - g_object_unref (menu); + g_object_unref (joined); g_object_notify_by_pspec (G_OBJECT (entry), props[PROP_EXTRA_MENU]); } diff --git a/gtk/gtktext.c b/gtk/gtktext.c index 8e7fbf3cc1..8d1bde4674 100644 --- a/gtk/gtktext.c +++ b/gtk/gtktext.c @@ -64,6 +64,7 @@ #include "gtkwindow.h" #include "gtknative.h" #include "gtkactionmuxerprivate.h" +#include "gtkjoinedmenuprivate.h" #include <cairo-gobject.h> #include <string.h> @@ -6035,9 +6036,11 @@ static GMenuModel * gtk_text_get_menu_model (GtkText *self) { GtkTextPrivate *priv = gtk_text_get_instance_private (self); + GtkJoinedMenu *joined; GMenu *menu, *section; GMenuItem *item; + joined = gtk_joined_menu_new (); menu = g_menu_new (); section = g_menu_new (); @@ -6075,10 +6078,13 @@ gtk_text_get_menu_model (GtkText *self) g_menu_append_section (menu, NULL, G_MENU_MODEL (section)); g_object_unref (section); + gtk_joined_menu_append_menu (joined, G_MENU_MODEL (menu)); + g_object_unref (menu); + if (priv->extra_menu) - g_menu_append_section (menu, NULL, priv->extra_menu); + gtk_joined_menu_append_menu (joined, priv->extra_menu); - return G_MENU_MODEL (menu); + return G_MENU_MODEL (joined); } static gboolean diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index 5bf134302c..4796a34b1e 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -56,6 +56,7 @@ #include "gtkpango.h" #include "gtknative.h" #include "gtkwidgetprivate.h" +#include "gtkjoinedmenuprivate.h" /** * GtkTextView: @@ -8733,9 +8734,12 @@ static GMenuModel * gtk_text_view_get_menu_model (GtkTextView *text_view) { GtkTextViewPrivate *priv = text_view->priv; + GtkJoinedMenu *joined; GMenu *menu, *section; GMenuItem *item; + joined = gtk_joined_menu_new (); + menu = g_menu_new (); section = g_menu_new (); @@ -8785,10 +8789,13 @@ gtk_text_view_get_menu_model (GtkTextView *text_view) g_menu_append_section (menu, NULL, G_MENU_MODEL (section)); g_object_unref (section); + gtk_joined_menu_append_menu (joined, G_MENU_MODEL (menu)); + g_object_unref (menu); + if (priv->extra_menu) - g_menu_append_section (menu, NULL, priv->extra_menu); + gtk_joined_menu_append_menu (joined, priv->extra_menu); - return G_MENU_MODEL (menu); + return G_MENU_MODEL (joined); } static void diff --git a/gtk/meson.build b/gtk/meson.build index 9e2e04d798..47ca9804a9 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -114,6 +114,7 @@ gtk_private_sources = files([ 'gtkiconcache.c', 'gtkiconcachevalidator.c', 'gtkiconhelper.c', + 'gtkjoinedmenu.c', 'gtkkineticscrolling.c', 'gtkmagnifier.c', 'gtkmenusectionbox.c', |