1 /*
2 * model: A simple and generic data model holding one value per row
3 *
4 * Copyright 2012-2020 Stephan Haller <nomad@froevel.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 *
21 *
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <libxfdashboard/model.h>
29
30 #include <glib/gi18n-lib.h>
31
32
33 /* Define theses classes in GObject system */
34 struct _XfdashboardModelPrivate
35 {
36 /* Instance related */
37 GSequence *data;
38 GDestroyNotify freeDataCallback;
39
40 XfdashboardModelSortFunc sortCallback;
41 gpointer sortUserData;
42 GDestroyNotify sortUserDataDestroyCallback;
43
44 XfdashboardModelFilterFunc filterCallback;
45 gpointer filterUserData;
46 GDestroyNotify filterUserDataDestroyCallback;
47 };
48
49 G_DEFINE_TYPE_WITH_PRIVATE(XfdashboardModel,
50 xfdashboard_model,
51 G_TYPE_OBJECT);
52
53 struct _XfdashboardModelIterPrivate
54 {
55 /* Instance related */
56 XfdashboardModel *model;
57 GSequenceIter *iter;
58 };
59
60 G_DEFINE_TYPE_WITH_PRIVATE(XfdashboardModelIter,
61 xfdashboard_model_iter,
62 G_TYPE_OBJECT);
63
64 /* Properties */
65 enum
66 {
67 PROP_0,
68
69 PROP_ROWS,
70 PROP_SORT_SET,
71 PROP_FILTER_SET,
72 PROP_FREE_DATA_CALLBACK,
73
74 PROP_LAST
75 };
76
77 static GParamSpec* XfdashboardModelProperties[PROP_LAST]={ 0, };
78
79 /* Signals */
80 enum
81 {
82 SIGNAL_ROW_ADDED,
83 SIGNAL_ROW_REMOVED,
84 SIGNAL_ROW_CHANGED,
85 SIGNAL_SORT_CHANGED,
86 SIGNAL_FILTER_CHANGED,
87
88 SIGNAL_LAST
89 };
90
91 static guint XfdashboardModelSignals[SIGNAL_LAST]={ 0, };
92
93
94 /* IMPLEMENTATION: Private variables and methods */
95
96 typedef struct _XfdashboardModelSortData XfdashboardModelSortData;
97 struct _XfdashboardModelSortData
98 {
99 XfdashboardModel *model;
100 XfdashboardModelIter *leftIter;
101 XfdashboardModelIter *rightIter;
102 };
103
104 /* Checks for valid iterator for model */
_xfdashboard_model_iter_is_valid(XfdashboardModelIter * self,gboolean inNeedsIter)105 static gboolean _xfdashboard_model_iter_is_valid(XfdashboardModelIter *self, gboolean inNeedsIter)
106 {
107 XfdashboardModelIterPrivate *priv;
108
109 g_return_val_if_fail(XFDASHBOARD_IS_MODEL_ITER(self), FALSE);
110
111 priv=self->priv;
112
113 /* Check if model is set */
114 if(!priv->model) return(FALSE);
115
116 /* Check if an iterator is set when an iterator is needed */
117 if(inNeedsIter && !priv->iter) return(FALSE);
118
119 /* Check if an iterator is set and if it is then check if associated
120 * GSequence at iterator matches the one associated with the model.
121 * If an iterator is needed the check before ensures that in this check
122 * an iterator exists and the check will be performed.
123 */
124 if(priv->iter)
125 {
126 if(g_sequence_iter_get_sequence(priv->iter)!=priv->model->priv->data) return(FALSE);
127 }
128
129 /* If we get here all tests are passed successfully and iterator is valid */
130 return(TRUE);
131 }
132
133 /* Checks if requested row is valid in this model */
_xfdashboard_model_is_valid_row(XfdashboardModel * self,gint inRow)134 static gboolean _xfdashboard_model_is_valid_row(XfdashboardModel *self, gint inRow)
135 {
136 XfdashboardModelPrivate *priv;
137
138 g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), FALSE);
139
140 priv=self->priv;
141
142 /* Check if row is a positive number and smaller than the total numbers
143 * of rows in model's data.
144 */
145 if(inRow<0 || inRow>=g_sequence_get_length(priv->data)) return(FALSE);
146
147 /* If we get here the requested row is within model's data and valid */
148 return(TRUE);
149 }
150
151 /* Internal callback function for sorting which creates iterators used for
152 * user supplied callback function.
153 */
_xfdashboard_model_sort_internal(GSequenceIter * inLeft,GSequenceIter * inRight,gpointer inUserData)154 static gint _xfdashboard_model_sort_internal(GSequenceIter *inLeft,
155 GSequenceIter *inRight,
156 gpointer inUserData)
157 {
158 XfdashboardModelSortData *sortData;
159 XfdashboardModelPrivate *priv;
160 gint result;
161
162 g_return_val_if_fail(inLeft, 0);
163 g_return_val_if_fail(inRight, 0);
164 g_return_val_if_fail(inUserData, 0);
165
166 sortData=(XfdashboardModelSortData*)inUserData;
167 priv=sortData->model->priv;
168
169 /* Update iterators to pass to user supplied sort callback function */
170 sortData->leftIter->priv->iter=inLeft;
171 sortData->rightIter->priv->iter=inRight;
172
173 /* Call user supplied sort callback function and return its result */
174 result=(priv->sortCallback)(sortData->leftIter,
175 sortData->rightIter,
176 priv->sortUserData);
177
178 /* Return result of user supplied sort callback function */
179 return(result);
180 }
181
182 /* IMPLEMENTATION: GObject */
183
184 /* Dispose this object of type XfdashboardModel */
_xfdashboard_model_dispose(GObject * inObject)185 static void _xfdashboard_model_dispose(GObject *inObject)
186 {
187 XfdashboardModel *self=XFDASHBOARD_MODEL(inObject);
188 XfdashboardModelPrivate *priv=self->priv;
189
190 /* Release our allocated variables */
191 if(priv->sortUserData &&
192 priv->sortUserDataDestroyCallback)
193 {
194 (priv->sortUserDataDestroyCallback)(priv->sortUserData);
195 }
196 priv->sortUserDataDestroyCallback=NULL;
197 priv->sortUserData=NULL;
198 priv->sortCallback=NULL;
199
200 if(priv->filterUserData &&
201 priv->filterUserDataDestroyCallback)
202 {
203 (priv->filterUserDataDestroyCallback)(priv->filterUserData);
204 }
205 priv->filterUserDataDestroyCallback=NULL;
206 priv->filterUserData=NULL;
207 priv->filterCallback=NULL;
208
209 if(priv->data)
210 {
211 g_sequence_free(priv->data);
212 priv->data=NULL;
213 }
214 priv->freeDataCallback=NULL;
215
216 /* Call parent's class dispose method */
217 G_OBJECT_CLASS(xfdashboard_model_parent_class)->dispose(inObject);
218 }
219
220 /* Set/get properties of type XfdashboardModel */
_xfdashboard_model_set_property(GObject * inObject,guint inPropID,const GValue * inValue,GParamSpec * inSpec)221 static void _xfdashboard_model_set_property(GObject *inObject,
222 guint inPropID,
223 const GValue *inValue,
224 GParamSpec *inSpec)
225 {
226 XfdashboardModel *self=XFDASHBOARD_MODEL(inObject);
227
228 switch(inPropID)
229 {
230 case PROP_FREE_DATA_CALLBACK:
231 self->priv->freeDataCallback=g_value_get_pointer(inValue);
232 break;
233
234 default:
235 G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
236 break;
237 }
238 }
239
_xfdashboard_model_get_property(GObject * inObject,guint inPropID,GValue * outValue,GParamSpec * inSpec)240 static void _xfdashboard_model_get_property(GObject *inObject,
241 guint inPropID,
242 GValue *outValue,
243 GParamSpec *inSpec)
244 {
245 XfdashboardModel *self=XFDASHBOARD_MODEL(inObject);
246
247 switch(inPropID)
248 {
249 case PROP_ROWS:
250 g_value_set_int(outValue, xfdashboard_model_get_rows_count(self));
251 break;
252
253 case PROP_SORT_SET:
254 g_value_set_boolean(outValue, xfdashboard_model_is_sorted(self));
255 break;
256
257 case PROP_FILTER_SET:
258 g_value_set_boolean(outValue, xfdashboard_model_is_filtered(self));
259 break;
260
261 default:
262 G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
263 break;
264 }
265 }
266
267 /* Class initialization of type XfdashboardModel
268 * Override functions in parent classes and define properties
269 * and signals
270 */
xfdashboard_model_class_init(XfdashboardModelClass * klass)271 static void xfdashboard_model_class_init(XfdashboardModelClass *klass)
272 {
273 GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
274
275 /* Override functions */
276 gobjectClass->dispose=_xfdashboard_model_dispose;
277 gobjectClass->set_property=_xfdashboard_model_set_property;
278 gobjectClass->get_property=_xfdashboard_model_get_property;
279
280 /* Define properties */
281 XfdashboardModelProperties[PROP_ROWS]=
282 g_param_spec_int("rows",
283 "Rows",
284 "The number of rows this model contains",
285 0, G_MAXINT,
286 0,
287 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
288
289 XfdashboardModelProperties[PROP_SORT_SET]=
290 g_param_spec_boolean("sort-set",
291 "Sort set",
292 "Whether a sorting function is set or not",
293 FALSE,
294 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
295
296 XfdashboardModelProperties[PROP_FILTER_SET]=
297 g_param_spec_boolean("filter-set",
298 "Filter set",
299 "Whether a filter is set or not",
300 FALSE,
301 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
302
303 XfdashboardModelProperties[PROP_FREE_DATA_CALLBACK]=
304 g_param_spec_pointer("free-data-callback",
305 "Free data callback",
306 "Callback function to free data when removing or overwriting data in model",
307 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
308
309 g_object_class_install_properties(gobjectClass, PROP_LAST, XfdashboardModelProperties);
310
311 /* Define signals */
312 XfdashboardModelSignals[SIGNAL_ROW_ADDED]=
313 g_signal_new("row-added",
314 G_TYPE_FROM_CLASS(klass),
315 G_SIGNAL_RUN_LAST,
316 G_STRUCT_OFFSET(XfdashboardModelClass, row_added),
317 NULL,
318 NULL,
319 g_cclosure_marshal_VOID__OBJECT,
320 G_TYPE_NONE,
321 1,
322 XFDASHBOARD_TYPE_MODEL_ITER);
323
324 XfdashboardModelSignals[SIGNAL_ROW_REMOVED]=
325 g_signal_new("row-removed",
326 G_TYPE_FROM_CLASS(klass),
327 G_SIGNAL_RUN_LAST,
328 G_STRUCT_OFFSET(XfdashboardModelClass, row_removed),
329 NULL,
330 NULL,
331 g_cclosure_marshal_VOID__OBJECT,
332 G_TYPE_NONE,
333 1,
334 XFDASHBOARD_TYPE_MODEL_ITER);
335
336 XfdashboardModelSignals[SIGNAL_ROW_CHANGED]=
337 g_signal_new("row-changed",
338 G_TYPE_FROM_CLASS(klass),
339 G_SIGNAL_RUN_LAST,
340 G_STRUCT_OFFSET(XfdashboardModelClass, row_changed),
341 NULL,
342 NULL,
343 g_cclosure_marshal_VOID__OBJECT,
344 G_TYPE_NONE,
345 1,
346 XFDASHBOARD_TYPE_MODEL_ITER);
347
348 XfdashboardModelSignals[SIGNAL_SORT_CHANGED]=
349 g_signal_new("sort-changed",
350 G_TYPE_FROM_CLASS(klass),
351 G_SIGNAL_RUN_LAST,
352 G_STRUCT_OFFSET(XfdashboardModelClass, sort_changed),
353 NULL,
354 NULL,
355 g_cclosure_marshal_VOID__VOID,
356 G_TYPE_NONE,
357 0);
358
359 XfdashboardModelSignals[SIGNAL_FILTER_CHANGED]=
360 g_signal_new("filter-changed",
361 G_TYPE_FROM_CLASS(klass),
362 G_SIGNAL_RUN_LAST,
363 G_STRUCT_OFFSET(XfdashboardModelClass, filter_changed),
364 NULL,
365 NULL,
366 g_cclosure_marshal_VOID__VOID,
367 G_TYPE_NONE,
368 0);
369 }
370
371 /* Object initialization of type XfdashboardModel
372 * Create private structure and set up default values
373 */
xfdashboard_model_init(XfdashboardModel * self)374 static void xfdashboard_model_init(XfdashboardModel *self)
375 {
376 XfdashboardModelPrivate *priv;
377
378 priv=self->priv=xfdashboard_model_get_instance_private(self);
379
380 /* Set up default values */
381 priv->data=g_sequence_new(NULL);
382 priv->freeDataCallback=NULL;
383
384 priv->sortCallback=NULL;
385 priv->sortUserData=NULL;
386 priv->sortUserDataDestroyCallback=NULL;
387
388 priv->filterCallback=NULL;
389 priv->filterUserData=NULL;
390 priv->filterUserDataDestroyCallback=NULL;
391 }
392
393 /* Dispose this object of type XfdashboardModelIter */
_xfdashboard_model_iter_dispose(GObject * inObject)394 static void _xfdashboard_model_iter_dispose(GObject *inObject)
395 {
396 XfdashboardModelIter *self=XFDASHBOARD_MODEL_ITER(inObject);
397 XfdashboardModelIterPrivate *priv=self->priv;
398
399 /* Release our allocated variables */
400 if(priv->model)
401 {
402 g_object_unref(priv->model);
403 priv->model=NULL;
404 }
405
406 priv->iter=NULL;
407
408 /* Call parent's class dispose method */
409 G_OBJECT_CLASS(xfdashboard_model_iter_parent_class)->dispose(inObject);
410 }
411
412 /* Class initialization of type XfdashboardModelIter
413 * Override functions in parent classes and define properties
414 * and signals
415 */
xfdashboard_model_iter_class_init(XfdashboardModelIterClass * klass)416 static void xfdashboard_model_iter_class_init(XfdashboardModelIterClass *klass)
417 {
418 GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
419
420 /* Override functions */
421 gobjectClass->dispose=_xfdashboard_model_iter_dispose;
422 }
423
424 /* Object initialization of type XfdashboardModelIter
425 * Create private structure and set up default values
426 */
xfdashboard_model_iter_init(XfdashboardModelIter * self)427 static void xfdashboard_model_iter_init(XfdashboardModelIter *self)
428 {
429 XfdashboardModelIterPrivate *priv;
430
431 priv=self->priv=xfdashboard_model_iter_get_instance_private(self);
432
433 /* Set up default values */
434 priv->model=NULL;
435 priv->iter=NULL;
436 }
437
438
439 /* IMPLEMENTATION: Public API of XfdashboardModel */
440
441 /* Model creation functions */
xfdashboard_model_new(void)442 XfdashboardModel* xfdashboard_model_new(void)
443 {
444 GObject *model;
445
446 model=g_object_new(XFDASHBOARD_TYPE_MODEL, NULL);
447 if(!model) return(NULL);
448
449 return(XFDASHBOARD_MODEL(model));
450 }
451
452 /* Return number of rows in this model */
xfdashboard_model_get_rows_count(XfdashboardModel * self)453 gint xfdashboard_model_get_rows_count(XfdashboardModel *self)
454 {
455 g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), 0);
456
457 return(g_sequence_get_length(self->priv->data));
458 }
459
460 /* Get item at requested row of this model */
xfdashboard_model_get(XfdashboardModel * self,gint inRow)461 gpointer xfdashboard_model_get(XfdashboardModel *self, gint inRow)
462 {
463 XfdashboardModelPrivate *priv;
464 GSequenceIter *iter;
465 gpointer item;
466
467 g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), NULL);
468 g_return_val_if_fail(_xfdashboard_model_is_valid_row(self, inRow), NULL);
469
470 priv=self->priv;
471 item=NULL;
472
473 /* Get iterator at requested row */
474 iter=g_sequence_get_iter_at_pos(priv->data, inRow);
475 if(iter)
476 {
477 /* Get item from iterator */
478 item=g_sequence_get(iter);
479 }
480
481 /* Return item found */
482 return(item);
483 }
484
485 /* Add a new item to end of model's data */
xfdashboard_model_append(XfdashboardModel * self,gpointer inData,XfdashboardModelIter ** outIter)486 gboolean xfdashboard_model_append(XfdashboardModel *self,
487 gpointer inData,
488 XfdashboardModelIter **outIter)
489 {
490 XfdashboardModelPrivate *priv;
491 XfdashboardModelIter *iter;
492 GSequenceIter *seqIter;
493
494 g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), FALSE);
495 g_return_val_if_fail(outIter==NULL || *outIter==NULL, FALSE);
496
497 priv=self->priv;
498
499 /* Append data to model's data */
500 seqIter=g_sequence_append(priv->data, inData);
501
502 /* Create iterator for returned sequence iterator */
503 iter=xfdashboard_model_iter_new(self);
504 iter->priv->iter=seqIter;
505
506 /* Emit signal */
507 g_signal_emit(self, XfdashboardModelSignals[SIGNAL_ROW_ADDED], 0, iter);
508
509 /* Store iterator if callee requested it */
510 if(outIter) *outIter=XFDASHBOARD_MODEL_ITER(g_object_ref(iter));
511
512 /* Release allocated resources */
513 if(iter) g_object_unref(iter);
514
515 /* Return TRUE for success */
516 return(TRUE);
517 }
518
519 /* Add a new item to begin of model's data */
xfdashboard_model_prepend(XfdashboardModel * self,gpointer inData,XfdashboardModelIter ** outIter)520 gboolean xfdashboard_model_prepend(XfdashboardModel *self,
521 gpointer inData,
522 XfdashboardModelIter **outIter)
523 {
524 XfdashboardModelPrivate *priv;
525 XfdashboardModelIter *iter;
526 GSequenceIter *seqIter;
527
528 g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), FALSE);
529 g_return_val_if_fail(outIter==NULL || *outIter==NULL, FALSE);
530
531 priv=self->priv;
532
533 /* Append data to model's data */
534 seqIter=g_sequence_prepend(priv->data, inData);
535
536 /* Create iterator for returned sequence iterator */
537 iter=xfdashboard_model_iter_new(self);
538 iter->priv->iter=seqIter;
539
540 /* Emit signal */
541 g_signal_emit(self, XfdashboardModelSignals[SIGNAL_ROW_ADDED], 0, iter);
542
543 /* Store iterator if callee requested it */
544 if(outIter) *outIter=XFDASHBOARD_MODEL_ITER(g_object_ref(iter));
545
546 /* Release allocated resources */
547 if(iter) g_object_unref(iter);
548
549 /* Return TRUE for success */
550 return(TRUE);
551 }
552
553 /* Add a new item at requested row (i.e. before the item at requested row)
554 * at model's data.
555 */
xfdashboard_model_insert(XfdashboardModel * self,gint inRow,gpointer inData,XfdashboardModelIter ** outIter)556 gboolean xfdashboard_model_insert(XfdashboardModel *self,
557 gint inRow,
558 gpointer inData,
559 XfdashboardModelIter **outIter)
560 {
561 XfdashboardModelPrivate *priv;
562 XfdashboardModelIter *iter;
563 GSequenceIter *seqIter;
564 GSequenceIter *insertIter;
565
566 g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), FALSE);
567 g_return_val_if_fail(_xfdashboard_model_is_valid_row(self, inRow), FALSE);
568 g_return_val_if_fail(outIter==NULL || *outIter==NULL, FALSE);
569
570 priv=self->priv;
571
572 /* Create sequence iterator where to insert new data at */
573 insertIter=g_sequence_get_iter_at_pos(priv->data, inRow);
574
575 /* Insert data at "insert iterator" at model's data */
576 seqIter=g_sequence_insert_before(insertIter, inData);
577
578 /* Create iterator for returned sequence iterator */
579 iter=xfdashboard_model_iter_new(self);
580 iter->priv->iter=seqIter;
581
582 /* Emit signal */
583 g_signal_emit(self, XfdashboardModelSignals[SIGNAL_ROW_ADDED], 0, iter);
584
585 /* Store iterator if callee requested it */
586 if(outIter) *outIter=XFDASHBOARD_MODEL_ITER(g_object_ref(iter));
587
588 /* Release allocated resources */
589 if(iter) g_object_unref(iter);
590
591 /* Return TRUE for success */
592 return(TRUE);
593 }
594
595 /* Set or replace data at iterator */
xfdashboard_model_set(XfdashboardModel * self,gint inRow,gpointer inData,XfdashboardModelIter ** outIter)596 gboolean xfdashboard_model_set(XfdashboardModel *self,
597 gint inRow,
598 gpointer inData,
599 XfdashboardModelIter **outIter)
600 {
601 XfdashboardModelPrivate *priv;
602 XfdashboardModelIter *iter;
603 GSequenceIter *seqIter;
604
605 g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), FALSE);
606 g_return_val_if_fail(_xfdashboard_model_is_valid_row(self, inRow), FALSE);
607
608 priv=self->priv;
609
610 /* Create sequence iterator to row which is set */
611 seqIter=g_sequence_get_iter_at_pos(priv->data, inRow);
612
613 /* If a function is provided to free data on removal then call it now */
614 if(priv->freeDataCallback)
615 {
616 gpointer oldData;
617
618 /* Get data to remove */
619 oldData=g_sequence_get(seqIter);
620
621 /* Call function to free data */
622 (priv->freeDataCallback)(oldData);
623 }
624
625 /* Set new data at iterator */
626 g_sequence_set(seqIter, inData);
627
628 /* Create iterator for returned sequence iterator */
629 iter=xfdashboard_model_iter_new(self);
630 iter->priv->iter=seqIter;
631
632 /* Emit signal */
633 g_signal_emit(self, XfdashboardModelSignals[SIGNAL_ROW_CHANGED], 0, iter);
634
635 /* Store iterator if callee requested it */
636 if(outIter) *outIter=XFDASHBOARD_MODEL_ITER(g_object_ref(iter));
637
638 /* Release allocated resources */
639 if(iter) g_object_unref(iter);
640
641 /* Return TRUE for success */
642 return(TRUE);
643 }
644
645 /* Remove data at requested row from model's data */
xfdashboard_model_remove(XfdashboardModel * self,gint inRow)646 gboolean xfdashboard_model_remove(XfdashboardModel *self, gint inRow)
647 {
648 XfdashboardModelPrivate *priv;
649 XfdashboardModelIter *iter;
650 GSequenceIter *seqIter;
651
652 g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), FALSE);
653 g_return_val_if_fail(_xfdashboard_model_is_valid_row(self, inRow), FALSE);
654
655 priv=self->priv;
656
657 /* Create sequence iterator to row which is to remove */
658 seqIter=g_sequence_get_iter_at_pos(priv->data, inRow);
659
660 /* Create iterator for returned sequence iterator */
661 iter=xfdashboard_model_iter_new(self);
662 iter->priv->iter=seqIter;
663
664 /* Emit signal before removal to give signal handlers a changed
665 * to access the data at iterator a last time.
666 */
667 g_signal_emit(self, XfdashboardModelSignals[SIGNAL_ROW_REMOVED], 0, iter);
668
669 /* If a function is provided to free data on removal then call it now */
670 if(priv->freeDataCallback)
671 {
672 gpointer oldData;
673
674 /* Get data to remove */
675 oldData=g_sequence_get(seqIter);
676
677 /* Call function to free data */
678 (priv->freeDataCallback)(oldData);
679 }
680
681 /* Remove data from model's data */
682 g_sequence_remove(seqIter);
683
684 /* Release allocated resources */
685 if(iter) g_object_unref(iter);
686
687 /* Return TRUE for success */
688 return(TRUE);
689 }
690
691 /* Remove all data from model's data */
xfdashboard_model_remove_all(XfdashboardModel * self)692 void xfdashboard_model_remove_all(XfdashboardModel *self)
693 {
694 XfdashboardModelPrivate *priv;
695 XfdashboardModelIter *iter;
696
697 g_return_if_fail(XFDASHBOARD_IS_MODEL(self));
698
699 priv=self->priv;
700
701 /* Create iterator used to iterate through all items in model's data
702 * and it is used when emitting signal.
703 */
704 iter=xfdashboard_model_iter_new(self);
705 iter->priv->iter=g_sequence_get_begin_iter(priv->data);
706
707 /* Iterate through all items in model's data, emit signal for each item
708 * being remove and remove them finally. If model provides a function to
709 * free data call it with the item to remove.
710 */
711 while(!g_sequence_iter_is_end(iter->priv->iter))
712 {
713 /* Emit signal before removal to give signal handlers a changed
714 * to access the data at iterator a last time.
715 */
716 g_signal_emit(self, XfdashboardModelSignals[SIGNAL_ROW_REMOVED], 0, iter);
717
718 /* If a function is provided to free data on removal then call it now */
719 if(priv->freeDataCallback)
720 {
721 gpointer oldData;
722
723 /* Get data to remove */
724 oldData=g_sequence_get(iter->priv->iter);
725
726 /* Call function to free data */
727 (priv->freeDataCallback)(oldData);
728 }
729
730 /* Remove data from model's data */
731 g_sequence_remove(iter->priv->iter);
732
733 /* Move iterator to next item in model's data */
734 iter->priv->iter=g_sequence_iter_next(iter->priv->iter);
735 }
736
737 /* Release allocated resources */
738 if(iter) g_object_unref(iter);
739 }
740
741 /* Iterate through all items in model's data and call user supplied callback
742 * function for each item.
743 */
xfdashboard_model_foreach(XfdashboardModel * self,XfdashboardModelForeachFunc inForeachCallback,gpointer inUserData)744 void xfdashboard_model_foreach(XfdashboardModel *self,
745 XfdashboardModelForeachFunc inForeachCallback,
746 gpointer inUserData)
747 {
748 XfdashboardModelIter *iter;
749 gpointer item;
750
751 g_return_if_fail(XFDASHBOARD_IS_MODEL(self));
752 g_return_if_fail(inForeachCallback);
753
754 /* Iterate through all items in model's data */
755 /* Call user supplied callback function */
756 iter=xfdashboard_model_iter_new(self);
757 while(xfdashboard_model_iter_next(iter))
758 {
759 /* Get item at position the iterator points to */
760 item=xfdashboard_model_iter_get(iter);
761
762 /* Call user supplied callback function */
763 (inForeachCallback)(iter, item, inUserData);
764 }
765
766 /* Release allocated resources */
767 if(iter) g_object_unref(iter);
768 }
769
770 /* Model sort functions */
xfdashboard_model_is_sorted(XfdashboardModel * self)771 gboolean xfdashboard_model_is_sorted(XfdashboardModel *self)
772 {
773 XfdashboardModelPrivate *priv;
774
775 g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), FALSE);
776
777 priv=self->priv;
778
779 /* If a sort function is set then return TRUE ... */
780 if(priv->sortCallback) return(TRUE);
781
782 /* ... otherwise FALSE */
783 return(FALSE);
784 }
785
786 /* Set sorting function */
xfdashboard_model_set_sort(XfdashboardModel * self,XfdashboardModelSortFunc inSortCallback,gpointer inUserData,GDestroyNotify inUserDataDestroyCallback)787 void xfdashboard_model_set_sort(XfdashboardModel *self,
788 XfdashboardModelSortFunc inSortCallback,
789 gpointer inUserData,
790 GDestroyNotify inUserDataDestroyCallback)
791 {
792 XfdashboardModelPrivate *priv;
793
794 g_return_if_fail(XFDASHBOARD_IS_MODEL(self));
795
796 priv=self->priv;
797
798 /* Set value if changed */
799 if(priv->sortCallback!=inSortCallback ||
800 priv->sortUserData!=inUserData ||
801 priv->sortUserDataDestroyCallback!=inUserDataDestroyCallback)
802 {
803 gboolean oldSortIsSet;
804 gboolean newSortIsSet;
805
806 /* Get old "sort-set" value. It is used later to determine if this
807 * property has changed also.
808 */
809 oldSortIsSet=xfdashboard_model_is_sorted(self);
810
811 /* Release old values */
812 if(priv->sortUserData &&
813 priv->sortUserDataDestroyCallback)
814 {
815 (priv->sortUserDataDestroyCallback)(priv->sortUserData);
816 }
817 priv->sortUserDataDestroyCallback=NULL;
818 priv->sortUserData=NULL;
819 priv->sortCallback=NULL;
820
821 /* Set value */
822 priv->sortCallback=inSortCallback;
823 priv->sortUserData=inUserData;
824 priv->sortUserDataDestroyCallback=inUserDataDestroyCallback;
825
826 /* Get new "sort-set" value to determine if this property has
827 * changed also.
828 */
829 newSortIsSet=xfdashboard_model_is_sorted(self);
830
831 /* Sort model if sort function is set */
832 if(newSortIsSet) xfdashboard_model_resort(self);
833
834 /* Notify about change of 'sort-set' if changed and after model
835 * was sorted.
836 */
837 if(oldSortIsSet!=newSortIsSet)
838 {
839 g_object_notify_by_pspec(G_OBJECT(self), XfdashboardModelProperties[PROP_SORT_SET]);
840 }
841
842 /* Emit signal that sorting has changed */
843 g_signal_emit(self, XfdashboardModelSignals[SIGNAL_SORT_CHANGED], 0);
844 }
845 }
846
847 /* Resort this model's data with sorting function set */
xfdashboard_model_resort(XfdashboardModel * self)848 void xfdashboard_model_resort(XfdashboardModel *self)
849 {
850 XfdashboardModelPrivate *priv;
851 XfdashboardModelSortData sortData;
852
853 g_return_if_fail(XFDASHBOARD_IS_MODEL(self));
854
855 priv=self->priv;
856
857 /* If no sort function is set return immediately because this model
858 * cannot be sorted without a sort function.
859 */
860 if(!priv->sortCallback) return;
861
862 /* Set up sort data which wraps all needed information into a structure.
863 * The interators are pre-initialized and updated in internal sort function
864 * which is passed to GSequence's sort function. This internal callback
865 * only updates the existing iterators to reduce creation and destructions
866 * of these iterator. This can be done because the model does not change
867 * while sorting.
868 */
869 sortData.model=XFDASHBOARD_MODEL(g_object_ref(self));
870 sortData.leftIter=xfdashboard_model_iter_new(self);
871 sortData.rightIter=xfdashboard_model_iter_new(self);
872
873 /* Sort this model again by using internal sorting function which
874 * calls user's sort function with adjusted parameters.
875 */
876 g_sequence_sort_iter(priv->data, _xfdashboard_model_sort_internal, &sortData);
877
878 /* Release allocated resources */
879 if(sortData.model) g_object_unref(sortData.model);
880 if(sortData.leftIter) g_object_unref(sortData.leftIter);
881 if(sortData.rightIter) g_object_unref(sortData.rightIter);
882 }
883
884 /* Model filter functions */
xfdashboard_model_is_filtered(XfdashboardModel * self)885 gboolean xfdashboard_model_is_filtered(XfdashboardModel *self)
886 {
887 XfdashboardModelPrivate *priv;
888
889 g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), FALSE);
890
891 priv=self->priv;
892
893 /* If a filter function is set then return TRUE ... */
894 if(priv->filterCallback) return(TRUE);
895
896 /* ... otherwise FALSE */
897 return(FALSE);
898 }
899
900 /* Set filter function */
xfdashboard_model_set_filter(XfdashboardModel * self,XfdashboardModelFilterFunc inFilterCallback,gpointer inUserData,GDestroyNotify inUserDataDestroyCallback)901 void xfdashboard_model_set_filter(XfdashboardModel *self,
902 XfdashboardModelFilterFunc inFilterCallback,
903 gpointer inUserData,
904 GDestroyNotify inUserDataDestroyCallback)
905 {
906 XfdashboardModelPrivate *priv;
907
908 g_return_if_fail(XFDASHBOARD_IS_MODEL(self));
909
910 priv=self->priv;
911
912 /* Set value if changed */
913 if(priv->filterCallback!=inFilterCallback ||
914 priv->filterUserData!=inUserData ||
915 priv->filterUserDataDestroyCallback!=inUserDataDestroyCallback)
916 {
917 gboolean oldFilterIsSet;
918 gboolean newFilterIsSet;
919
920 /* Get old "filter-set" value. It is used later to determine if this
921 * property has changed also.
922 */
923 oldFilterIsSet=xfdashboard_model_is_filtered(self);
924
925 /* Release old values */
926 if(priv->filterUserData &&
927 priv->filterUserDataDestroyCallback)
928 {
929 (priv->filterUserDataDestroyCallback)(priv->filterUserData);
930 }
931 priv->filterUserDataDestroyCallback=NULL;
932 priv->filterUserData=NULL;
933 priv->filterCallback=NULL;
934
935 /* Set value */
936 priv->filterCallback=inFilterCallback;
937 priv->filterUserData=inUserData;
938 priv->filterUserDataDestroyCallback=inUserDataDestroyCallback;
939
940 /* Get new "sort-set" value to determine if this property has
941 * changed also.
942 */
943 newFilterIsSet=xfdashboard_model_is_filtered(self);
944
945 /* Notify about change of 'sort-set' if changed and after model
946 * was sorted.
947 */
948 if(oldFilterIsSet!=newFilterIsSet)
949 {
950 g_object_notify_by_pspec(G_OBJECT(self), XfdashboardModelProperties[PROP_FILTER_SET]);
951 }
952
953 /* Emit signal that filter has changed */
954 g_signal_emit(self, XfdashboardModelSignals[SIGNAL_FILTER_CHANGED], 0);
955 }
956 }
957
958 /* Check if requested row is filtered */
xfdashboard_model_filter_row(XfdashboardModel * self,gint inRow)959 gboolean xfdashboard_model_filter_row(XfdashboardModel *self, gint inRow)
960 {
961 XfdashboardModelPrivate *priv;
962 XfdashboardModelIter *iter;
963 gboolean isVisible;
964
965 g_return_val_if_fail(XFDASHBOARD_IS_MODEL(self), FALSE);
966 g_return_val_if_fail(_xfdashboard_model_is_valid_row(self, inRow), FALSE);
967
968 priv=self->priv;
969 isVisible=TRUE;
970
971 /* Call user supplied filter callback function to determine if this
972 * row should be filtered or not but only if filter function is set.
973 */
974 if(priv->filterCallback)
975 {
976 /* Create iterator */
977 iter=xfdashboard_model_iter_new_for_row(self, inRow);
978
979 /* Determine if row is filtered */
980 isVisible=(priv->filterCallback)(iter, priv->filterUserData);
981
982 /* Release allocated resources */
983 if(iter) g_object_unref(iter);
984 }
985
986 /* Return filter status */
987 return(isVisible);
988 }
989
990 /* Create iterator for model */
xfdashboard_model_iter_new(XfdashboardModel * inModel)991 XfdashboardModelIter* xfdashboard_model_iter_new(XfdashboardModel *inModel)
992 {
993 XfdashboardModelIter *iter;
994
995 g_return_val_if_fail(XFDASHBOARD_IS_MODEL(inModel), NULL);
996
997 /* Create iterator */
998 iter=XFDASHBOARD_MODEL_ITER(g_object_new(XFDASHBOARD_TYPE_MODEL_ITER, NULL));
999
1000 /* Set up iterator */
1001 iter->priv->model=XFDASHBOARD_MODEL(g_object_ref(inModel));
1002 iter->priv->iter=NULL;
1003
1004 /* Return newly created iterator */
1005 return(iter);
1006 }
1007
1008 /* Create iterator for model at requested row */
xfdashboard_model_iter_new_for_row(XfdashboardModel * inModel,gint inRow)1009 XfdashboardModelIter* xfdashboard_model_iter_new_for_row(XfdashboardModel *inModel, gint inRow)
1010 {
1011 XfdashboardModelIter *iter;
1012 XfdashboardModelPrivate *modelPriv;
1013
1014 g_return_val_if_fail(XFDASHBOARD_IS_MODEL(inModel), NULL);
1015 g_return_val_if_fail(_xfdashboard_model_is_valid_row(inModel, inRow), NULL);
1016
1017 modelPriv=inModel->priv;
1018
1019 /* Create iterator */
1020 iter=XFDASHBOARD_MODEL_ITER(g_object_new(XFDASHBOARD_TYPE_MODEL_ITER, NULL));
1021
1022 /* Set up iterator */
1023 iter->priv->model=XFDASHBOARD_MODEL(g_object_ref(inModel));
1024 iter->priv->iter=g_sequence_get_iter_at_pos(modelPriv->data, inRow);
1025
1026 /* Return newly created iterator */
1027 return(iter);
1028 }
1029
1030 /* Create copy of an iterator */
xfdashboard_model_iter_copy(XfdashboardModelIter * self)1031 XfdashboardModelIter* xfdashboard_model_iter_copy(XfdashboardModelIter *self)
1032 {
1033 XfdashboardModelIterPrivate *priv;
1034 XfdashboardModelIter *copyIter;
1035
1036 g_return_val_if_fail(XFDASHBOARD_IS_MODEL_ITER(self), NULL);
1037
1038 priv=self->priv;
1039
1040 /* Create iterator */
1041 copyIter=XFDASHBOARD_MODEL_ITER(g_object_new(XFDASHBOARD_TYPE_MODEL_ITER, NULL));
1042
1043 /* Set up iterator to be a copy of given iterator */
1044 copyIter->priv->model=XFDASHBOARD_MODEL(g_object_ref(priv->model));
1045 copyIter->priv->iter=priv->iter;
1046
1047 /* Return copy of iterator */
1048 return(copyIter);
1049 }
1050
1051 /* Move iterator to next item in model's data */
xfdashboard_model_iter_next(XfdashboardModelIter * self)1052 gboolean xfdashboard_model_iter_next(XfdashboardModelIter *self)
1053 {
1054 XfdashboardModelIterPrivate *priv;
1055 XfdashboardModelPrivate *modelPriv;
1056 GSequenceIter *newIter;
1057
1058 g_return_val_if_fail(_xfdashboard_model_iter_is_valid(self, FALSE), FALSE);
1059
1060 priv=self->priv;
1061 modelPriv=priv->model->priv;
1062
1063 /* If no iterator was initialized then create an iterator pointing
1064 * to begin of model's data ...
1065 */
1066 if(!priv->iter)
1067 {
1068 /* Get and set iterator pointing to begin of model's data */
1069 newIter=g_sequence_get_begin_iter(modelPriv->data);
1070 }
1071 /* ... otherwise move iterator to next item in model's data */
1072 else
1073 {
1074 /* Move iterator to next item in model's data */
1075 newIter=g_sequence_iter_next(priv->iter);
1076 }
1077
1078 /* If iterator is invalid or end of model's data is reached
1079 * return FALSE here.
1080 */
1081 if(!newIter ||
1082 g_sequence_iter_is_end(newIter))
1083 {
1084 return(FALSE);
1085 }
1086
1087 /* New iterator is valid so set it */
1088 priv->iter=newIter;
1089
1090 /* If we get here then all went well and we can return TRUE */
1091 return(TRUE);
1092 }
1093
1094 /* Move iterator to previous item in model's data */
xfdashboard_model_iter_prev(XfdashboardModelIter * self)1095 gboolean xfdashboard_model_iter_prev(XfdashboardModelIter *self)
1096 {
1097 XfdashboardModelIterPrivate *priv;
1098 XfdashboardModelPrivate *modelPriv;
1099 GSequenceIter *newIter;
1100
1101 g_return_val_if_fail(_xfdashboard_model_iter_is_valid(self, FALSE), FALSE);
1102
1103 priv=self->priv;
1104 modelPriv=priv->model->priv;
1105
1106 /* If no iterator was initialized then create an iterator pointing
1107 * to end of model's data ...
1108 */
1109 if(!priv->iter)
1110 {
1111 /* Get and set iterator pointing to end of model's data */
1112 newIter=g_sequence_get_end_iter(modelPriv->data);
1113 }
1114 /* ... otherwise move iterator to previous item in model's data */
1115 else
1116 {
1117 /* Move iterator to next item in model's data */
1118 newIter=g_sequence_iter_prev(priv->iter);
1119 }
1120
1121 /* If iterator is invalid or begin of model's data is reached
1122 * return FALSE here.
1123 */
1124 if(!newIter ||
1125 g_sequence_iter_is_begin(newIter))
1126 {
1127 return(FALSE);
1128 }
1129
1130 /* New iterator is valid so set it */
1131 priv->iter=newIter;
1132
1133 /* If we get here then all went well and we can return TRUE */
1134 return(TRUE);
1135 }
1136
1137 /* Move iterator to requested row in model's data */
xfdashboard_model_iter_move_to_row(XfdashboardModelIter * self,gint inRow)1138 gboolean xfdashboard_model_iter_move_to_row(XfdashboardModelIter *self, gint inRow)
1139 {
1140 XfdashboardModelIterPrivate *priv;
1141 XfdashboardModelPrivate *modelPriv;
1142
1143 g_return_val_if_fail(_xfdashboard_model_iter_is_valid(self, FALSE), FALSE);
1144
1145 priv=self->priv;
1146 modelPriv=priv->model->priv;
1147
1148 /* Check that requested row is within range */
1149 g_return_val_if_fail(_xfdashboard_model_is_valid_row(priv->model, inRow), FALSE);
1150
1151 /* Move iterator to requested row */
1152 priv->iter=g_sequence_get_iter_at_pos(modelPriv->data, inRow);
1153
1154 /* If we get here then all went well and we can return TRUE */
1155 return(TRUE);
1156 }
1157
1158 /* Get model to which this iterator belongs to */
xfdashboard_model_iter_get_model(XfdashboardModelIter * self)1159 XfdashboardModel* xfdashboard_model_iter_get_model(XfdashboardModelIter *self)
1160 {
1161 g_return_val_if_fail(_xfdashboard_model_iter_is_valid(self, FALSE), FALSE);
1162
1163 return(self->priv->model);
1164 }
1165
1166 /* Get row at model's data this iterator points to currently */
xfdashboard_model_iter_get_row(XfdashboardModelIter * self)1167 guint xfdashboard_model_iter_get_row(XfdashboardModelIter *self)
1168 {
1169 XfdashboardModelIterPrivate *priv;
1170 gint position;
1171
1172 g_return_val_if_fail(_xfdashboard_model_iter_is_valid(self, TRUE), 0);
1173
1174 priv=self->priv;
1175
1176 /* Get position from iterator */
1177 position=g_sequence_iter_get_position(priv->iter);
1178 if(position<0) position=0;
1179
1180 /* Return position (maybe corrected for unsigned integer) */
1181 return((guint)position);
1182 }
1183
1184 /* Get item at position and model of this iterator */
xfdashboard_model_iter_get(XfdashboardModelIter * self)1185 gpointer xfdashboard_model_iter_get(XfdashboardModelIter *self)
1186 {
1187 XfdashboardModelIterPrivate *priv;
1188
1189 g_return_val_if_fail(_xfdashboard_model_iter_is_valid(self, TRUE), NULL);
1190
1191 priv=self->priv;
1192
1193 /* Get item of model this iterator belongs to */
1194 return(g_sequence_get(priv->iter));
1195 }
1196
1197 /* Set or replace data at iterator */
xfdashboard_model_iter_set(XfdashboardModelIter * self,gpointer inData)1198 gboolean xfdashboard_model_iter_set(XfdashboardModelIter *self, gpointer inData)
1199 {
1200 XfdashboardModelIterPrivate *priv;
1201 XfdashboardModelPrivate *modelPriv;
1202
1203 g_return_val_if_fail(_xfdashboard_model_iter_is_valid(self, TRUE), FALSE);
1204
1205 priv=self->priv;
1206 modelPriv=priv->model->priv;
1207
1208 /* If a function at model is provided to free data on removal
1209 * then call it now.
1210 */
1211 if(modelPriv->freeDataCallback)
1212 {
1213 gpointer oldData;
1214
1215 /* Get data to remove */
1216 oldData=g_sequence_get(priv->iter);
1217
1218 /* Call function to free data */
1219 (modelPriv->freeDataCallback)(oldData);
1220 }
1221
1222 /* Set new data at iterator */
1223 g_sequence_set(priv->iter, inData);
1224
1225 /* Emit signal */
1226 g_signal_emit(self, XfdashboardModelSignals[SIGNAL_ROW_CHANGED], 0, self);
1227
1228 /* Return TRUE for success */
1229 return(TRUE);
1230 }
1231
1232 /* Remove data at iterator */
xfdashboard_model_iter_remove(XfdashboardModelIter * self)1233 gboolean xfdashboard_model_iter_remove(XfdashboardModelIter *self)
1234 {
1235 XfdashboardModelIterPrivate *priv;
1236 XfdashboardModelPrivate *modelPriv;
1237
1238 g_return_val_if_fail(_xfdashboard_model_iter_is_valid(self, TRUE), FALSE);
1239
1240 priv=self->priv;
1241 modelPriv=priv->model->priv;
1242
1243 /* Emit signal before removal to give signal handlers a changed
1244 * to access the data at iterator a last time.
1245 */
1246 g_signal_emit(self, XfdashboardModelSignals[SIGNAL_ROW_REMOVED], 0, self);
1247
1248 /* If a function at model is provided to free data on removal
1249 * then call it now.
1250 */
1251 if(modelPriv->freeDataCallback)
1252 {
1253 gpointer oldData;
1254
1255 /* Get data to remove */
1256 oldData=g_sequence_get(priv->iter);
1257
1258 /* Call function to free data */
1259 (modelPriv->freeDataCallback)(oldData);
1260 }
1261
1262 /* Remove data from model's data */
1263 g_sequence_remove(priv->iter);
1264
1265 /* Return TRUE for success */
1266 return(TRUE);
1267 }
1268
1269 /* Check if row is filtered to which this iterator is pointing to */
xfdashboard_model_iter_filter(XfdashboardModelIter * self)1270 gboolean xfdashboard_model_iter_filter(XfdashboardModelIter *self)
1271 {
1272 XfdashboardModelIterPrivate *priv;
1273 XfdashboardModelPrivate *modelPriv;
1274 gboolean isVisible;
1275
1276 g_return_val_if_fail(_xfdashboard_model_iter_is_valid(self, TRUE), FALSE);
1277
1278 priv=self->priv;
1279 modelPriv=priv->model->priv;
1280 isVisible=TRUE;
1281
1282 /* Call user supplied filter callback function to determine if this
1283 * row should be filtered or not but only if filter function is set.
1284 */
1285 if(modelPriv->filterCallback)
1286 {
1287 /* Determine if row is filtered */
1288 isVisible=(modelPriv->filterCallback)(self, modelPriv->filterUserData);
1289 }
1290
1291 /* Return filter status */
1292 return(isVisible);
1293 }
1294