1 /* font-manager-font-model.c
2  *
3  * Copyright (C) 2009 - 2021 Jerry Casiano
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program 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
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.
17  *
18  * If not, see <http://www.gnu.org/licenses/gpl-3.0.txt>.
19 */
20 
21 #include "font-manager-font-model.h"
22 
23 /**
24  * SECTION: font-manager-font-model
25  * @short_description: Font data model
26  * @title: Font Model
27  * @include: font-manager-font-model.h
28  * @see_also: #FontManagerFamily #FontManagerFont
29  *
30  * Minimal implementation which wraps the #JsonArray returned by #font_manager_sort_json_font_listing().
31  *
32  * This model provides read-only access to available #FontManagerFamily objects.
33  */
34 
35 struct _FontManagerFontModel
36 {
37     GObject parent_instance;
38 
39     gint stamp;
40     JsonArray *available_fonts;
41 };
42 
43 static void g_list_model_interface_init (GListModelInterface *iface);
44 static void gtk_tree_model_interface_init (GtkTreeModelIface *iface);
45 static void gtk_tree_drag_source_interface_init (GtkTreeDragSourceIface *iface);
46 static void gtk_tree_drag_dest_interface_init (GtkTreeDragDestIface *iface);
47 
48 G_DEFINE_TYPE_WITH_CODE(FontManagerFontModel, font_manager_font_model, G_TYPE_OBJECT,
49     G_IMPLEMENT_INTERFACE(G_TYPE_LIST_MODEL, g_list_model_interface_init)
50     G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL, gtk_tree_model_interface_init)
51     G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_DRAG_SOURCE, gtk_tree_drag_source_interface_init)
52     G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_DRAG_DEST, gtk_tree_drag_dest_interface_init))
53 
54 enum
55 {
56     PROP_RESERVED,
57     PROP_SOURCE,
58     N_PROPERTIES
59 };
60 
61 GType COLUMN_TYPES [FONT_MANAGER_FONT_MODEL_N_COLUMNS] = {
62     G_TYPE_OBJECT,
63     G_TYPE_STRING,
64     G_TYPE_STRING,
65     G_TYPE_INT
66 };
67 
68 GType
font_manager_font_model_column_get_type(void)69 font_manager_font_model_column_get_type (void)
70 {
71   static volatile gsize g_define_type_id__volatile = 0;
72 
73   if (g_once_init_enter (&g_define_type_id__volatile))
74     {
75       static const GEnumValue values[] = {
76         { FONT_MANAGER_FONT_MODEL_OBJECT, "FONT_MANAGER_FONT_MODEL_OBJECT", "object" },
77         { FONT_MANAGER_FONT_MODEL_NAME, "FONT_MANAGER_FONT_MODEL_NAME", "name" },
78         { FONT_MANAGER_FONT_MODEL_DESCRIPTION, "FONT_MANAGER_FONT_MODEL_DESCRIPTION", "description" },
79         { FONT_MANAGER_FONT_MODEL_COUNT, "FONT_MANAGER_FONT_MODEL_COUNT", "count" },
80         { FONT_MANAGER_FONT_MODEL_N_COLUMNS, "FONT_MANAGER_FONT_MODEL_N_COLUMNS", "n-columns" },
81         { 0, NULL, NULL }
82       };
83       GType g_define_type_id =
84         g_enum_register_static (g_intern_static_string ("FontManagerFontModelColumn"), values);
85       g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
86     }
87 
88   return g_define_type_id__volatile;
89 }
90 
91 #define SOURCE "source-array"
92 
93 gint
get_n_variations(FontManagerFontModel * self,gint index)94 get_n_variations (FontManagerFontModel *self, gint index)
95 {
96     JsonObject *family = json_array_get_object_element(self->available_fonts, index);
97     return (gint) json_object_get_int_member(family, "n_variations");
98 }
99 
100 gboolean
invalid_iter(GtkTreeIter * iter)101 invalid_iter (GtkTreeIter *iter) {
102     iter->stamp = 0;
103     return FALSE;
104 }
105 
106 /* GtkTreeModelIface */
107 
108 static GtkTreeModelFlags
font_manager_font_model_get_flags(G_GNUC_UNUSED GtkTreeModel * tree_model)109 font_manager_font_model_get_flags (G_GNUC_UNUSED GtkTreeModel *tree_model)
110 {
111     return GTK_TREE_MODEL_ITERS_PERSIST;
112 }
113 
114 static gint
font_manager_font_model_get_n_columns(G_GNUC_UNUSED GtkTreeModel * tree_model)115 font_manager_font_model_get_n_columns (G_GNUC_UNUSED GtkTreeModel *tree_model)
116 {
117     return FONT_MANAGER_FONT_MODEL_N_COLUMNS;
118 }
119 
120 static GType
font_manager_font_model_get_column_type(G_GNUC_UNUSED GtkTreeModel * tree_model,gint index)121 font_manager_font_model_get_column_type (G_GNUC_UNUSED GtkTreeModel *tree_model, gint index)
122 {
123     g_return_val_if_fail(index < FONT_MANAGER_FONT_MODEL_N_COLUMNS, G_TYPE_INVALID);
124     return COLUMN_TYPES[index];
125 }
126 
127 static gboolean
font_manager_font_model_get_iter(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreePath * path)128 font_manager_font_model_get_iter (GtkTreeModel *tree_model,
129                                   GtkTreeIter *iter,
130                                   GtkTreePath *path)
131 {
132     FontManagerFontModel *self = FONT_MANAGER_FONT_MODEL(tree_model);
133     g_return_val_if_fail(self != NULL, FALSE);
134     g_return_val_if_fail(path != NULL, FALSE);
135     gint depth = gtk_tree_path_get_depth(path);
136     gint *indices = gtk_tree_path_get_indices(path);
137     if (!self->available_fonts || indices[0] >= ((int) json_array_get_length(self->available_fonts)))
138         return invalid_iter(iter);
139     iter->stamp = self->stamp;
140     iter->user_data = GINT_TO_POINTER(indices[0]);
141     iter->user_data2 = GINT_TO_POINTER(-1);
142     if (depth > 1) {
143         gint n_variations = get_n_variations(self, indices[0]);
144         if (indices[1] >= n_variations)
145             return invalid_iter(iter);
146         iter->user_data2 = GINT_TO_POINTER(indices[1]);
147     }
148     return TRUE;
149 }
150 
151 static GtkTreePath *
font_manager_font_model_get_path(GtkTreeModel * tree_model,GtkTreeIter * iter)152 font_manager_font_model_get_path (GtkTreeModel *tree_model, GtkTreeIter *iter)
153 {
154     FontManagerFontModel *self = FONT_MANAGER_FONT_MODEL(tree_model);
155     g_return_val_if_fail(self != NULL, NULL);
156     g_return_val_if_fail(iter->stamp == self->stamp, NULL);
157     GtkTreePath *path = gtk_tree_path_new();
158     gtk_tree_path_append_index(path, GPOINTER_TO_INT(iter->user_data));
159     if (GPOINTER_TO_INT(iter->user_data2) != -1)
160         gtk_tree_path_append_index(path, GPOINTER_TO_INT(iter->user_data2));
161     return path;
162 }
163 
164 static void
font_manager_font_model_get_value(GtkTreeModel * tree_model,GtkTreeIter * iter,gint column,GValue * value)165 font_manager_font_model_get_value (GtkTreeModel *tree_model,
166                                    GtkTreeIter *iter,
167                                    gint column,
168                                    GValue *value)
169 {
170     FontManagerFontModel *self = FONT_MANAGER_FONT_MODEL(tree_model);
171     g_return_if_fail(self != NULL);
172     g_return_if_fail(self->available_fonts != NULL);
173     g_return_if_fail(json_array_get_length(self->available_fonts) > 0);
174     g_return_if_fail(iter != NULL);
175     g_return_if_fail(iter->stamp == self->stamp);
176     g_value_init(value, COLUMN_TYPES[column]);
177     JsonObject *root = NULL, *child = NULL;
178     root = json_array_get_object_element(self->available_fonts, GPOINTER_TO_INT(iter->user_data));
179     gboolean root_node = (GPOINTER_TO_INT(iter->user_data2) == -1);
180     if (!root_node) {
181         JsonArray *children = json_object_get_array_member(root, "variations");
182         child = json_array_get_object_element(children, GPOINTER_TO_INT(iter->user_data2));
183     }
184     JsonObject *obj = root_node ? root : child;
185     const gchar *member = root_node ? "family" : "style";
186     switch (column) {
187         case FONT_MANAGER_FONT_MODEL_NAME:
188             g_value_set_string(value, json_object_get_string_member(obj, member));
189             break;
190         case FONT_MANAGER_FONT_MODEL_DESCRIPTION:
191             g_value_set_string(value, json_object_get_string_member(obj, "description"));
192             break;
193         case FONT_MANAGER_FONT_MODEL_COUNT:
194             if (root_node)
195                 g_value_set_int(value, get_n_variations(self, GPOINTER_TO_INT(iter->user_data)));
196             else
197                 g_value_set_int(value, -1);
198             break;
199         case FONT_MANAGER_FONT_MODEL_OBJECT:
200             if (root_node) {
201                 g_autoptr(FontManagerFamily) family = font_manager_family_new();
202                 g_object_set(family, "source-object", obj, NULL);
203                 g_value_set_object(value, family);
204             } else {
205                 g_autoptr(FontManagerFont) font = font_manager_font_new();
206                 g_object_set(font, "source-object", obj, NULL);
207                 g_value_set_object(value, font);
208             }
209             break;
210         default:
211             g_critical(G_STRLOC ": Invalid column index : %i", column);
212     }
213     return;
214 }
215 
216 static gboolean
font_manager_font_model_iter_next(GtkTreeModel * tree_model,GtkTreeIter * iter)217 font_manager_font_model_iter_next (GtkTreeModel *tree_model, GtkTreeIter *iter)
218 {
219     FontManagerFontModel *self = FONT_MANAGER_FONT_MODEL(tree_model);
220     g_return_val_if_fail(self != NULL, FALSE);
221     g_return_val_if_fail(iter != NULL, FALSE);
222     g_return_val_if_fail(iter->stamp == self->stamp, FALSE);
223     if (!self->available_fonts || json_array_get_length(self->available_fonts) < 1)
224         return invalid_iter(iter);
225     gint index = GPOINTER_TO_INT(iter->user_data);
226     if (GPOINTER_TO_INT(iter->user_data2) == -1) {
227         gint n_root_nodes = (gint) json_array_get_length(self->available_fonts);
228         if (!(index < n_root_nodes - 1))
229             return invalid_iter(iter);
230         iter->user_data = GINT_TO_POINTER(index + 1);
231     } else {
232         gint n_children = get_n_variations(self, index);
233         index = GPOINTER_TO_INT(iter->user_data2);
234         if (!(index < n_children - 1))
235             return invalid_iter(iter);
236         iter->user_data2 = GINT_TO_POINTER(index + 1);
237     }
238     return TRUE;
239 }
240 
241 static gboolean
font_manager_font_model_iter_previous(GtkTreeModel * tree_model,GtkTreeIter * iter)242 font_manager_font_model_iter_previous (GtkTreeModel *tree_model, GtkTreeIter *iter)
243 {
244     FontManagerFontModel *self = FONT_MANAGER_FONT_MODEL(tree_model);
245     g_return_val_if_fail(self != NULL, FALSE);
246     g_return_val_if_fail(iter != NULL, FALSE);
247     g_return_val_if_fail(iter->stamp == self->stamp, FALSE);
248     if (!self->available_fonts || json_array_get_length(self->available_fonts) < 1)
249         return invalid_iter(iter);
250     gint index = GPOINTER_TO_INT(iter->user_data);
251     if (GPOINTER_TO_INT(iter->user_data2) == -1) {
252         if (index < 1)
253             return invalid_iter(iter);
254         iter->user_data = GINT_TO_POINTER(index - 1);
255     } else {
256         index = GPOINTER_TO_INT(iter->user_data2);
257         if (index < 1)
258             return invalid_iter(iter);
259         iter->user_data2 = GINT_TO_POINTER(index - 1);
260     }
261     return TRUE;
262 }
263 
264 static gboolean
font_manager_font_model_iter_children(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * parent)265 font_manager_font_model_iter_children (GtkTreeModel *tree_model,
266                                        GtkTreeIter *iter,
267                                        GtkTreeIter *parent)
268 {
269     FontManagerFontModel *self = FONT_MANAGER_FONT_MODEL(tree_model);
270     g_return_val_if_fail(self != NULL, FALSE);
271     iter->stamp = self->stamp;
272     if (!self->available_fonts || json_array_get_length(self->available_fonts) < 1)
273         return invalid_iter(iter);
274     /* Special case - if parent equals %NULL this function should return the first node */
275     if (parent == NULL) {
276         iter->user_data = GINT_TO_POINTER(0);
277         iter->user_data2 = GINT_TO_POINTER(-1);
278     } else if (GPOINTER_TO_INT(parent->user_data2) != -1) {
279         /* Maximum depth of this model is 2 */
280         return invalid_iter(iter);
281     } else {
282         iter->user_data = parent->user_data;
283         iter->user_data2 = GINT_TO_POINTER(0);
284     }
285     return TRUE;
286 }
287 
288 static gboolean
font_manager_font_model_iter_has_child(GtkTreeModel * tree_model,GtkTreeIter * iter)289 font_manager_font_model_iter_has_child (GtkTreeModel *tree_model, GtkTreeIter *iter)
290 {
291     FontManagerFontModel *self = FONT_MANAGER_FONT_MODEL(tree_model);
292     g_return_val_if_fail(self != NULL, FALSE);
293     g_return_val_if_fail(iter != NULL, FALSE);
294     g_return_val_if_fail(iter->stamp == self->stamp, FALSE);
295     if (!self->available_fonts || json_array_get_length(self->available_fonts) < 1)
296         return FALSE;
297     return (GPOINTER_TO_INT(iter->user_data2) == -1);
298 }
299 
300 static gint
font_manager_font_model_iter_n_children(GtkTreeModel * tree_model,GtkTreeIter * iter)301 font_manager_font_model_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter)
302 {
303     FontManagerFontModel *self = FONT_MANAGER_FONT_MODEL(tree_model);
304     g_return_val_if_fail(self != NULL, 0);
305     g_return_val_if_fail(self->available_fonts != NULL, 0);
306     /* Special case - if iter is %NULL this function should return the number of toplevel nodes */
307     if (iter == NULL)
308         return ((gint) json_array_get_length(self->available_fonts));
309     return get_n_variations(self, GPOINTER_TO_INT(iter->user_data));
310 }
311 
312 static gboolean
font_manager_font_model_iter_nth_child(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * parent,gint n)313 font_manager_font_model_iter_nth_child (GtkTreeModel *tree_model,
314                                         GtkTreeIter *iter,
315                                         GtkTreeIter *parent,
316                                         gint n)
317 {
318     FontManagerFontModel *self = FONT_MANAGER_FONT_MODEL(tree_model);
319     g_return_val_if_fail(self != NULL, FALSE);
320     g_return_val_if_fail(n >= 0, FALSE);
321     if (!self->available_fonts || json_array_get_length(self->available_fonts) < 1)
322         return FALSE;
323     iter->stamp = self->stamp;
324     /* Special case - if parent is %NULL this function should set iter to toplevel node n */
325     if (parent == NULL) {
326         gint n_root_nodes = (gint) json_array_get_length(self->available_fonts);
327         if (n < n_root_nodes) {
328             iter->user_data = GINT_TO_POINTER(n);
329             iter->user_data2 = GINT_TO_POINTER(-1);
330             return TRUE;
331         } else {
332             return invalid_iter(iter);
333         }
334     }
335 
336     g_return_val_if_fail(parent->stamp == self->stamp, FALSE);
337     gint n_children = get_n_variations(self, GPOINTER_TO_INT(parent->user_data));
338 
339     if (n > n_children -1) {
340         return invalid_iter(iter);
341     } else {
342         iter->user_data = parent->user_data;
343         iter->user_data2 = GINT_TO_POINTER(n);
344         return TRUE;
345     }
346 
347 }
348 
349 static gboolean
font_manager_font_model_iter_parent(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * child)350 font_manager_font_model_iter_parent (GtkTreeModel *tree_model,
351                                      GtkTreeIter *iter,
352                                      GtkTreeIter *child)
353 {
354     FontManagerFontModel *self = FONT_MANAGER_FONT_MODEL(tree_model);
355     g_return_val_if_fail(self != NULL, FALSE);
356     g_return_val_if_fail(child->stamp == self->stamp, FALSE);
357     g_return_val_if_fail(child->user_data != NULL, FALSE);
358     g_return_val_if_fail(child->user_data2 != NULL, FALSE);
359     iter->stamp = self->stamp;
360     iter->user_data = child->user_data;
361     iter->user_data2 = GINT_TO_POINTER(-1);
362     return TRUE;
363 }
364 
365 /* GListModelInterface */
366 
367 static GType
font_manager_font_model_get_item_type(G_GNUC_UNUSED GListModel * self)368 font_manager_font_model_get_item_type (G_GNUC_UNUSED GListModel *self)
369 {
370     return FONT_MANAGER_TYPE_FAMILY;
371 }
372 
373 static guint
font_manager_font_model_get_n_items(GListModel * self)374 font_manager_font_model_get_n_items (GListModel *self)
375 {
376     g_return_val_if_fail(self != NULL, 0);
377     FontManagerFontModel *model = FONT_MANAGER_FONT_MODEL(self);
378     return json_array_get_length(model->available_fonts);
379 }
380 
381 static gpointer
font_manager_font_model_get_item(GListModel * self,guint position)382 font_manager_font_model_get_item (GListModel *self, guint position)
383 {
384     g_return_val_if_fail(self != NULL, NULL);
385     if (position >= font_manager_font_model_get_n_items(self))
386         return NULL;
387     FontManagerFontModel *model = FONT_MANAGER_FONT_MODEL(self);
388     JsonObject *obj = json_array_get_object_element(model->available_fonts, position);
389     FontManagerFamily *family = font_manager_family_new();
390     g_object_set(G_OBJECT(family), "source-object", obj, NULL);
391     return family;
392 }
393 
394 static void
g_list_model_interface_init(GListModelInterface * iface)395 g_list_model_interface_init (GListModelInterface *iface)
396 {
397     iface->get_item_type = font_manager_font_model_get_item_type;
398     iface->get_n_items = font_manager_font_model_get_n_items;
399     iface->get_item = font_manager_font_model_get_item;
400     return;
401 }
402 
403 /* GtkTreeModelIface */
404 
405 static void
gtk_tree_model_interface_init(GtkTreeModelIface * iface)406 gtk_tree_model_interface_init (GtkTreeModelIface *iface)
407 {
408     iface->get_flags = font_manager_font_model_get_flags;
409     iface->get_n_columns = font_manager_font_model_get_n_columns;
410     iface->get_column_type = font_manager_font_model_get_column_type;
411     iface->get_iter = font_manager_font_model_get_iter;
412     iface->get_path = font_manager_font_model_get_path;
413     iface->get_value = font_manager_font_model_get_value;
414     iface->iter_next = font_manager_font_model_iter_next;
415     iface->iter_previous = font_manager_font_model_iter_previous;
416     iface->iter_children = font_manager_font_model_iter_children;
417     iface->iter_has_child = font_manager_font_model_iter_has_child;
418     iface->iter_n_children = font_manager_font_model_iter_n_children;
419     iface->iter_nth_child = font_manager_font_model_iter_nth_child;
420     iface->iter_parent = font_manager_font_model_iter_parent;
421     return;
422 }
423 
424 /* GtkTreeDragSourceIface */
425 
426 static gboolean
font_manager_font_model_row_draggable(G_GNUC_UNUSED GtkTreeDragSource * source,G_GNUC_UNUSED GtkTreePath * path)427 font_manager_font_model_row_draggable (G_GNUC_UNUSED GtkTreeDragSource *source,
428                                        G_GNUC_UNUSED GtkTreePath *path)
429 {
430     return TRUE;
431 }
432 
433 static gboolean
font_manager_font_model_drag_data_get(GtkTreeDragSource * source,GtkTreePath * path,GtkSelectionData * selection_data)434 font_manager_font_model_drag_data_get (GtkTreeDragSource *source,
435                                        GtkTreePath *path,
436                                        GtkSelectionData *selection_data)
437 {
438     if (gtk_tree_set_row_drag_data(selection_data, GTK_TREE_MODEL(source), path))
439         return TRUE;
440     return FALSE;
441 }
442 
443 static gboolean
font_manager_font_model_drag_data_delete(G_GNUC_UNUSED GtkTreeDragSource * drag_source,G_GNUC_UNUSED GtkTreePath * path)444 font_manager_font_model_drag_data_delete (G_GNUC_UNUSED GtkTreeDragSource *drag_source,
445                                           G_GNUC_UNUSED GtkTreePath *path)
446 {
447     /* This model is read-only */
448     return FALSE;
449 }
450 
451 static void
gtk_tree_drag_source_interface_init(GtkTreeDragSourceIface * iface)452 gtk_tree_drag_source_interface_init (GtkTreeDragSourceIface *iface)
453 {
454     iface->row_draggable = font_manager_font_model_row_draggable;
455     iface->drag_data_get = font_manager_font_model_drag_data_get;
456     iface->drag_data_delete = font_manager_font_model_drag_data_delete;
457     return;
458 }
459 
460 /* GtkTreeDragDestIface */
461 
462 static gboolean
font_manager_font_model_drag_data_received(G_GNUC_UNUSED GtkTreeDragDest * drag_dest,G_GNUC_UNUSED GtkTreePath * dest,G_GNUC_UNUSED GtkSelectionData * selection_data)463 font_manager_font_model_drag_data_received (G_GNUC_UNUSED GtkTreeDragDest *drag_dest,
464                                             G_GNUC_UNUSED GtkTreePath *dest,
465                                             G_GNUC_UNUSED GtkSelectionData *selection_data)
466 {
467     return FALSE;
468 }
469 
470 static gboolean
font_manager_font_model_row_drop_possible(G_GNUC_UNUSED GtkTreeDragDest * drag_dest,G_GNUC_UNUSED GtkTreePath * dest,G_GNUC_UNUSED GtkSelectionData * selection_data)471 font_manager_font_model_row_drop_possible (G_GNUC_UNUSED GtkTreeDragDest *drag_dest,
472                                            G_GNUC_UNUSED GtkTreePath *dest,
473                                            G_GNUC_UNUSED GtkSelectionData *selection_data)
474 {
475     /* This model is read-only */
476     return FALSE;
477 }
478 
479 static void
gtk_tree_drag_dest_interface_init(GtkTreeDragDestIface * iface)480 gtk_tree_drag_dest_interface_init (GtkTreeDragDestIface *iface)
481 {
482     iface->drag_data_received = font_manager_font_model_drag_data_received;
483     iface->row_drop_possible = font_manager_font_model_row_drop_possible;
484     return;
485 }
486 
487 /* FontManagerFontModel */
488 
489 static void
font_manager_font_model_init(FontManagerFontModel * self)490 font_manager_font_model_init (FontManagerFontModel *self)
491 {
492     do { self->stamp = g_random_int(); } while (self->stamp == 0);
493     self->available_fonts = json_array_new();
494     return;
495 }
496 
497 static void
font_manager_font_model_dispose(GObject * gobject)498 font_manager_font_model_dispose (GObject *gobject)
499 {
500     g_return_if_fail(gobject != NULL);
501     FontManagerFontModel *self = FONT_MANAGER_FONT_MODEL(gobject);
502     g_clear_pointer(&self->available_fonts, json_array_unref);
503     G_OBJECT_CLASS(font_manager_font_model_parent_class)->dispose(gobject);
504     return;
505 }
506 
507 static void
font_manager_font_model_get_property(GObject * gobject,guint property_id,GValue * value,GParamSpec * pspec)508 font_manager_font_model_get_property (GObject *gobject,
509                                       guint property_id,
510                                       GValue *value,
511                                       GParamSpec *pspec)
512 {
513     g_return_if_fail(gobject != NULL);
514     FontManagerFontModel *self = FONT_MANAGER_FONT_MODEL(gobject);
515     g_return_if_fail(self->available_fonts != NULL);
516 
517     switch (property_id) {
518         case PROP_SOURCE:
519             g_value_set_boxed(value, self->available_fonts);
520             break;
521         default:
522             G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec);
523             break;
524     }
525 
526     return;
527 }
528 
529 static void
set_source(FontManagerFontModel * self,JsonArray * value)530 set_source (FontManagerFontModel *self, JsonArray *value)
531 {
532     g_return_if_fail(self != NULL);
533     if (self->available_fonts == value)
534         return;
535     if (self->available_fonts != NULL)
536         json_array_unref(self->available_fonts);
537     self->available_fonts = value ? json_array_ref(value) : NULL;
538     g_object_notify(G_OBJECT(self), SOURCE);
539     return;
540 }
541 
542 static void
font_manager_font_model_set_property(GObject * gobject,guint property_id,const GValue * value,GParamSpec * pspec)543 font_manager_font_model_set_property (GObject *gobject,
544                                       guint property_id,
545                                       const GValue *value,
546                                       GParamSpec *pspec)
547 {
548     g_return_if_fail(gobject != NULL);
549     FontManagerFontModel *self = FONT_MANAGER_FONT_MODEL(gobject);
550 
551     switch (property_id) {
552         case PROP_SOURCE:
553             set_source(self, g_value_get_boxed(value));
554             break;
555         default:
556             G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, property_id, pspec);
557             break;
558     }
559 
560     return;
561 }
562 
563 static void
font_manager_font_model_class_init(FontManagerFontModelClass * klass)564 font_manager_font_model_class_init (FontManagerFontModelClass *klass)
565 {
566     GObjectClass *object_class = G_OBJECT_CLASS(klass);
567     object_class->get_property = font_manager_font_model_get_property;
568     object_class->set_property = font_manager_font_model_set_property;
569     object_class->dispose = font_manager_font_model_dispose;
570 
571     /**
572      * FontManagerFontModel:source-array:
573      *
574      * #JsonArray source.
575      */
576     g_object_class_install_property(object_class,
577                                     PROP_SOURCE,
578                                     g_param_spec_boxed(SOURCE,
579                                                        NULL,
580                                                        "#JsonArray backing this model",
581                                                        JSON_TYPE_ARRAY,
582                                                        G_PARAM_STATIC_STRINGS |
583                                                        G_PARAM_READWRITE));
584     return;
585 }
586 
587 /**
588  * font_manager_font_model_new:
589  *
590  * Returns : (transfer full) : A newly created #FontManagerFontModel.
591  * Free the returned object using #g_object_unref().
592  */
593 FontManagerFontModel *
font_manager_font_model_new(void)594 font_manager_font_model_new (void)
595 {
596     return g_object_new(FONT_MANAGER_TYPE_FONT_MODEL, NULL);
597 }
598