/*
* 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"
/**
* 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);
}