1 /*
2 * biji-notebook.c
3 *
4 * Copyright (C) Pierre-Yves LUYTEN 2013 <py@luyten.fr>
5 *
6 * bijiben is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * bijiben is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*
21 * in current implementation, one cannot add a notebook
22 * to a notebook
23 * but tracker would be fine with this
24 * given we prevent self-containment.
25 */
26
27 /*
28 * biji_create_notebook_icon
29 * is adapted from Photos (photos_utils_create_notebook_icon),
30 * which is ported from Documents
31 */
32
33 #include <math.h>
34
35 #include "biji-notebook.h"
36 #include "biji-tracker.h"
37
38
39 static void biji_notebook_update_collected (GList *result, gpointer user_data);
40
41
42 struct _BijiNotebook
43 {
44 BijiItem parent_instance;
45
46 gchar *urn;
47 gchar *name;
48 gint64 mtime;
49
50 cairo_surface_t *icon;
51 cairo_surface_t *emblem;
52
53 GList *collected_items;
54 };
55
56 static void biji_notebook_finalize (GObject *object);
57
58 G_DEFINE_TYPE (BijiNotebook, biji_notebook, BIJI_TYPE_ITEM)
59
60 /* Properties */
61 enum {
62 PROP_0,
63 PROP_URN,
64 PROP_NAME,
65 PROP_MTIME,
66 BIJI_NOTEBOOK_PROPERTIES
67 };
68
69
70 /* Signals */
71 enum {
72 NOTEBOOK_ICON_UPDATED,
73 BIJI_NOTEBOOKS_SIGNALS
74 };
75
76 static GParamSpec *properties[BIJI_NOTEBOOK_PROPERTIES] = { NULL, };
77
78 static guint biji_notebooks_signals [BIJI_NOTEBOOKS_SIGNALS] = { 0 };
79
80
81 static const gchar *
biji_notebook_get_title(BijiItem * coll)82 biji_notebook_get_title (BijiItem *coll)
83 {
84 BijiNotebook *self;
85
86 g_return_val_if_fail (BIJI_IS_NOTEBOOK (coll), NULL);
87 self = BIJI_NOTEBOOK (coll);
88
89 return self->name;
90 }
91
92
93 static const gchar *
biji_notebook_get_uuid(BijiItem * coll)94 biji_notebook_get_uuid (BijiItem *coll)
95 {
96 BijiNotebook *self;
97
98 g_return_val_if_fail (BIJI_IS_NOTEBOOK (coll), NULL);
99 self = BIJI_NOTEBOOK (coll);
100
101 return self->urn;
102 }
103
104
105 static cairo_surface_t *
biji_create_notebook_icon(gint base_size,gint scale,GList * surfaces)106 biji_create_notebook_icon (gint base_size, gint scale, GList *surfaces)
107 {
108 cairo_surface_t *surface, *pix;
109 cairo_t *cr;
110 GList *l;
111 GtkStyleContext *context;
112 GtkWidgetPath *path;
113 gint cur_x;
114 gint cur_y;
115 gint idx;
116 gint padding;
117 gint pix_height;
118 gint pix_width;
119 gdouble pix_scale_x;
120 gdouble pix_scale_y;
121 gint scale_size;
122 gint tile_size;
123
124 /* TODO: use notes thumbs */
125
126 padding = MAX (floor (base_size / 10), 4);
127 tile_size = (base_size - (3 * padding)) / 2;
128
129 context = gtk_style_context_new ();
130 gtk_style_context_add_class (context, "biji-notebook-icon");
131
132 path = gtk_widget_path_new ();
133 gtk_widget_path_append_type (path, GTK_TYPE_ICON_VIEW);
134 gtk_style_context_set_path (context, path);
135 gtk_widget_path_unref (path);
136
137 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, base_size * scale, base_size * scale);
138 cairo_surface_set_device_scale (surface, scale, scale);
139 cr = cairo_create (surface);
140
141 gtk_render_background (context, cr, 0, 0, base_size, base_size);
142 gtk_render_frame (context, cr, 0, 0, base_size, base_size);
143
144 l = surfaces;
145 idx = 0;
146 cur_x = padding;
147 cur_y = padding;
148
149
150 while (l != NULL && idx < 4)
151 {
152 pix = l->data;
153 cairo_surface_get_device_scale (pix, &pix_scale_x, &pix_scale_y);
154 pix_width = cairo_image_surface_get_width (pix) / (gint) pix_scale_x;
155 pix_height = cairo_image_surface_get_height (pix) / (gint) pix_scale_y;
156
157 scale_size = MIN (pix_width, pix_height);
158
159 cairo_save (cr);
160
161 cairo_translate (cr, cur_x, cur_y);
162
163 cairo_rectangle (cr, 0, 0,
164 tile_size, tile_size);
165 cairo_clip (cr);
166
167 cairo_scale (cr, (gdouble) tile_size / (gdouble) scale_size, (gdouble) tile_size / (gdouble) scale_size);
168 cairo_set_source_surface (cr, pix, 0, 0);
169
170 cairo_paint (cr);
171 cairo_restore (cr);
172
173 if ((idx % 2) == 0)
174 {
175 cur_x += tile_size + padding;
176 }
177 else
178 {
179 cur_x = padding;
180 cur_y += tile_size + padding;
181 }
182
183 idx++;
184 l = l->next;
185 }
186
187 cairo_destroy (cr);
188 g_object_unref (context);
189
190 return surface;
191 }
192
193 static GList *
get_collected_pix(BijiNotebook * self,gint scale)194 get_collected_pix (BijiNotebook *self,
195 gint scale)
196 {
197 GList *result = NULL, *l;
198
199 for (l = self->collected_items ; l != NULL; l = l->next)
200 {
201 if (BIJI_IS_ITEM (l->data))
202 result = g_list_prepend (
203 result,
204 biji_item_get_pristine (BIJI_ITEM (l->data), scale));
205 }
206
207 return result;
208 }
209
210 static cairo_surface_t *
biji_notebook_get_icon(BijiItem * coll,gint scale)211 biji_notebook_get_icon (BijiItem *coll,
212 gint scale)
213 {
214 BijiNotebook *self = BIJI_NOTEBOOK (coll);
215 GList *pix;
216
217 if (!self->icon)
218 {
219 pix = get_collected_pix (self, scale);
220 self->icon = biji_create_notebook_icon (BIJI_ICON_WIDTH, scale, pix);
221 g_list_free (pix);
222 }
223
224 return self->icon;
225 }
226
227
228 static cairo_surface_t *
biji_notebook_get_emblem(BijiItem * coll,gint scale)229 biji_notebook_get_emblem (BijiItem *coll,
230 gint scale)
231 {
232 BijiNotebook *self = BIJI_NOTEBOOK (coll);
233 GList *pix;
234
235 if (!self->emblem)
236 {
237 pix = get_collected_pix (self, scale);
238 self->emblem = biji_create_notebook_icon (BIJI_EMBLEM_WIDTH, scale, pix);
239 g_list_free (pix);
240 }
241
242 return self->emblem;
243 }
244
245
246
247 static gint64
biji_notebook_get_mtime(BijiItem * coll)248 biji_notebook_get_mtime (BijiItem *coll)
249 {
250 BijiNotebook *self;
251
252 g_return_val_if_fail (BIJI_IS_NOTEBOOK (coll), 0);
253 self = BIJI_NOTEBOOK (coll);
254
255 return self->mtime;
256 }
257
258
259 /* As of today, notebooks are only local
260 * We'll need to override this */
261
262 static const gchar *
biji_notebook_get_place(BijiItem * coll)263 biji_notebook_get_place (BijiItem *coll)
264 {
265 return _("Local");
266 }
267
268
269 static gboolean
biji_notebook_trash(BijiItem * item)270 biji_notebook_trash (BijiItem *item)
271 {
272 BijiNotebook *self;
273 g_return_val_if_fail (BIJI_IS_NOTEBOOK (item), FALSE);
274
275 self = BIJI_NOTEBOOK (item);
276 biji_remove_notebook_from_tracker (biji_item_get_manager (item), self->urn);
277
278 return TRUE;
279 }
280
281 static gboolean
biji_notebook_delete(BijiItem * item)282 biji_notebook_delete (BijiItem *item)
283 {
284 g_return_val_if_fail (BIJI_IS_NOTEBOOK (item), FALSE);
285
286 g_warning ("Notebooks delete is not yet implemented");
287 return FALSE;
288 }
289
290 static gboolean
biji_notebook_restore(BijiItem * item,gchar ** old_uuid)291 biji_notebook_restore (BijiItem *item,
292 gchar **old_uuid)
293 {
294 g_warning ("Notebooks restore is not yet implemented");
295 return FALSE;
296 }
297
298
299 static gboolean
biji_notebook_has_notebook(BijiItem * item,gchar * notebook)300 biji_notebook_has_notebook (BijiItem *item, gchar *notebook)
301 {
302 //todo
303 return FALSE;
304 }
305
306
307 static gboolean
biji_notebook_add_notebook(BijiItem * item,BijiItem * coll,gchar * title)308 biji_notebook_add_notebook (BijiItem *item, BijiItem *coll, gchar *title)
309 {
310 g_warning ("biji notebook add notebook is not implemented.");
311 return FALSE;
312 }
313
314
315 static gboolean
biji_notebook_remove_notebook(BijiItem * item,BijiItem * notebook)316 biji_notebook_remove_notebook (BijiItem *item, BijiItem *notebook)
317 {
318 g_warning ("biji notebook remove notebook is not implemented.");
319 return FALSE;
320 }
321
322
323 static void
biji_notebook_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)324 biji_notebook_set_property (GObject *object,
325 guint property_id,
326 const GValue *value,
327 GParamSpec *pspec)
328 {
329 BijiNotebook *self = BIJI_NOTEBOOK (object);
330
331
332 switch (property_id)
333 {
334 case PROP_URN:
335 self->urn = g_strdup (g_value_get_string (value));
336 break;
337 case PROP_NAME:
338 self->name = g_strdup (g_value_get_string (value));
339 break;
340 case PROP_MTIME:
341 self->mtime = g_value_get_int64 (value);
342 break;
343 default:
344 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
345 break;
346 }
347 }
348
349
350 static void
biji_notebook_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)351 biji_notebook_get_property (GObject *object,
352 guint property_id,
353 GValue *value,
354 GParamSpec *pspec)
355 {
356 BijiNotebook *self = BIJI_NOTEBOOK (object);
357
358 switch (property_id)
359 {
360 case PROP_URN:
361 g_value_set_string (value, self->urn);
362 break;
363 case PROP_NAME:
364 g_value_set_string (value, self->name);
365 break;
366 case PROP_MTIME:
367 g_value_set_int64 (value, self->mtime);
368 break;
369 default:
370 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
371 break;
372 }
373 }
374
375
376 static void
on_collected_item_change(BijiNotebook * self)377 on_collected_item_change (BijiNotebook *self)
378 {
379 BijiManager *manager;
380 GList *l;
381
382 manager = biji_item_get_manager (BIJI_ITEM (self));
383
384 /* Diconnected any handler */
385 for (l = self->collected_items; l != NULL; l = l->next)
386 {
387 g_signal_handlers_disconnect_by_func (l->data, on_collected_item_change, self);
388 }
389
390 /* Then re-process the whole stuff */
391 biji_get_items_with_notebook_async (manager,
392 self->name,
393 biji_notebook_update_collected,
394 self);
395 }
396
397 /* For convenience, items are retrieved async.
398 * Thus use a signal once icon & emblem updated.*/
399 static void
biji_notebook_update_collected(GList * result,gpointer user_data)400 biji_notebook_update_collected (GList *result,
401 gpointer user_data)
402 {
403 BijiNotebook *self = user_data;
404 GList *l;
405
406 g_clear_pointer (&self->collected_items, g_list_free);
407 g_clear_pointer (&self->icon, cairo_surface_destroy);
408 g_clear_pointer (&self->emblem, cairo_surface_destroy);
409
410 self->collected_items = result;
411
412 /* Connect */
413 for (l = self->collected_items; l != NULL; l = l->next)
414 {
415 g_signal_connect_swapped (l->data, "color-changed",
416 G_CALLBACK (on_collected_item_change), self);
417
418 g_signal_connect_swapped (l->data, "trashed",
419 G_CALLBACK (on_collected_item_change), self);
420 }
421
422 g_signal_emit (self, biji_notebooks_signals[NOTEBOOK_ICON_UPDATED], 0);
423 }
424
425 void
biji_notebook_refresh(BijiNotebook * self)426 biji_notebook_refresh (BijiNotebook *self)
427 {
428 on_collected_item_change (self);
429 }
430
431 static void
biji_notebook_constructed(GObject * obj)432 biji_notebook_constructed (GObject *obj)
433 {
434 BijiNotebook *self = BIJI_NOTEBOOK (obj);
435 BijiManager *manager;
436
437
438 manager = biji_item_get_manager (BIJI_ITEM (obj));
439
440 biji_get_items_with_notebook_async (manager,
441 self->name,
442 biji_notebook_update_collected,
443 self);
444 }
445
446 static gboolean
say_no(BijiItem * item)447 say_no (BijiItem *item)
448 {
449 return FALSE;
450 }
451
452
453 static void
biji_notebook_class_init(BijiNotebookClass * klass)454 biji_notebook_class_init (BijiNotebookClass *klass)
455 {
456 GObjectClass *g_object_class;
457 BijiItemClass* item_class;
458
459 g_object_class = G_OBJECT_CLASS (klass);
460 item_class = BIJI_ITEM_CLASS (klass);
461
462 g_object_class->constructed = biji_notebook_constructed;
463 g_object_class->finalize = biji_notebook_finalize;
464 g_object_class->set_property = biji_notebook_set_property;
465 g_object_class->get_property = biji_notebook_get_property;
466
467 properties[PROP_URN] =
468 g_param_spec_string ("urn",
469 "Collection URN",
470 "Collection URN as in Tracker",
471 NULL,
472 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
473
474 properties[PROP_NAME] =
475 g_param_spec_string ("name",
476 "Collection label",
477 "The tag as a string",
478 NULL,
479 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
480
481 properties[PROP_MTIME] =
482 g_param_spec_int64 ("mtime",
483 "Modification time",
484 "Last modified time",
485 G_MININT64, G_MAXINT64, 0,
486 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
487
488 g_object_class_install_properties (g_object_class, BIJI_NOTEBOOK_PROPERTIES, properties);
489
490 biji_notebooks_signals[NOTEBOOK_ICON_UPDATED] =
491 g_signal_new ("icon-changed",
492 G_OBJECT_CLASS_TYPE (klass),
493 G_SIGNAL_RUN_LAST,
494 0,
495 NULL,
496 NULL,
497 g_cclosure_marshal_VOID__VOID,
498 G_TYPE_NONE,
499 0);
500
501 /* Interface */
502 item_class->get_title = biji_notebook_get_title;
503 item_class->get_uuid = biji_notebook_get_uuid;
504 item_class->get_icon = biji_notebook_get_icon;
505 item_class->get_emblem = biji_notebook_get_emblem;
506 item_class->get_pristine = biji_notebook_get_emblem;
507 item_class->get_mtime = biji_notebook_get_mtime;
508 item_class->get_place = biji_notebook_get_place;
509 item_class->has_color = say_no;
510 item_class->trash = biji_notebook_trash;
511 item_class->delete = biji_notebook_delete;
512 item_class->restore = biji_notebook_restore;
513 item_class->is_collectable = say_no;
514 item_class->has_notebook = biji_notebook_has_notebook;
515 item_class->add_notebook = biji_notebook_add_notebook;
516 item_class->remove_notebook = biji_notebook_remove_notebook;
517 }
518
519
520 static void
biji_notebook_finalize(GObject * object)521 biji_notebook_finalize (GObject *object)
522 {
523 BijiNotebook *self;
524 g_return_if_fail (BIJI_IS_NOTEBOOK (object));
525
526 self = BIJI_NOTEBOOK (object);
527 g_free (self->name);
528 g_free (self->urn);
529
530 G_OBJECT_CLASS (biji_notebook_parent_class)->finalize (object);
531 }
532
533
534 static void
biji_notebook_init(BijiNotebook * self)535 biji_notebook_init (BijiNotebook *self)
536 {
537 }
538
539
540 BijiNotebook *
biji_notebook_new(GObject * manager,gchar * urn,gchar * name,gint64 mtime)541 biji_notebook_new (GObject *manager, gchar *urn, gchar *name, gint64 mtime)
542 {
543 return g_object_new (BIJI_TYPE_NOTEBOOK,
544 "manager", manager,
545 "name", name,
546 "urn", urn,
547 "mtime", mtime,
548 NULL);
549 }
550