Mercurial > geeqie.yaz
annotate src/ui_utildlg.c @ 276:4f526d436873
Implement secure rc file saving.
First data is written to a temporary file, then if nothing
was wrong, this file is renamed to the final name.
This way the risk of corrupted rc file is greatly reduced.
The code is borrowed from ELinks (http://elinks.cz).
author | zas_ |
---|---|
date | Tue, 08 Apr 2008 21:55:58 +0000 |
parents | 9faf34f047b1 |
children | 9995c5fb202a |
rev | line source |
---|---|
9 | 1 /* |
2 * (SLIK) SimpLIstic sKin functions | |
3 * (C) 2004 John Ellis | |
4 * | |
5 * Author: John Ellis | |
6 * | |
7 * This software is released under the GNU General Public License (GNU GPL). | |
8 * Please read the included file COPYING for more information. | |
9 * This software comes with no warranty of any kind, use at your own risk! | |
10 */ | |
11 | |
12 #ifdef HAVE_CONFIG_H | |
13 # include "config.h" | |
14 #endif | |
15 #include "intl.h" | |
16 | |
17 #include <stdio.h> | |
18 #include <stdlib.h> | |
19 #include <string.h> | |
20 #include <sys/types.h> | |
21 #include <gtk/gtk.h> | |
22 | |
23 #include <gdk/gdkkeysyms.h> /* for keyboard values */ | |
24 | |
138 | 25 #include "gqview.h" |
9 | 26 #include "ui_utildlg.h" |
27 | |
232 | 28 #include "filelist.h" |
9 | 29 #include "ui_fileops.h" |
30 #include "ui_misc.h" | |
31 #include "ui_pathsel.h" | |
32 #include "ui_tabcomp.h" | |
33 | |
34 | |
35 /* | |
36 *----------------------------------------------------------------------------- | |
37 * generic dialog | |
38 *----------------------------------------------------------------------------- | |
39 */ | |
40 | |
41 void generic_dialog_close(GenericDialog *gd) | |
42 { | |
43 gtk_widget_destroy(gd->dialog); | |
44 g_free(gd); | |
45 } | |
46 | |
47 static void generic_dialog_click_cb(GtkWidget *widget, gpointer data) | |
48 { | |
49 GenericDialog *gd = data; | |
50 void (*func)(GenericDialog *, gpointer); | |
51 gint auto_close; | |
52 | |
53 func = g_object_get_data(G_OBJECT(widget), "dialog_function"); | |
54 auto_close = gd->auto_close; | |
55 | |
56 if (func) func(gd, gd->data); | |
57 if (auto_close) generic_dialog_close(gd); | |
58 } | |
59 | |
60 static gint generic_dialog_default_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) | |
61 { | |
62 GenericDialog *gd = data; | |
63 | |
64 if (event->keyval == GDK_Return && GTK_WIDGET_HAS_FOCUS(widget) | |
65 && gd->default_cb) | |
66 { | |
67 gint auto_close; | |
68 | |
69 auto_close = gd->auto_close; | |
70 gd->default_cb(gd, gd->data); | |
71 if (auto_close) generic_dialog_close(gd); | |
72 | |
73 return TRUE; | |
74 } | |
75 return FALSE; | |
76 } | |
77 | |
78 void generic_dialog_attach_default(GenericDialog *gd, GtkWidget *widget) | |
79 { | |
80 if (!gd || !widget) return; | |
81 g_signal_connect(G_OBJECT(widget), "key_press_event", | |
82 G_CALLBACK(generic_dialog_default_key_press_cb), gd); | |
83 } | |
84 | |
85 static gint generic_dialog_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) | |
86 { | |
87 GenericDialog *gd = data; | |
88 | |
89 if (event->keyval == GDK_Escape) | |
90 { | |
91 if (gd->cancel_cb) gd->cancel_cb(gd, gd->data); | |
92 if (gd->auto_close) generic_dialog_click_cb(widget, data); | |
93 return TRUE; | |
94 } | |
95 return FALSE; | |
96 } | |
97 | |
98 static gint generic_dialog_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data) | |
99 { | |
100 GenericDialog *gd = data; | |
101 gint auto_close; | |
102 | |
103 auto_close = gd->auto_close; | |
104 | |
105 if (gd->cancel_cb) gd->cancel_cb(gd, gd->data); | |
106 if (auto_close) generic_dialog_close(gd); | |
107 | |
108 return TRUE; | |
109 } | |
110 | |
111 static void generic_dialog_show_cb(GtkWidget *widget, gpointer data) | |
112 { | |
113 GenericDialog *gd = data; | |
114 if (gd->cancel_button) | |
115 { | |
116 gtk_box_reorder_child(GTK_BOX(gd->hbox), gd->cancel_button, -1); | |
117 } | |
118 | |
119 g_signal_handlers_disconnect_by_func(G_OBJECT(widget), | |
120 G_CALLBACK(generic_dialog_show_cb), gd); | |
121 } | |
122 | |
123 gint generic_dialog_get_alternative_button_order(GtkWidget *widget) | |
124 { | |
125 GtkSettings *settings; | |
126 GObjectClass *klass; | |
127 gint alternative_order = FALSE; | |
128 | |
129 settings = gtk_settings_get_for_screen(gtk_widget_get_screen(widget)); | |
130 klass = G_OBJECT_CLASS(GTK_SETTINGS_GET_CLASS(settings)); | |
131 if (g_object_class_find_property(klass, "gtk-alternative-button-order")) | |
132 { | |
133 g_object_get(settings, "gtk-alternative-button-order", &alternative_order, NULL); | |
134 } | |
135 | |
136 return alternative_order; | |
137 } | |
138 | |
139 GtkWidget *generic_dialog_add_button(GenericDialog *gd, const gchar *stock_id, const gchar *text, | |
140 void (*func_cb)(GenericDialog *, gpointer), gint is_default) | |
141 { | |
142 GtkWidget *button; | |
143 gint alternative_order; | |
144 | |
145 button = pref_button_new(NULL, stock_id, text, FALSE, | |
146 G_CALLBACK(generic_dialog_click_cb), gd); | |
147 | |
148 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); | |
149 g_object_set_data(G_OBJECT(button), "dialog_function", func_cb); | |
150 | |
151 gtk_container_add(GTK_CONTAINER(gd->hbox), button); | |
152 | |
153 alternative_order = generic_dialog_get_alternative_button_order(gd->hbox); | |
154 | |
155 if (is_default) | |
156 { | |
157 gtk_widget_grab_default(button); | |
158 gtk_widget_grab_focus(button); | |
159 gd->default_cb = func_cb; | |
160 | |
161 if (!alternative_order) gtk_box_reorder_child(GTK_BOX(gd->hbox), button, -1); | |
162 } | |
163 else | |
164 { | |
165 if (!alternative_order) gtk_box_reorder_child(GTK_BOX(gd->hbox), button, 0); | |
166 } | |
167 | |
168 gtk_widget_show(button); | |
169 | |
170 return button; | |
171 } | |
172 | |
173 GtkWidget *generic_dialog_add_message(GenericDialog *gd, const gchar *icon_stock_id, | |
174 const gchar *heading, const gchar *text) | |
175 { | |
176 GtkWidget *hbox; | |
177 GtkWidget *vbox; | |
178 GtkWidget *label; | |
179 | |
180 hbox = pref_box_new(gd->vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE); | |
181 if (icon_stock_id) | |
182 { | |
183 GtkWidget *image; | |
184 | |
185 image = gtk_image_new_from_stock(icon_stock_id, GTK_ICON_SIZE_DIALOG); | |
186 gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.0); | |
187 gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0); | |
188 gtk_widget_show(image); | |
189 } | |
190 | |
191 vbox = pref_box_new(hbox, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE); | |
192 if (heading) | |
193 { | |
194 label = pref_label_new(vbox, heading); | |
195 pref_label_bold(label, TRUE, TRUE); | |
196 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); | |
197 } | |
198 if (text) | |
199 { | |
200 label = pref_label_new(vbox, text); | |
201 gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); | |
15
3263965d5f9e
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
202 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); |
9 | 203 } |
204 | |
205 return vbox; | |
206 } | |
207 | |
208 static void generic_dialog_setup(GenericDialog *gd, | |
209 const gchar *title, | |
210 const gchar *wmclass, const gchar *wmsubclass, | |
211 GtkWidget *parent, gint auto_close, | |
212 void (*cancel_cb)(GenericDialog *, gpointer), gpointer data) | |
213 { | |
214 GtkWidget *vbox; | |
215 | |
216 gd->auto_close = auto_close; | |
217 gd->data = data; | |
218 gd->cancel_cb = cancel_cb; | |
219 | |
220 gd->dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
221 gtk_window_set_type_hint(GTK_WINDOW(gd->dialog), GDK_WINDOW_TYPE_HINT_DIALOG); | |
222 gtk_window_set_wmclass(GTK_WINDOW(gd->dialog), wmsubclass, wmclass); | |
223 if (parent) | |
224 { | |
225 GtkWindow *window = NULL; | |
226 | |
227 if (GTK_IS_WINDOW(parent)) | |
228 { | |
229 window = GTK_WINDOW(parent); | |
230 } | |
231 else | |
232 { | |
233 GtkWidget *top; | |
234 | |
235 top = gtk_widget_get_toplevel(parent); | |
236 if (GTK_IS_WINDOW(top) && GTK_WIDGET_TOPLEVEL(top)) window = GTK_WINDOW(top); | |
237 } | |
238 | |
239 if (window) gtk_window_set_transient_for(GTK_WINDOW(gd->dialog), window); | |
240 } | |
241 | |
242 g_signal_connect(G_OBJECT(gd->dialog), "delete_event", | |
243 G_CALLBACK(generic_dialog_delete_cb), gd); | |
244 g_signal_connect(G_OBJECT(gd->dialog), "key_press_event", | |
245 G_CALLBACK(generic_dialog_key_press_cb), gd); | |
246 | |
247 gtk_window_set_resizable(GTK_WINDOW(gd->dialog), TRUE); | |
248 gtk_window_set_title(GTK_WINDOW (gd->dialog), title); | |
249 gtk_container_set_border_width(GTK_CONTAINER(gd->dialog), PREF_PAD_BORDER); | |
250 | |
251 vbox = gtk_vbox_new(FALSE, PREF_PAD_BUTTON_SPACE); | |
252 gtk_container_add(GTK_CONTAINER(gd->dialog), vbox); | |
253 gtk_widget_show(vbox); | |
254 | |
255 gd->vbox = gtk_vbox_new(FALSE, PREF_PAD_GAP); | |
256 gtk_box_pack_start(GTK_BOX(vbox), gd->vbox, TRUE, TRUE, 0); | |
257 gtk_widget_show(gd->vbox); | |
258 | |
259 gd->hbox = gtk_hbutton_box_new(); | |
260 gtk_button_box_set_layout(GTK_BUTTON_BOX(gd->hbox), GTK_BUTTONBOX_END); | |
261 gtk_box_set_spacing(GTK_BOX(gd->hbox), PREF_PAD_BUTTON_GAP); | |
262 gtk_box_pack_start(GTK_BOX(vbox), gd->hbox, FALSE, FALSE, 0); | |
263 gtk_widget_show(gd->hbox); | |
264 | |
265 if (gd->cancel_cb) | |
266 { | |
267 gd->cancel_button = generic_dialog_add_button(gd, GTK_STOCK_CANCEL, NULL, gd->cancel_cb, TRUE); | |
268 } | |
269 else | |
270 { | |
271 gd->cancel_button = NULL; | |
272 } | |
273 | |
274 if (generic_dialog_get_alternative_button_order(gd->hbox)) | |
275 { | |
276 g_signal_connect(G_OBJECT(gd->dialog), "show", | |
277 G_CALLBACK(generic_dialog_show_cb), gd); | |
278 } | |
279 | |
280 gd->default_cb = NULL; | |
281 } | |
282 | |
283 GenericDialog *generic_dialog_new(const gchar *title, | |
284 const gchar *wmclass, const gchar *wmsubclass, | |
285 GtkWidget *parent, gint auto_close, | |
286 void (*cancel_cb)(GenericDialog *, gpointer), gpointer data) | |
287 { | |
288 GenericDialog *gd; | |
289 | |
290 gd = g_new0(GenericDialog, 1); | |
291 generic_dialog_setup(gd, title, wmclass, wmsubclass, | |
292 parent, auto_close, cancel_cb, data); | |
293 return gd; | |
294 } | |
295 /* | |
296 *----------------------------------------------------------------------------- | |
297 * simple warning dialog | |
298 *----------------------------------------------------------------------------- | |
299 */ | |
300 | |
301 static void warning_dialog_ok_cb(GenericDialog *gd, gpointer data) | |
302 { | |
303 /* no op */ | |
304 } | |
305 | |
306 GenericDialog *warning_dialog(const gchar *heading, const gchar *text, | |
307 const gchar *icon_stock_id, GtkWidget *parent) | |
308 { | |
309 GenericDialog *gd; | |
310 | |
254
9faf34f047b1
Make the wmclass value unique among the code by defining
zas_
parents:
232
diff
changeset
|
311 gd = generic_dialog_new(heading, GQ_WMCLASS, "warning", parent, TRUE, NULL, NULL); |
9 | 312 generic_dialog_add_button(gd, GTK_STOCK_OK, NULL, warning_dialog_ok_cb, TRUE); |
313 | |
314 generic_dialog_add_message(gd, icon_stock_id, heading, text); | |
315 | |
316 gtk_widget_show(gd->dialog); | |
317 | |
318 return gd; | |
319 } | |
320 | |
321 /* | |
322 *----------------------------------------------------------------------------- | |
323 * generic file ops dialog routines | |
324 *----------------------------------------------------------------------------- | |
325 */ | |
326 | |
138 | 327 void file_dialog_close(FileDialog *fdlg) |
9 | 328 { |
138 | 329 file_data_unref(fdlg->source_fd); |
330 g_free(fdlg->dest_path); | |
331 if (fdlg->source_list) filelist_free(fdlg->source_list); | |
9 | 332 |
138 | 333 generic_dialog_close(GENERIC_DIALOG(fdlg)); |
9 | 334 } |
335 | |
336 FileDialog *file_dialog_new(const gchar *title, | |
337 const gchar *wmclass, const gchar *wmsubclass, | |
338 GtkWidget *parent, | |
339 void (*cancel_cb)(FileDialog *, gpointer), gpointer data) | |
340 { | |
138 | 341 FileDialog *fdlg = NULL; |
9 | 342 |
138 | 343 fdlg = g_new0(FileDialog, 1); |
9 | 344 |
138 | 345 generic_dialog_setup(GENERIC_DIALOG(fdlg), title, |
9 | 346 wmclass, wmsubclass, parent, FALSE, |
347 (void *)cancel_cb, data); | |
348 | |
138 | 349 return fdlg; |
9 | 350 } |
351 | |
138 | 352 GtkWidget *file_dialog_add_button(FileDialog *fdlg, const gchar *stock_id, const gchar *text, |
9 | 353 void (*func_cb)(FileDialog *, gpointer), gint is_default) |
354 { | |
138 | 355 return generic_dialog_add_button(GENERIC_DIALOG(fdlg), stock_id, text, |
9 | 356 (void *)func_cb, is_default); |
357 } | |
358 | |
359 static void file_dialog_entry_cb(GtkWidget *widget, gpointer data) | |
360 { | |
138 | 361 FileDialog *fdlg = data; |
362 g_free(fdlg->dest_path); | |
363 fdlg->dest_path = remove_trailing_slash(gtk_entry_get_text(GTK_ENTRY(fdlg->entry))); | |
9 | 364 } |
365 | |
366 static void file_dialog_entry_enter_cb(const gchar *path, gpointer data) | |
367 { | |
368 GenericDialog *gd = data; | |
369 | |
370 file_dialog_entry_cb(NULL, data); | |
371 | |
372 if (gd->default_cb) gd->default_cb(gd, gd->data); | |
373 } | |
374 | |
138 | 375 void file_dialog_add_path_widgets(FileDialog *fdlg, const gchar *default_path, const gchar *path, |
9 | 376 const gchar *history_key, const gchar *filter, const gchar *filter_desc) |
377 { | |
378 GtkWidget *tabcomp; | |
379 GtkWidget *list; | |
380 | |
138 | 381 if (fdlg->entry) return; |
9 | 382 |
138 | 383 tabcomp = tab_completion_new_with_history(&fdlg->entry, NULL, |
384 history_key, -1, file_dialog_entry_enter_cb, fdlg); | |
385 gtk_box_pack_end(GTK_BOX(GENERIC_DIALOG(fdlg)->vbox), tabcomp, FALSE, FALSE, 0); | |
386 generic_dialog_attach_default(GENERIC_DIALOG(fdlg), fdlg->entry); | |
9 | 387 gtk_widget_show(tabcomp); |
388 | |
389 if (path && path[0] == '/') | |
390 { | |
138 | 391 fdlg->dest_path = g_strdup(path); |
9 | 392 } |
393 else | |
394 { | |
395 const gchar *base; | |
396 | |
138 | 397 base = tab_completion_set_to_last_history(fdlg->entry); |
9 | 398 |
399 if (!base) base = default_path; | |
400 if (!base) base = homedir(); | |
401 | |
402 if (path) | |
403 { | |
138 | 404 fdlg->dest_path = concat_dir_and_file(base, path); |
9 | 405 } |
406 else | |
407 { | |
138 | 408 fdlg->dest_path = g_strdup(base); |
9 | 409 } |
410 } | |
411 | |
138 | 412 list = path_selection_new_with_files(fdlg->entry, fdlg->dest_path, filter, filter_desc); |
413 path_selection_add_select_func(fdlg->entry, file_dialog_entry_enter_cb, fdlg); | |
414 gtk_box_pack_end(GTK_BOX(GENERIC_DIALOG(fdlg)->vbox), list, TRUE, TRUE, 0); | |
9 | 415 gtk_widget_show(list); |
416 | |
138 | 417 gtk_widget_grab_focus(fdlg->entry); |
418 if (fdlg->dest_path) | |
9 | 419 { |
138 | 420 gtk_entry_set_text(GTK_ENTRY(fdlg->entry), fdlg->dest_path); |
421 gtk_editable_set_position(GTK_EDITABLE(fdlg->entry), strlen(fdlg->dest_path)); | |
9 | 422 } |
423 | |
138 | 424 g_signal_connect(G_OBJECT(fdlg->entry), "changed", |
425 G_CALLBACK(file_dialog_entry_cb), fdlg); | |
9 | 426 } |
427 | |
138 | 428 void file_dialog_add_filter(FileDialog *fdlg, const gchar *filter, const gchar *filter_desc, gint set) |
9 | 429 { |
138 | 430 if (!fdlg->entry) return; |
431 path_selection_add_filter(fdlg->entry, filter, filter_desc, set); | |
9 | 432 } |
433 | |
138 | 434 void file_dialog_clear_filter(FileDialog *fdlg) |
9 | 435 { |
138 | 436 if (!fdlg->entry) return; |
437 path_selection_clear_filter(fdlg->entry); | |
9 | 438 } |
439 | |
138 | 440 void file_dialog_sync_history(FileDialog *fdlg, gint dir_only) |
9 | 441 { |
138 | 442 if (!fdlg->dest_path) return; |
9 | 443 |
444 if (!dir_only || | |
138 | 445 (dir_only && isdir(fdlg->dest_path)) ) |
9 | 446 { |
138 | 447 tab_completion_append_to_history(fdlg->entry, fdlg->dest_path); |
9 | 448 } |
449 else | |
450 { | |
138 | 451 gchar *buf = remove_level_from_path(fdlg->dest_path); |
452 tab_completion_append_to_history(fdlg->entry, buf); | |
9 | 453 g_free(buf); |
454 } | |
455 } | |
456 | |
457 |