1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2009 The 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 <gtk/gtk.h>
24 #include <gthumb.h>
25 #include "dlg-edit-metadata.h"
26 #include "gth-edit-metadata-dialog.h"
27
28
29 #define UPDATE_SELECTION_DELAY 50
30
31
32 typedef struct {
33 int ref;
34 GthBrowser *browser;
35 GtkWidget *dialog;
36 GtkWidget *keep_open_checkbutton;
37 GtkWidget *info;
38 char *dialog_name;
39 GList *file_list; /* GthFileData list */
40 GList *parents;
41 gboolean never_shown;
42 gboolean close_dialog;
43 GthTask *loader;
44 gulong file_selection_changed_event;
45 guint update_selectection_event;
46 } DialogData;
47
48
49 static DialogData *
dialog_data_ref(DialogData * data)50 dialog_data_ref (DialogData *data)
51 {
52 g_atomic_int_inc (&data->ref);
53 return data;
54 }
55
56
57 static void
cancel_file_list_loading(DialogData * data)58 cancel_file_list_loading (DialogData *data)
59 {
60 if (data->loader == NULL)
61 return;
62 gth_task_cancel (data->loader);
63 g_object_unref (data->loader);
64 data->loader = NULL;
65 }
66
67
68 static void
dialog_data_unref(DialogData * data)69 dialog_data_unref (DialogData *data)
70 {
71 if (! g_atomic_int_dec_and_test (&data->ref))
72 return;
73
74 if (data->file_selection_changed_event != 0) {
75 g_signal_handler_disconnect (gth_browser_get_file_list_view (data->browser),
76 data->file_selection_changed_event);
77 data->file_selection_changed_event = 0;
78 }
79 if (data->update_selectection_event != 0) {
80 g_source_remove (data->update_selectection_event);
81 data->update_selectection_event = 0;
82 }
83 cancel_file_list_loading (data);
84
85 gth_browser_set_dialog (data->browser, data->dialog_name, NULL);
86 gtk_widget_destroy (data->dialog);
87
88 g_free (data->dialog_name);
89 _g_object_list_unref (data->file_list);
90 _g_object_list_unref (data->parents);
91 g_free (data);
92 }
93
94
95 static void
close_dialog(DialogData * data)96 close_dialog (DialogData *data)
97 {
98 if (data->file_selection_changed_event != 0) {
99 g_signal_handler_disconnect (gth_browser_get_file_list_view (data->browser),
100 data->file_selection_changed_event);
101 data->file_selection_changed_event = 0;
102 }
103 gtk_widget_hide (data->dialog);
104 dialog_data_unref (data);
105 }
106
107
108 static void
saver_completed_cb(GthTask * task,GError * error,gpointer user_data)109 saver_completed_cb (GthTask *task,
110 GError *error,
111 gpointer user_data)
112 {
113 DialogData *data = user_data;
114 GthMonitor *monitor;
115 GList *scan;
116
117 monitor = gth_main_get_default_monitor ();
118 for (scan = data->parents; scan; scan = scan->next)
119 gth_monitor_resume (monitor, (GFile *) scan->data);
120
121 if (error != NULL) {
122 if (! g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
123 _gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->dialog), _("Could not save the file metadata"), error);
124 }
125 else {
126 for (scan = data->file_list; scan; scan = scan->next) {
127 GthFileData *file_data = scan->data;
128 GFile *parent;
129 GList *files;
130
131 parent = g_file_get_parent (file_data->file);
132 files = g_list_prepend (NULL, g_object_ref (file_data->file));
133 gth_monitor_folder_changed (monitor, parent, files, GTH_MONITOR_EVENT_CHANGED);
134 gth_monitor_metadata_changed (monitor, file_data);
135
136 _g_object_list_unref (files);
137 }
138 }
139
140 if (data->close_dialog)
141 close_dialog (data);
142 else
143 gth_browser_show_next_image (data->browser, FALSE, FALSE);
144
145 dialog_data_unref (data);
146 _g_object_unref (task);
147 }
148
149
150 static void
edit_metadata_dialog__response_cb(GtkDialog * dialog,int response,gpointer user_data)151 edit_metadata_dialog__response_cb (GtkDialog *dialog,
152 int response,
153 gpointer user_data)
154 {
155 DialogData *data = user_data;
156 GthMonitor *monitor;
157 GHashTable *parents;
158 GList *scan;
159 GthTask *task;
160
161 if (response != GTK_RESPONSE_OK) {
162 cancel_file_list_loading (data);
163 close_dialog (data);
164 return;
165 }
166
167 if (data->file_list == NULL)
168 return;
169
170 data->close_dialog = ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->keep_open_checkbutton));
171
172 /* get the parents list */
173
174 parents = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
175 for (scan = data->file_list; scan; scan = scan->next) {
176 GthFileData *file_data = scan->data;
177 GFile *parent;
178
179 parent = g_file_get_parent (file_data->file);
180 if (G_LIKELY (parent != NULL)) {
181 if (g_hash_table_lookup (parents, parent) == NULL)
182 g_hash_table_insert (parents, g_object_ref (parent), GINT_TO_POINTER (1));
183 g_object_unref (parent);
184 }
185 }
186 _g_object_list_unref (data->parents);
187 data->parents = g_hash_table_get_keys (parents);
188 g_list_foreach (data->parents, (GFunc) g_object_ref, NULL);
189 g_hash_table_unref (parents);
190
191 /* ignore changes to all the parents */
192
193 monitor = gth_main_get_default_monitor ();
194 for (scan = data->parents; scan; scan = scan->next)
195 gth_monitor_pause (monitor, (GFile *) scan->data);
196
197 gth_edit_metadata_dialog_update_info (GTH_EDIT_METADATA_DIALOG (data->dialog), data->file_list);
198
199 dialog_data_ref (data);
200 task = gth_save_file_data_task_new (data->file_list, "*");
201 g_signal_connect (task,
202 "completed",
203 G_CALLBACK (saver_completed_cb),
204 data);
205 gth_browser_exec_task (data->browser, task, GTH_TASK_FLAGS_IGNORE_ERROR);
206 }
207
208
209 typedef struct {
210 DialogData *data;
211 GList *files;
212 } LoaderData;
213
214
215 static void
loader_data_free(LoaderData * loader_data)216 loader_data_free (LoaderData *loader_data)
217 {
218 dialog_data_unref (loader_data->data);
219 _g_object_list_unref (loader_data->files);
220 g_free (loader_data);
221 }
222
223
224 static void
loader_completed_cb(GthTask * task,GError * error,gpointer user_data)225 loader_completed_cb (GthTask *task,
226 GError *error,
227 gpointer user_data)
228 {
229 LoaderData *loader_data = user_data;
230 DialogData *data = loader_data->data;
231
232 if (error != NULL) {
233 if (! g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
234 _gtk_error_dialog_from_gerror_show (GTK_WINDOW (data->browser), _("Cannot read file information"), error);
235 loader_data_free (loader_data);
236 if (data->never_shown)
237 close_dialog (data);
238 return;
239 }
240
241 _g_object_list_unref (data->file_list);
242 data->file_list = _g_object_list_ref (gth_load_file_data_task_get_result (GTH_LOAD_FILE_DATA_TASK (task)));
243
244 gth_file_selection_info_set_file_list (GTH_FILE_SELECTION_INFO (data->info), data->file_list);
245 gth_edit_metadata_dialog_set_file_list (GTH_EDIT_METADATA_DIALOG (data->dialog), data->file_list);
246
247 gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (data->browser));
248 gtk_window_set_modal (GTK_WINDOW (data->dialog), FALSE);
249 gtk_window_present (GTK_WINDOW (data->dialog));
250
251 data->never_shown = FALSE;
252
253 loader_data_free (loader_data);
254 }
255
256
257 static gboolean
update_file_list(gpointer user_data)258 update_file_list (gpointer user_data)
259 {
260 DialogData *data = user_data;
261 LoaderData *loader_data;
262 GList *items;
263 GList *file_data_list;
264
265 if (data->update_selectection_event != 0) {
266 g_source_remove (data->update_selectection_event);
267 data->update_selectection_event = 0;
268 }
269
270 cancel_file_list_loading (data);
271
272 loader_data = g_new0 (LoaderData, 1);
273 loader_data->data = dialog_data_ref (data);
274
275 items = gth_file_selection_get_selected (GTH_FILE_SELECTION (gth_browser_get_file_list_view (data->browser)));
276 file_data_list = gth_file_list_get_files (GTH_FILE_LIST (gth_browser_get_file_list (data->browser)), items);
277 loader_data->files = gth_file_data_list_to_file_list (file_data_list);
278
279 gtk_dialog_set_response_sensitive (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK, loader_data->files != NULL);
280
281 data->loader = gth_load_file_data_task_new (loader_data->files, "*");
282 g_signal_connect (data->loader,
283 "completed",
284 G_CALLBACK (loader_completed_cb),
285 loader_data);
286 gth_browser_exec_task (data->browser, data->loader, GTH_TASK_FLAGS_IGNORE_ERROR);
287
288 _g_object_list_unref (file_data_list);
289 _gtk_tree_path_list_free (items);
290
291 return FALSE;
292 }
293
294
295 static void
file_selection_changed_cb(GthFileSelection * self,DialogData * data)296 file_selection_changed_cb (GthFileSelection *self,
297 DialogData *data)
298 {
299 if (data->update_selectection_event != 0)
300 g_source_remove (data->update_selectection_event);
301 data->update_selectection_event = g_timeout_add (UPDATE_SELECTION_DELAY, update_file_list, data);
302 }
303
304
305 static void
keep_open_button_toggled_cb(GtkToggleButton * button,DialogData * data)306 keep_open_button_toggled_cb (GtkToggleButton *button,
307 DialogData *data)
308 {
309 gth_file_selection_info_set_visible (GTH_FILE_SELECTION_INFO (data->info),
310 gtk_toggle_button_get_active (button));
311 }
312
313
314 void
dlg_edit_metadata(GthBrowser * browser,GType dialog_type,const char * dialog_name)315 dlg_edit_metadata (GthBrowser *browser,
316 GType dialog_type,
317 const char *dialog_name)
318 {
319 DialogData *data;
320
321 if (gth_browser_get_dialog (browser, dialog_name)) {
322 gtk_window_present (GTK_WINDOW (gth_browser_get_dialog (browser, dialog_name)));
323 return;
324 }
325
326 data = g_new0 (DialogData, 1);
327 data->ref = 1;
328 data->browser = browser;
329 data->dialog = g_object_new (dialog_type,
330 "transient-for", GTK_WINDOW (browser),
331 "modal", FALSE,
332 "use-header-bar", _gtk_settings_get_dialogs_use_header (),
333 NULL);
334 data->dialog_name = g_strdup (dialog_name);
335 data->never_shown = TRUE;
336
337 data->info = gth_file_selection_info_new ();
338 gtk_widget_show (data->info);
339 gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (data->dialog))), data->info, FALSE, FALSE, 0);
340
341 gtk_dialog_add_buttons (GTK_DIALOG (data->dialog),
342 _GTK_LABEL_CLOSE, GTK_RESPONSE_CANCEL,
343 _GTK_LABEL_SAVE, GTK_RESPONSE_OK,
344 NULL);
345
346 data->keep_open_checkbutton = _gtk_toggle_image_button_new_for_header_bar ("pinned-symbolic");
347 gtk_widget_set_tooltip_text (data->keep_open_checkbutton, _("Keep the dialog open"));
348 gtk_widget_show (data->keep_open_checkbutton);
349 _gtk_dialog_add_action_widget (GTK_DIALOG (data->dialog), data->keep_open_checkbutton);
350
351 _gtk_dialog_add_class_to_response (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK, GTK_STYLE_CLASS_SUGGESTED_ACTION);
352 gth_browser_set_dialog (browser, data->dialog_name, data->dialog);
353
354 g_signal_connect (G_OBJECT (data->dialog),
355 "delete-event",
356 G_CALLBACK (gtk_true),
357 NULL);
358 g_signal_connect (data->dialog,
359 "response",
360 G_CALLBACK (edit_metadata_dialog__response_cb),
361 data);
362 g_signal_connect (data->keep_open_checkbutton,
363 "toggled",
364 G_CALLBACK (keep_open_button_toggled_cb),
365 data);
366 data->file_selection_changed_event =
367 g_signal_connect (gth_browser_get_file_list_view (data->browser),
368 "file-selection-changed",
369 G_CALLBACK (file_selection_changed_cb),
370 data);
371
372 update_file_list (data);
373 }
374