1 /*
2  * Copyright 2015 Lars Uebernickel
3  * Copyright 2015 Ryan Lortie
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17  *
18  * Authors:
19  *     Lars Uebernickel <lars@uebernic.de>
20  *     Ryan Lortie <desrt@desrt.ca>
21  */
22 
23 #include "config.h"
24 
25 #include "glistmodel.h"
26 #include "glibintl.h"
27 #include "gmarshal-internal.h"
28 
G_DEFINE_INTERFACE(GListModel,g_list_model,G_TYPE_OBJECT)29 G_DEFINE_INTERFACE (GListModel, g_list_model, G_TYPE_OBJECT)
30 
31 /**
32  * SECTION:glistmodel
33  * @title: GListModel
34  * @short_description: An interface describing a dynamic list of objects
35  * @include: gio/gio.h
36  * @see_also: #GListStore
37  *
38  * #GListModel is an interface that represents a mutable list of
39  * #GObjects. Its main intention is as a model for various widgets in
40  * user interfaces, such as list views, but it can also be used as a
41  * convenient method of returning lists of data, with support for
42  * updates.
43  *
44  * Each object in the list may also report changes in itself via some
45  * mechanism (normally the #GObject::notify signal).  Taken together
46  * with the #GListModel::items-changed signal, this provides for a list
47  * that can change its membership, and in which the members can change
48  * their individual properties.
49  *
50  * A good example would be the list of visible wireless network access
51  * points, where each access point can report dynamic properties such as
52  * signal strength.
53  *
54  * It is important to note that the #GListModel itself does not report
55  * changes to the individual items.  It only reports changes to the list
56  * membership.  If you want to observe changes to the objects themselves
57  * then you need to connect signals to the objects that you are
58  * interested in.
59  *
60  * All items in a #GListModel are of (or derived from) the same type.
61  * g_list_model_get_item_type() returns that type.  The type may be an
62  * interface, in which case all objects in the list must implement it.
63  *
64  * The semantics are close to that of an array:
65  * g_list_model_get_n_items() returns the number of items in the list and
66  * g_list_model_get_item() returns an item at a (0-based) position. In
67  * order to allow implementations to calculate the list length lazily,
68  * you can also iterate over items: starting from 0, repeatedly call
69  * g_list_model_get_item() until it returns %NULL.
70  *
71  * An implementation may create objects lazily, but must take care to
72  * return the same object for a given position until all references to
73  * it are gone.
74  *
75  * On the other side, a consumer is expected only to hold references on
76  * objects that are currently "user visible", in order to facilitate the
77  * maximum level of laziness in the implementation of the list and to
78  * reduce the required number of signal connections at a given time.
79  *
80  * This interface is intended only to be used from a single thread.  The
81  * thread in which it is appropriate to use it depends on the particular
82  * implementation, but typically it will be from the thread that owns
83  * the [thread-default main context][g-main-context-push-thread-default]
84  * in effect at the time that the model was created.
85  */
86 
87 /**
88  * GListModelInterface:
89  * @g_iface: parent #GTypeInterface
90  * @get_item_type: the virtual function pointer for g_list_model_get_item_type()
91  * @get_n_items: the virtual function pointer for g_list_model_get_n_items()
92  * @get_item: the virtual function pointer for g_list_model_get_item()
93  *
94  * The virtual function table for #GListModel.
95  *
96  * Since: 2.44
97  */
98 
99 /**
100  * GListModelInterface::get_item:
101  * @list: a #GListModel
102  * @position: the position of the item to fetch
103  *
104  * Get the item at @position. If @position is greater than the number of
105  * items in @list, %NULL is returned.
106  *
107  * %NULL is never returned for an index that is smaller than the length
108  * of the list.  See g_list_model_get_n_items().
109  *
110  * Returns: (type GObject) (transfer full) (nullable): the object at @position.
111  *
112  * Since: 2.44
113  */
114 
115 /**
116  * GListModel:
117  *
118  * #GListModel is an opaque data structure and can only be accessed
119  * using the following functions.
120  **/
121 
122 static guint g_list_model_changed_signal;
123 
124 static void
125 g_list_model_default_init (GListModelInterface *iface)
126 {
127   /**
128    * GListModel::items-changed:
129    * @list: the #GListModel that changed
130    * @position: the position at which @list changed
131    * @removed: the number of items removed
132    * @added: the number of items added
133    *
134    * This signal is emitted whenever items were added to or removed
135    * from @list. At @position, @removed items were removed and @added
136    * items were added in their place.
137    *
138    * Note: If @removed != @added, the positions of all later items
139    * in the model change.
140    *
141    * Since: 2.44
142    */
143   g_list_model_changed_signal = g_signal_new (I_("items-changed"),
144                                               G_TYPE_LIST_MODEL,
145                                               G_SIGNAL_RUN_LAST,
146                                               0,
147                                               NULL, NULL,
148                                               _g_cclosure_marshal_VOID__UINT_UINT_UINT,
149                                               G_TYPE_NONE,
150                                               3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
151   g_signal_set_va_marshaller (g_list_model_changed_signal,
152                               G_TYPE_FROM_INTERFACE (iface),
153                               _g_cclosure_marshal_VOID__UINT_UINT_UINTv);
154 }
155 
156 /**
157  * g_list_model_get_item_type:
158  * @list: a #GListModel
159  *
160  * Gets the type of the items in @list. All items returned from
161  * g_list_model_get_type() are of that type or a subtype, or are an
162  * implementation of that interface.
163  *
164  * The item type of a #GListModel can not change during the life of the
165  * model.
166  *
167  * Returns: the #GType of the items contained in @list.
168  *
169  * Since: 2.44
170  */
171 GType
g_list_model_get_item_type(GListModel * list)172 g_list_model_get_item_type (GListModel *list)
173 {
174   g_return_val_if_fail (G_IS_LIST_MODEL (list), G_TYPE_NONE);
175 
176   return G_LIST_MODEL_GET_IFACE (list)->get_item_type (list);
177 }
178 
179 /**
180  * g_list_model_get_n_items:
181  * @list: a #GListModel
182  *
183  * Gets the number of items in @list.
184  *
185  * Depending on the model implementation, calling this function may be
186  * less efficient than iterating the list with increasing values for
187  * @position until g_list_model_get_item() returns %NULL.
188  *
189  * Returns: the number of items in @list.
190  *
191  * Since: 2.44
192  */
193 guint
g_list_model_get_n_items(GListModel * list)194 g_list_model_get_n_items (GListModel *list)
195 {
196   g_return_val_if_fail (G_IS_LIST_MODEL (list), 0);
197 
198   return G_LIST_MODEL_GET_IFACE (list)->get_n_items (list);
199 }
200 
201 /**
202  * g_list_model_get_item: (skip)
203  * @list: a #GListModel
204  * @position: the position of the item to fetch
205  *
206  * Get the item at @position. If @position is greater than the number of
207  * items in @list, %NULL is returned.
208  *
209  * %NULL is never returned for an index that is smaller than the length
210  * of the list.  See g_list_model_get_n_items().
211  *
212  * Returns: (transfer full) (nullable): the item at @position.
213  *
214  * Since: 2.44
215  */
216 gpointer
g_list_model_get_item(GListModel * list,guint position)217 g_list_model_get_item (GListModel *list,
218                        guint       position)
219 {
220   g_return_val_if_fail (G_IS_LIST_MODEL (list), NULL);
221 
222   return G_LIST_MODEL_GET_IFACE (list)->get_item (list, position);
223 }
224 
225 /**
226  * g_list_model_get_object: (rename-to g_list_model_get_item)
227  * @list: a #GListModel
228  * @position: the position of the item to fetch
229  *
230  * Get the item at @position. If @position is greater than the number of
231  * items in @list, %NULL is returned.
232  *
233  * %NULL is never returned for an index that is smaller than the length
234  * of the list.  See g_list_model_get_n_items().
235  *
236  * Returns: (transfer full) (nullable): the object at @position.
237  *
238  * Since: 2.44
239  */
240 GObject *
g_list_model_get_object(GListModel * list,guint position)241 g_list_model_get_object (GListModel *list,
242                          guint       position)
243 {
244   gpointer item;
245 
246   g_return_val_if_fail (G_IS_LIST_MODEL (list), NULL);
247 
248   item = g_list_model_get_item (list, position);
249 
250   return G_OBJECT (item);
251 }
252 
253 /**
254  * g_list_model_items_changed:
255  * @list: a #GListModel
256  * @position: the position at which @list changed
257  * @removed: the number of items removed
258  * @added: the number of items added
259  *
260  * Emits the #GListModel::items-changed signal on @list.
261  *
262  * This function should only be called by classes implementing
263  * #GListModel. It has to be called after the internal representation
264  * of @list has been updated, because handlers connected to this signal
265  * might query the new state of the list.
266  *
267  * Implementations must only make changes to the model (as visible to
268  * its consumer) in places that will not cause problems for that
269  * consumer.  For models that are driven directly by a write API (such
270  * as #GListStore), changes can be reported in response to uses of that
271  * API.  For models that represent remote data, changes should only be
272  * made from a fresh mainloop dispatch.  It is particularly not
273  * permitted to make changes in response to a call to the #GListModel
274  * consumer API.
275  *
276  * Stated another way: in general, it is assumed that code making a
277  * series of accesses to the model via the API, without returning to the
278  * mainloop, and without calling other code, will continue to view the
279  * same contents of the model.
280  *
281  * Since: 2.44
282  */
283 void
g_list_model_items_changed(GListModel * list,guint position,guint removed,guint added)284 g_list_model_items_changed (GListModel *list,
285                             guint       position,
286                             guint       removed,
287                             guint       added)
288 {
289   g_return_if_fail (G_IS_LIST_MODEL (list));
290 
291   g_signal_emit (list, g_list_model_changed_signal, 0, position, removed, added);
292 }
293