/* * Copyright © 2019 Benjamin Otte * * 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 . * * Authors: Benjamin Otte */ #include "config.h" #include "gtksignallistitemfactory.h" #include "gtkintl.h" #include "gtklistitemfactoryprivate.h" #include "gtklistitem.h" /** * GtkSignalListItemFactory: * * `GtkSignalListItemFactory` is a `GtkListItemFactory` that emits signals * to to manage listitems. * * Signals are emitted for every listitem in the same order: * * 1. [signal@Gtk.SignalListItemFactory::setup] is emitted to set up permanent * things on the listitem. This usually means constructing the widgets used in * the row and adding them to the listitem. * * 2. [signal@Gtk.SignalListItemFactory::bind] is emitted to bind the item passed * via [property@Gtk.ListItem:item] to the widgets that have been created in * step 1 or to add item-specific widgets. Signals are connected to listen to * changes - both to changes in the item to update the widgets or to changes * in the widgets to update the item. After this signal has been called, the * listitem may be shown in a list widget. * * 3. [signal@Gtk.SignalListItemFactory::unbind] is emitted to undo everything * done in step 2. Usually this means disconnecting signal handlers. Once this * signal has been called, the listitem will no longer be used in a list * widget. * * 4. [signal@Gtk.SignalListItemFactory::bind] and * [signal@Gtk.SignalListItemFactory::unbind] may be emitted multiple times * again to bind the listitem for use with new items. By reusing listitems, * potentially costly setup can be avoided. However, it means code needs to * make sure to properly clean up the listitem in step 3 so that no information * from the previous use leaks into the next use. * * 5. [signal@Gtk.SignalListItemFactory::teardown] is emitted to allow undoing * the effects of [signal@Gtk.SignalListItemFactory::setup]. After this signal * was emitted on a listitem, the listitem will be destroyed and not be used again. * * Note that during the signal emissions, changing properties on the * `GtkListItem`s passed will not trigger notify signals as the listitem's * notifications are frozen. See g_object_freeze_notify() for details. * * For tracking changes in other properties in the `GtkListItem`, the * ::notify signal is recommended. The signal can be connected in the * [signal@Gtk.SignalListItemFactory::setup] signal and removed again during * [signal@Gtk.SignalListItemFactory::teardown]. */ struct _GtkSignalListItemFactory { GtkListItemFactory parent_instance; }; struct _GtkSignalListItemFactoryClass { GtkListItemFactoryClass parent_class; void (* setup) (GtkSignalListItemFactory *self, GtkListItem *list_item); void (* teardown) (GtkSignalListItemFactory *self, GtkListItem *list_item); void (* bind) (GtkSignalListItemFactory *self, GtkListItem *list_item); void (* unbind) (GtkSignalListItemFactory *self, GtkListItem *list_item); }; enum { SETUP, BIND, UNBIND, TEARDOWN, LAST_SIGNAL }; G_DEFINE_TYPE (GtkSignalListItemFactory, gtk_signal_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY) static guint signals[LAST_SIGNAL] = { 0 }; static void gtk_signal_list_item_factory_setup (GtkListItemFactory *factory, GtkListItemWidget *widget, GtkListItem *list_item) { g_signal_emit (factory, signals[SETUP], 0, list_item); GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->setup (factory, widget, list_item); if (gtk_list_item_get_item (list_item)) g_signal_emit (factory, signals[BIND], 0, list_item); } static void gtk_signal_list_item_factory_update (GtkListItemFactory *factory, GtkListItemWidget *widget, GtkListItem *list_item, guint position, gpointer item, gboolean selected) { if (gtk_list_item_get_item (list_item)) g_signal_emit (factory, signals[UNBIND], 0, list_item); GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->update (factory, widget, list_item, position, item, selected); if (item) g_signal_emit (factory, signals[BIND], 0, list_item); } static void gtk_signal_list_item_factory_teardown (GtkListItemFactory *factory, GtkListItemWidget *widget, GtkListItem *list_item) { if (gtk_list_item_get_item (list_item)) g_signal_emit (factory, signals[UNBIND], 0, list_item); GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->teardown (factory, widget, list_item); g_signal_emit (factory, signals[TEARDOWN], 0, list_item); } static void gtk_signal_list_item_factory_class_init (GtkSignalListItemFactoryClass *klass) { GtkListItemFactoryClass *factory_class = GTK_LIST_ITEM_FACTORY_CLASS (klass); factory_class->setup = gtk_signal_list_item_factory_setup; factory_class->teardown = gtk_signal_list_item_factory_teardown; factory_class->update = gtk_signal_list_item_factory_update; /** * GtkSignalListItemFactory::setup: * @self: The `GtkSignalListItemFactory` * @listitem: The `GtkListItem` to set up * * Emitted when a new listitem has been created and needs to be setup for use. * * It is the first signal emitted for every listitem. * * The [signal@Gtk.SignalListItemFactory::teardown] signal is the opposite * of this signal and can be used to undo everything done in this signal. */ signals[SETUP] = g_signal_new (I_("setup"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GtkSignalListItemFactoryClass, setup), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GTK_TYPE_LIST_ITEM); g_signal_set_va_marshaller (signals[SETUP], G_TYPE_FROM_CLASS (klass), g_cclosure_marshal_VOID__OBJECTv); /** * GtkSignalListItemFactory::bind: * @self: The `GtkSignalListItemFactory` * @listitem: The `GtkListItem` to bind * * Emitted when a new [property@Gtk.ListItem:item] has been set * on the @listitem and should be bound for use. * * After this signal was emitted, the listitem might be shown in * a [class@Gtk.ListView] or other list widget. * * The [signal@Gtk.SignalListItemFactory::unbind] signal is the * opposite of this signal and can be used to undo everything done * in this signal. */ signals[BIND] = g_signal_new (I_("bind"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GtkSignalListItemFactoryClass, bind), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GTK_TYPE_LIST_ITEM); g_signal_set_va_marshaller (signals[BIND], G_TYPE_FROM_CLASS (klass), g_cclosure_marshal_VOID__OBJECTv); /** * GtkSignalListItemFactory::unbind: * @self: The `GtkSignalListItemFactory` * @listitem: The `GtkListItem` to unbind * * Emitted when a listitem has been removed from use in a list widget * and its new [property@Gtk.ListItem:item] is about to be unset. * * This signal is the opposite of the [signal@Gtk.SignalListItemFactory::bind] * signal and should be used to undo everything done in that signal. */ signals[UNBIND] = g_signal_new (I_("unbind"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GtkSignalListItemFactoryClass, unbind), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GTK_TYPE_LIST_ITEM); g_signal_set_va_marshaller (signals[UNBIND], G_TYPE_FROM_CLASS (klass), g_cclosure_marshal_VOID__OBJECTv); /** * GtkSignalListItemFactory::teardown: * @self: The `GtkSignalListItemFactory` * @listitem: The `GtkListItem` to teardown * * Emitted when a listitem is about to be destroyed. * * It is the last signal ever emitted for this @listitem. * * This signal is the opposite of the [signal@Gtk.SignalListItemFactory::setup] * signal and should be used to undo everything done in that signal. */ signals[TEARDOWN] = g_signal_new (I_("teardown"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GtkSignalListItemFactoryClass, teardown), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GTK_TYPE_LIST_ITEM); g_signal_set_va_marshaller (signals[TEARDOWN], G_TYPE_FROM_CLASS (klass), g_cclosure_marshal_VOID__OBJECTv); } static void gtk_signal_list_item_factory_init (GtkSignalListItemFactory *self) { } /** * gtk_signal_list_item_factory_new: * * Creates a new `GtkSignalListItemFactory`. * * You need to connect signal handlers before you use it. * * Returns: a new `GtkSignalListItemFactory` **/ GtkListItemFactory * gtk_signal_list_item_factory_new (void) { return g_object_new (GTK_TYPE_SIGNAL_LIST_ITEM_FACTORY, NULL); }