diff --git a/src/MainWindow.vala b/src/MainWindow.vala index 57ded41e28..a74d13c3f6 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -896,7 +896,7 @@ namespace Scratch { if (doc.is_file_temporary == true) { action_save_as (); } else { - doc.save_with_hold.begin (true); + doc.save_request (); } } } diff --git a/src/Services/Document.vala b/src/Services/Document.vala index c6069d9900..6f4651996b 100644 --- a/src/Services/Document.vala +++ b/src/Services/Document.vala @@ -93,6 +93,7 @@ namespace Scratch.Services { private string last_save_content; public bool saved = true; private bool completion_shown = false; + private bool inhibit_autosave = false; private Gtk.ScrolledWindow scroll; private Gtk.InfoBar info_bar; @@ -183,17 +184,27 @@ namespace Scratch.Services { // Focus out event for SourceView this.source_view.focus_out_event.connect (() => { - if (Scratch.settings.get_boolean ("autosave")) { + if (Scratch.settings.get_boolean ("autosave") && !inhibit_autosave) { save.begin (); } return false; }); + // Focus in event for SourceView + this.source_view.focus_in_event.connect (() => { + if (!inhibit_autosave && !is_file_temporary) { + check_file_status (); + check_undoable_actions (); + } + + return false; + }); + source_view.buffer.changed.connect (() => { if (source_view.buffer.text != last_save_content) { saved = false; - if (!Scratch.settings.get_boolean ("autosave")) { + if (inhibit_autosave || !Scratch.settings.get_boolean ("autosave")) { set_saved_status (false); } } else { @@ -227,13 +238,14 @@ namespace Scratch.Services { onchange_handler_id = source_view.buffer.changed.connect (() => { check_undoable_actions (); // Save if autosave is ON - if (Scratch.settings.get_boolean ("autosave")) { + if (Scratch.settings.get_boolean ("autosave") && !inhibit_autosave) { if (timeout_saving > 0) { Source.remove (timeout_saving); timeout_saving = 0; } timeout_saving = Timeout.add (1000, () => { - save.begin (); + check_file_status (); + save.begin (); // Not forced timeout_saving = 0; return false; }); @@ -251,6 +263,8 @@ namespace Scratch.Services { /* Loading improper files may hang so we cancel after a certain time as a fallback. * In most cases, an error will be thrown and caught. */ loaded = false; + inhibit_autosave = false; + if (load_cancellable != null) { /* just in case */ load_cancellable.cancel (); } @@ -341,14 +355,6 @@ namespace Scratch.Services { } } - // Focus in event for SourceView - this.source_view.focus_in_event.connect (() => { - check_file_status (); - check_undoable_actions (); - - return false; - }); - // Change syntax highlight this.source_view.change_syntax_highlight_from_file (this.file); @@ -436,6 +442,13 @@ namespace Scratch.Services { return ret_value; } + // Handle save action (only use for user interaction) + public void save_request () { + check_undoable_actions (); + check_file_status (); // Need to check for external changes before forcing save + save_with_hold.begin (true); + } + private bool is_saving = false; public async bool save_with_hold (bool force = false, bool saving_as = false) { // Prevent reentry which could result in mismatched holds on Application @@ -510,6 +523,11 @@ namespace Scratch.Services { this.set_saved_status (true); last_save_content = source_view.buffer.text; + // If saving in response to external changes hide the infobar now. + if (inhibit_autosave) { + inhibit_autosave = false; + hide_info_bar (); + } debug ("File ā€œ%sā€ saved successfully", get_basename ()); @@ -555,9 +573,9 @@ namespace Scratch.Services { var is_saved = false; if (success) { - source_view.buffer.set_modified (true); is_saved = yield save (true, true); if (is_saved) { + source_view.buffer.set_modified (false); if (is_current_file_temporary) { try { // Delete temporary file @@ -830,8 +848,11 @@ namespace Scratch.Services { this.source_view.editable = true; } - // Detect external changes - if (loaded) { + // Detect external changes by comparing file content with buffer content. + // Only done when no unsaved internal changes else difference from saved + // file are to be expected. If user selects to continue regardless then no further + // check made for this document - external changes will be overwritten on next (auto) save + if (loaded && !is_saving) { var new_buffer = new Gtk.SourceBuffer (null); var source_file_loader = new Gtk.SourceFileLoader (new_buffer, source_file); source_file_loader.load_async.begin (GLib.Priority.DEFAULT, null, null, (obj, res) => { @@ -843,26 +864,37 @@ namespace Scratch.Services { return; } - if (source_view.buffer.text == new_buffer.text) { + if (last_save_content == new_buffer.text) { return; } - if (!source_view.buffer.get_modified ()) { - if (Scratch.settings.get_boolean ("autosave")) { - source_view.set_text (new_buffer.text, false); - } else { - string message = _( - "File ā€œ%sā€ was modified by an external application. Do you want to load it again or continue your editing?" + string message; + if (source_view.buffer.get_modified ()) { + message = _( + "File \"%s\" was modified by an external application. \nThere are also unsaved changes. \nReload the document and lose the unsaved changes? \nOtherwise, overwrite the external changes or save with a different name." + ).printf ("%s".printf (get_basename ())); + } else { + message = _( + "File \"%s\" was modified by an external application. \nReload the document? \nOtherwise, overwrite the external changes or save with a different name." ).printf ("%s".printf (get_basename ())); + } - set_message (Gtk.MessageType.WARNING, message, _("Load"), () => { - this.source_view.set_text (new_buffer.text, false); - hide_info_bar (); - }, _("Continue"), () => { - hide_info_bar (); - }); + inhibit_autosave = true; + set_message ( + Gtk.MessageType.WARNING, + message, + _("Reload"), () => { + source_view.buffer.text = new_buffer.text; + source_view.buffer.set_modified (false); + last_save_content = source_view.buffer.text; + set_saved_status (true); + inhibit_autosave = false; + hide_info_bar (); + }, + _("Overwrite"), () => { + save_with_hold.begin (true, false); } - } + ); }); } }