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 | |
73 | struct _GtkSignalListItemFactory |
74 | { |
75 | GtkListItemFactory parent_instance; |
76 | }; |
77 | |
78 | struct _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 | |
92 | enum { |
93 | SETUP, |
94 | BIND, |
95 | UNBIND, |
96 | TEARDOWN, |
97 | |
98 | LAST_SIGNAL |
99 | }; |
100 | |
101 | G_DEFINE_TYPE (GtkSignalListItemFactory, gtk_signal_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY) |
102 | static guint signals[LAST_SIGNAL] = { 0 }; |
103 | |
104 | static void |
105 | gtk_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 | |
117 | static void |
118 | gtk_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 | |
134 | static void |
135 | gtk_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 | |
147 | static void |
148 | gtk_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 | |
259 | static void |
260 | gtk_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 | **/ |
273 | GtkListItemFactory * |
274 | gtk_signal_list_item_factory_new (void) |
275 | { |
276 | return g_object_new (GTK_TYPE_SIGNAL_LIST_ITEM_FACTORY, NULL); |
277 | } |
278 | |