Skip to content
2 changes: 1 addition & 1 deletion src/MainWindow.vala
Original file line number Diff line number Diff line change
Expand Up @@ -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 ();
}
}
}
Expand Down
90 changes: 61 additions & 29 deletions src/Services/Document.vala
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
});
Expand All @@ -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 ();
}
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 ());

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) => {
Expand All @@ -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 ("<b>%s</b>".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 ("<b>%s</b>".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);
}
}
);
});
}
}
Expand Down