1 /* Bluefish HTML Editor
2 * file_dialogs.c - file dialogs
3 *
4 * Copyright (C) 2005-2018 Olivier Sessink
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /* indented with indent -ts4 -kr -l110 */
21 /*#define DEBUG*/
22
23 #include <gtk/gtk.h>
24 #include <string.h> /* memcpy */
25 #include <time.h> /* strftime() */
26
27 #include "bluefish.h"
28 #include "file_dialogs.h"
29 #include "bfwin.h"
30 #include "bookmark.h"
31 #include "dialog_utils.h"
32 #include "document.h"
33 #include "doc_text_tools.h"
34 #include "file_autosave.h"
35 #include "file.h"
36 #include "filebrowser2.h"
37 #include "gtk_easy.h"
38 #include "snr3.h" /* snr3_run_extern_replace() */
39 #include "stringlist.h"
40 #include "undo_redo.h"
41
42 static gchar *modified_on_disk_warning_string(const gchar * filename, GFileInfo * oldfinfo,
43 GFileInfo * newfinfo);
44
45 /**************************************************************************/
46 /* the start of the callback functions for the menu, acting on a document */
47 /**************************************************************************/
48 typedef struct {
49 GtkWidget *dialog;
50 GtkWidget *basedir;
51 GtkWidget *find_pattern;
52 GtkWidget *matchname;
53 GtkWidget *recursive;
54 GtkWidget *max_recursion;
55 GtkWidget *grep_pattern;
56 GtkWidget *is_regex;
57 GtkWidget *regexwarn;
58 Tbfwin *bfwin;
59 } Tfiles_advanced;
60
61 static void
files_advanced_win_findpattern_changed(GtkComboBox * combobox,Tfiles_advanced * tfs)62 files_advanced_win_findpattern_changed(GtkComboBox * combobox, Tfiles_advanced * tfs)
63 {
64 if (strlen(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(tfs->find_pattern))))) > 0) {
65 gtk_dialog_set_response_sensitive(GTK_DIALOG(tfs->dialog), GTK_RESPONSE_ACCEPT, TRUE);
66 } else {
67 gtk_dialog_set_response_sensitive(GTK_DIALOG(tfs->dialog), GTK_RESPONSE_ACCEPT, FALSE);
68 }
69 }
70
71 static gboolean
files_advanced_win_ok_clicked(Tfiles_advanced * tfs)72 files_advanced_win_ok_clicked(Tfiles_advanced * tfs)
73 {
74 GFile *baseuri;
75 gchar *basedir, *content_filter, *extension_filter;
76 gboolean retval;
77 GError *gerror = NULL;
78 extension_filter =
79 gtk_editable_get_chars(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(tfs->find_pattern))), 0, -1);
80 basedir = gtk_editable_get_chars(GTK_EDITABLE(tfs->basedir), 0, -1);
81 baseuri = g_file_new_for_uri(basedir);
82 content_filter = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(tfs->grep_pattern));
83 if (content_filter && content_filter[0]!='\0')
84 tfs->bfwin->session->searchlist = add_to_history_stringlist(tfs->bfwin->session->searchlist, content_filter, TRUE);
85 if (extension_filter && extension_filter[0] != '\0')
86 tfs->bfwin->session->filegloblist = add_to_history_stringlist(tfs->bfwin->session->filegloblist, extension_filter, TRUE);
87
88 retval =
89 open_advanced(tfs->bfwin, baseuri, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tfs->recursive))
90 , 500, !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tfs->matchname))
91 ,
92 strlen(extension_filter) == 0 ? NULL : extension_filter,
93 strlen(content_filter) == 0 ? NULL : content_filter,
94 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tfs->is_regex)), &gerror);
95 if (!retval && gerror) {
96 gtk_label_set_line_wrap(GTK_LABEL(tfs->regexwarn), TRUE);
97 gtk_label_set_text(GTK_LABEL(tfs->regexwarn), gerror->message);
98 g_error_free(gerror);
99 }
100 g_free(basedir);
101 g_free(content_filter);
102 g_free(extension_filter);
103 g_object_unref(baseuri);
104
105 tfs->bfwin->session->adv_open_recursive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tfs->recursive));
106 tfs->bfwin->session->adv_open_matchname =
107 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tfs->matchname));
108 if (retval)
109 bfwin_statusbar_message(tfs->bfwin,_("Started open advanced..."), 1);
110 return retval;
111 }
112
113 static void
files_advanced_win_select_basedir_lcb(GtkWidget * widget,Tfiles_advanced * tfs)114 files_advanced_win_select_basedir_lcb(GtkWidget * widget, Tfiles_advanced * tfs)
115 {
116 gchar *newdir = NULL;
117 GtkWidget *dialog;
118
119 dialog =
120 file_chooser_dialog(tfs->bfwin, _("Select basedir"), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, (gchar *)
121 gtk_entry_get_text(GTK_ENTRY(tfs->basedir)), TRUE, FALSE, NULL, FALSE);
122 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
123 newdir = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
124 }
125 gtk_widget_destroy(dialog);
126
127 if (newdir) {
128 gtk_entry_set_text(GTK_ENTRY(tfs->basedir), newdir);
129 g_free(newdir);
130 }
131 }
132
133 void
files_advanced_win(Tbfwin * bfwin,gchar * basedir)134 files_advanced_win(Tbfwin * bfwin, gchar * basedir)
135 {
136 GtkWidget *alignment, *button, *carea, *table, *vbox, *vbox2;
137 Tfiles_advanced *tfs;
138
139 tfs = g_new(Tfiles_advanced, 1);
140 tfs->bfwin = bfwin;
141
142 tfs->dialog = gtk_dialog_new_with_buttons(_("Advanced open file selector"),
143 GTK_WINDOW(tfs->bfwin->main_window),
144 GTK_DIALOG_DESTROY_WITH_PARENT,
145 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
146 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
147
148 #if !GTK_CHECK_VERSION(3, 0, 0)
149 gtk_dialog_set_has_separator(GTK_DIALOG(tfs->dialog), FALSE);
150 #endif /* gtk3 */
151 carea = gtk_dialog_get_content_area(GTK_DIALOG(tfs->dialog));
152
153 alignment = gtk_alignment_new(0, 0, 1, 1);
154 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 12, 0, 12, 6);
155 gtk_box_pack_start(GTK_BOX(carea), alignment, FALSE, FALSE, 0);
156 vbox = gtk_vbox_new(FALSE, 0);
157 gtk_container_add(GTK_CONTAINER(alignment), vbox);
158
159 vbox2 = dialog_vbox_labeled(_("<b>Files</b>"), vbox);
160
161 table = dialog_table_in_vbox(2, 6, 0, vbox2, FALSE, FALSE, 6);
162
163 if (!basedir) {
164 tfs->basedir = dialog_entry_in_table(bfwin->session->opendir, table, 1, 5, 0, 1);
165 } else {
166 tfs->basedir = dialog_entry_in_table(basedir, table, 1, 5, 0, 1);
167 }
168 dialog_mnemonic_label_in_table(_("Base _Dir:"), tfs->basedir, table, 0, 1, 0, 1);
169
170 button =
171 dialog_button_new_with_image_in_table(NULL, GTK_STOCK_OPEN, G_CALLBACK(files_advanced_win_select_basedir_lcb), tfs,
172 TRUE, FALSE,
173 table, 5, 6, 0,1);
174
175
176 /*lstore = gtk_list_store_new(1, G_TYPE_STRING);
177 for (tmplist = g_list_first(bfwin->session->filegloblist); tmplist; tmplist = g_list_next(tmplist)) {
178 gtk_list_store_append(GTK_LIST_STORE(lstore), &iter);
179 gtk_list_store_set(GTK_LIST_STORE(lstore), &iter, 0, tmplist->data, -1);
180 }
181 tfs->find_pattern = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(lstore), 0);
182 g_object_unref(lstore);*/
183 tfs->find_pattern = combobox_with_popdown("", bfwin->session->filegloblist, TRUE);
184 dialog_mnemonic_label_in_table(_("_Pattern:"), tfs->find_pattern, table, 0, 1, 1, 2);
185 gtk_table_attach_defaults(GTK_TABLE(table), tfs->find_pattern, 1, 5, 1, 2);
186 g_signal_connect(G_OBJECT(tfs->find_pattern), "changed",
187 G_CALLBACK(files_advanced_win_findpattern_changed), tfs);
188
189 table = dialog_table_in_vbox(3, 2, 0, vbox2, FALSE, FALSE, 0);
190
191 tfs->matchname = checkbut_with_value(NULL, tfs->bfwin ? !tfs->bfwin->session->adv_open_matchname : FALSE);
192 dialog_mnemonic_label_in_table(_("Pattern matches _full path:"), tfs->matchname, table, 0, 1, 0, 1);
193 gtk_table_attach(GTK_TABLE(table), tfs->matchname, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
194
195 tfs->recursive = checkbut_with_value(NULL, tfs->bfwin ? tfs->bfwin->session->adv_open_recursive : TRUE);
196 dialog_mnemonic_label_in_table(_("_Recursive:"), tfs->recursive, table, 0, 1, 1, 2);
197 gtk_table_attach(GTK_TABLE(table), tfs->recursive, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
198
199 tfs->max_recursion = spinbut_with_value("100", 1, 100000, 1, 10);
200 dialog_mnemonic_label_in_table(_("Ma_x recursion:"), tfs->max_recursion, table, 0, 1, 2, 3);
201 gtk_table_attach(GTK_TABLE(table), tfs->max_recursion, 1, 2, 2, 3, GTK_SHRINK, GTK_SHRINK, 0, 0);
202
203 alignment = gtk_alignment_new(0, 0, 1, 1);
204 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 12, 18, 12, 6);
205 gtk_box_pack_start(GTK_BOX(carea), alignment, FALSE, FALSE, 0);
206 vbox = gtk_vbox_new(FALSE, 0);
207 gtk_container_add(GTK_CONTAINER(alignment), vbox);
208
209 vbox2 = dialog_vbox_labeled(_("<b>Contains</b>"), vbox);
210
211 table = dialog_table_in_vbox(2, 4, 0, vbox2, FALSE, FALSE, 6);
212
213 tfs->grep_pattern = combobox_with_popdown("", bfwin->session->searchlist, TRUE);
214 dialog_mnemonic_label_in_table(_("Pa_ttern:"), tfs->grep_pattern, table, 0, 1, 0, 1);
215 gtk_table_attach_defaults(GTK_TABLE(table), tfs->grep_pattern, 1, 4, 0, 1);
216
217 tfs->is_regex = checkbut_with_value(NULL, 0);
218 dialog_mnemonic_label_in_table(_("Is rege_x:"), tfs->is_regex, table, 0, 1, 1, 2);
219 gtk_table_attach(GTK_TABLE(table), tfs->is_regex, 1, 2, 1, 2, GTK_FILL, GTK_SHRINK, 0, 0);
220
221 tfs->regexwarn = gtk_label_new(NULL);
222 gtk_table_attach(GTK_TABLE(table), tfs->regexwarn, 1, 2, 2, 3, GTK_FILL, GTK_FILL, 0, 0);
223
224 gtk_dialog_set_response_sensitive(GTK_DIALOG(tfs->dialog), GTK_RESPONSE_ACCEPT, FALSE);
225 gtk_widget_show_all(carea);
226
227 while (gtk_dialog_run(GTK_DIALOG(tfs->dialog)) == GTK_RESPONSE_ACCEPT) {
228 if (files_advanced_win_ok_clicked(tfs))
229 break;
230 }
231
232 gtk_widget_destroy(tfs->dialog);
233 g_free(tfs);
234 }
235
236 void
file_open_advanced_cb(GtkWidget * widget,Tbfwin * bfwin)237 file_open_advanced_cb(GtkWidget * widget, Tbfwin * bfwin)
238 {
239 files_advanced_win(bfwin, NULL);
240 }
241
242 /*************** end of advanced open code *************/
243
244 static void
file_open_ok_lcb(GtkDialog * dialog,gint response,Tbfwin * bfwin)245 file_open_ok_lcb(GtkDialog * dialog, gint response, Tbfwin * bfwin)
246 {
247 if (response == GTK_RESPONSE_ACCEPT) {
248 GSList *slist, *tmpslist;
249 GtkComboBox *combo;
250 bfwin->focus_next_new_doc = TRUE;
251 combo = g_object_get_data(G_OBJECT(dialog), "encodings");
252 if (combo) {
253 GtkTreeIter iter;
254 if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter)) {
255 GtkTreeModel *model;
256 gchar **arr;
257 model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
258 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, 1, &arr, -1);
259 if (bfwin->session->encoding)
260 g_free(bfwin->session->encoding);
261 if (arr) {
262 bfwin->session->encoding = g_strdup(arr[1]);
263 } else {
264 bfwin->session->encoding = NULL;
265 }
266 DEBUG_MSG("file_open_ok_lcb, session encoding is set to %s\n", bfwin->session->encoding);
267 }
268 }
269
270 tmpslist = slist = gtk_file_chooser_get_files(GTK_FILE_CHOOSER(dialog));
271 while (tmpslist) {
272 doc_new_from_uri(bfwin, (GFile *) tmpslist->data, NULL, (slist->next != NULL), FALSE, -1, -1, -1, TRUE, FALSE);
273 g_object_unref((GFile *) tmpslist->data);
274 tmpslist = tmpslist->next;
275 }
276 g_slist_free(slist);
277 }
278 gtk_widget_destroy(GTK_WIDGET(dialog));
279 }
280
281 void
file_open_doc(Tbfwin * bfwin)282 file_open_doc(Tbfwin * bfwin)
283 {
284 GtkWidget *dialog;
285
286 dialog =
287 file_chooser_dialog(bfwin, _("Select files"), GTK_FILE_CHOOSER_ACTION_OPEN, NULL, FALSE, TRUE, NULL,
288 TRUE);
289 g_signal_connect(dialog, "response", G_CALLBACK(file_open_ok_lcb), bfwin);
290 gtk_widget_show_all(dialog);
291 }
292
293 /**
294 * file_open_cb:
295 * @widget: unused #GtkWidget
296 * @bfwin: #Tbfwin* with the current window
297 *
298 * Prompt user for files to open.
299 *
300 * Return value: void
301 **/
302 void
file_open_cb(GtkWidget * widget,Tbfwin * bfwin)303 file_open_cb(GtkWidget * widget, Tbfwin * bfwin)
304 {
305 GtkWidget *dialog;
306 dialog =
307 file_chooser_dialog(bfwin, _("Select files"), GTK_FILE_CHOOSER_ACTION_OPEN, NULL, FALSE, TRUE, NULL,
308 TRUE);
309 g_signal_connect(dialog, "response", G_CALLBACK(file_open_ok_lcb), bfwin);
310 gtk_widget_show_all(dialog);
311 /* if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
312 GSList *slist;
313 bfwin->focus_next_new_doc = TRUE;
314 slist = gtk_file_chooser_get_uris(GTK_FILE_CHOOSER(dialog));
315 docs_new_from_uris(bfwin, slist, FALSE);
316 g_slist_free(slist);
317 }
318 gtk_widget_destroy(dialog);*/
319 }
320
321 typedef struct {
322 Tbfwin *bfwin;
323 GtkWidget *win;
324 GtkWidget *entry;
325 } Tou;
326 static void
open_url_destroy_lcb(GtkWidget * widget,Tou * ou)327 open_url_destroy_lcb(GtkWidget * widget, Tou * ou)
328 {
329 g_free(ou);
330 }
331
332 static void
open_url_cancel_lcb(GtkWidget * widget,Tou * ou)333 open_url_cancel_lcb(GtkWidget * widget, Tou * ou)
334 {
335 gtk_widget_destroy(ou->win);
336 }
337
338 static void
open_url_ok_lcb(GtkWidget * widget,Tou * ou)339 open_url_ok_lcb(GtkWidget * widget, Tou * ou)
340 {
341 gchar *url = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(ou->entry));
342 DEBUG_MSG("open_url_ok_lcb, url=%s\n", url);
343 doc_new_from_input(ou->bfwin, url, FALSE, FALSE, -1);
344 g_free(url);
345 gtk_widget_destroy(ou->win);
346 }
347
348 /**
349 * file_open_url_cb:
350 * @widget: #GtkWidget* ignored
351 * @bfwin: #Tbfwin* bfwin pointer
352 *
353 * opens a dialog where you can enter an URL to open of any kind
354 * supported by gnome-vfs
355 *
356 * Return value: void
357 **/
358 void
file_open_url_cb(GtkAction * action,Tbfwin * bfwin)359 file_open_url_cb(GtkAction * action, Tbfwin * bfwin)
360 {
361 GtkWidget *align, *vbox, *hbox, *but;
362 Tou *ou;
363 GList *urlhistory = NULL, *tmplist = NULL;
364 ou = g_new(Tou, 1);
365 ou->bfwin = bfwin;
366 ou->win =
367 window_full2(_("Open URL"), GTK_WIN_POS_CENTER_ON_PARENT, 12, G_CALLBACK(open_url_destroy_lcb), ou,
368 TRUE, bfwin->main_window);
369 gtk_widget_set_size_request(ou->win, 450, -1);
370 vbox = gtk_vbox_new(FALSE, 5);
371 /* gtk_box_pack_start(GTK_BOX(vbox), bf_label_with_markup(_("<b>Open URL</b>")), FALSE, FALSE, 5);*/
372 gtk_container_add(GTK_CONTAINER(ou->win), vbox);
373 tmplist = g_list_first(bfwin->session->recent_files);
374 while (tmplist) {
375 if (tmplist->data && strlen(tmplist->data) > 5 && strncmp(tmplist->data, "file:", 5) != 0) {
376 urlhistory = g_list_prepend(urlhistory, g_strdup(tmplist->data));
377 }
378 tmplist = g_list_next(tmplist);
379 }
380 ou->entry = boxed_combobox_with_popdown("", urlhistory, TRUE, vbox);
381 free_stringlist(urlhistory);
382 /* ou->entry = boxed_entry_with_text("", 255, vbox); */
383
384 align = gtk_alignment_new(0.0, 1.0, 1.0, 0.0);
385 gtk_alignment_set_padding(GTK_ALIGNMENT(align), 12, 0 ,0, 0);
386 gtk_box_pack_start(GTK_BOX(vbox), align, FALSE, FALSE, 0);
387
388 #if GTK_CHECK_VERSION(3,0,0)
389 hbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
390 #else
391 hbox = gtk_hbutton_box_new();
392 #endif
393 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
394 gtk_box_set_spacing(GTK_BOX(hbox), 6);
395 gtk_container_add(GTK_CONTAINER(align), hbox);
396 but = bf_stock_cancel_button(G_CALLBACK(open_url_cancel_lcb), ou);
397 gtk_box_pack_start(GTK_BOX(hbox), but, FALSE, TRUE, 0);
398 but = bf_stock_ok_button(G_CALLBACK(open_url_ok_lcb), ou);
399 gtk_box_pack_start(GTK_BOX(hbox), but, FALSE, TRUE, 0);
400 gtk_window_set_default(GTK_WINDOW(ou->win), but);
401 gtk_widget_show_all(ou->win);
402 }
403
404 /***********************************/
405 /* async save code */
406
407 typedef struct {
408 Tdocument *doc;
409 GFile *unlink_uri;
410 GFile *fbrefresh_uri;
411 Tdocsave_mode savemode;
412 } Tdocsavebackend;
413
414 static void
docsavebackend_cleanup(Tdocsavebackend * dsb)415 docsavebackend_cleanup(Tdocsavebackend * dsb)
416 {
417 if (dsb->unlink_uri)
418 g_object_unref(dsb->unlink_uri);
419 if (dsb->fbrefresh_uri)
420 g_object_unref(dsb->fbrefresh_uri);
421 g_free(dsb);
422 }
423
424 static void
docsavebackend_async_unlink_lcb(gpointer data)425 docsavebackend_async_unlink_lcb(gpointer data)
426 {
427 Tdocsavebackend *dsb = data;
428 fb2_refresh_parent_of_uri(dsb->unlink_uri);
429 docsavebackend_cleanup(dsb);
430 }
431
432 static TcheckNsave_return
doc_checkNsave_lcb(TcheckNsave_status status,GError * gerror,gpointer data)433 doc_checkNsave_lcb(TcheckNsave_status status, GError * gerror, gpointer data)
434 {
435 Tdocsavebackend *dsb = data;
436 Tdocument *doc = dsb->doc;
437 gchar *errmessage;
438 DEBUG_MSG("doc_checkNsave_lcb, doc=%p, status=%d, doc->uri=%p, simplesearch_snr3run=%p\n", doc, status, doc->uri, BFWIN(doc->bfwin)->simplesearch_snr3run);
439 switch (status) {
440 case CHECKANDSAVE_ERROR_NOBACKUP:
441 if (main_v->props.backup_abort_action == 0) {
442 return CHECKNSAVE_CONT;
443 } else if (main_v->props.backup_abort_action == 1) {
444 doc->save = NULL;
445 gtk_text_view_set_editable(GTK_TEXT_VIEW(doc->view), TRUE);
446 return CHECKNSAVE_STOP;
447 } else { /* if (main_v->props.backup_abort_action == 2) */
448
449 /* we have to ask the user */
450 const gchar *buttons[] = { _("_Abort save"), _("_Continue save"), NULL };
451 gint retval;
452 gchar *tmpstr =
453 g_strdup_printf(_
454 ("A backupfile for %s could not be created. If you continue, this file will be overwritten."),
455 gtk_label_get_text(GTK_LABEL(doc->tab_label)));
456 retval =
457 message_dialog_new_multi(BFWIN(doc->bfwin)->main_window, GTK_MESSAGE_WARNING, buttons,
458 _("File backup failure"), tmpstr);
459 g_free(tmpstr);
460 DEBUG_MSG("doc_checkNsave_lcb, retval=%d, returning %d\n", retval,
461 (retval == 0) ? CHECKNSAVE_STOP : CHECKNSAVE_CONT);
462 if (retval == 0) {
463 doc->save = NULL;
464 gtk_text_view_set_editable(GTK_TEXT_VIEW(doc->view), TRUE);
465 return CHECKNSAVE_STOP;
466 }
467 return CHECKNSAVE_CONT;
468 }
469 break;
470 case CHECKANDSAVE_ERROR:
471 case CHECKANDSAVE_ERROR_NOWRITE:
472 {
473 errmessage =
474 g_strconcat(_("Could not save file "), gtk_label_get_text(GTK_LABEL(doc->tab_label)), NULL);
475 message_dialog_new(BFWIN(doc->bfwin)->main_window, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
476 errmessage, gerror->message);
477 g_free(errmessage);
478 }
479 /* no break - fall through */
480 case CHECKANDSAVE_ERROR_CANCELLED:
481 doc->save = NULL;
482 gtk_text_view_set_editable(GTK_TEXT_VIEW(doc->view), TRUE);
483 docsavebackend_cleanup(dsb);
484 break;
485 case CHECKANDSAVE_ERROR_MODIFIED:
486 {
487 /* we have to ask the user what to do */
488 const gchar *buttons[] = { _("_Abort save"), _("_Continue save"), NULL };
489 GFileInfo *newfinfo;
490 GError *gerror = NULL;
491 gint retval;
492 gchar *tmpstr, *utf8uri;
493
494 newfinfo =
495 g_file_query_info(doc->uri, G_FILE_ATTRIBUTE_STANDARD_SIZE "," G_FILE_ATTRIBUTE_TIME_MODIFIED,
496 0, NULL, &gerror);
497 if (gerror) {
498 g_warning("file was modified on disk, but now an error??");
499 g_error_free(gerror);
500 return CHECKNSAVE_CONT;
501 }
502 utf8uri = g_file_get_uri(doc->uri);
503 tmpstr = modified_on_disk_warning_string(utf8uri, doc->fileinfo, newfinfo);
504 /*g_strdup_printf(_("File %s has been modified on disk, overwrite?"), utf8uri); */
505 g_free(utf8uri);
506 g_object_unref(newfinfo);
507 retval =
508 message_dialog_new_multi(BFWIN(doc->bfwin)->main_window, GTK_MESSAGE_WARNING, buttons,
509 _("File changed on disk\n"), tmpstr);
510 g_free(tmpstr);
511 if (retval == 0) {
512 doc->save = NULL;
513 gtk_text_view_set_editable(GTK_TEXT_VIEW(doc->view), TRUE);
514 return CHECKNSAVE_STOP;
515 }
516 return CHECKNSAVE_CONT;
517 }
518 case CHECKANDSAVE_FINISHED:
519 if (dsb->unlink_uri) {
520 file_delete_async(dsb->unlink_uri, FALSE, docsavebackend_async_unlink_lcb, dsb);
521 }
522 /* if the user wanted to close the doc we should do very diffferent things here !! */
523 doc->save = NULL;
524 if (doc->close_doc) {
525 Tbfwin *bfwin = doc->bfwin;
526 gboolean close_window = doc->close_window;
527 doc_destroy(doc, doc->close_window);
528 if (close_window && test_only_empty_doc_left(bfwin->documentlist)) {
529 bfwin_destroy_and_cleanup(bfwin);
530 }
531 return CHECKNSAVE_STOP; /* it actually doesn't matter what we return, this was the last callback anyway */
532 } else {
533 /* YES! we're done! update the fileinfo ! */
534 gtk_text_view_set_editable(GTK_TEXT_VIEW(doc->view), TRUE);
535 if (dsb->savemode != docsave_copy) {
536 DEBUG_MSG("doc_checkNsave_lcb, re-set async doc->fileinfo (current=%p) for uri %p\n", doc->fileinfo, doc->uri);
537 if (doc->fileinfo)
538 g_object_unref(doc->fileinfo);
539 doc->fileinfo = NULL;
540 file_doc_fill_fileinfo(doc, doc->uri);
541 if (main_v->props.clear_undo_on_save) {
542 doc_unre_clear_all(doc);
543 } else {
544 doc_unre_clear_not_modified(doc);
545 }
546 doc_set_modified(doc, 0);
547 }
548 /* in fact the filebrowser should also be refreshed if the document was closed, but
549 when a document is closed, the filebrowser is anyway refreshed (hmm perhaps only if
550 'follow document focus' is set). */
551 if (dsb->unlink_uri && dsb->fbrefresh_uri) {
552 GFile *parent1, *parent2;
553 parent1 = g_file_get_parent(dsb->unlink_uri);
554 parent2 = g_file_get_parent(dsb->fbrefresh_uri);
555 if (!g_file_equal(parent1, parent2)) {
556 /* if they are equal, the directory will be refreshed by the unlink callback */
557 filetreemodel_refresh_uri_async(FB2CONFIG(main_v->fb2config)->ftm, parent2);
558 }
559 g_object_unref(parent1);
560 g_object_unref(parent2);
561 } else if (dsb->fbrefresh_uri) {
562 fb2_refresh_parent_of_uri(dsb->fbrefresh_uri);
563 }
564 }
565 if (!dsb->unlink_uri) {
566 /* if there is an unlink uri, that means the unlink callback will free the dsb structure */
567 docsavebackend_cleanup(dsb);
568 }
569 break;
570 }
571 return CHECKNSAVE_CONT;
572 }
573
574 /**
575 * ask_new_filename:
576 * @bfwin: #Tbfwin* mainly used to set the dialog transient
577 * @oldfilename: #gchar* with the old filename
578 * @gui_name: #const gchar* with the name of the file used in the GUI
579 * @is_move: #gboolean if the title should be move or save as
580 *
581 * returns a newly allocated string with a new filename
582 *
583 * if a file with the selected name name was
584 * open already it will ask the user what to do, return NULL if
585 * the user wants to abort, or will remove the name of the other file if the user wants
586 * to continue
587 *
588 * Return value: gchar* with newly allocated string, or NULL on failure or abort
589 **/
590 gchar *
ask_new_filename(Tbfwin * bfwin,const gchar * old_curi,const gchar * dialogtext)591 ask_new_filename(Tbfwin * bfwin, const gchar * old_curi, const gchar *dialogtext)
592 {
593 Tdocument *exdoc;
594 GList *alldocs;
595 gchar *new_curi = NULL;
596 GFile *uri;
597 GtkWidget *dialog;
598
599
600 dialog =
601 file_chooser_dialog(bfwin, dialogtext, GTK_FILE_CHOOSER_ACTION_SAVE, old_curi, FALSE, FALSE, NULL,
602 FALSE);
603 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
604 new_curi = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
605 }
606 gtk_widget_destroy(dialog);
607
608 if (!new_curi)
609 return NULL;
610 if (old_curi && strcmp(old_curi, new_curi) == 0) {
611 g_free(new_curi);
612 return NULL;
613 }
614
615 alldocs = return_allwindows_documentlist();
616 uri = g_file_new_for_uri(new_curi);
617 exdoc = documentlist_return_document_from_uri(alldocs, uri);
618 g_object_unref(uri);
619 g_list_free(alldocs);
620 DEBUG_MSG("ask_new_filename, exdoc=%p, newfilename=%s\n", exdoc, new_curi);
621 if (exdoc) {
622 gchar *tmpstr;
623 gint retval;
624 const gchar *buttons[] = { _("_Cancel"), _("_Overwrite"), NULL };
625 tmpstr = g_strdup_printf(_("File %s exists and is open, overwrite?"), new_curi);
626 retval =
627 message_dialog_new_multi(bfwin->main_window, GTK_MESSAGE_WARNING, buttons, tmpstr,
628 _("The file you have selected is being edited in Bluefish."));
629 g_free(tmpstr);
630 if (retval == 0) {
631 g_free(new_curi);
632 return NULL;
633 } else {
634 document_unset_filename(exdoc);
635 }
636 } else {
637 GFile *tmp;
638 gboolean exists;
639 tmp = g_file_new_for_uri(new_curi);
640 exists = g_file_query_exists(tmp, NULL);
641 g_object_unref(tmp);
642 if (exists) {
643 gchar *tmpstr;
644 gint retval;
645 const gchar *buttons[] = { _("_Cancel"), _("_Overwrite"), NULL };
646 tmpstr = g_strdup_printf(_("A file named \"%s\" already exists."), new_curi);
647 retval =
648 message_dialog_new_multi(bfwin->main_window, GTK_MESSAGE_WARNING, buttons, tmpstr,
649 _("Do you want to replace the existing file?"));
650 g_free(tmpstr);
651 if (retval == 0) {
652 g_free(new_curi);
653 return NULL;
654 }
655 }
656 }
657 return new_curi;
658 }
659
660 static void
session_set_savedir(Tbfwin * bfwin,GFile * uri)661 session_set_savedir(Tbfwin * bfwin, GFile *uri)
662 {
663 if (uri) {
664 GFile *parent = g_file_get_parent(uri);
665 if (bfwin->session->savedir)
666 g_free(bfwin->session->savedir);
667 bfwin->session->savedir = g_file_get_uri(parent);
668 }
669 }
670
671 void
doc_save_backend(Tdocument * doc,Tdocsave_mode savemode,gboolean close_doc,gboolean close_window)672 doc_save_backend(Tdocument * doc, Tdocsave_mode savemode, gboolean close_doc,
673 gboolean close_window)
674 {
675 gchar *tmp;
676 Trefcpointer *buffer;
677 gsize numbytes=0;
678 Tdocsavebackend *dsb;
679 GFile *dest_uri=NULL;
680 GFileInfo *dest_finfo=NULL;
681 gboolean firstsave;
682 DEBUG_MSG
683 ("doc_save_backend, started for doc %p, mode=%d, close_doc=%d, close_window=%d\n", doc,
684 savemode, close_doc, close_window);
685
686 if (doc->readonly && savemode == docsave_normal) {
687 g_print("Cannot save readonly document !?!?");
688 return;
689 }
690
691 dsb = g_new0(Tdocsavebackend, 1);
692 dsb->doc = doc;
693 dsb->savemode = savemode;
694 if (main_v->props.editor_spacingtoclick) {
695 bluefish_text_view_remove_spacingtoclick(BLUEFISH_TEXT_VIEW(doc->view));
696 }
697
698 /* should be moved to a plugin interface, because this is HTML specific */
699 /* update author meta tag */
700 if (main_v->props.auto_update_meta_author) {
701 const gchar *realname = g_get_real_name();
702 if (realname && strlen(realname) > 0) {
703 gchar *author_tmp;
704 author_tmp = g_strconcat("<meta name=\"author\" content=\"", realname, "\" ", NULL);
705 snr3_run_extern_replace(doc,
706 "<meta[ \t\n]+name[ \t\n]*=[ \t\n]*\"author\"[ \t\n]+content[ \t\n]*=[ \t\n]*\"[^\"]*\"[ \t\n]*",
707 snr3scope_doc, snr3type_pcre, FALSE, author_tmp, FALSE, FALSE);
708 g_free(author_tmp);
709 }
710 }
711
712 /* update date meta tag */
713 if (main_v->props.auto_update_meta_date) {
714 time_t time_var;
715 struct tm *time_struct;
716 gchar isotime[60];
717 gchar *date_tmp;
718
719 time_var = time(NULL);
720 time_struct = localtime(&time_var);
721 #ifdef WIN32
722 {
723 glong hours, mins;
724 gchar gmtsign;
725 gchar tmptime[50];
726
727 strftime(tmptime, 30, "%Y-%m-%dT%H:%M:%S", time_struct);
728 gmtsign = _timezone > 0 ? '-' : '+';
729 hours = abs(_timezone) / 3600;
730 mins = (abs(_timezone) % 3600) / 60;
731 sprintf(isotime, "%s%c%02ld%02ld", tmptime, gmtsign, hours, mins);
732 }
733 #else /* WIN32 */
734 strftime(isotime, 30, "%Y-%m-%dT%H:%M:%S%z", time_struct);
735 #endif /* WIN32 */
736 DEBUG_MSG("doc_save_backend, ISO-8601 time %s\n", isotime);
737
738 date_tmp = g_strconcat("<meta name=\"date\" content=\"", isotime, "\" ", NULL);
739 snr3_run_extern_replace(doc,
740 "<meta[ \t\n]+name[ \t\n]*=[ \t\n]*\"date\"[ \t\n]+content[ \t\n]*=[ \t\n]*\"[^\"]*\"[ \t\n]*",
741 snr3scope_doc, snr3type_pcre, FALSE, date_tmp, FALSE, FALSE);
742 g_free(date_tmp);
743 }
744
745 /* update generator meta tag */
746 if (main_v->props.auto_update_meta_generator) {
747 snr3_run_extern_replace(doc,
748 "<meta[ \t\n]+name[ \t\n]*=[ \t\n]*\"generator\"[ \t\n]+content[ \t\n]*=[ \t\n]*\"[^\"]*\"[ \t\n]*",
749 snr3scope_doc, snr3type_pcre, FALSE,
750 "<meta name=\"generator\" content=\"Bluefish " VERSION "\" ", FALSE, FALSE);
751 }
752 if (main_v->props.strip_trailing_spaces_on_save) {
753 strip_trailing_spaces(doc);
754 }
755
756 if (doc->save) {
757 gchar *errmessage;
758 /* this message is not in very nice english I'm afraid */
759 errmessage =
760 g_strconcat(_("File:\n\""), gtk_label_get_text(GTK_LABEL(doc->tab_label)),
761 _("\" save is in progress"), NULL);
762 message_dialog_new(BFWIN(doc->bfwin)->main_window, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
763 _("Save in progress!"), errmessage);
764 g_free(errmessage);
765 g_free(dsb);
766 DEBUG_MSG("doc_save_backend, already save in progress, return\n");
767 return;
768 }
769 firstsave = (doc->uri == NULL && savemode == docsave_normal);
770 if (firstsave || savemode != docsave_normal) {
771 gchar *newfilename, *curi, *dialogtext;
772 const gchar *gui_name = gtk_label_get_text(GTK_LABEL(doc->tab_label));
773 DEBUG_MSG("doc_save_backend, no uri (doc->uri=%p), or saveas/copy/move (savemode=%d)\n", doc->uri, savemode);
774 curi = doc->uri ? g_file_get_uri(doc->uri) : g_strdup(gui_name); /* SaveFile dialog shows weird behavior if NULL is passed as suggested filename */
775 if (savemode == docsave_normal) {
776 dialogtext = g_strdup(_("Save"));
777 } else if (savemode == docsave_saveas){
778 dialogtext = g_strdup_printf(_("Save %s as"), gui_name);
779 } else if (savemode == docsave_move){
780 dialogtext = g_strdup_printf(_("Move/rename %s to"), gui_name);
781 } else {
782 dialogtext = g_strdup_printf(_("Save copy of %s"), gui_name);
783 }
784 newfilename =
785 ask_new_filename(BFWIN(doc->bfwin), curi, dialogtext);
786 g_free(dialogtext);
787 g_free(curi);
788 if (!newfilename) {
789 DEBUG_MSG("doc_save_backend, no newfilename, return\n");
790 g_free(dsb);
791 return;
792 }
793 dest_uri = g_file_new_for_uri(newfilename);
794 if (doc->uri == NULL || savemode == docsave_saveas || savemode == docsave_move) {
795 if (doc->uri) {
796 if (savemode == docsave_move) {
797 dsb->unlink_uri = doc->uri; /* unlink and refresh this uri later */
798 g_object_ref(dsb->unlink_uri);
799 }
800 }
801 doc_set_uri(doc, dest_uri, FALSE);
802 dest_finfo = doc->fileinfo;
803 }
804 DEBUG_MSG("doc_save_backend, newfilename=%s, dest_uri=%p, doc->uri=%p\n",newfilename, dest_uri, doc->uri);
805 g_free(newfilename);
806 } else {
807 DEBUG_MSG("doc_save_backend, have uri and normal save\n");
808 dest_uri = doc->uri;
809 dest_finfo = doc->fileinfo;
810 g_object_ref(dest_uri);
811 }
812 DEBUG_MSG("doc_save_backend, dest_uri=%p\n",dest_uri);
813 if ((firstsave || savemode != docsave_normal)&& dest_uri) {
814 dsb->fbrefresh_uri = dest_uri; /* refresh this uri later */
815 g_object_ref(dest_uri);
816 }
817
818 session_set_savedir(doc->bfwin, dest_uri);
819
820 tmp = doc_get_buffer_in_encoding(doc, &numbytes);
821 if (!tmp) {
822 DEBUG_MSG("doc_save_backend, got NULL after encoding, ABORT\n");
823 g_warning("got NULL buffer after encoding, abort save\n");
824 g_free(dsb);
825 return;
826 }
827
828 buffer = refcpointer_new(tmp);
829 doc->close_doc = close_doc;
830 doc->close_window = close_window;
831 gtk_text_view_set_editable(GTK_TEXT_VIEW(doc->view), FALSE);
832 DEBUG_MSG("doc_save_backend, calling file_checkNsave_uri_async with uri %p for %zd bytes\n", dest_uri, strlen(buffer->data));
833 doc->save =
834 file_checkNsave_uri_async(dest_uri, dest_finfo, buffer, numbytes, (savemode == docsave_normal && main_v->props.check_for_modified_on_disk!=0),
835 main_v->props.backup_file, doc_checkNsave_lcb, dsb, doc->bfwin);
836
837 if (firstsave || savemode == docsave_saveas || savemode == docsave_move) {
838 if(doc->readonly) {
839 doc_set_readonly(doc, FALSE);
840 }
841 doc_reset_filetype(doc, doc->uri, buffer->data, numbytes);
842 doc_set_title(doc, NULL);
843 doc_force_activate(doc);
844 }
845 g_object_unref(dest_uri);
846 refcpointer_unref(buffer);
847 }
848
849 /**
850 * file_save_cb:
851 * @widget: unused #GtkWidget
852 * @bfwin: #Tbfwin* with the current window
853 *
854 * Save the current document.
855 *
856 * Return value: void
857 **/
858 void
file_save_cb(GtkWidget * widget,Tbfwin * bfwin)859 file_save_cb(GtkWidget * widget, Tbfwin * bfwin)
860 {
861 if (bfwin->current_document)
862 doc_save_backend(bfwin->current_document, docsave_normal, FALSE, FALSE);
863 }
864
865 /**
866 * file_save_as_cb:
867 * @widget: unused #GtkWidget
868 * @bfwin: #Tbfwin* with the current window
869 *
870 * Save current document, let user choose filename.
871 *
872 * Return value: void
873 **/
874 void
file_save_as_cb(GtkWidget * widget,Tbfwin * bfwin)875 file_save_as_cb(GtkWidget * widget, Tbfwin * bfwin)
876 {
877 if (bfwin->current_document)
878 doc_save_backend(bfwin->current_document, docsave_saveas, FALSE, FALSE);
879 }
880
881 /**
882 * file_move_to_cb:
883 * @widget: unused #GtkWidget
884 * @bfwin: #Tbfwin* with the current window
885 *
886 * Move current document, let user choose filename.
887 *
888 * Return value: void
889 **/
890 void
file_move_to_cb(GtkWidget * widget,Tbfwin * bfwin)891 file_move_to_cb(GtkWidget * widget, Tbfwin * bfwin)
892 {
893 if (bfwin->current_document)
894 doc_save_backend(bfwin->current_document, docsave_move, FALSE, FALSE);
895 }
896
897 void
file_save_all(Tbfwin * bfwin)898 file_save_all(Tbfwin * bfwin)
899 {
900 GList *tmplist;
901 Tdocument *tmpdoc;
902
903 tmplist = g_list_first(bfwin->documentlist);
904 while (tmplist) {
905 tmpdoc = (Tdocument *) tmplist->data;
906 if (tmpdoc->modified) {
907 doc_save_backend(tmpdoc, docsave_normal, FALSE, FALSE);
908 }
909 tmplist = g_list_next(tmplist);
910 }
911 }
912
913 /**
914 * file_save_all_cb:
915 * @widget: unused #GtkWidget
916 * @bfwin: the #Tbfwin* window pointer
917 *
918 * Save all editor notebooks
919 *
920 * Return value: void
921 **/
922 void
file_save_all_cb(GtkWidget * widget,Tbfwin * bfwin)923 file_save_all_cb(GtkWidget * widget, Tbfwin * bfwin)
924 {
925 GList *tmplist;
926 Tdocument *tmpdoc;
927
928 tmplist = g_list_first(bfwin->documentlist);
929 while (tmplist) {
930 tmpdoc = (Tdocument *) tmplist->data;
931 if (tmpdoc->modified) {
932 doc_save_backend(tmpdoc, docsave_normal, FALSE, FALSE);
933 }
934 tmplist = g_list_next(tmplist);
935 }
936 }
937
938 gint
doc_modified_dialog(Tdocument * doc)939 doc_modified_dialog(Tdocument * doc)
940 {
941 const gchar *buttons[] = { _("Close _Without Saving"), GTK_STOCK_CANCEL, GTK_STOCK_SAVE, NULL };
942 gchar *text;
943 gint retval;
944 text = g_strdup_printf(_("Save changes to \"%s\" before closing?"),
945 gtk_label_get_text(GTK_LABEL(doc->tab_label)));
946 retval = message_dialog_new_multi(BFWIN(doc->bfwin)->main_window, GTK_MESSAGE_QUESTION, buttons, text,
947 _("If you don't save your changes they will be lost."));
948 g_free(text);
949 return retval;
950 }
951
952 gint
project_not_found_dialog(Tbfwin * bfwin,GFile * uri)953 project_not_found_dialog(Tbfwin * bfwin, GFile * uri)
954 {
955 gchar *tmpstr;
956 gint retval;
957 const gchar *buttons[] = { GTK_STOCK_CANCEL, _("_Reopen"), NULL };
958 gchar *path = g_file_get_parse_name(uri);
959 tmpstr = g_strdup_printf(_("Project \"%s\" failed to load"), path);
960 retval = message_dialog_new_multi(bfwin->main_window, GTK_MESSAGE_WARNING, buttons, tmpstr,
961 _("Do you want to reopen project from another location?"));
962 g_free(tmpstr);
963 g_free(path);
964 return retval;
965 }
966
967 Tclose_mode
multiple_files_modified_dialog(Tbfwin * bfwin)968 multiple_files_modified_dialog(Tbfwin * bfwin)
969 {
970 const gchar *buttons[] = { _("Choose per _File"), _("Close _All"), _("_Cancel"), _("_Save All"), NULL };
971 int retval = message_dialog_new_multi(bfwin->main_window,
972 GTK_MESSAGE_QUESTION, buttons,
973 _("One or more open files have been changed."),
974 _("If you don't save your changes they will be lost."));
975 return (Tclose_mode) retval;
976 }
977
978 /* return TRUE if all are either closed or saved
979 return FALSE on cancel*/
980 gboolean
choose_per_file(Tbfwin * bfwin,gboolean close_window)981 choose_per_file(Tbfwin * bfwin, gboolean close_window)
982 {
983 GList *duplist, *tmplist;
984 duplist = g_list_copy(bfwin->documentlist);
985 tmplist = g_list_first(duplist);
986 while (tmplist) {
987 gint retval;
988 Tdocument *tmpdoc = (Tdocument *) tmplist->data;
989 DEBUG_MSG("choose_per_file, tmpdoc=%p\n", tmpdoc);
990 if (tmpdoc->modified) {
991 retval = doc_modified_dialog(tmpdoc);
992 switch (retval) {
993 case 0: /* close */
994 DEBUG_MSG("choose_per_file, call doc_close\n");
995 tmpdoc->modified = FALSE;
996 doc_close_single_backend(tmpdoc, TRUE, close_window);
997 break;
998 case 1: /* cancel */
999 return FALSE;
1000 break;
1001 case 2: /* save */
1002 DEBUG_MSG("choose_per_file, call doc_save\n");
1003 doc_save_backend(tmpdoc, docsave_normal, TRUE, close_window);
1004 break;
1005 }
1006 } else {
1007 doc_close_single_backend(tmpdoc, TRUE, close_window);
1008 }
1009 tmplist = g_list_next(tmplist);
1010 }
1011 g_list_free(duplist);
1012 return TRUE;
1013 }
1014
1015 gboolean
doc_close_single_backend(Tdocument * doc,gboolean delay_activate,gboolean close_window)1016 doc_close_single_backend(Tdocument * doc, gboolean delay_activate, gboolean close_window)
1017 {
1018 Tbfwin *bfwin = doc->bfwin;
1019 if (doc->checkmodified)
1020 checkmodified_cancel(doc->checkmodified);
1021 if (doc->autosave_progress || doc->autosaved || doc->need_autosave)
1022 remove_autosave(doc);
1023 if (doc->load != NULL || doc->info != NULL) {
1024 /* we should cancel the action now..., and then let the callbacks close it...
1025 the order is important, because the info callback will not close the document,
1026 only the load callback will call doc_close_single_backend */
1027 doc->close_doc = TRUE;
1028 doc->close_window = close_window;
1029 if (doc->info)
1030 file_asyncfileinfo_cancel(doc->info);
1031 if (doc->load)
1032 file2doc_cancel(doc->load);
1033 /* we will not cancel save operations, because it might corrupt the file, let
1034 them just timeout */
1035 DEBUG_MSG("doc_close_single_backend, cancelled load/info and set close_doc to TRUE, returning now\n");
1036 return FALSE;
1037 }
1038 if (doc->autosaved || doc->autosave_progress || doc->need_autosave) {
1039 remove_autosave(doc);
1040 }
1041 if (doc_is_empty_non_modified_and_nameless(doc)
1042 && g_list_length(BFWIN(doc->bfwin)->documentlist) <= 1) {
1043 if (close_window) {
1044 bfwin_destroy_and_cleanup(BFWIN(doc->bfwin));
1045 }
1046 DEBUG_MSG("doc_close_single_backend, doc_is_empty_non_modified_and_nameless returned TRUE, return TRUE\n");
1047 return TRUE;
1048 }
1049 if (doc->modified) {
1050 gint retval = doc_modified_dialog(doc);
1051 switch (retval) {
1052 case 0:
1053 doc_destroy(doc, close_window || delay_activate);
1054 break;
1055 case 1:
1056 return FALSE;
1057 break;
1058 case 2:
1059 doc_save_backend(doc, docsave_normal, TRUE, close_window);
1060 break;
1061 }
1062 } else {
1063 doc_destroy(doc, close_window || delay_activate);
1064 }
1065 if (close_window && bfwin->documentlist == NULL) { /* the documentlist is empty */
1066 bfwin_destroy_and_cleanup(bfwin);
1067 }
1068 DEBUG_MSG("doc_close_single_backend, finished!\n");
1069 return TRUE;
1070 }
1071
1072 void
doc_save_all_close(Tbfwin * bfwin)1073 doc_save_all_close(Tbfwin * bfwin)
1074 {
1075 GList *tmplist, *duplist;
1076 Tdocument *tmpdoc;
1077 duplist = g_list_copy(bfwin->documentlist); /* Copy ducumentlist first, since using just tmplist causes unexpected behavior as docs are destroyed */
1078 tmplist = g_list_first(duplist);
1079 while (tmplist) {
1080 Tdocument *tmpdoc = (Tdocument *) tmplist->data;
1081 #ifdef MAC_INTEGRATION
1082 if (tmpdoc->uri == NULL && main_v->osx_status == 1 ) { /* if osx app is terminating we do not save untitled files that does not have uri */
1083 DEBUG_MSG("bfwin_osx_terminate_event, closing untitled document\n");
1084 tmpdoc->modified = FALSE;
1085 doc_close_single_backend(tmpdoc, TRUE, TRUE); }
1086 else {
1087 DEBUG_MSG("bfwin_osx_terminate_event, saving document\n");
1088 doc_save_backend(tmpdoc, docsave_normal, TRUE, TRUE);
1089 }
1090 #else
1091 doc_save_backend(tmpdoc, docsave_normal, TRUE, TRUE);
1092 #endif
1093 tmplist = g_list_next(tmplist);
1094 }
1095 g_list_free(duplist);
1096 }
1097
1098 /**
1099 * file_close_cb:
1100 * @widget: unused #GtkWidget
1101 * @data: unused #gpointer
1102 *
1103 * Close the current document.
1104 *
1105 * Return value: void
1106 **/
1107 void
file_close_cb(GtkWidget * widget,Tbfwin * bfwin)1108 file_close_cb(GtkWidget * widget, Tbfwin * bfwin)
1109 {
1110 if (bfwin->current_document)
1111 doc_close_single_backend(bfwin->current_document, FALSE, FALSE);
1112 }
1113
1114 void
doc_close_multiple_backend(Tbfwin * bfwin,gboolean close_window,Tclose_mode close_mode)1115 doc_close_multiple_backend(Tbfwin * bfwin, gboolean close_window, Tclose_mode close_mode)
1116 {
1117 GList *tmplist, *duplist;
1118 Tdocument *tmpdoc;
1119
1120 /* if (g_list_length(bfwin->documentlist) == 1) {
1121 return doc_close_single_backend(bfwin->current_document, FALSE, close_window);
1122 }
1123 if (have_modified_documents(bfwin->documentlist)) {
1124 retval = multiple_files_modified_dialog(bfwin);
1125 if (retval == 2) {
1126 return FALSE;
1127 }
1128 }*/
1129
1130 /* we duplicate the documentlist so we can safely walk trough the list, in
1131 our duplicate list there is no chance that the list is changed during the time
1132 we walk the list */
1133 duplist = g_list_copy(bfwin->documentlist);
1134 tmplist = g_list_first(duplist);
1135 while (tmplist) {
1136 tmpdoc = (Tdocument *) tmplist->data;
1137 if (close_mode == close_mode_close_all) {
1138 /* fake that this document was not modified */
1139 tmpdoc->modified = FALSE;
1140 doc_close_single_backend(tmpdoc, TRUE, close_window);
1141 } else if (close_mode == close_mode_save_all) {
1142 doc_save_backend(tmpdoc, docsave_normal, TRUE, close_window);
1143 }
1144 tmplist = g_list_next(tmplist);
1145 }
1146 g_list_free(duplist);
1147 DEBUG_MSG("doc_close_multiple_backend, finished\n");
1148 if (!close_window)
1149 bfwin_notebook_changed(bfwin, -1);
1150 }
1151
1152 void
file_close_all(Tbfwin * bfwin)1153 file_close_all(Tbfwin * bfwin)
1154 {
1155 if (have_modified_documents(bfwin->documentlist)) {
1156 Tclose_mode retval = multiple_files_modified_dialog(bfwin);
1157 switch (retval) {
1158 case close_mode_cancel:
1159 return;
1160 break;
1161 case close_mode_per_file:
1162 choose_per_file(bfwin, FALSE);
1163 break;
1164 case close_mode_save_all:
1165 case close_mode_close_all:
1166 doc_close_multiple_backend(bfwin, FALSE, retval);
1167 break;
1168 }
1169 } else {
1170 doc_close_multiple_backend(bfwin, FALSE, close_mode_close_all);
1171 }
1172 }
1173
1174 /**
1175 * file_close_all_cb:
1176 * @widget: unused #GtkWidget
1177 * @bfwin: #Tbfwin*
1178 *
1179 * Close all open files. Prompt user when neccessary.
1180 *
1181 * Return value: void
1182 **/
1183 void
file_close_all_cb(GtkWidget * widget,Tbfwin * bfwin)1184 file_close_all_cb(GtkWidget * widget, Tbfwin * bfwin)
1185 {
1186 if (have_modified_documents(bfwin->documentlist)) {
1187 Tclose_mode retval = multiple_files_modified_dialog(bfwin);
1188 switch (retval) {
1189 case close_mode_cancel:
1190 return;
1191 break;
1192 case close_mode_per_file:
1193 choose_per_file(bfwin, FALSE);
1194 break;
1195 case close_mode_save_all:
1196 case close_mode_close_all:
1197 doc_close_multiple_backend(bfwin, FALSE, retval);
1198 break;
1199 }
1200 } else {
1201 doc_close_multiple_backend(bfwin, FALSE, close_mode_close_all);
1202 }
1203 }
1204
1205 void
file_new_doc(Tbfwin * bfwin)1206 file_new_doc(Tbfwin * bfwin)
1207 {
1208 Tdocument *doc;
1209 GFile *template = NULL;
1210
1211 if (bfwin->session->template && bfwin->session->template[0]) {
1212 template = g_file_new_for_commandline_arg(bfwin->session->template);
1213 bfwin->focus_next_new_doc = TRUE;
1214 }
1215 doc = doc_new_with_template(bfwin, template, TRUE);
1216 if (!template && doc != bfwin->current_document) {
1217 /* for a template this will be done by the template loader callback */
1218 bfwin_switch_to_document_by_pointer(bfwin, doc);
1219 }
1220 }
1221
1222 /**
1223 * file_new_cb:
1224 * @windget: #GtkWidget* ignored
1225 * @bfwin: Tbfwin* where to open the new document
1226 *
1227 * Create a new, empty file in window bfwin
1228 *
1229 * Return value: void
1230 **/
1231 void
file_new_cb(GtkWidget * widget,Tbfwin * bfwin)1232 file_new_cb(GtkWidget * widget, Tbfwin * bfwin)
1233 {
1234 file_new_doc(bfwin);
1235 }
1236
1237 static void
file_reload_all_modified_check_lcb(Tcheckmodified_status status,GError * gerror,GFileInfo * orig,GFileInfo * new,gpointer user_data)1238 file_reload_all_modified_check_lcb(Tcheckmodified_status status, GError * gerror,
1239 GFileInfo * orig, GFileInfo * new, gpointer user_data)
1240 {
1241 if (status == CHECKMODIFIED_MODIFIED) {
1242 DEBUG_MSG("file_reload_all_modified_check_lcb, reload %p\n", user_data);
1243 doc_reload(DOCUMENT(user_data), new, FALSE);
1244 }
1245 }
1246
1247 void
file_reload_all_modified(Tbfwin * bfwin)1248 file_reload_all_modified(Tbfwin * bfwin)
1249 {
1250 GList *tmplist = g_list_first(bfwin->documentlist);
1251 while (tmplist) {
1252 if (DOCUMENT(tmplist->data)->uri && DOCUMENT(tmplist->data)->status == DOC_STATUS_COMPLETE) {
1253 DEBUG_MSG("file_reload_all_modified, check %p\n", tmplist->data);
1254 file_checkmodified_uri_async(DOCUMENT(tmplist->data)->uri, DOCUMENT(tmplist->data)->fileinfo,
1255 file_reload_all_modified_check_lcb, tmplist->data);
1256 }
1257 tmplist = g_list_next(tmplist);
1258 }
1259 }
1260
1261 typedef struct {
1262 GtkWidget *dialog;
1263 Tbfwin *bfwin;
1264 GtkWidget *entry_local;
1265 GtkWidget *entry_remote;
1266 GtkWidget *delete_deprecated;
1267 GtkWidget *include_hidden;
1268 GtkWidget *include_backup;
1269 GtkWidget *progress;
1270 GtkWidget *messagelabel;
1271 gulong signal_id;
1272 } Tsyncdialog;
1273
1274 static void
sync_progress(GFile * uri,gint total,gint done,gint failed,gpointer user_data)1275 sync_progress(GFile *uri, gint total, gint done, gint failed, gpointer user_data)
1276 {
1277 Tsyncdialog *sd = user_data;
1278 if (total > 0) {
1279 gchar *text;
1280 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(sd->progress), 1.0 * done / total);
1281 if (uri) {
1282 gchar * curi = g_file_get_uri(uri);
1283 text = g_strdup_printf("%s (%d / %d)", curi, done, total);
1284 g_free(curi);
1285 } else {
1286 text = g_strdup_printf("%d / %d", done, total);
1287 }
1288 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(sd->progress), text);
1289 /* g_print("%s\n",text);*/
1290 g_free(text);
1291 if (failed > 0) {
1292 text =
1293 g_strdup_printf(ngettext
1294 ("<span color=\"red\">%d failure</span>",
1295 "<span color=\"red\">%d failures</span>", failed), failed);
1296 gtk_label_set_markup(GTK_LABEL(sd->messagelabel), text);
1297 gtk_widget_show(sd->messagelabel);
1298 g_free(text);
1299 }
1300 } else if (total == -1) {
1301 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(sd->progress), 1);
1302 if (failed > 0) {
1303 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(sd->progress), _("incomplete finished"));
1304 } else {
1305 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(sd->progress), _("completed"));
1306 }
1307 g_signal_handler_unblock(sd->dialog, sd->signal_id);
1308 }
1309 }
1310
1311 static void
sync_dialog_response_lcb(GtkDialog * dialog,gint response_id,gpointer user_data)1312 sync_dialog_response_lcb(GtkDialog * dialog, gint response_id, gpointer user_data)
1313 {
1314 Tsyncdialog *sd = user_data;
1315 DEBUG_MSG("sync_dialog_response_lcb, response=%d\n", response_id);
1316 if (response_id > 0) {
1317 GFile *local, *remote;
1318 gtk_label_set_text(GTK_LABEL(sd->messagelabel), "");
1319 gtk_widget_hide(sd->messagelabel);
1320 local = g_file_new_for_commandline_arg(gtk_entry_get_text(GTK_ENTRY(sd->entry_local)));
1321 remote = g_file_new_for_commandline_arg(gtk_entry_get_text(GTK_ENTRY(sd->entry_remote)));
1322 if (response_id == 1) {
1323 sync_directory(local, remote,
1324 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sd->delete_deprecated)),
1325 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sd->include_hidden)),
1326 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sd->include_backup)), sync_progress,
1327 sd);
1328 } else if (response_id == 2) {
1329 sync_directory(remote, local,
1330 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sd->delete_deprecated)),
1331 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sd->include_hidden)),
1332 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sd->include_backup)), sync_progress,
1333 sd);
1334 }
1335 sd->bfwin->session->sync_delete_deprecated =
1336 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sd->delete_deprecated));
1337 sd->bfwin->session->sync_include_hidden =
1338 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sd->include_hidden));
1339 sd->bfwin->session->sync_include_backup =
1340 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sd->include_backup));
1341 g_signal_handler_block(sd->dialog, sd->signal_id);
1342 g_free(sd->bfwin->session->sync_local_uri);
1343 sd->bfwin->session->sync_local_uri = g_file_get_uri(local);
1344 g_free(sd->bfwin->session->sync_remote_uri);
1345 sd->bfwin->session->sync_remote_uri = g_file_get_uri(remote);
1346
1347 g_object_unref(local);
1348 g_object_unref(remote);
1349 } else {
1350 gtk_widget_destroy(sd->dialog);
1351 g_slice_free(Tsyncdialog, sd);
1352 }
1353 }
1354
1355 void
sync_dialog(Tbfwin * bfwin)1356 sync_dialog(Tbfwin * bfwin)
1357 {
1358 Tsyncdialog *sd;
1359 GtkWidget *carea, *table;
1360
1361 sd = g_slice_new0(Tsyncdialog);
1362 sd->bfwin = bfwin;
1363 sd->dialog = gtk_dialog_new_with_buttons(_("Upload / Download"),
1364 GTK_WINDOW(bfwin->main_window),
1365 GTK_DIALOG_DESTROY_WITH_PARENT,
1366 _("Upload"), 1, _("Download"), 2,
1367 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
1368
1369 carea = gtk_dialog_get_content_area(GTK_DIALOG(sd->dialog));
1370 table = dialog_table_in_vbox(5, 3, 6, carea, TRUE,TRUE, 3);
1371
1372 sd->entry_local = dialog_entry_in_table(NULL, table, 1, 2,0, 1);
1373 dialog_mnemonic_label_in_table(_("Local directory"), sd->entry_local, table,
1374 0, 1, 0,1);
1375 gtk_table_attach(GTK_TABLE(table), file_but_new2(sd->entry_local, 1, bfwin, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER),
1376 2, 3, 0, 1, GTK_FILL, GTK_FILL, 3, 3);
1377
1378 sd->entry_remote = dialog_entry_in_table(NULL, table, 1, 2,1, 2);
1379 dialog_mnemonic_label_in_table(_("Remote directory"), sd->entry_remote, table,
1380 0, 1, 1,2);
1381 gtk_table_attach(GTK_TABLE(table), file_but_new2(sd->entry_remote, 1, bfwin, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER),
1382 2, 3, 1, 2, GTK_FILL, GTK_FILL, 3, 3);
1383
1384 sd->delete_deprecated = dialog_check_button_in_table(_("Delete deprecated files"),
1385 bfwin->session->sync_delete_deprecated, table,
1386 0, 3, 2, 3);
1387
1388 sd->include_hidden = dialog_check_button_in_table(_("Include hidden files"),
1389 bfwin->session->sync_include_hidden, table,
1390 0, 3, 3, 4);
1391 sd->include_backup = dialog_check_button_in_table(_("Include backup files"),
1392 bfwin->session->sync_include_backup, table,
1393 0, 3, 4, 5);
1394
1395 sd->messagelabel = gtk_label_new(NULL);
1396 gtk_box_pack_start(GTK_BOX(carea), sd->messagelabel, FALSE, FALSE, 4);
1397
1398 sd->progress = gtk_progress_bar_new();
1399 gtk_progress_bar_set_ellipsize(GTK_PROGRESS_BAR(sd->progress), PANGO_ELLIPSIZE_MIDDLE);
1400 #if GTK_CHECK_VERSION(3, 0, 0)
1401 gtk_progress_bar_set_show_text (GTK_PROGRESS_BAR(sd->progress), TRUE);
1402 #endif
1403 gtk_box_pack_start(GTK_BOX(carea), sd->progress, FALSE, FALSE, 4);
1404
1405 if (bfwin->session->sync_local_uri && bfwin->session->sync_local_uri[0] != '\0') {
1406 gtk_entry_set_text(GTK_ENTRY(sd->entry_local), bfwin->session->sync_local_uri);
1407 } else if (bfwin->session->recent_dirs && bfwin->session->recent_dirs->data
1408 && *(gchar *) bfwin->session->recent_dirs->data != '\0') {
1409 gtk_entry_set_text(GTK_ENTRY(sd->entry_local), bfwin->session->recent_dirs->data);
1410 }
1411
1412 if (bfwin->session->sync_remote_uri && bfwin->session->sync_remote_uri[0] != '\0') {
1413 gtk_entry_set_text(GTK_ENTRY(sd->entry_remote), bfwin->session->sync_remote_uri);
1414 }
1415
1416 sd->signal_id = g_signal_connect(sd->dialog, "response", G_CALLBACK(sync_dialog_response_lcb), sd);
1417 gtk_widget_show_all(sd->dialog);
1418 gtk_widget_hide(sd->messagelabel);
1419 }
1420
1421 static gchar *
modified_on_disk_warning_string(const gchar * filename,GFileInfo * oldfinfo,GFileInfo * newfinfo)1422 modified_on_disk_warning_string(const gchar * filename, GFileInfo * oldfinfo, GFileInfo * newfinfo)
1423 {
1424 gchar *tmpstr, *oldtimestr, *newtimestr;
1425 time_t newtime, oldtime;
1426 goffset oldsize, newsize;
1427 gchar *strnewsize, *stroldsize;
1428
1429 newtime = (time_t) g_file_info_get_attribute_uint64(newfinfo, G_FILE_ATTRIBUTE_TIME_MODIFIED);
1430 oldtime = (time_t) g_file_info_get_attribute_uint64(oldfinfo, G_FILE_ATTRIBUTE_TIME_MODIFIED);
1431 newtimestr = bf_portable_time(&newtime);
1432 oldtimestr = bf_portable_time(&oldtime);
1433 newsize = g_file_info_get_size(newfinfo);
1434 oldsize = g_file_info_get_size(oldfinfo);
1435 strnewsize = g_strdup_printf("%"G_GOFFSET_FORMAT"", newsize);
1436 stroldsize = g_strdup_printf("%"G_GOFFSET_FORMAT"", oldsize);
1437 /*g_print("oldtimestr=%s, newtimestr=%s\n",oldtimestr,newtimestr); */
1438 tmpstr = g_strdup_printf(_("Filename:%s changed on disk.\n\n"
1439 "Original modification time was %s\n"
1440 "New modification time is %s\n"
1441 "Original size was %s bytes\n"
1442 "New size is %s bytes"), filename, oldtimestr, newtimestr, stroldsize, strnewsize);
1443 g_free(newtimestr);
1444 g_free(oldtimestr);
1445 g_free(strnewsize);
1446 g_free(stroldsize);
1447 return tmpstr;
1448 }
1449
1450 static void
doc_activate_modified_lcb(Tcheckmodified_status status,GError * gerror,GFileInfo * orig,GFileInfo * new,gpointer callback_data)1451 doc_activate_modified_lcb(Tcheckmodified_status status, GError * gerror, GFileInfo * orig, GFileInfo * new,
1452 gpointer callback_data)
1453 {
1454 Tdocument *doc = callback_data;
1455 switch (status) {
1456 case CHECKMODIFIED_ERROR:
1457 DEBUG_MSG("doc_activate_modified_lcb, CHECKMODIFIED_ERROR ??\n");
1458 if (gerror->code == G_IO_ERROR_NOT_FOUND) {
1459 gchar *tmpstr;
1460 gint retval;
1461 const gchar *buttons[] = { _("_Unset file name"), _("_Save"), NULL };
1462 /* file is deleted on disk, what do we do now ? */
1463 tmpstr = g_strdup_printf(_("File name: %s"), gtk_label_get_text(GTK_LABEL(doc->tab_menu)));
1464 retval = message_dialog_new_multi(BFWIN(doc->bfwin)->main_window,
1465 GTK_MESSAGE_WARNING,
1466 buttons, _("File disappeared from disk\n"), tmpstr);
1467 g_free(tmpstr);
1468 if (retval == 1) { /* save */
1469 doc_save_backend(doc, docsave_normal, FALSE, FALSE);
1470 } else { /* unset */
1471 document_unset_filename(doc);
1472 }
1473 } else {
1474 /* TODO: warn the user */
1475 }
1476 break;
1477 case CHECKMODIFIED_CANCELLED:
1478 DEBUG_MSG("doc_activate_modified_lcb, CHECKMODIFIED_CANCELLED\n");
1479 break;
1480 case CHECKMODIFIED_MODIFIED:
1481 {
1482 gchar *tmpstr /*, *oldtimestr, *newtimestr */ ;
1483 gint retval;
1484 const gchar *buttons[] =
1485 { _("_Ignore"), _("_Reload"), _("Check and reload all documents"), NULL };
1486 /*time_t newtime,origtime;
1487
1488 newtime = (time_t)g_file_info_get_attribute_uint64(new,G_FILE_ATTRIBUTE_TIME_MODIFIED);
1489 origtime = (time_t)g_file_info_get_attribute_uint64(orig,G_FILE_ATTRIBUTE_TIME_MODIFIED);
1490 g_print("doc_activate_modified_lcb, newtime=%ld,%d origtime=%ld,%d newsize=%"G_GOFFSET_FORMAT" origsize=%"G_GOFFSET_FORMAT"\n",
1491 newtime,g_file_info_get_attribute_uint32(new,G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC),
1492 origtime,g_file_info_get_attribute_uint32(orig,G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC),
1493 g_file_info_get_size(new),g_file_info_get_size(orig));
1494 newtimestr = bf_portable_time(&newtime);
1495 oldtimestr = bf_portable_time(&origtime);
1496
1497 tmpstr = g_strdup_printf(_("Filename: %s\n\nNew modification time is: %s\nOld modification time is: %s"), gtk_label_get_text(GTK_LABEL(doc->tab_menu)), newtimestr, oldtimestr);
1498 */
1499 tmpstr = modified_on_disk_warning_string(gtk_label_get_text(GTK_LABEL(doc->tab_menu)), orig, new);
1500 retval = message_dialog_new_multi(BFWIN(doc->bfwin)->main_window,
1501 GTK_MESSAGE_WARNING,
1502 buttons, _("File changed on disk\n"), tmpstr);
1503 g_free(tmpstr);
1504 /*g_free(newtimestr);
1505 g_free(oldtimestr); */
1506 if (retval == 0) { /* ignore */
1507 /*if (doc->fileinfo) {
1508 g_object_unref(doc->fileinfo);
1509 }
1510 doc->fileinfo = new;
1511 g_object_ref(doc->fileinfo); */
1512 GTimeVal mtime;
1513 g_file_info_set_size(doc->fileinfo, g_file_info_get_size(new));
1514 g_file_info_get_modification_time(new, &mtime);
1515 g_file_info_set_modification_time(doc->fileinfo, &mtime);
1516 g_file_info_set_attribute_string(doc->fileinfo, "etag::value",
1517 g_file_info_get_attribute_string(new, "etag::value"));
1518 doc_set_tooltip(doc);
1519 } else if (retval == 1) { /* reload */
1520 doc_reload(doc, new, FALSE);
1521 } else { /* reload all modified documents */
1522 file_reload_all_modified(doc->bfwin);
1523 }
1524 }
1525 break;
1526 case CHECKMODIFIED_OK:
1527 /* do nothing */
1528 break;
1529 }
1530 doc->checkmodified = NULL;
1531 }
1532
1533 void
doc_start_modified_check(Tdocument * doc)1534 doc_start_modified_check(Tdocument * doc)
1535 {
1536 if (doc->uri && doc->fileinfo && !doc->checkmodified && !doc->save) { /* don't check during another check, or during save */
1537 doc->checkmodified =
1538 file_checkmodified_uri_async(doc->uri, doc->fileinfo, doc_activate_modified_lcb, doc);
1539 }
1540 }
1541
1542 static gboolean
modified_on_disk_check_lcb(gpointer data)1543 modified_on_disk_check_lcb(gpointer data)
1544 {
1545 GList *tmplist = g_list_first(main_v->bfwinlist);
1546 while (tmplist) {
1547 Tbfwin *bfwin = tmplist->data;
1548 if (bfwin->current_document) {
1549 doc_start_modified_check(bfwin->current_document);
1550 }
1551 tmplist = g_list_next(tmplist);
1552 }
1553 return TRUE;
1554 }
1555
1556 void
modified_on_disk_check_init(void)1557 modified_on_disk_check_init(void)
1558 {
1559 if (main_v->props.check_for_modified_on_disk==1 && !main_v->periodic_check_id)
1560 main_v->periodic_check_id =
1561 g_timeout_add_seconds_full(G_PRIORITY_LOW, 15, modified_on_disk_check_lcb, NULL, NULL);
1562 else if (main_v->props.check_for_modified_on_disk!=1 && main_v->periodic_check_id) {
1563 g_source_remove(main_v->periodic_check_id);
1564 main_v->periodic_check_id = 0;
1565 }
1566 }
1567