gtksignallistitemfactory.c 10.6 KB
Newer Older
Benjamin Otte's avatar
Benjamin Otte committed
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
/*
 * 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"

/**
29
30
31
32
 * GtkSignalListItemFactory:
 *
 * `GtkSignalListItemFactory` is a `GtkListItemFactory` that emits signals
 * to to manage listitems.
Benjamin Otte's avatar
Benjamin Otte committed
33
34
35
 *
 * Signals are emitted for every listitem in the same order:
 *
36
37
38
 *  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.
Benjamin Otte's avatar
Benjamin Otte committed
39
 *
40
41
42
43
44
 *  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
Benjamin Otte's avatar
Benjamin Otte committed
45
46
 *  listitem may be shown in a list widget.
 *
47
48
 *  3. [signal@Gtk.SignalListItemFactory::unbind] is emitted to undo everything
 *  done in step 2. Usually this means disconnecting signal handlers. Once this
Benjamin Otte's avatar
Benjamin Otte committed
49
50
51
 *  signal has been called, the listitem will no longer be used in a list
 *  widget.
 *
52
53
54
55
56
57
 *  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.
Benjamin Otte's avatar
Benjamin Otte committed
58
 *
59
60
61
 * 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.
Benjamin Otte's avatar
Benjamin Otte committed
62
63
 *
 * Note that during the signal emissions, changing properties on the
Matthias Clasen's avatar
Matthias Clasen committed
64
 * `GtkListItem`s passed will not trigger notify signals as the listitem's
Benjamin Otte's avatar
Benjamin Otte committed
65
66
 * notifications are frozen. See g_object_freeze_notify() for details.
 *
67
68
69
70
 * 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].
Benjamin Otte's avatar
Benjamin Otte committed
71
72
73
74
75
76
77
78
79
80
81
 */

struct _GtkSignalListItemFactory
{
  GtkListItemFactory parent_instance;
};

struct _GtkSignalListItemFactoryClass
{
  GtkListItemFactoryClass parent_class;

Benjamin Otte's avatar
Benjamin Otte committed
82
83
84
85
86
87
88
89
  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);
Benjamin Otte's avatar
Benjamin Otte committed
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
};

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,
Benjamin Otte's avatar
Benjamin Otte committed
106
                                    GtkListItemWidget  *widget,
Benjamin Otte's avatar
Benjamin Otte committed
107
108
109
                                    GtkListItem        *list_item)
{
  g_signal_emit (factory, signals[SETUP], 0, list_item);
Benjamin Otte's avatar
Benjamin Otte committed
110
111
112
113
114

  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);
Benjamin Otte's avatar
Benjamin Otte committed
115
116
117
}

static void                  
118
gtk_signal_list_item_factory_update (GtkListItemFactory *factory,
Benjamin Otte's avatar
Benjamin Otte committed
119
                                     GtkListItemWidget  *widget,
Benjamin Otte's avatar
Benjamin Otte committed
120
121
122
123
124
                                     GtkListItem        *list_item,
                                     guint               position,
                                     gpointer            item,
                                     gboolean            selected)
{
125
126
  if (gtk_list_item_get_item (list_item))
    g_signal_emit (factory, signals[UNBIND], 0, list_item);
Benjamin Otte's avatar
Benjamin Otte committed
127

Benjamin Otte's avatar
Benjamin Otte committed
128
  GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->update (factory, widget, list_item, position, item, selected);
Benjamin Otte's avatar
Benjamin Otte committed
129

130
131
  if (item)
    g_signal_emit (factory, signals[BIND], 0, list_item);
Benjamin Otte's avatar
Benjamin Otte committed
132
133
134
135
}

