From ceaa2df6e9f96245a0e99edeb29a8b28c39c79b1 Mon Sep 17 00:00:00 2001 From: Sergey Chupligin Date: Tue, 14 Jun 2022 15:35:59 +0300 Subject: src/gtk Update gtk part to version 3.24.8 Fixes https://github.com/AyatanaIndicators/qmenumodel/issues/22 --- libqmenumodel/src/gtk/gtkmenutracker.c | 245 +++++++++++++++++++++++++-------- 1 file changed, 184 insertions(+), 61 deletions(-) (limited to 'libqmenumodel/src/gtk/gtkmenutracker.c') diff --git a/libqmenumodel/src/gtk/gtkmenutracker.c b/libqmenumodel/src/gtk/gtkmenutracker.c index 8b72965..95e08bc 100644 --- a/libqmenumodel/src/gtk/gtkmenutracker.c +++ b/libqmenumodel/src/gtk/gtkmenutracker.c @@ -12,9 +12,7 @@ * 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, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library; if not, see . * * Author: Ryan Lortie */ @@ -23,7 +21,7 @@ #include "gtkmenutracker.h" -/** +/*< private > * SECTION:gtkmenutracker * @Title: GtkMenuTracker * @Short_description: A helper class for interpreting #GMenuModel @@ -48,11 +46,11 @@ * * Certain properties on the #GtkMenuTrackerItem are mutable, and you must * listen for changes in the item. For more details, see the documentation - * for #GtkMenuTrackerItem along with https://live.gnome.org/GApplication/GMenuModel. + * for #GtkMenuTrackerItem along with https://wiki.gnome.org/Projects/GLib/GApplication/GMenuModel. * * The idea of @with_separators is for special cases where menu models may * be tracked in places where separators are not available, like in toplevel - * "File", "Edit" menu bars. Ignoring separator items is wrong, as #GtkMenuTracker + * "File", “Edit” menu bars. Ignoring separator items is wrong, as #GtkMenuTracker * expects the position to change, so we must tell #GtkMenuTracker to ignore * separators itself. */ @@ -62,6 +60,8 @@ typedef struct _GtkMenuTrackerSection GtkMenuTrackerSection; struct _GtkMenuTracker { GtkActionObservable *observable; + guint merge_sections : 1; + guint mac_os_mode : 1; GtkMenuTrackerInsertFunc insert_func; GtkMenuTrackerRemoveFunc remove_func; gpointer user_data; @@ -71,12 +71,14 @@ struct _GtkMenuTracker struct _GtkMenuTrackerSection { - GMenuModel *model; + gpointer model; /* may be a GtkMenuTrackerItem or a GMenuModel */ GSList *items; gchar *action_namespace; + guint separator_label : 1; guint with_separators : 1; guint has_separator : 1; + guint is_fake : 1; gulong handler; }; @@ -84,6 +86,7 @@ struct _GtkMenuTrackerSection static GtkMenuTrackerSection * gtk_menu_tracker_section_new (GtkMenuTracker *tracker, GMenuModel *model, gboolean with_separators, + gboolean separator_label, gint offset, const gchar *action_namespace, GPtrArray *items_already_created); @@ -91,7 +94,7 @@ static void gtk_menu_tracker_section_free (GtkMenuTrackerS static GtkMenuTrackerSection * gtk_menu_tracker_section_find_model (GtkMenuTrackerSection *section, - GMenuModel *model, + gpointer model, gint *offset) { GSList *item; @@ -119,7 +122,7 @@ gtk_menu_tracker_section_find_model (GtkMenuTrackerSection *section, (*offset)++; } - return FALSE; + return NULL; } /* this is responsible for syncing the showing of a separator for a @@ -127,7 +130,7 @@ gtk_menu_tracker_section_find_model (GtkMenuTrackerSection *section, * * we only ever show separators if we have _actual_ children (ie: we do * not show a separator if the section contains only empty child - * sections). it's difficult to determine this on-the-fly, so we have + * sections). it’s difficult to determine this on-the-fly, so we have * this separate function to come back later and figure it out. * * 'section' is that section. @@ -142,11 +145,11 @@ gtk_menu_tracker_section_find_model (GtkMenuTrackerSection *section, * * could_have_separator is true in two situations: * - * - our parent section had with_separators defined and we are not the - * first section (ie: we should add a separator if we have content in + * - our parent section had with_separators defined and there are items + * before us (ie: we should add a separator if we have content in * order to divide us from the items above) * - * - if we had a 'label' attribute set for this section + * - if we had a “label” attribute set for this section * * parent_model and parent_index are passed in so that we can give them * to the insertion callback so that it can see the label (and anything @@ -168,6 +171,7 @@ gtk_menu_tracker_section_sync_separators (GtkMenuTrackerSection *section, gboolean should_have_separator; gint n_items = 0; GSList *item; + GPtrArray *items = g_ptr_array_new (); gint i = 0; for (item = section->items; item; item = item->next) @@ -176,13 +180,15 @@ gtk_menu_tracker_section_sync_separators (GtkMenuTrackerSection *section, if (subsection) { - gboolean could_have_separator; + gboolean separator; - could_have_separator = (section->with_separators && i > 0) || - g_menu_model_get_item_attribute (section->model, i, "label", "s", NULL); + separator = (section->with_separators && n_items > 0) || subsection->separator_label; + /* Only pass the parent_model and parent_index in case they may be used to create the separator. */ n_items += gtk_menu_tracker_section_sync_separators (subsection, tracker, offset + n_items, - could_have_separator, section->model, i); + separator, + separator ? section->model : NULL, + separator ? i : 0); } else n_items++; @@ -190,19 +196,18 @@ gtk_menu_tracker_section_sync_separators (GtkMenuTrackerSection *section, i++; } - should_have_separator = could_have_separator && n_items != 0; + should_have_separator = !section->is_fake && could_have_separator && n_items != 0; if (should_have_separator > section->has_separator) { /* Add a separator */ - GtkMenuTrackerItem *item; - GPtrArray *items = g_ptr_array_new (); + GtkMenuTrackerItem *separator; - item = _gtk_menu_tracker_item_new (tracker->observable, parent_model, parent_index, NULL, TRUE); - g_ptr_array_add (items, (gpointer) item); + separator = _gtk_menu_tracker_item_new (tracker->observable, parent_model, parent_index, FALSE, NULL, TRUE); + g_ptr_array_add (items, (gpointer) separator); (* tracker->insert_func) (items, offset, tracker->user_data); g_ptr_array_unref (items); - g_object_unref (item); + g_object_unref (separator); section->has_separator = TRUE; } @@ -218,6 +223,41 @@ gtk_menu_tracker_section_sync_separators (GtkMenuTrackerSection *section, return n_items; } +static void +gtk_menu_tracker_item_visibility_changed (GtkMenuTrackerItem *item, + GParamSpec *pspec, + gpointer user_data) +{ + GtkMenuTracker *tracker = user_data; + GtkMenuTrackerSection *section; + gboolean is_now_visible; + gboolean was_visible; + gint offset = 0; + + is_now_visible = gtk_menu_tracker_item_get_is_visible (item); + + /* remember: the item is our model */ + section = gtk_menu_tracker_section_find_model (tracker->toplevel, item, &offset); + + was_visible = section->items != NULL; + + if (is_now_visible == was_visible) + return; + + if (is_now_visible) + { + section->items = g_slist_prepend (NULL, NULL); + (* tracker->insert_func) (section->model, offset, tracker->user_data); + } + else + { + section->items = g_slist_delete_link (section->items, section->items); + (* tracker->remove_func) (offset, 1, tracker->user_data); + } + + gtk_menu_tracker_section_sync_separators (tracker->toplevel, tracker, 0, FALSE, NULL, 0); +} + static gint gtk_menu_tracker_section_measure (GtkMenuTrackerSection *section) { @@ -258,6 +298,7 @@ gtk_menu_tracker_remove_items (GtkMenuTracker *tracker, n = gtk_menu_tracker_section_measure (subsection); gtk_menu_tracker_section_free (subsection); + while (n--) n_total_items += n; } @@ -275,28 +316,32 @@ gtk_menu_tracker_add_items (GtkMenuTracker *tracker, GMenuModel *model, gint position, gint n_items, - GPtrArray *items_already_created - ) + GPtrArray *items_already_created) { - GPtrArray *items; - if (items_already_created) - { - items = items_already_created; - } - else - { - items = g_ptr_array_new (); - } + GPtrArray *items; + if (items_already_created) + { + items = items_already_created; + } + else + { + items = g_ptr_array_new (); + } while (n_items--) { GMenuModel *submenu; submenu = g_menu_model_get_item_link (model, position + n_items, G_MENU_LINK_SECTION); g_assert (submenu != model); - if (submenu != NULL) + + if (submenu != NULL && tracker->merge_sections) { GtkMenuTrackerSection *subsection; gchar *action_namespace = NULL; + gboolean has_label; + + has_label = g_menu_model_get_item_attribute (model, position + n_items, + G_MENU_ATTRIBUTE_LABEL, "s", NULL); g_menu_model_get_item_attribute (model, position + n_items, G_MENU_ATTRIBUTE_ACTION_NAMESPACE, "s", &action_namespace); @@ -306,11 +351,11 @@ gtk_menu_tracker_add_items (GtkMenuTracker *tracker, gchar *namespace; namespace = g_strjoin (".", section->action_namespace, action_namespace, NULL); - subsection = gtk_menu_tracker_section_new (tracker, submenu, FALSE, offset, namespace, items_already_created); + subsection = gtk_menu_tracker_section_new (tracker, submenu, FALSE, has_label, offset, namespace, items_already_created); g_free (namespace); } else - subsection = gtk_menu_tracker_section_new (tracker, submenu, FALSE, offset, section->action_namespace, items_already_created); + subsection = gtk_menu_tracker_section_new (tracker, submenu, FALSE, has_label, offset, action_namespace, items_already_created); *change_point = g_slist_prepend (*change_point, subsection); g_free (action_namespace); @@ -321,22 +366,76 @@ gtk_menu_tracker_add_items (GtkMenuTracker *tracker, GtkMenuTrackerItem *item; item = _gtk_menu_tracker_item_new (tracker->observable, model, position + n_items, - section->action_namespace, FALSE); - g_ptr_array_insert (items, 0, (gpointer) item); + tracker->mac_os_mode, + section->action_namespace, submenu != NULL); + + /* In the case that the item may disappear we handle that by + * treating the item that we just created as being its own + * subsection. This happens as so: + * + * - the subsection is created without the possibility of + * showing a separator + * + * - the subsection will have either 0 or 1 item in it at all + * times: either the shown item or not (in the case it is + * hidden) + * + * - the created item acts as the "model" for this section + * and we use its "visiblity-changed" signal in the same + * way that we use the "items-changed" signal from a real + * GMenuModel + * + * We almost never use the '->model' stored in the section for + * anything other than lookups and for dropped the ref and + * disconnecting the signal when we destroy the menu, and we + * need to do exactly those things in this case as well. + * + * The only other thing that '->model' is used for is in the + * case that we want to show a separator, but we will never do + * that because separators are not shown for this fake section. + */ + if (gtk_menu_tracker_item_may_disappear (item)) + { + GtkMenuTrackerSection *fake_section; + + fake_section = g_slice_new0 (GtkMenuTrackerSection); + fake_section->is_fake = TRUE; + fake_section->model = g_object_ref (item); + fake_section->handler = g_signal_connect (item, "notify::is-visible", + G_CALLBACK (gtk_menu_tracker_item_visibility_changed), + tracker); + *change_point = g_slist_prepend (*change_point, fake_section); + + if (gtk_menu_tracker_item_get_is_visible (item)) + { + (* tracker->insert_func) (items, offset, tracker->user_data); + fake_section->items = g_slist_prepend (NULL, NULL); + } + } + else + { + /* In the normal case, we store NULL in the linked list. + * The measurement and lookup code count NULL always as + * exactly 1: an item that will always be there. + */ + g_ptr_array_insert (items, 0, (gpointer) item); + *change_point = g_slist_prepend (*change_point, NULL); + } - *change_point = g_slist_prepend (*change_point, NULL); + g_object_unref (item); } } + if (!items_already_created) - { - if (items->len) - { - (* tracker->insert_func) (items, offset, tracker->user_data); - for (gint i = 0; i < items->len; ++i) - g_object_unref(g_ptr_array_index(items, i)); - } - g_ptr_array_unref (items); - } + { + if (items->len) + { + (* tracker->insert_func) (items, offset, tracker->user_data); + for (gint i = 0; i < items->len; ++i) + g_object_unref(g_ptr_array_index(items, i)); + } + g_ptr_array_unref (items); + } } static void @@ -406,6 +505,7 @@ static GtkMenuTrackerSection * gtk_menu_tracker_section_new (GtkMenuTracker *tracker, GMenuModel *model, gboolean with_separators, + gboolean separator_label, gint offset, const gchar *action_namespace, GPtrArray *items_already_created) @@ -416,6 +516,7 @@ gtk_menu_tracker_section_new (GtkMenuTracker *tracker, section->model = g_object_ref (model); section->with_separators = with_separators; section->action_namespace = g_strdup (action_namespace); + section->separator_label = separator_label; gtk_menu_tracker_add_items (tracker, section, §ion->items, offset, model, 0, g_menu_model_get_n_items (model), items_already_created); section->handler = g_signal_connect (model, "items-changed", G_CALLBACK (gtk_menu_tracker_model_changed), tracker); @@ -428,6 +529,10 @@ gtk_menu_tracker_section_new (GtkMenuTracker *tracker, * @model: the model to flatten * @with_separators: if the toplevel should have separators (ie: TRUE * for menus, FALSE for menubars) + * @merge_sections: if sections should have their items merged in the + * usual way or reported only as separators (which can be queried to + * manually handle the items) + * @mac_os_mode: if this is on behalf of the Mac OS menubar * @action_namespace: the passed-in action namespace * @insert_func: insert callback * @remove_func: remove callback @@ -458,7 +563,7 @@ gtk_menu_tracker_section_new (GtkMenuTracker *tracker, * information about the menu item to insert. @action_namespace is the * action namespace that actions referred to from that item should place * themselves in. Note that if the item is a submenu and the - * "action-namespace" attribute is defined on the item, it will _not_ be + * “action-namespace” attribute is defined on the item, it will _not_ be * applied to the @action_namespace argument as it is meant for the * items inside of the submenu, not the submenu item itself. * @@ -466,7 +571,7 @@ gtk_menu_tracker_section_new (GtkMenuTracker *tracker, * separator. @model and @item_index will still be meaningfully set in * this case -- to the section menu item corresponding to the separator. * This is useful if the section specifies a label, for example. If - * there is an "action-namespace" attribute on this menu item then it + * there is an “action-namespace” attribute on this menu item then it * should be ignored by the consumer because #GtkMenuTracker has already * handled it. * @@ -478,7 +583,10 @@ GtkMenuTracker * gtk_menu_tracker_new (GtkActionObservable *observable, GMenuModel *model, gboolean with_separators, + gboolean merge_sections, + gboolean mac_os_mode, const gchar *action_namespace, + GPtrArray *items_already_created, GtkMenuTrackerInsertFunc insert_func, GtkMenuTrackerRemoveFunc remove_func, gpointer user_data) @@ -486,28 +594,43 @@ gtk_menu_tracker_new (GtkActionObservable *observable, GtkMenuTracker *tracker; tracker = g_slice_new (GtkMenuTracker); + tracker->merge_sections = merge_sections; + tracker->mac_os_mode = mac_os_mode; tracker->observable = g_object_ref (observable); tracker->insert_func = insert_func; tracker->remove_func = remove_func; tracker->user_data = user_data; - tracker->toplevel = gtk_menu_tracker_section_new (tracker, model, with_separators, 0, action_namespace, NULL); + tracker->toplevel = gtk_menu_tracker_section_new (tracker, model, with_separators, FALSE, 0, action_namespace, NULL); gtk_menu_tracker_section_sync_separators (tracker->toplevel, tracker, 0, FALSE, NULL, 0); return tracker; } GtkMenuTracker * -gtk_menu_tracker_new_for_item_submenu (GtkMenuTrackerItem *item, - GtkMenuTrackerInsertFunc insert_func, - GtkMenuTrackerRemoveFunc remove_func, - gpointer user_data) +gtk_menu_tracker_new_for_item_link (GtkMenuTrackerItem *item, + const gchar *link_name, + gboolean merge_sections, + gboolean mac_os_mode, + GtkMenuTrackerInsertFunc insert_func, + GtkMenuTrackerRemoveFunc remove_func, + gpointer user_data) { - return gtk_menu_tracker_new (_gtk_menu_tracker_item_get_observable (item), - _gtk_menu_tracker_item_get_submenu (item), - TRUE, - _gtk_menu_tracker_item_get_submenu_namespace (item), - insert_func, remove_func, user_data); + GtkMenuTracker *tracker; + GMenuModel *submenu; + gchar *namespace; + + submenu = _gtk_menu_tracker_item_get_link (item, link_name); + namespace = _gtk_menu_tracker_item_get_link_namespace (item); + + tracker = gtk_menu_tracker_new (_gtk_menu_tracker_item_get_observable (item), submenu, + TRUE, merge_sections, mac_os_mode, + namespace, NULL, insert_func, remove_func, user_data); + + g_object_unref (submenu); + g_free (namespace); + + return tracker; } /*< private > -- cgit v1.2.3