1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2009 Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23 #include <glib/gi18n.h>
24 #include "glib-utils.h"
25 #include "gth-image-viewer.h"
26 #include "gth-metadata-provider.h"
27 #include "gth-overwrite-dialog.h"
28 #include "gth-thumb-loader.h"
29 #include "gtk-utils.h"
30
31
32 #define ICON_SIZE 128
33 #define PREVIEW_SIZE 256
34
35
36 struct _GthOverwriteDialogPrivate {
37 GtkBuilder *builder;
38 GFile *source;
39 GthImage *source_image;
40 GFile *destination;
41 GtkWidget *old_image_viewer;
42 GtkWidget *new_image_viewer;
43 GthFileData *source_data;
44 GthFileData *destination_data;
45 GthThumbLoader *old_image_loader;
46 GthThumbLoader *new_image_loader;
47 };
48
49
G_DEFINE_TYPE_WITH_CODE(GthOverwriteDialog,gth_overwrite_dialog,GTK_TYPE_DIALOG,G_ADD_PRIVATE (GthOverwriteDialog))50 G_DEFINE_TYPE_WITH_CODE (GthOverwriteDialog,
51 gth_overwrite_dialog,
52 GTK_TYPE_DIALOG,
53 G_ADD_PRIVATE (GthOverwriteDialog))
54
55
56 static void
57 gth_overwrite_dialog_finalize (GObject *object)
58 {
59 GthOverwriteDialog *dialog;
60
61 dialog = GTH_OVERWRITE_DIALOG (object);
62
63 _g_object_unref (dialog->priv->source_data);
64 _g_object_unref (dialog->priv->destination_data);
65 g_object_unref (dialog->priv->builder);
66 _g_object_unref (dialog->priv->source);
67 _g_object_unref (dialog->priv->source_image);
68 g_object_unref (dialog->priv->destination);
69 _g_object_unref (dialog->priv->old_image_loader);
70 _g_object_unref (dialog->priv->new_image_loader);
71
72 G_OBJECT_CLASS (gth_overwrite_dialog_parent_class)->finalize (object);
73 }
74
75
76 static void
gth_overwrite_dialog_class_init(GthOverwriteDialogClass * klass)77 gth_overwrite_dialog_class_init (GthOverwriteDialogClass *klass)
78 {
79 G_OBJECT_CLASS (klass)->finalize = gth_overwrite_dialog_finalize;
80 }
81
82
83 static void
gth_overwrite_dialog_init(GthOverwriteDialog * self)84 gth_overwrite_dialog_init (GthOverwriteDialog *self)
85 {
86 self->priv = gth_overwrite_dialog_get_instance_private (self);
87 self->priv->source_data = NULL;
88 self->priv->destination_data = NULL;
89 }
90
91
92 typedef struct {
93 GtkWidget *widget;
94 GCancellable *cancellable;
95 guint destroy_id;
96 } LoadData;
97
98
99 static void
load_data_widget_destroy_cb(GtkWidget * widget,gpointer user_data)100 load_data_widget_destroy_cb (GtkWidget *widget,
101 gpointer user_data)
102 {
103 LoadData *load_data = user_data;
104 g_cancellable_cancel (load_data->cancellable);
105 }
106
107
108 static LoadData *
load_data_new(GtkWidget * widget)109 load_data_new (GtkWidget *widget)
110 {
111 LoadData *load_data;
112
113 load_data = g_new0 (LoadData, 1);
114 load_data->widget = g_object_ref (widget);
115 load_data->cancellable = g_cancellable_new ();
116 load_data->destroy_id =
117 g_signal_connect (load_data->widget,
118 "destroy",
119 G_CALLBACK (load_data_widget_destroy_cb),
120 load_data);
121
122 return load_data;
123 }
124
125
126 static void
load_data_free(LoadData * data)127 load_data_free (LoadData *data)
128 {
129 g_signal_handler_disconnect (data->widget, data->destroy_id);
130 g_object_unref (data->cancellable);
131 g_free (data);
132 }
133
134
135 static void
image_loader_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)136 image_loader_ready_cb (GObject *source_object,
137 GAsyncResult *result,
138 gpointer user_data)
139 {
140 LoadData *load_data = user_data;
141 GthOverwriteDialog *self = GTH_OVERWRITE_DIALOG (load_data->widget);
142 GError *error = NULL;
143 cairo_surface_t *image = NULL;
144 GtkWidget *viewer;
145
146 if (! gth_thumb_loader_load_finish (GTH_THUMB_LOADER (source_object),
147 result,
148 &image,
149 &error))
150 {
151 load_data_free (load_data);
152 return;
153 }
154
155 if (image != NULL) {
156 if (GTH_THUMB_LOADER (source_object) == self->priv->old_image_loader)
157 viewer = self->priv->old_image_viewer;
158 else
159 viewer = self->priv->new_image_viewer;
160 gth_image_viewer_set_transparency_style (GTH_IMAGE_VIEWER (viewer), GTH_TRANSPARENCY_STYLE_CHECKERED);
161 gth_image_viewer_set_surface (GTH_IMAGE_VIEWER (viewer), image, -1, -1);
162
163 cairo_surface_destroy (image);
164 }
165
166 load_data_free (load_data);
167 }
168
169
170 static void
info_ready_cb(GList * files,GError * error,gpointer user_data)171 info_ready_cb (GList *files,
172 GError *error,
173 gpointer user_data)
174 {
175 LoadData *load_data = user_data;
176 GthOverwriteDialog *self = GTH_OVERWRITE_DIALOG (load_data->widget);
177 char *text;
178 GTimeVal *timeval;
179 GIcon *icon;
180 GdkPixbuf *pixbuf;
181 LoadData *old_image_load_data;
182
183 if (error != NULL) {
184 _gtk_error_dialog_from_gerror_show (GTK_WINDOW (self), _("Cannot read file information"), error);
185 load_data_free (load_data);
186 return;
187 }
188
189 /* new image */
190
191 if (self->priv->source != NULL) {
192 LoadData *new_image_load_data;
193
194 self->priv->source_data = g_object_ref (files->data);
195
196 gtk_label_set_text (GTK_LABEL (_gtk_builder_get_widget (self->priv->builder, "new_image_filename_label")), g_file_info_get_display_name (self->priv->source_data->info));
197 gtk_widget_set_tooltip_text (_gtk_builder_get_widget (self->priv->builder, "new_image_filename_label"), g_file_info_get_display_name (self->priv->source_data->info));
198
199 text = g_format_size (g_file_info_get_size (self->priv->source_data->info));
200 gtk_label_set_text (GTK_LABEL (_gtk_builder_get_widget (self->priv->builder, "new_image_size_label")), text);
201 g_free (text);
202
203 timeval = gth_file_data_get_modification_time (self->priv->source_data);
204 text = _g_time_val_to_exif_date (timeval);
205 gtk_label_set_text (GTK_LABEL (_gtk_builder_get_widget (self->priv->builder, "new_image_time_label")), text);
206 g_free (text);
207
208 icon = (GIcon*) g_file_info_get_attribute_object (self->priv->source_data->info, "preview::icon");
209 if (icon == NULL)
210 icon = g_content_type_get_icon (g_file_info_get_content_type (self->priv->source_data->info));
211 pixbuf = _g_icon_get_pixbuf (icon, ICON_SIZE, _gtk_widget_get_icon_theme (GTK_WIDGET (self)));
212 if (pixbuf != NULL) {
213 gth_image_viewer_set_pixbuf (GTH_IMAGE_VIEWER (self->priv->new_image_viewer), pixbuf, -1, -1);
214 g_object_unref (pixbuf);
215 }
216
217 gtk_widget_show (_gtk_builder_get_widget (self->priv->builder, "new_filename_label"));
218 gtk_widget_show (_gtk_builder_get_widget (self->priv->builder, "new_size_label"));
219 gtk_widget_show (_gtk_builder_get_widget (self->priv->builder, "new_modified_label"));
220
221 new_image_load_data = load_data_new (GTK_WIDGET (self));
222 gth_thumb_loader_load (self->priv->new_image_loader,
223 self->priv->source_data,
224 new_image_load_data->cancellable,
225 image_loader_ready_cb,
226 new_image_load_data);
227 }
228 else if (self->priv->source_image != NULL) {
229 gtk_widget_hide (_gtk_builder_get_widget (self->priv->builder, "new_filename_label"));
230 gtk_widget_hide (_gtk_builder_get_widget (self->priv->builder, "new_size_label"));
231 gtk_widget_hide (_gtk_builder_get_widget (self->priv->builder, "new_modified_label"));
232 gth_image_viewer_set_image (GTH_IMAGE_VIEWER (self->priv->new_image_viewer), self->priv->source_image, -1, -1);
233 }
234
235 /* old image */
236
237 if (self->priv->source != NULL)
238 self->priv->destination_data = g_object_ref (files->next->data);
239 else
240 self->priv->destination_data = g_object_ref (files->data);
241
242 gtk_label_set_text (GTK_LABEL (_gtk_builder_get_widget (self->priv->builder, "old_image_filename_label")), g_file_info_get_display_name (self->priv->destination_data->info));
243 gtk_widget_set_tooltip_text (_gtk_builder_get_widget (self->priv->builder, "old_image_filename_label"), g_file_info_get_display_name (self->priv->destination_data->info));
244
245 text = g_format_size (g_file_info_get_size (self->priv->destination_data->info));
246 gtk_label_set_text (GTK_LABEL (_gtk_builder_get_widget (self->priv->builder, "old_image_size_label")), text);
247 g_free (text);
248
249 timeval = gth_file_data_get_modification_time (self->priv->destination_data);
250 text = _g_time_val_to_exif_date (timeval);
251 gtk_label_set_text (GTK_LABEL (_gtk_builder_get_widget (self->priv->builder, "old_image_time_label")), text);
252 g_free (text);
253
254 gtk_entry_set_text (GTK_ENTRY (_gtk_builder_get_widget (self->priv->builder, "overwrite_rename_entry")), g_file_info_get_edit_name (self->priv->destination_data->info));
255
256 icon = (GIcon*) g_file_info_get_attribute_object (self->priv->destination_data->info, "preview::icon");
257 if (icon == NULL)
258 icon = g_content_type_get_icon (g_file_info_get_content_type (self->priv->destination_data->info));
259 pixbuf = _g_icon_get_pixbuf (icon, ICON_SIZE, _gtk_widget_get_icon_theme (GTK_WIDGET (self)));
260 if (pixbuf != NULL) {
261 gth_image_viewer_set_pixbuf (GTH_IMAGE_VIEWER (self->priv->old_image_viewer), pixbuf, -1, -1);
262 g_object_unref (pixbuf);
263 }
264
265 old_image_load_data = load_data_new (GTK_WIDGET (self));
266 gth_thumb_loader_load (self->priv->old_image_loader,
267 self->priv->destination_data,
268 old_image_load_data->cancellable,
269 image_loader_ready_cb,
270 old_image_load_data);
271
272 load_data_free (load_data);
273 }
274
275
276 static void
_gth_overwrite_dialog_load_file_info(GthOverwriteDialog * self)277 _gth_overwrite_dialog_load_file_info (GthOverwriteDialog *self)
278 {
279 LoadData *load_data;
280 GList *files;
281
282 load_data = load_data_new (GTK_WIDGET (self));
283
284 files = NULL;
285 if (self->priv->source != NULL)
286 files = g_list_append (files, self->priv->source);
287 files = g_list_append (files, self->priv->destination);
288 _g_query_all_metadata_async (files,
289 GTH_LIST_DEFAULT,
290 "standard::*,time::modified,time::modified-usec,preview::icon",
291 load_data->cancellable,
292 info_ready_cb,
293 load_data);
294
295 g_list_free (files);
296 }
297
298
299 static void
overwrite_rename_radiobutton_toggled_cb(GtkToggleButton * button,gpointer user_data)300 overwrite_rename_radiobutton_toggled_cb (GtkToggleButton *button,
301 gpointer user_data)
302 {
303 GthOverwriteDialog *self = user_data;
304
305 gtk_widget_set_sensitive (_gtk_builder_get_widget (self->priv->builder, "overwrite_rename_entry"),
306 gtk_toggle_button_get_active (button));
307 }
308
309
310 static void
show_cb(GtkWidget * widget,gpointer user_data)311 show_cb (GtkWidget *widget,
312 gpointer user_data)
313 {
314 _gth_overwrite_dialog_load_file_info (GTH_OVERWRITE_DIALOG (user_data));
315 }
316
317
318 static void
gth_overwrite_dialog_construct(GthOverwriteDialog * self,GthOverwriteResponse default_response,gboolean single_file)319 gth_overwrite_dialog_construct (GthOverwriteDialog *self,
320 GthOverwriteResponse default_response,
321 gboolean single_file)
322 {
323 GtkWidget *box;
324 GtkWidget *overwrite_radiobutton;
325
326 gtk_window_set_title (GTK_WINDOW (self), _("Overwrite"));
327 gtk_window_set_resizable (GTK_WINDOW (self), TRUE);
328
329 gtk_dialog_add_button (GTK_DIALOG (self), _GTK_LABEL_CANCEL, GTK_RESPONSE_CANCEL);
330 gtk_dialog_add_button (GTK_DIALOG (self), _GTK_LABEL_EXECUTE, GTK_RESPONSE_OK);
331 gtk_dialog_set_default_response (GTK_DIALOG (self), GTK_RESPONSE_OK);
332 _gtk_dialog_add_class_to_response (GTK_DIALOG (self), GTK_RESPONSE_OK, GTK_STYLE_CLASS_SUGGESTED_ACTION);
333
334 self->priv->builder = _gtk_builder_new_from_file ("overwrite-dialog.ui", NULL);
335 box = _gtk_builder_get_widget (self->priv->builder, "overwrite_dialog_box");
336 gtk_widget_show (box);
337 gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), box, TRUE, TRUE, 0);
338
339 switch (default_response) {
340 case GTH_OVERWRITE_RESPONSE_YES:
341 default:
342 overwrite_radiobutton = _gtk_builder_get_widget (self->priv->builder, "overwrite_yes_radiobutton");
343 break;
344 case GTH_OVERWRITE_RESPONSE_NO:
345 case GTH_OVERWRITE_RESPONSE_UNSPECIFIED:
346 overwrite_radiobutton = _gtk_builder_get_widget (self->priv->builder, "overwrite_no_radiobutton");
347 break;
348 case GTH_OVERWRITE_RESPONSE_ALWAYS_YES:
349 overwrite_radiobutton = _gtk_builder_get_widget (self->priv->builder, "overwrite_all_radiobutton");
350 break;
351 case GTH_OVERWRITE_RESPONSE_ALWAYS_NO:
352 overwrite_radiobutton = _gtk_builder_get_widget (self->priv->builder, "overwrite_none_radiobutton");
353 break;
354 case GTH_OVERWRITE_RESPONSE_RENAME:
355 overwrite_radiobutton = _gtk_builder_get_widget (self->priv->builder, "overwrite_rename_radiobutton");
356 break;
357 }
358 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (overwrite_radiobutton), TRUE);
359
360 if (single_file) {
361 gtk_widget_hide (_gtk_builder_get_widget (self->priv->builder, "overwrite_all_radiobutton"));
362 gtk_widget_hide (_gtk_builder_get_widget (self->priv->builder, "overwrite_none_radiobutton"));
363 }
364
365 gtk_widget_set_sensitive (_gtk_builder_get_widget (self->priv->builder, "overwrite_rename_entry"), default_response == GTH_OVERWRITE_RESPONSE_RENAME);
366 if (default_response == GTH_OVERWRITE_RESPONSE_RENAME)
367 gtk_widget_grab_focus (_gtk_builder_get_widget (self->priv->builder, "overwrite_rename_entry"));
368
369 gtk_widget_set_size_request (_gtk_builder_get_widget (self->priv->builder, "old_image_frame"), PREVIEW_SIZE, PREVIEW_SIZE);
370 gtk_widget_set_size_request (_gtk_builder_get_widget (self->priv->builder, "new_image_frame"), PREVIEW_SIZE, PREVIEW_SIZE);
371
372 self->priv->old_image_viewer = gth_image_viewer_new ();
373 gth_image_viewer_set_fit_mode (GTH_IMAGE_VIEWER (self->priv->old_image_viewer), GTH_FIT_SIZE_IF_LARGER);
374 gth_image_viewer_hide_frame (GTH_IMAGE_VIEWER (self->priv->old_image_viewer));
375 gth_image_viewer_set_transparency_style (GTH_IMAGE_VIEWER (self->priv->old_image_viewer), GTH_TRANSPARENCY_STYLE_GRAY);
376 gtk_widget_show (self->priv->old_image_viewer);
377 gtk_container_add (GTK_CONTAINER (_gtk_builder_get_widget (self->priv->builder, "old_image_frame")), self->priv->old_image_viewer);
378
379 self->priv->new_image_viewer = gth_image_viewer_new ();
380 gth_image_viewer_set_fit_mode (GTH_IMAGE_VIEWER (self->priv->new_image_viewer), GTH_FIT_SIZE_IF_LARGER);
381 gth_image_viewer_hide_frame (GTH_IMAGE_VIEWER (self->priv->new_image_viewer));
382 gth_image_viewer_set_transparency_style (GTH_IMAGE_VIEWER (self->priv->new_image_viewer), GTH_TRANSPARENCY_STYLE_GRAY);
383 gtk_widget_show (self->priv->new_image_viewer);
384 gtk_container_add (GTK_CONTAINER (_gtk_builder_get_widget (self->priv->builder, "new_image_frame")), self->priv->new_image_viewer);
385
386 g_signal_connect (_gtk_builder_get_widget (self->priv->builder, "overwrite_rename_radiobutton"),
387 "toggled",
388 G_CALLBACK (overwrite_rename_radiobutton_toggled_cb),
389 self);
390
391 self->priv->old_image_loader = gth_thumb_loader_new (PREVIEW_SIZE);
392 gth_thumb_loader_set_save_thumbnails (self->priv->old_image_loader, FALSE);
393
394 self->priv->new_image_loader = gth_thumb_loader_new (PREVIEW_SIZE);
395 gth_thumb_loader_set_save_thumbnails (self->priv->new_image_loader, FALSE);
396
397 g_signal_connect (self, "show", G_CALLBACK (show_cb), self);
398 }
399
400
401 GtkWidget *
gth_overwrite_dialog_new(GFile * source,GthImage * source_image,GFile * destination,GthOverwriteResponse default_respose,gboolean single_file)402 gth_overwrite_dialog_new (GFile *source,
403 GthImage *source_image,
404 GFile *destination,
405 GthOverwriteResponse default_respose,
406 gboolean single_file)
407 {
408 GthOverwriteDialog *self;
409
410 g_return_val_if_fail (G_IS_FILE (destination), NULL);
411
412 self = g_object_new (GTH_TYPE_OVERWRITE_DIALOG,
413 "use-header-bar", _gtk_settings_get_dialogs_use_header (),
414 NULL);
415 self->priv->source = _g_object_ref (source);
416 self->priv->source_image = _g_object_ref (source_image);
417 self->priv->destination = g_object_ref (destination);
418 gth_overwrite_dialog_construct (self, default_respose, single_file);
419
420 return (GtkWidget *) self;
421 }
422
423
424 GthOverwriteResponse
gth_overwrite_dialog_get_response(GthOverwriteDialog * self)425 gth_overwrite_dialog_get_response (GthOverwriteDialog *self)
426 {
427 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (_gtk_builder_get_widget (self->priv->builder, "overwrite_yes_radiobutton"))))
428 return GTH_OVERWRITE_RESPONSE_YES;
429 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (_gtk_builder_get_widget (self->priv->builder, "overwrite_no_radiobutton"))))
430 return GTH_OVERWRITE_RESPONSE_NO;
431 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (_gtk_builder_get_widget (self->priv->builder, "overwrite_all_radiobutton"))))
432 return GTH_OVERWRITE_RESPONSE_ALWAYS_YES;
433 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (_gtk_builder_get_widget (self->priv->builder, "overwrite_none_radiobutton"))))
434 return GTH_OVERWRITE_RESPONSE_ALWAYS_NO;
435 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (_gtk_builder_get_widget (self->priv->builder, "overwrite_rename_radiobutton"))))
436 return GTH_OVERWRITE_RESPONSE_RENAME;
437 return GTH_OVERWRITE_RESPONSE_UNSPECIFIED;
438 }
439
440
441 const char *
gth_overwrite_dialog_get_filename(GthOverwriteDialog * self)442 gth_overwrite_dialog_get_filename (GthOverwriteDialog *self)
443 {
444 return gtk_entry_get_text (GTK_ENTRY (_gtk_builder_get_widget (self->priv->builder, "overwrite_rename_entry")));
445 }
446