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
32 struct _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
46 struct _GtkListListModelClass
47 {
48 GObjectClass parent_class;
49 };
50
51 static GType
gtk_list_list_model_get_item_type(GListModel * list)52 gtk_list_list_model_get_item_type (GListModel *list)
53 {
54 return G_TYPE_OBJECT;
55 }
56
57 static guint
gtk_list_list_model_get_n_items(GListModel * list)58 gtk_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
65 static gpointer
gtk_list_list_model_get_item(GListModel * list,guint position)66 gtk_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
100 static void
gtk_list_list_model_list_model_init(GListModelInterface * iface)101 gtk_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
G_DEFINE_TYPE_WITH_CODE(GtkListListModel,gtk_list_list_model,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,gtk_list_list_model_list_model_init))108 G_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
112 static void
113 gtk_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
126 static void
gtk_list_list_model_class_init(GtkListListModelClass * klass)127 gtk_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
134 static void
gtk_list_list_model_init(GtkListListModel * self)135 gtk_list_list_model_init (GtkListListModel *self)
136 {
137 }
138
139 GtkListListModel *
gtk_list_list_model_new(gpointer (* get_first)(gpointer),gpointer (* get_next)(gpointer,gpointer),gpointer (* get_previous)(gpointer,gpointer),gpointer (* get_last)(gpointer),gpointer (* get_item)(gpointer,gpointer),gpointer data,GDestroyNotify notify)140 gtk_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
167 GtkListListModel *
gtk_list_list_model_new_with_size(guint n_items,gpointer (* get_first)(gpointer),gpointer (* get_next)(gpointer,gpointer),gpointer (* get_previous)(gpointer,gpointer),gpointer (* get_last)(gpointer),gpointer (* get_item)(gpointer,gpointer),gpointer data,GDestroyNotify notify)168 gtk_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
198 static guint
gtk_list_list_model_find(GtkListListModel * self,gpointer item)199 gtk_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
214 void
gtk_list_list_model_item_added(GtkListListModel * self,gpointer item)215 gtk_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, gtk_list_list_model_find (self, item));
222 }
223
224 void
gtk_list_list_model_item_added_at(GtkListListModel * self,guint position)225 gtk_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 (G_LIST_MODEL (self), position, 0, 1);
234 }
235
236 void
gtk_list_list_model_item_removed(GtkListListModel * self,gpointer previous)237 gtk_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, previous);
248
249 gtk_list_list_model_item_removed_at (self, position);
250 }
251
252 void
gtk_list_list_model_item_moved(GtkListListModel * self,gpointer item,gpointer previous_previous)253 gtk_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, 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 (G_LIST_MODEL (self), min, max - min, max - min);
283 }
284
285 void
gtk_list_list_model_item_removed_at(GtkListListModel * self,guint position)286 gtk_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 (G_LIST_MODEL (self), position, 1, 0);
295 }
296
297 void
gtk_list_list_model_clear(GtkListListModel * self)298 gtk_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 (G_LIST_MODEL (self), 0, n_items, 0);
314 }
315
316
317