2 * Copyright 2015 Lars Uebernickel
3 * Copyright 2015 Ryan Lortie
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 of the License, or (at your option) any later version.
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.
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/>.
19 * Lars Uebernickel <lars@uebernic.de>
20 * Ryan Lortie <desrt@desrt.ca>
25 #include "gliststore.h"
26 #include "glistmodel.h"
31 * @short_description: A simple implementation of #GListModel
34 * #GListStore is a simple implementation of #GListModel that stores all
37 * It provides insertions, deletions, and lookups in logarithmic time
38 * with a fast path for the common case of iterating the list linearly.
43 GObject parent_instance
;
50 GSequenceIter
*last_iter
;
60 static void g_list_store_iface_init (GListModelInterface
*iface
);
62 G_DEFINE_TYPE_WITH_CODE (GListStore
, g_list_store
, G_TYPE_OBJECT
,
63 G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL
, g_list_store_iface_init
));
66 g_list_store_items_changed (GListStore
*store
,
71 /* check if the iter cache may have been invalidated */
72 if (position
<= store
->last_position
)
74 store
->last_iter
= NULL
;
75 store
->last_position
= -1u;
78 g_list_model_items_changed (G_LIST_MODEL (store
), position
, removed
, added
);
82 g_list_store_dispose (GObject
*object
)
84 GListStore
*store
= G_LIST_STORE (object
);
86 g_clear_pointer (&store
->items
, g_sequence_free
);
88 G_OBJECT_CLASS (g_list_store_parent_class
)->dispose (object
);
92 g_list_store_get_property (GObject
*object
,
97 GListStore
*store
= G_LIST_STORE (object
);
102 g_value_set_gtype (value
, store
->item_type
);
106 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
111 g_list_store_set_property (GObject
*object
,
116 GListStore
*store
= G_LIST_STORE (object
);
120 case PROP_ITEM_TYPE
: /* construct-only */
121 store
->item_type
= g_value_get_gtype (value
);
122 if (!g_type_is_a (store
->item_type
, G_TYPE_OBJECT
))
123 g_critical ("GListStore cannot store items of type '%s'. Items must be GObjects.",
124 g_type_name (store
->item_type
));
128 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
133 g_list_store_class_init (GListStoreClass
*klass
)
135 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
137 object_class
->dispose
= g_list_store_dispose
;
138 object_class
->get_property
= g_list_store_get_property
;
139 object_class
->set_property
= g_list_store_set_property
;
142 * GListStore:item-type:
144 * The type of items contained in this list store. Items must be
145 * subclasses of #GObject.
149 g_object_class_install_property (object_class
, PROP_ITEM_TYPE
,
150 g_param_spec_gtype ("item-type", "", "", G_TYPE_OBJECT
,
151 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
));
155 g_list_store_get_item_type (GListModel
*list
)
157 GListStore
*store
= G_LIST_STORE (list
);
159 return store
->item_type
;
163 g_list_store_get_n_items (GListModel
*list
)
165 GListStore
*store
= G_LIST_STORE (list
);
167 return g_sequence_get_length (store
->items
);
171 g_list_store_get_item (GListModel
*list
,
174 GListStore
*store
= G_LIST_STORE (list
);
175 GSequenceIter
*it
= NULL
;
177 if (store
->last_position
!= -1u)
179 if (store
->last_position
== position
+ 1)
180 it
= g_sequence_iter_prev (store
->last_iter
);
181 else if (store
->last_position
== position
- 1)
182 it
= g_sequence_iter_next (store
->last_iter
);
183 else if (store
->last_position
== position
)
184 it
= store
->last_iter
;
188 it
= g_sequence_get_iter_at_pos (store
->items
, position
);
190 store
->last_iter
= it
;
191 store
->last_position
= position
;
193 if (g_sequence_iter_is_end (it
))
196 return g_object_ref (g_sequence_get (it
));
200 g_list_store_iface_init (GListModelInterface
*iface
)
202 iface
->get_item_type
= g_list_store_get_item_type
;
203 iface
->get_n_items
= g_list_store_get_n_items
;
204 iface
->get_item
= g_list_store_get_item
;
208 g_list_store_init (GListStore
*store
)
210 store
->items
= g_sequence_new (g_object_unref
);
211 store
->last_position
= -1u;
216 * @item_type: the #GType of items in the list
218 * Creates a new #GListStore with items of type @item_type. @item_type
219 * must be a subclass of #GObject.
221 * Returns: a new #GListStore
225 g_list_store_new (GType item_type
)
227 /* We only allow GObjects as item types right now. This might change
230 g_return_val_if_fail (g_type_is_a (item_type
, G_TYPE_OBJECT
), NULL
);
232 return g_object_new (G_TYPE_LIST_STORE
,
233 "item-type", item_type
,
238 * g_list_store_insert:
239 * @store: a #GListStore
240 * @position: the position at which to insert the new item
241 * @item: the new item
243 * Inserts @item into @store at @position. @item must be of type
244 * #GListStore:item-type or derived from it. @position must be smaller
245 * than the length of the list, or equal to it to append.
247 * This function takes a ref on @item.
249 * Use g_list_store_splice() to insert multiple items at the same time
255 g_list_store_insert (GListStore
*store
,
261 g_return_if_fail (G_IS_LIST_STORE (store
));
262 g_return_if_fail (g_type_is_a (G_OBJECT_TYPE (item
), store
->item_type
));
263 g_return_if_fail (position
<= g_sequence_get_length (store
->items
));
265 it
= g_sequence_get_iter_at_pos (store
->items
, position
);
266 g_sequence_insert_before (it
, g_object_ref (item
));
268 g_list_store_items_changed (store
, position
, 0, 1);
272 * g_list_store_append:
273 * @store: a #GListStore
274 * @item: the new item
276 * Appends @item to @store. @item must be of type #GListStore:item-type.
278 * This function takes a ref on @item.
280 * Use g_list_store_splice() to append multiple items at the same time
286 g_list_store_append (GListStore
*store
,
291 g_return_if_fail (G_IS_LIST_STORE (store
));
292 g_return_if_fail (g_type_is_a (G_OBJECT_TYPE (item
), store
->item_type
));
294 n_items
= g_sequence_get_length (store
->items
);
295 g_sequence_append (store
->items
, g_object_ref (item
));
297 g_list_store_items_changed (store
, n_items
, 0, 1);
301 * g_list_store_remove:
302 * @store: a #GListStore
303 * @position: the position of the item that is to be removed
305 * Removes the item from @store that is at @position. @position must be
306 * smaller than the current length of the list.
308 * Use g_list_store_splice() to remove multiple items at the same time
314 g_list_store_remove (GListStore
*store
,
319 g_return_if_fail (G_IS_LIST_STORE (store
));
321 it
= g_sequence_get_iter_at_pos (store
->items
, position
);
322 g_return_if_fail (!g_sequence_iter_is_end (it
));
324 g_sequence_remove (it
);
325 g_list_store_items_changed (store
, position
, 1, 0);
329 * g_list_store_remove_all:
330 * @store: a #GListStore
332 * Removes all items from @store.
337 g_list_store_remove_all (GListStore
*store
)
341 g_return_if_fail (G_IS_LIST_STORE (store
));
343 n_items
= g_sequence_get_length (store
->items
);
344 g_sequence_remove_range (g_sequence_get_begin_iter (store
->items
),
345 g_sequence_get_end_iter (store
->items
));
347 g_list_store_items_changed (store
, 0, n_items
, 0);
351 * g_list_store_splice:
352 * @store: a #GListStore
353 * @position: the position at which to make the change
354 * @n_removals: the number of items to remove
355 * @additions: (array length=n_additions): the items to add
356 * @n_additions: the number of items to add
358 * Changes @store by removing @n_removals items and adding @n_additions
359 * items to it. @additions must contain @n_additions items of type
360 * #GListStore:item-type. %NULL is not permitted.
362 * This function is more efficient than g_list_store_insert() and
363 * g_list_store_remove(), because it only emits
364 * #GListModel::items-changed once for the change.
366 * This function takes a ref on each item in @additions.
368 * The parameters @position and @n_removals must be correct (ie:
369 * @position + @n_removals must be less than or equal to the length of
370 * the list at the time this function is called).
375 g_list_store_splice (GListStore
*store
,
384 g_return_if_fail (G_IS_LIST_STORE (store
));
385 g_return_if_fail (position
+ n_removals
>= position
); /* overflow */
387 n_items
= g_sequence_get_length (store
->items
);
388 g_return_if_fail (position
+ n_removals
<= n_items
);
390 it
= g_sequence_get_iter_at_pos (store
->items
, position
);
396 end
= g_sequence_iter_move (it
, n_removals
);
397 g_sequence_remove_range (it
, end
);
406 it
= g_sequence_iter_next (it
);
407 for (i
= 0; i
< n_additions
; i
++)
409 if G_UNLIKELY (!g_type_is_a (G_OBJECT_TYPE (additions
[i
]), store
->item_type
))
411 g_critical ("%s: item %d is a %s instead of a %s. GListStore is now in an undefined state.",
412 G_STRFUNC
, i
, G_OBJECT_TYPE_NAME (additions
[i
]), g_type_name (store
->item_type
));
416 it
= g_sequence_insert_before (it
, g_object_ref (additions
[i
]));
420 g_list_store_items_changed (store
, position
, n_removals
, n_additions
);