summaryrefslogtreecommitdiff
path: root/gtk/gtksignallistitemfactory.c
blob: 934047b429d5a43f3aa1280ced7292e417e5b258 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/*
 * 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 "gtklistitemfactoryprivate.h"
#include "gtklistitem.h"
#include "gtkprivate.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,
                                    GObject            *item,
                                    gboolean            bind,
                                    GFunc               func,
                                    gpointer            data)
{
  g_signal_emit (factory, signals[SETUP], 0, item);

  GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->setup (factory, item, bind, func, data);

  if (bind)
    g_signal_emit (factory, signals[BIND], 0, item);
}

static void                  
gtk_signal_list_item_factory_update (GtkListItemFactory *factory,
                                     GObject            *item,
                                     gboolean            unbind,
                                     gboolean            bind,
                                     GFunc               func,
                                     gpointer            data)
{
  if (unbind)
    g_signal_emit (factory, signals[UNBIND], 0, item);

  GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->update (factory, item, unbind, bind, func, data);

  if (bind)
    g_signal_emit (factory, signals[BIND], 0, item);
}

static void
gtk_signal_list_item_factory_teardown (GtkListItemFactory *factory,
                                       GObject            *item,
                                       gboolean            unbind,
                                       GFunc               func,
                                       gpointer            data)
{
  if (unbind)
    g_signal_emit (factory, signals[UNBIND], 0, item);

  GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->teardown (factory, item, unbind, func, data);

  g_signal_emit (factory, signals[TEARDOWN], 0, 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`
   * @object: The `GObject` 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,
                  G_TYPE_OBJECT);
  g_signal_set_va_marshaller (signals[SETUP],
                              G_TYPE_FROM_CLASS (klass),
                              g_cclosure_marshal_VOID__OBJECTv);

  /**
   * GtkSignalListItemFactory::bind:
   * @self: The `GtkSignalListItemFactory`
   * @object: The `GObject` to bind
   *
   * Emitted when an object has been bound, for example when a
   * new [property@Gtk.ListItem:item] has been set on a
   * `GtkListItem` and should be bound for use.
   *
   * After this signal was emitted, the object might be shown in
   * a [class@Gtk.ListView] or other 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,
                  G_TYPE_OBJECT);
  g_signal_set_va_marshaller (signals[BIND],
                              G_TYPE_FROM_CLASS (klass),
                              g_cclosure_marshal_VOID__OBJECTv);

  /**
   * GtkSignalListItemFactory::unbind:
   * @self: The `GtkSignalListItemFactory`
   * @object: The `GObject` to unbind
   *
   * Emitted when a object has been unbound from its item, for example when
   * a listitem was 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,
                  G_TYPE_OBJECT);
  g_signal_set_va_marshaller (signals[UNBIND],
                              G_TYPE_FROM_CLASS (klass),
                              g_cclosure_marshal_VOID__OBJECTv);

  /**
   * GtkSignalListItemFactory::teardown:
   * @self: The `GtkSignalListItemFactory`
   * @object: The `GObject` to tear down
   *
   * Emitted when an object is about to be destroyed.
   *
   * It is the last signal ever emitted for this @object.
   *
   * 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,
                  G_TYPE_OBJECT);
  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);
}