1/*
2 * Copyright © 2018 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
21/*
22 * GtkListListModel:
23 *
24 * `GtkListListModel` is a `GListModel` implementation that takes a list API
25 * and provides it as a `GListModel`.
26 */
27
28#include "config.h"
29
30#include "gtklistlistmodelprivate.h"
31
32struct _GtkListListModel
33{
34 GObject parent_instance;
35
36 guint n_items;
37 gpointer (* get_first) (gpointer);
38 gpointer (* get_next) (gpointer, gpointer);
39 gpointer (* get_previous) (gpointer, gpointer);
40 gpointer (* get_last) (gpointer);
41 gpointer (* get_item) (gpointer, gpointer);
42 gpointer data;
43 GDestroyNotify notify;
44};
45
46struct _GtkListListModelClass
47{
48 GObjectClass parent_class;
49};
50
51static GType
52gtk_list_list_model_get_item_type (GListModel *list)
53{
54 return G_TYPE_OBJECT;
55}
56
57static guint
58gtk_list_list_model_get_n_items (GListModel *list)
59{
60 GtkListListModel *self = GTK_LIST_LIST_MODEL (list);
61
62 return self->n_items;
63}
64
65static gpointer
66gtk_list_list_model_get_item (GListModel *list,
67 guint position)
68{
69 GtkListListModel *self = GTK_LIST_LIST_MODEL (list);
70 gpointer result;
71 guint i;
72
73 if (position >= self->n_items)
74 {
75 return NULL;
76 }
77 else if (self->get_last &&
78 position >= self->n_items / 2)
79 {
80 result = self->get_last (self->data);
81
82 for (i = self->n_items - 1; i > position; i--)
83 {
84 result = self->get_previous (result, self->data);
85 }
86 }
87 else
88 {
89 result = self->get_first (self->data);
90
91 for (i = 0; i < position; i++)
92 {
93 result = self->get_next (result, self->data);
94 }
95 }
96
97 return self->get_item (result, self->data);
98}
99
100static void
101gtk_list_list_model_list_model_init (GListModelInterface *iface)
102{
103 iface->get_item_type = gtk_list_list_model_get_item_type;
104 iface->get_n_items = gtk_list_list_model_get_n_items;
105 iface->get_item = gtk_list_list_model_get_item;
106}
107
108G_DEFINE_TYPE_WITH_CODE (GtkListListModel, gtk_list_list_model,
109 G_TYPE_OBJECT,
110 G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_list_list_model_list_model_init))
111
112static void
113gtk_list_list_model_dispose (GObject *object)
114{
115 GtkListListModel *self = GTK_LIST_LIST_MODEL (object);
116
117 if (self->notify)
118 self->notify (self->data);
119
120 self->n_items = 0;
121 self->notify = NULL;
122
123 G_OBJECT_CLASS (gtk_list_list_model_parent_class)->dispose (object);
124}
125
126static void
127gtk_list_list_model_class_init (GtkListListModelClass *klass)
128{
129 GObjectClass *object_class = G_OBJECT_CLASS (klass);
130
131 object_class->dispose = gtk_list_list_model_dispose;
132}
133
134static void
135gtk_list_list_model_init (GtkListListModel *self)
136{
137}
138
139GtkListListModel *
140gtk_list_list_model_new (gpointer (* get_first) (gpointer),
141 gpointer (* get_next) (gpointer, gpointer),
142 gpointer (* get_previous) (gpointer, gpointer),
143 gpointer (* get_last) (gpointer),
144 gpointer (* get_item) (gpointer, gpointer),
145 gpointer data,
146 GDestroyNotify notify)
147{
148 guint n_items;
149 gpointer item;
150
151 n_items = 0;
152 for (item = get_first (data);
153 item != NULL;
154 item = get_next (item, data))
155 n_items++;
156
157 return gtk_list_list_model_new_with_size (n_items,
158 get_first,
159 get_next,
160 get_previous,
161 get_last,
162 get_item,
163 data,
164 notify);
165}
166
167GtkListListModel *
168gtk_list_list_model_new_with_size (guint n_items,
169 gpointer (* get_first) (gpointer),
170 gpointer (* get_next) (gpointer, gpointer),
171 gpointer (* get_previous) (gpointer, gpointer),
172 gpointer (* get_last) (gpointer),
173 gpointer (* get_item) (gpointer, gpointer),
174 gpointer data,
175 GDestroyNotify notify)
176{
177 GtkListListModel *result;
178
179 g_return_val_if_fail (get_first != NULL, NULL);
180 g_return_val_if_fail (get_next != NULL, NULL);
181 g_return_val_if_fail (get_previous != NULL, NULL);
182 g_return_val_if_fail (get_item != NULL, NULL);
183
184 result = g_object_new (GTK_TYPE_LIST_LIST_MODEL, NULL);
185
186 result->n_items = n_items;
187 result->get_first = get_first;
188 result->get_next = get_next;
189 result->get_previous = get_previous;
190 result->get_last = get_last;
191 result->get_item = get_item;
192 result->data = data;
193 result->notify = notify;
194
195 return result;
196}
197
198static guint
199gtk_list_list_model_find (GtkListListModel *self,
200 gpointer item)
201{
202 guint position;
203 gpointer x;
204
205 position = 0;
206 for (x = self->get_first (self->data);
207 x != item;
208 x = self->get_next (x, self->data))
209 position++;
210
211 return position;
212}
213
214void
215gtk_list_list_model_item_added (GtkListListModel *self,
216 gpointer item)
217{
218 g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
219 g_return_if_fail (item != NULL);
220
221 gtk_list_list_model_item_added_at (self, position: gtk_list_list_model_find (self, item));
222}
223
224void
225gtk_list_list_model_item_added_at (GtkListListModel *self,
226 guint position)
227{
228 g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
229 g_return_if_fail (position <= self->n_items);
230
231 self->n_items += 1;
232
233 g_list_model_items_changed (list: G_LIST_MODEL (ptr: self), position, removed: 0, added: 1);
234}
235
236void
237gtk_list_list_model_item_removed (GtkListListModel *self,
238 gpointer previous)
239{
240 guint position;
241
242 g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
243
244 if (previous == NULL)
245 position = 0;
246 else
247 position = 1 + gtk_list_list_model_find (self, item: previous);
248
249 gtk_list_list_model_item_removed_at (self, position);
250}
251
252void
253gtk_list_list_model_item_moved (GtkListListModel *self,
254 gpointer item,
255 gpointer previous_previous)
256{
257 guint position, previous_position;
258 guint min, max;
259
260 g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
261 g_return_if_fail (item != previous_previous);
262
263 position = gtk_list_list_model_find (self, item);
264
265 if (previous_previous == NULL)
266 {
267 previous_position = 0;
268 }
269 else
270 {
271 previous_position = gtk_list_list_model_find (self, item: previous_previous);
272 if (position > previous_position)
273 previous_position++;
274 }
275
276 /* item didn't move */
277 if (position == previous_position)
278 return;
279
280 min = MIN (position, previous_position);
281 max = MAX (position, previous_position) + 1;
282 g_list_model_items_changed (list: G_LIST_MODEL (ptr: self), position: min, removed: max - min, added: max - min);
283}
284
285void
286gtk_list_list_model_item_removed_at (GtkListListModel *self,
287 guint position)
288{
289 g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
290 g_return_if_fail (position < self->n_items);
291
292 self->n_items -= 1;
293
294 g_list_model_items_changed (list: G_LIST_MODEL (ptr: self), position, removed: 1, added: 0);
295}
296
297void
298gtk_list_list_model_clear (GtkListListModel *self)
299{
300 guint n_items;
301
302 g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
303
304 n_items = self->n_items;
305
306 if (self->notify)
307 self->notify (self->data);
308
309 self->n_items = 0;
310 self->notify = NULL;
311
312 if (n_items > 0)
313 g_list_model_items_changed (list: G_LIST_MODEL (ptr: self), position: 0, removed: n_items, added: 0);
314}
315
316
317

source code of gtk/gtk/gtklistlistmodel.c