gtk2/gtk/gtksignallistitemfactory.c
Benjamin Otte 1af004361c listitemfactory: Simplify
Instead of 6 vfuncs, we now have 3 and rely on the factory keeping track
of what it needs to do.

We're doing lots of dancing from one object to another here, but this
will hopefully get simpler with further commits.
2020-05-30 19:26:46 -04:00

277 lines
11 KiB
C

/*
* 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 <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtksignallistitemfactory.h"
#include "gtkintl.h"
#include "gtklistitemfactoryprivate.h"
#include "gtklistitem.h"
/**
* SECTION:gtksignallistitemfactory
* @title: GtkSignalListItemFactory
* @short_description: A listitem factory providing signals
* @see_also: #GtkListItemFactory
*
* #GtkSignalListItemFactory is a #GtkListItemFactory that provides signals
* that user code can connect to to manage listitems.
* Signals are emitted for every listitem in the same order:
*
* 1. #GtkSignalListItemFactory::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. #GtkSignalListItemFactory::bind is emitted to bind the item passed via
* #GtkListItem: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. #GtkSignalListItemFactory::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. #GtkSignalListItemFactory::bind and #GtkSignalListItemFactory::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. #GtkSignalListItemFactory::teardown is emitted to allow undoing the
* effects of #GtkSignalListItemFactory::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
* #GtkListItems 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
* #GtkListItem::notify signal is recommended. The signal can be connected
* in the #GtkSignalListItemFactory::setup signal and removed again during
* #GtkSignalListItemFactory::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
*
* The ::setup signal is 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 GtkSignalListItemFactory::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
*
* The ::bind signal is emitted when a new GtkListItem: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 #GtkListView
* or other list widget.
*
* The GtkSignalListItemFactory::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
*
* The ::unbind signal is emitted when a listitem has been removed from use
* in a list widget and its new GtkListItem:item is about to be unset.
*
* This signal is the opposite of the GtkSignalListItemFactory::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
*
* The ::teardown signal is 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 GtkSignalListItemFactory::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);
}