1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License along
13 * with this program; if not, write to the Free Software Foundation, Inc.,
14 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 */
16
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include "eog-file-chooser.h"
22 #include "eog-pixbuf-util.h"
23
24 #include <stdlib.h>
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <gio/gio.h>
29 #include <gtk/gtk.h>
30
31 /* We must define GNOME_DESKTOP_USE_UNSTABLE_API to be able
32 to use GnomeDesktopThumbnail */
33 #ifndef GNOME_DESKTOP_USE_UNSTABLE_API
34 #define GNOME_DESKTOP_USE_UNSTABLE_API
35 #endif
36 #include <libgnome-desktop/gnome-desktop-thumbnail.h>
37
38 static char *last_dir[] = { NULL, NULL, NULL, NULL };
39
40 #define FILE_FORMAT_KEY "file-format"
41
42 struct _EogFileChooserPrivate
43 {
44 GnomeDesktopThumbnailFactory *thumb_factory;
45
46 GtkWidget *image;
47 GtkWidget *size_label;
48 GtkWidget *dim_label;
49 GtkWidget *creator_label;
50 };
51
G_DEFINE_TYPE_WITH_PRIVATE(EogFileChooser,eog_file_chooser,GTK_TYPE_FILE_CHOOSER_DIALOG)52 G_DEFINE_TYPE_WITH_PRIVATE (EogFileChooser, eog_file_chooser, GTK_TYPE_FILE_CHOOSER_DIALOG)
53
54 static void
55 eog_file_chooser_finalize (GObject *object)
56 {
57 EogFileChooserPrivate *priv;
58
59 priv = EOG_FILE_CHOOSER (object)->priv;
60
61 if (priv->thumb_factory != NULL)
62 g_object_unref (priv->thumb_factory);
63
64 (* G_OBJECT_CLASS (eog_file_chooser_parent_class)->finalize) (object);
65 }
66
67 static void
eog_file_chooser_class_init(EogFileChooserClass * klass)68 eog_file_chooser_class_init (EogFileChooserClass *klass)
69 {
70 GObjectClass *object_class = (GObjectClass *) klass;
71
72 object_class->finalize = eog_file_chooser_finalize;
73 }
74
75 static void
eog_file_chooser_init(EogFileChooser * chooser)76 eog_file_chooser_init (EogFileChooser *chooser)
77 {
78 chooser->priv = eog_file_chooser_get_instance_private (chooser);
79 }
80
81 static void
response_cb(GtkDialog * dlg,gint id,gpointer data)82 response_cb (GtkDialog *dlg, gint id, gpointer data)
83 {
84 char *dir;
85 GtkFileChooserAction action;
86
87 if (id == GTK_RESPONSE_OK) {
88 dir = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dlg));
89 action = gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dlg));
90
91 if (last_dir [action] != NULL)
92 g_free (last_dir [action]);
93
94 last_dir [action] = dir;
95 }
96 }
97
98 static void
save_response_cb(GtkDialog * dlg,gint id,gpointer data)99 save_response_cb (GtkDialog *dlg, gint id, gpointer data)
100 {
101 GFile *file;
102 GdkPixbufFormat *format;
103
104 if (id != GTK_RESPONSE_OK)
105 return;
106
107 file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dlg));
108 format = eog_pixbuf_get_format (file);
109 g_object_unref (file);
110
111 if (!format || !gdk_pixbuf_format_is_writable (format)) {
112 GtkWidget *msg_dialog;
113
114 msg_dialog = gtk_message_dialog_new (
115 GTK_WINDOW (dlg),
116 GTK_DIALOG_MODAL,
117 GTK_MESSAGE_ERROR,
118 GTK_BUTTONS_OK,
119 _("File format is unknown or unsupported"));
120
121 gtk_message_dialog_format_secondary_text (
122 GTK_MESSAGE_DIALOG (msg_dialog),
123 "%s\n%s",
124 _("Image Viewer could not determine a supported writable file format based on the filename."),
125 _("Please try a different file extension like .png or .jpg."));
126
127 gtk_dialog_run (GTK_DIALOG (msg_dialog));
128 gtk_widget_destroy (msg_dialog);
129
130 g_signal_stop_emission_by_name (dlg, "response");
131 } else {
132 response_cb (dlg, id, data);
133 }
134 }
135
136 static GSList*
_eog_file_chooser_prepare_save_file_filter(GtkFileFilter * all_img_filter)137 _eog_file_chooser_prepare_save_file_filter (GtkFileFilter *all_img_filter)
138 {
139 GSList *filters = NULL;
140 GSList *formats = NULL;
141 GSList *it;
142 GtkFileFilter *filter;
143 gchar **mime_types, **pattern, *tmp;
144 int i;
145
146 formats = eog_pixbuf_get_savable_formats ();
147
148 /* Image filters */
149 for (it = formats; it != NULL; it = it->next) {
150 char *filter_name;
151 char *description, *extension;
152 GdkPixbufFormat *format;
153 filter = gtk_file_filter_new ();
154
155 format = (GdkPixbufFormat*) it->data;
156 description = gdk_pixbuf_format_get_description (format);
157 extension = gdk_pixbuf_format_get_name (format);
158
159 /* Filter name: First description then file extension, eg. "The PNG-Format (*.png)".*/
160 filter_name = g_strdup_printf (_("%s (*.%s)"), description, extension);
161 g_free (description);
162 g_free (extension);
163
164 gtk_file_filter_set_name (filter, filter_name);
165 g_free (filter_name);
166
167 mime_types = gdk_pixbuf_format_get_mime_types ((GdkPixbufFormat *) it->data);
168 for (i = 0; mime_types[i] != NULL; i++) {
169 gtk_file_filter_add_mime_type (filter, mime_types[i]);
170 gtk_file_filter_add_mime_type (all_img_filter, mime_types[i]);
171 }
172 g_strfreev (mime_types);
173
174 pattern = gdk_pixbuf_format_get_extensions ((GdkPixbufFormat *) it->data);
175 for (i = 0; pattern[i] != NULL; i++) {
176 tmp = g_strconcat ("*.", pattern[i], NULL);
177 gtk_file_filter_add_pattern (filter, tmp);
178 gtk_file_filter_add_pattern (all_img_filter, tmp);
179 g_free (tmp);
180 }
181 g_strfreev (pattern);
182
183 /* attach GdkPixbufFormat to filter, see also
184 * eog_file_chooser_get_format. */
185 g_object_set_data (G_OBJECT (filter),
186 FILE_FORMAT_KEY,
187 format);
188
189 filters = g_slist_prepend (filters, filter);
190 }
191 g_slist_free (formats);
192
193 return filters;
194 }
195 static void
eog_file_chooser_add_filter(EogFileChooser * chooser)196 eog_file_chooser_add_filter (EogFileChooser *chooser)
197 {
198 GSList *it;
199 GtkFileFilter *all_file_filter;
200 GtkFileFilter *all_img_filter;
201 GSList *filters = NULL;
202 GtkFileChooserAction action;
203
204 action = gtk_file_chooser_get_action (GTK_FILE_CHOOSER (chooser));
205
206 if (action != GTK_FILE_CHOOSER_ACTION_SAVE && action != GTK_FILE_CHOOSER_ACTION_OPEN) {
207 return;
208 }
209
210 /* All Files Filter */
211 all_file_filter = gtk_file_filter_new ();
212 gtk_file_filter_set_name (all_file_filter, _("All files"));
213 gtk_file_filter_add_pattern (all_file_filter, "*");
214
215 /* All Image Filter */
216 all_img_filter = gtk_file_filter_new ();
217 gtk_file_filter_set_name (all_img_filter, _("Supported image files"));
218
219 if (action == GTK_FILE_CHOOSER_ACTION_SAVE) {
220 filters = _eog_file_chooser_prepare_save_file_filter(all_img_filter);
221 }
222 else {
223 gtk_file_filter_add_pixbuf_formats(all_img_filter);
224 }
225
226 /* Add filter to filechooser */
227 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), all_file_filter);
228 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), all_img_filter);
229 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (chooser), all_img_filter);
230
231 for (it = filters; it != NULL; it = it->next) {
232 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), GTK_FILE_FILTER (it->data));
233 }
234 g_slist_free (filters);
235 }
236
237 static void
set_preview_label(GtkWidget * label,const char * str)238 set_preview_label (GtkWidget *label, const char *str)
239 {
240 if (str == NULL) {
241 gtk_widget_hide (GTK_WIDGET (label));
242 }
243 else {
244 gtk_label_set_text (GTK_LABEL (label), str);
245 gtk_widget_show (GTK_WIDGET (label));
246 }
247 }
248
249 /* Sets the pixbuf as preview thumbnail and tries to read and display
250 * further information according to the thumbnail spec.
251 */
252 static void
set_preview_pixbuf(EogFileChooser * chooser,GdkPixbuf * pixbuf,goffset size)253 set_preview_pixbuf (EogFileChooser *chooser, GdkPixbuf *pixbuf, goffset size)
254 {
255 EogFileChooserPrivate *priv;
256 int bytes;
257 int pixels;
258 const char *bytes_str;
259 const char *width;
260 const char *height;
261 const char *creator = NULL;
262 char *size_str = NULL;
263 char *dim_str = NULL;
264
265 g_return_if_fail (EOG_IS_FILE_CHOOSER (chooser));
266
267 priv = chooser->priv;
268
269 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
270
271 if (pixbuf != NULL) {
272 /* try to read file size */
273 bytes_str = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Size");
274 if (bytes_str != NULL) {
275 bytes = atoi (bytes_str);
276 size_str = g_format_size (bytes);
277 }
278 else {
279 size_str = g_format_size (size);
280 }
281
282 /* try to read image dimensions */
283 width = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Width");
284 height = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::Image::Height");
285
286 if ((width != NULL) && (height != NULL)) {
287 pixels = atoi (height);
288 /* Pixel size of image: width x height in pixel */
289 dim_str = g_strdup_printf ("%s x %s %s", width, height, ngettext ("pixel", "pixels", pixels));
290 }
291
292 #if 0
293 /* Not sure, if this is really useful, therefore its commented out for now. */
294
295 /* try to read creator of the thumbnail */
296 creator = gdk_pixbuf_get_option (pixbuf, "tEXt::Software");
297
298 /* stupid workaround to display nicer string if the
299 * thumbnail is created through the gnome libraries.
300 */
301 if (g_ascii_strcasecmp (creator, "Gnome::ThumbnailFactory") == 0) {
302 creator = "GNOME Libs";
303 }
304 #endif
305 }
306
307 set_preview_label (priv->size_label, size_str);
308 set_preview_label (priv->dim_label, dim_str);
309 set_preview_label (priv->creator_label, creator);
310
311 if (size_str != NULL) {
312 g_free (size_str);
313 }
314
315 if (dim_str != NULL) {
316 g_free (dim_str);
317 }
318 }
319
320 static void
update_preview_cb(GtkFileChooser * file_chooser,gpointer data)321 update_preview_cb (GtkFileChooser *file_chooser, gpointer data)
322 {
323 EogFileChooserPrivate *priv;
324 char *uri;
325 char *thumb_path = NULL;
326 GFile *file;
327 GFileInfo *file_info;
328 GdkPixbuf *pixbuf = NULL;
329 gboolean have_preview = FALSE;
330
331 priv = EOG_FILE_CHOOSER (file_chooser)->priv;
332
333 uri = gtk_file_chooser_get_preview_uri (file_chooser);
334 if (uri == NULL) {
335 gtk_file_chooser_set_preview_widget_active (file_chooser, FALSE);
336 return;
337 }
338
339 file = g_file_new_for_uri (uri);
340 file_info = g_file_query_info (file,
341 G_FILE_ATTRIBUTE_TIME_MODIFIED ","
342 G_FILE_ATTRIBUTE_STANDARD_TYPE ","
343 G_FILE_ATTRIBUTE_STANDARD_SIZE ","
344 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
345 0, NULL, NULL);
346 g_object_unref (file);
347
348 if ((file_info != NULL) && (priv->thumb_factory != NULL)
349 && g_file_info_get_file_type (file_info) != G_FILE_TYPE_SPECIAL) {
350 guint64 mtime;
351
352 mtime = g_file_info_get_attribute_uint64 (file_info,
353 G_FILE_ATTRIBUTE_TIME_MODIFIED);
354 thumb_path = gnome_desktop_thumbnail_factory_lookup (priv->thumb_factory, uri, mtime);
355
356 if (thumb_path != NULL && g_file_test (thumb_path, G_FILE_TEST_EXISTS)) {
357 /* try to load and display preview thumbnail */
358 pixbuf = gdk_pixbuf_new_from_file (thumb_path, NULL);
359 } else if (g_file_info_get_size (file_info) <= 100000) {
360 /* read files smaller than 100kb directly */
361
362 gchar *mime_type = g_content_type_get_mime_type (
363 g_file_info_get_content_type (file_info));
364
365
366 if (G_LIKELY (mime_type)) {
367 gboolean can_thumbnail, has_failed;
368
369 can_thumbnail = gnome_desktop_thumbnail_factory_can_thumbnail (
370 priv->thumb_factory,
371 uri, mime_type, mtime);
372 has_failed = gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (
373 priv->thumb_factory,
374 uri, mtime);
375
376 if (G_LIKELY (can_thumbnail && !has_failed))
377 pixbuf = gnome_desktop_thumbnail_factory_generate_thumbnail (
378 priv->thumb_factory, uri, mime_type);
379
380 g_free (mime_type);
381 }
382 }
383
384 if (pixbuf != NULL) {
385 have_preview = TRUE;
386
387 set_preview_pixbuf (EOG_FILE_CHOOSER (file_chooser), pixbuf,
388 g_file_info_get_size (file_info));
389
390 if (pixbuf != NULL) {
391 g_object_unref (pixbuf);
392 }
393 }
394 }
395
396 if (thumb_path != NULL) {
397 g_free (thumb_path);
398 }
399
400 g_free (uri);
401 g_object_unref (file_info);
402
403 gtk_file_chooser_set_preview_widget_active (file_chooser, have_preview);
404 }
405
406 static void
eog_file_chooser_add_preview(GtkWidget * widget)407 eog_file_chooser_add_preview (GtkWidget *widget)
408 {
409 EogFileChooserPrivate *priv;
410 GtkWidget *vbox;
411
412 priv = EOG_FILE_CHOOSER (widget)->priv;
413
414 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
415 gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
416
417 priv->image = gtk_image_new ();
418 /* 128x128 is maximum size of thumbnails */
419 gtk_widget_set_size_request (priv->image, 128,128);
420
421 priv->dim_label = gtk_label_new (NULL);
422 priv->size_label = gtk_label_new (NULL);
423 priv->creator_label = gtk_label_new (NULL);
424
425 gtk_box_pack_start (GTK_BOX (vbox), priv->image, FALSE, TRUE, 0);
426 gtk_box_pack_start (GTK_BOX (vbox), priv->dim_label, FALSE, TRUE, 0);
427 gtk_box_pack_start (GTK_BOX (vbox), priv->size_label, FALSE, TRUE, 0);
428 gtk_box_pack_start (GTK_BOX (vbox), priv->creator_label, FALSE, TRUE, 0);
429
430 gtk_widget_show_all (vbox);
431
432 gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (widget), vbox);
433 gtk_file_chooser_set_preview_widget_active (GTK_FILE_CHOOSER (widget), FALSE);
434
435 priv->thumb_factory = gnome_desktop_thumbnail_factory_new (GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL);
436
437 g_signal_connect (widget, "update-preview",
438 G_CALLBACK (update_preview_cb), NULL);
439 }
440
441 GtkWidget *
eog_file_chooser_new(GtkFileChooserAction action)442 eog_file_chooser_new (GtkFileChooserAction action)
443 {
444 GtkWidget *chooser;
445 gchar *title = NULL;
446
447 chooser = g_object_new (EOG_TYPE_FILE_CHOOSER,
448 "action", action,
449 "select-multiple", (action == GTK_FILE_CHOOSER_ACTION_OPEN),
450 "local-only", FALSE,
451 NULL);
452
453 switch (action) {
454 case GTK_FILE_CHOOSER_ACTION_OPEN:
455 gtk_dialog_add_buttons (GTK_DIALOG (chooser),
456 _("_Cancel"), GTK_RESPONSE_CANCEL,
457 _("_Open"), GTK_RESPONSE_OK,
458 NULL);
459 title = _("Open Image");
460 break;
461
462 case GTK_FILE_CHOOSER_ACTION_SAVE:
463 gtk_dialog_add_buttons (GTK_DIALOG (chooser),
464 _("_Cancel"), GTK_RESPONSE_CANCEL,
465 _("_Save"), GTK_RESPONSE_OK,
466 NULL);
467 title = _("Save Image");
468 break;
469
470 case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
471 gtk_dialog_add_buttons (GTK_DIALOG (chooser),
472 _("_Cancel"), GTK_RESPONSE_CANCEL,
473 _("_Open"), GTK_RESPONSE_OK,
474 NULL);
475 title = _("Open Folder");
476 break;
477
478 default:
479 g_assert_not_reached ();
480 }
481
482 if (action != GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) {
483 eog_file_chooser_add_filter (EOG_FILE_CHOOSER (chooser));
484 eog_file_chooser_add_preview (chooser);
485 }
486
487 if (last_dir[action] != NULL) {
488 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser), last_dir [action]);
489 }
490
491 g_signal_connect (chooser, "response",
492 G_CALLBACK ((action == GTK_FILE_CHOOSER_ACTION_SAVE) ?
493 save_response_cb : response_cb),
494 NULL);
495
496 gtk_window_set_title (GTK_WINDOW (chooser), title);
497 gtk_dialog_set_default_response (GTK_DIALOG (chooser), GTK_RESPONSE_OK);
498
499 gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (chooser), TRUE);
500
501 return chooser;
502 }
503
504 GdkPixbufFormat *
eog_file_chooser_get_format(EogFileChooser * chooser)505 eog_file_chooser_get_format (EogFileChooser *chooser)
506 {
507 GtkFileFilter *filter;
508 GdkPixbufFormat* format;
509
510 g_return_val_if_fail (EOG_IS_FILE_CHOOSER (chooser), NULL);
511
512 filter = gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (chooser));
513 if (filter == NULL)
514 return NULL;
515
516 format = g_object_get_data (G_OBJECT (filter), FILE_FORMAT_KEY);
517
518 return format;
519 }
520