static void
gtk_signal_list_item_factory_teardown (GtkListItemFactory *factory,
Benjamin Otte's avatar
Benjamin Otte committed
136
                                       GtkListItemWidget  *widget,
Benjamin Otte's avatar
Benjamin Otte committed
137
138
                                       GtkListItem        *list_item)
{
139
140
141
  if (gtk_list_item_get_item (list_item))
    g_signal_emit (factory, signals[UNBIND], 0, list_item);

Benjamin Otte's avatar
Benjamin Otte committed
142
  GTK_LIST_ITEM_FACTORY_CLASS (gtk_signal_list_item_factory_parent_class)->teardown (factory, widget, list_item);
Benjamin Otte's avatar
Benjamin Otte committed
143

Benjamin Otte's avatar
Benjamin Otte committed
144
  g_signal_emit (factory, signals[TEARDOWN], 0, list_item);
Benjamin Otte's avatar
Benjamin Otte committed
145
146
147
148
149
150
151
152
153
}

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;
154
  factory_class->update = gtk_signal_list_item_factory_update;
Benjamin Otte's avatar
Benjamin Otte committed
155
156
157

  /**
   * GtkSignalListItemFactory::setup:
158
   * @self: The `GtkSignalListItemFactory`
Matthias Clasen's avatar
Matthias Clasen committed
159
   * @listitem: The `GtkListItem` to set up
Benjamin Otte's avatar
Benjamin Otte committed
160
   *
161
162
163
   * Emitted when a new listitem has been created and needs to be setup for use.
   *
   * It is the first signal emitted for every listitem.
Benjamin Otte's avatar
Benjamin Otte committed
164
   *
165
166
   * The [signal@Gtk.SignalListItemFactory::teardown] signal is the opposite
   * of this signal and can be used to undo everything done in this signal.
Benjamin Otte's avatar
Benjamin Otte committed
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
   */
  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:
183
   * @self: The `GtkSignalListItemFactory`
Matthias Clasen's avatar
Matthias Clasen committed
184
   * @listitem: The `GtkListItem` to bind
Benjamin Otte's avatar
Benjamin Otte committed
185
   *
186
   * Emitted when a new [property@Gtk.ListItem:item] has been set
Benjamin Otte's avatar
Benjamin Otte committed
187
188
   * on the @listitem and should be bound for use.
   *
189
190
   * After this signal was emitted, the listitem might be shown in
   * a [class@Gtk.ListView] or other list widget.
Benjamin Otte's avatar
Benjamin Otte committed
191
   *
192
193
194
   * The [signal@Gtk.SignalListItemFactory::unbind] signal is the
   * opposite of this signal and can be used to undo everything done
   * in this signal.
Benjamin Otte's avatar
Benjamin Otte committed
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
   */
  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:
211
   * @self: The `GtkSignalListItemFactory`
Matthias Clasen's avatar
Matthias Clasen committed
212
   * @listitem: The `GtkListItem` to unbind
Benjamin Otte's avatar
Benjamin Otte committed
213
   *
214
215
   * Emitted when a listitem has been removed from use in a list widget
   * and its new [property@Gtk.ListItem:item] is about to be unset.
Benjamin Otte's avatar
Benjamin Otte committed
216
   *
217
218
   * This signal is the opposite of the [signal@Gtk.SignalListItemFactory::bind]
   * signal and should be used to undo everything done in that signal.
Benjamin Otte's avatar
Benjamin Otte committed
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
   */
  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:
235
   * @self: The `GtkSignalListItemFactory`
Matthias Clasen's avatar
Matthias Clasen committed
236
   * @listitem: The `GtkListItem` to teardown
Benjamin Otte's avatar
Benjamin Otte committed
237
   *
238
239
   * Emitted when a listitem is about to be destroyed.
   *
Benjamin Otte's avatar
Benjamin Otte committed
240
241
   * It is the last signal ever emitted for this @listitem.
   *
242
243
   * This signal is the opposite of the [signal@Gtk.SignalListItemFactory::setup]
   * signal and should be used to undo everything done in that signal.
Benjamin Otte's avatar
Benjamin Otte committed
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
   */
  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:
 *
267
 * Creates a new `GtkSignalListItemFactory`.
Benjamin Otte's avatar
Benjamin Otte committed
268
 *
269
270
271
 * You need to connect signal handlers before you use it.
 *
 * Returns: a new `GtkSignalListItemFactory`
Benjamin Otte's avatar
Benjamin Otte committed
272
273
274
275
276
277
 **/
GtkListItemFactory *
gtk_signal_list_item_factory_new (void)
{
  return g_object_new (GTK_TYPE_SIGNAL_LIST_ITEM_FACTORY, NULL);
}