1/*
2 * Copyright © 2019 Benjamin Otte
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Benjamin Otte <otte@gnome.org>
18 */
19
20#include "config.h"
21
22#include "gtksignallistitemfactory.h"
23
24#include "gtkintl.h"
25#include "gtklistitemfactoryprivate.h"
26#include "gtklistitem.h"
27
28/**
29 * GtkSignalListItemFactory:
30 *
31 * `GtkSignalListItemFactory` is a `GtkListItemFactory` that emits signals
32 * to to manage listitems.
33 *
34 * Signals are emitted for every listitem in the same order:
35 *
36 * 1. [signal@Gtk.SignalListItemFactory::setup] is emitted to set up permanent
37 * things on the listitem. This usually means constructing the widgets used in
38 * the row and adding them to the listitem.
39 *
40 * 2. [signal@Gtk.SignalListItemFactory::bind] is emitted to bind the item passed
41 * via [property@Gtk.ListItem:item] to the widgets that have been created in
42 * step 1 or to add item-specific widgets. Signals are connected to listen to
43 * changes - both to changes in the item to update the widgets or to changes
44 * in the widgets to update the item. After this signal has been called, the
45 * listitem may be shown in a list widget.
46 *
47 * 3. [signal@Gtk.SignalListItemFactory::unbind] is emitted to undo everything
48 * done in step 2. Usually this means disconnecting signal handlers. Once this
49 * signal has been called, the listitem will no longer be used in a list
50 * widget.
51 *
52 * 4. [signal@Gtk.SignalListItemFactory::bind] and
53 * [signal@Gtk.SignalListItemFactory::unbind] may be emitted multiple times
54 * again to bind the listitem for use with new items. By reusing listitems,
55 * potentially costly setup can be avoided. However, it means code needs to
56 * make sure to properly clean up the listitem in step 3 so that no information
57 * from the previous use leaks into the next use.
58 *
59 * 5. [signal@Gtk.SignalListItemFactory::teardown] is emitted to allow undoing
60 * the effects of [signal@Gtk.SignalListItemFactory::setup]. After this signal
61 * was emitted on a listitem, the listitem will be destroyed and not be used again.
62 *
63 * Note that during the signal emissions, changing properties on the
64 * `GtkListItem`s passed will not trigger notify signals as the listitem's
65 * notifications are frozen. See g_object_freeze_notify() for details.
66 *
67 * For tracking changes in other properties in the `GtkListItem`, the
68 * ::notify signal is recommended. The signal can be connected in the
69 * [signal@Gtk.SignalListItemFactory::setup] signal and removed again during
70 * [signal@Gtk.SignalListItemFactory::teardown].
71 */
72
73struct _GtkSignalListItemFactory
74{
75 GtkListItemFactory parent_instance;
76};
77
78struct _GtkSignalListItemFactoryClass
79{
80 GtkListItemFactoryClass parent_class;
81
82 void (* setup) (GtkSignalListItemFactory *self,
83 GtkListItem *list_item);
84 void (* teardown) (GtkSignalListItemFactory *self,
85 GtkListItem *list_item);
86 void (* bind) (GtkSignalListItemFactory *self,
87 GtkListItem *list_item);
88 void (* unbind) (GtkSignalListItemFactory *self,
89 GtkListItem *list_item);
90};
91
92enum {
93 SETUP,
94 BIND,
95 UNBIND,
96 TEARDOWN,
97
98 LAST_SIGNAL
99};
100
101G_DEFINE_TYPE (GtkSignalListItemFactory, gtk_signal_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY)
102static guint signals[LAST_SIGNAL] = { 0 };
103
104static void
105gtk_signal_list_item_factory_setup (GtkListItemFactory *factory,
106 GtkListItemWidget *widget,
107 GtkListItem *list_item)
108{
109 g_signal_emit (instance: factory, signal_id: signals[SETUP], detail: 0, list_item);
110
111 GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->setup (factory, widget, list_item);
112
113 if (gtk_list_item_get_item (self: list_item))
114 g_signal_emit (instance: factory, signal_id: signals[BIND], detail: 0, list_item);
115}
116
117static void
118gtk_signal_list_item_factory_update (GtkListItemFactory *factory,
119 GtkListItemWidget *widget,
120 GtkListItem *list_item,
121 guint position,
122 gpointer item,
123 gboolean selected)
124{
125 if (gtk_list_item_get_item (self: list_item))
126 g_signal_emit (instance: factory, signal_id: signals[UNBIND], detail: 0, list_item);
127
128 GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->update (factory, widget, list_item, position, item, selected);
129
130 if (item)
131 g_signal_emit (instance: factory, signal_id: signals[BIND], detail: 0, list_item);
132}
133
134static void
135gtk_signal_list_item_factory_teardown (GtkListItemFactory *factory,
136 GtkListItemWidget *widget,
137 GtkListItem *list_item)
138{
139 if (gtk_list_item_get_item (self: list_item))
140 g_signal_emit (instance: factory, signal_id: signals[UNBIND], detail: 0, list_item);
141
142 GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->teardown (factory, widget, list_item);
143
144 g_signal_emit (instance: factory, signal_id: signals[TEARDOWN], detail: 0, list_item);
145}
146
147static void
148gtk_signal_list_item_factory_class_init (GtkSignalListItemFactoryClass *klass)
149{
150 GtkListItemFactoryClass *factory_class = GTK_LIST_ITEM_FACTORY_CLASS (klass);
151
152 factory_class->setup = gtk_signal_list_item_factory_setup;
153 factory_class->teardown = gtk_signal_list_item_factory_teardown;
154 factory_class->update = gtk_signal_list_item_factory_update;
155
156 /**
157 * GtkSignalListItemFactory::setup:
158 * @self: The `GtkSignalListItemFactory`
159 * @listitem: The `GtkListItem` to set up
160 *
161 * Emitted when a new listitem has been created and needs to be setup for use.
162 *
163 * It is the first signal emitted for every listitem.
164 *
165 * The [signal@Gtk.SignalListItemFactory::teardown] signal is the opposite
166 * of this signal and can be used to undo everything done in this signal.
167 */
168 signals[SETUP] =
169 g_signal_new (I_("setup"),
170 G_TYPE_FROM_CLASS (klass),
171 signal_flags: G_SIGNAL_RUN_FIRST,
172 G_STRUCT_OFFSET (GtkSignalListItemFactoryClass, setup),
173 NULL, NULL,
174 c_marshaller: g_cclosure_marshal_VOID__OBJECT,
175 G_TYPE_NONE, n_params: 1,
176 GTK_TYPE_LIST_ITEM);
177 g_signal_set_va_marshaller (signal_id: signals[SETUP],
178 G_TYPE_FROM_CLASS (klass),
179 va_marshaller: g_cclosure_marshal_VOID__OBJECTv);
180
181 /**
182 * GtkSignalListItemFactory::bind:
183 * @self: The `GtkSignalListItemFactory`
184 * @listitem: The `GtkListItem` to bind
185 *
186 * Emitted when a new [property@Gtk.ListItem:item] has been set
187 * on the @listitem and should be bound for use.
188 *
189 * After this signal was emitted, the listitem might be shown in
190 * a [class@Gtk.ListView] or other list widget.
191 *
192 * The [signal@Gtk.SignalListItemFactory::unbind] signal is the
193 * opposite of this signal and can be used to undo everything done
194 * in this signal.
195 */
196 signals[BIND] =
197 g_signal_new (I_("bind"),
198 G_TYPE_FROM_CLASS (klass),
199 signal_flags: G_SIGNAL_RUN_FIRST,
200 G_STRUCT_OFFSET (GtkSignalListItemFactoryClass, bind),
201 NULL, NULL,
202 c_marshaller: g_cclosure_marshal_VOID__OBJECT,
203 G_TYPE_NONE, n_params: 1,
204 GTK_TYPE_LIST_ITEM);
205 g_signal_set_va_marshaller (signal_id: signals[BIND],
206 G_TYPE_FROM_CLASS (klass),
207 va_marshaller: g_cclosure_marshal_VOID__OBJECTv);
208
209 /**
210 * GtkSignalListItemFactory::unbind:
211 * @self: The `GtkSignalListItemFactory`
212 * @listitem: The `GtkListItem` to unbind
213 *
214 * Emitted when a listitem has been removed from use in a list widget
215 * and its new [property@Gtk.ListItem:item] is about to be unset.
216 *
217 * This signal is the opposite of the [signal@Gtk.SignalListItemFactory::bind]
218 * signal and should be used to undo everything done in that signal.
219 */
220 signals[UNBIND] =
221 g_signal_new (I_("unbind"),
222 G_TYPE_FROM_CLASS (klass),
223 signal_flags: G_SIGNAL_RUN_FIRST,
224 G_STRUCT_OFFSET (GtkSignalListItemFactoryClass, unbind),
225 NULL, NULL,
226 c_marshaller: g_cclosure_marshal_VOID__OBJECT,
227 G_TYPE_NONE, n_params: 1,
228 GTK_TYPE_LIST_ITEM);
229 g_signal_set_va_marshaller (signal_id: signals[UNBIND],
230 G_TYPE_FROM_CLASS (klass),
231 va_marshaller: g_cclosure_marshal_VOID__OBJECTv);
232
233 /**
234 * GtkSignalListItemFactory::teardown:
235 * @self: The `GtkSignalListItemFactory`
236 * @listitem: The `GtkListItem` to teardown
237 *
238 * Emitted when a listitem is about to be destroyed.
239 *
240 * It is the last signal ever emitted for this @listitem.
241 *
242 * This signal is the opposite of the [signal@Gtk.SignalListItemFactory::setup]
243 * signal and should be used to undo everything done in that signal.
244 */
245 signals[TEARDOWN] =
246 g_signal_new (I_("teardown"),
247 G_TYPE_FROM_CLASS (klass),
248 signal_flags: G_SIGNAL_RUN_FIRST,
249 G_STRUCT_OFFSET (GtkSignalListItemFactoryClass, teardown),
250 NULL, NULL,
251 c_marshaller: g_cclosure_marshal_VOID__OBJECT,
252 G_TYPE_NONE, n_params: 1,
253 GTK_TYPE_LIST_ITEM);
254 g_signal_set_va_marshaller (signal_id: signals[TEARDOWN],
255 G_TYPE_FROM_CLASS (klass),
256 va_marshaller: g_cclosure_marshal_VOID__OBJECTv);
257}
258
259static void
260gtk_signal_list_item_factory_init (GtkSignalListItemFactory *self)
261{
262}
263
264/**
265 * gtk_signal_list_item_factory_new:
266 *
267 * Creates a new `GtkSignalListItemFactory`.
268 *
269 * You need to connect signal handlers before you use it.
270 *
271 * Returns: a new `GtkSignalListItemFactory`
272 **/
273GtkListItemFactory *
274gtk_signal_list_item_factory_new (void)
275{
276 return g_object_new (GTK_TYPE_SIGNAL_LIST_ITEM_FACTORY, NULL);
277}
278

source code of gtk/gtk/gtksignallistitemfactory.c