No issues found
1 /*
2 * e-picture-gallery.c
3 *
4 * This program 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 of the License, or (at your option) version 3.
8 *
9 * This program 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 the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 *
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
19 *
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include "e-util/e-icon-factory.h"
27
28 #include "e-picture-gallery.h"
29
30 #define E_PICTURE_GALLERY_GET_PRIVATE(obj) \
31 (G_TYPE_INSTANCE_GET_PRIVATE \
32 ((obj), E_TYPE_PICTURE_GALLERY, EPictureGalleryPrivate))
33
34 struct _EPictureGalleryPrivate {
35 gboolean initialized;
36 gchar *path;
37 GFileMonitor *monitor;
38 };
39
40 enum {
41 PROP_0,
42 PROP_PATH
43 };
44
45 enum {
46 COL_PIXBUF = 0,
47 COL_URI,
48 COL_FILENAME_TEXT
49 };
50
51 G_DEFINE_TYPE (EPictureGallery, e_picture_gallery, GTK_TYPE_ICON_VIEW)
52
53 static gboolean
54 update_file_iter (GtkListStore *list_store,
55 GtkTreeIter *iter,
56 GFile *file,
57 gboolean force_thumbnail_update)
58 {
59 GFileInfo *file_info;
60 gchar *uri;
61 gboolean res = FALSE;
62
63 g_return_val_if_fail (list_store != NULL, FALSE);
64 g_return_val_if_fail (iter != NULL, FALSE);
65 g_return_val_if_fail (file != NULL, FALSE);
66
67 uri = g_file_get_uri (file);
68
69 file_info = g_file_query_info (
70 file,
71 G_FILE_ATTRIBUTE_THUMBNAIL_PATH ","
72 G_FILE_ATTRIBUTE_THUMBNAILING_FAILED ","
73 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
74 G_FILE_ATTRIBUTE_STANDARD_SIZE,
75 G_FILE_QUERY_INFO_NONE,
76 NULL,
77 NULL);
78
79 if (file_info != NULL) {
80 const gchar *existing_thumb = g_file_info_get_attribute_byte_string (file_info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
81 gchar *new_thumb = NULL;
82
83 if (!existing_thumb || force_thumbnail_update) {
84 gchar *filename;
85
86 filename = g_file_get_path (file);
87 if (filename) {
88 new_thumb = e_icon_factory_create_thumbnail (filename);
89 if (new_thumb)
90 existing_thumb = new_thumb;
91 g_free (filename);
92 }
93 }
94
95 if (existing_thumb && !g_file_info_get_attribute_boolean (file_info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED)) {
96 GdkPixbuf * pixbuf;
97
98 pixbuf = gdk_pixbuf_new_from_file (existing_thumb, NULL);
99
100 if (pixbuf) {
101 const gchar *filename;
102 gchar *filename_text = NULL;
103 guint64 filesize;
104
105 filename = g_file_info_get_attribute_string (file_info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME);
106 if (filename) {
107 filesize = g_file_info_get_attribute_uint64 (file_info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
108 if (filesize) {
109 gchar *tmp = g_format_size_for_display ((goffset) filesize);
110 filename_text = g_strdup_printf ("%s (%s)", filename, tmp);
111 g_free (tmp);
112 }
113
114 res = TRUE;
115 gtk_list_store_set (
116 list_store, iter,
117 COL_PIXBUF, pixbuf,
118 COL_URI, uri,
119 COL_FILENAME_TEXT, filename_text ? filename_text : filename,
120 -1);
121 }
122
123 g_object_unref (pixbuf);
124 g_free (filename_text);
125 }
126 }
127
128 g_free (new_thumb);
129 }
130
131 g_free (uri);
132
133 return res;
134 }
135
136 static void
137 add_file (GtkListStore *list_store,
138 GFile *file)
139 {
140 GtkTreeIter iter;
141
142 g_return_if_fail (list_store != NULL);
143 g_return_if_fail (file != NULL);
144
145 gtk_list_store_append (list_store, &iter);
146 if (!update_file_iter (list_store, &iter, file, FALSE))
147 gtk_list_store_remove (list_store, &iter);
148 }
149
150 static gboolean
151 find_file_uri (GtkListStore *list_store,
152 const gchar *uri,
153 GtkTreeIter *iter)
154 {
155 GtkTreeModel *model;
156
157 g_return_val_if_fail (list_store != NULL, FALSE);
158 g_return_val_if_fail (uri != NULL, FALSE);
159 g_return_val_if_fail (iter != NULL, FALSE);
160
161 model = GTK_TREE_MODEL (list_store);
162 g_return_val_if_fail (model != NULL, FALSE);
163
164 if (!gtk_tree_model_get_iter_first (model, iter))
165 return FALSE;
166
167 do {
168 gchar *iter_uri = NULL;
169
170 gtk_tree_model_get (
171 model, iter,
172 COL_URI, &iter_uri,
173 -1);
174
175 if (iter_uri && g_ascii_strcasecmp (uri, iter_uri) == 0) {
176 g_free (iter_uri);
177 return TRUE;
178 }
179
180 g_free (iter_uri);
181 } while (gtk_tree_model_iter_next (model, iter));
182
183 return FALSE;
184 }
185
186 static void
187 picture_gallery_dir_changed_cb (GFileMonitor *monitor,
188 GFile *file,
189 GFile *other_file,
190 GFileMonitorEvent event_type,
191 EPictureGallery *gallery)
192 {
193 gchar *uri;
194 GtkListStore *list_store;
195 GtkTreeIter iter;
196
197 g_return_if_fail (file != NULL);
198
199 list_store = GTK_LIST_STORE (gtk_icon_view_get_model (GTK_ICON_VIEW (gallery)));
200 g_return_if_fail (list_store != NULL);
201
202 uri = g_file_get_uri (file);
203 if (!uri)
204 return;
205
206 switch (event_type) {
207 case G_FILE_MONITOR_EVENT_CREATED:
208 if (find_file_uri (list_store, uri, &iter)) {
209 if (!update_file_iter (list_store, &iter, file, TRUE))
210 gtk_list_store_remove (list_store, &iter);
211 } else {
212 add_file (list_store, file);
213 }
214 break;
215 case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
216 if (find_file_uri (list_store, uri, &iter)) {
217 if (!update_file_iter (list_store, &iter, file, TRUE))
218 gtk_list_store_remove (list_store, &iter);
219 }
220 break;
221 case G_FILE_MONITOR_EVENT_DELETED:
222 if (find_file_uri (list_store, uri, &iter))
223 gtk_list_store_remove (list_store, &iter);
224 break;
225 default:
226 break;
227 }
228
229 g_free (uri);
230 }
231
232 static gboolean
233 picture_gallery_start_loading_cb (EPictureGallery *gallery)
234 {
235 GtkIconView *icon_view;
236 GtkListStore *list_store;
237 GDir *dir;
238 const gchar *dirname;
239
240 icon_view = GTK_ICON_VIEW (gallery);
241 list_store = GTK_LIST_STORE (gtk_icon_view_get_model (icon_view));
242 g_return_val_if_fail (list_store != NULL, FALSE);
243
244 dirname = e_picture_gallery_get_path (gallery);
245 if (!dirname)
246 return FALSE;
247
248 dir = g_dir_open (dirname, 0, NULL);
249 if (dir) {
250 GFile *file;
251 const gchar *basename;
252
253 while ((basename = g_dir_read_name (dir)) != NULL) {
254 gchar *filename;
255
256 filename = g_build_filename (dirname, basename, NULL);
257 file = g_file_new_for_path (filename);
258
259 add_file (list_store, file);
260
261 g_free (filename);
262 g_object_unref (file);
263 }
264
265 g_dir_close (dir);
266
267 file = g_file_new_for_path (dirname);
268 gallery->priv->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
269 g_object_unref (file);
270
271 if (gallery->priv->monitor)
272 g_signal_connect (
273 gallery->priv->monitor, "changed",
274 G_CALLBACK (picture_gallery_dir_changed_cb),
275 gallery);
276 }
277
278 g_object_unref (icon_view);
279
280 return FALSE;
281 }
282
283 const gchar *
284 e_picture_gallery_get_path (EPictureGallery *gallery)
285 {
286 g_return_val_if_fail (gallery != NULL, NULL);
287 g_return_val_if_fail (E_IS_PICTURE_GALLERY (gallery), NULL);
288 g_return_val_if_fail (gallery->priv != NULL, NULL);
289
290 return gallery->priv->path;
291 }
292
293 static void
294 picture_gallery_set_path (EPictureGallery *gallery,
295 const gchar *path)
296 {
297 g_return_if_fail (E_IS_PICTURE_GALLERY (gallery));
298 g_return_if_fail (gallery->priv != NULL);
299
300 g_free (gallery->priv->path);
301
302 if (!path || !*path || !g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
303 gallery->priv->path = g_strdup (g_get_user_special_dir (G_USER_DIRECTORY_PICTURES));
304 else
305 gallery->priv->path = g_strdup (path);
306 }
307
308 static void
309 picture_gallery_get_property (GObject *object,
310 guint property_id,
311 GValue *value,
312 GParamSpec *pspec)
313 {
314 switch (property_id) {
315 case PROP_PATH:
316 g_value_set_string (value, e_picture_gallery_get_path (E_PICTURE_GALLERY (object)));
317 return;
318 }
319
320 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
321 }
322
323 static void
324 picture_gallery_set_property (GObject *object,
325 guint property_id,
326 const GValue *value,
327 GParamSpec *pspec)
328 {
329 switch (property_id) {
330 case PROP_PATH:
331 picture_gallery_set_path (E_PICTURE_GALLERY (object), g_value_get_string (value));
332 return;
333 }
334
335 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
336 }
337
338 static void
339 visible_cb (EPictureGallery *gallery)
340 {
341 if (!gallery->priv->initialized && gtk_widget_get_visible (GTK_WIDGET (gallery))) {
342 gallery->priv->initialized = TRUE;
343
344 g_idle_add ((GSourceFunc) picture_gallery_start_loading_cb, gallery);
345 }
346 }
347
348 static void
349 picture_gallery_constructed (GObject *object)
350 {
351 GtkIconView *icon_view;
352 GtkListStore *list_store;
353 GtkTargetEntry *targets;
354 GtkTargetList *list;
355 gint n_targets;
356
357 /* Chain up to parent's constructed() method. */
358 G_OBJECT_CLASS (e_picture_gallery_parent_class)->constructed (object);
359
360 icon_view = GTK_ICON_VIEW (object);
361
362 list_store = gtk_list_store_new (3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
363 gtk_icon_view_set_model (icon_view, GTK_TREE_MODEL (list_store));
364 g_object_unref (list_store);
365
366 gtk_icon_view_set_pixbuf_column (icon_view, COL_PIXBUF);
367 gtk_icon_view_set_text_column (icon_view, COL_FILENAME_TEXT);
368 gtk_icon_view_set_tooltip_column (icon_view, -1);
369
370 list = gtk_target_list_new (NULL, 0);
371 gtk_target_list_add_uri_targets (list, 0);
372 targets = gtk_target_table_new_from_list (list, &n_targets);
373
374 gtk_icon_view_enable_model_drag_source (
375 icon_view, GDK_BUTTON1_MASK,
376 targets, n_targets, GDK_ACTION_COPY);
377
378 gtk_target_table_free (targets, n_targets);
379 gtk_target_list_unref (list);
380
381 g_signal_connect (object, "notify::visible", G_CALLBACK (visible_cb), NULL);
382 }
383
384 static void
385 picture_gallery_dispose (GObject *object)
386 {
387 EPictureGallery *gallery;
388
389 gallery = E_PICTURE_GALLERY (object);
390
391 if (gallery->priv->monitor) {
392 g_object_unref (gallery->priv->monitor);
393 gallery->priv->monitor = NULL;
394 }
395
396 /* Chain up to parent's dispose() method. */
397 G_OBJECT_CLASS (e_picture_gallery_parent_class)->dispose (object);
398 }
399
400 static void
401 e_picture_gallery_class_init (EPictureGalleryClass *class)
402 {
403 GObjectClass *object_class;
404
405 g_type_class_add_private (class, sizeof (EPictureGalleryPrivate));
406
407 object_class = G_OBJECT_CLASS (class);
408 object_class->get_property = picture_gallery_get_property;
409 object_class->set_property = picture_gallery_set_property;
410 object_class->constructed = picture_gallery_constructed;
411 object_class->dispose = picture_gallery_dispose;
412
413 g_object_class_install_property (
414 object_class,
415 PROP_PATH,
416 g_param_spec_string (
417 "path",
418 "Gallery path",
419 NULL,
420 NULL,
421 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
422 }
423
424 static void
425 e_picture_gallery_init (EPictureGallery *gallery)
426 {
427 gallery->priv = E_PICTURE_GALLERY_GET_PRIVATE (gallery);
428 gallery->priv->initialized = FALSE;
429 gallery->priv->monitor = NULL;
430 picture_gallery_set_path (gallery, NULL);
431 }
432
433 GtkWidget *
434 e_picture_gallery_new (const gchar *path)
435 {
436 return g_object_new (E_TYPE_PICTURE_GALLERY, "path", path, NULL);
437 }