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