Warn user before exiting if composer window has unsent text: Closes #5279
YorbaApplication's 'exiting' signal now allows callbacks to cancel the edit. GearyController now keeps a list of all open ComposerWindows, and listens for YorbaApplication's 'exiting' signal. When the signal is received, GearyController warns the user about each open ComposerWindow with unsent text. If the user cancels the closing of any of these ComposerWindows, GearyController cancels the edit.
This commit is contained in:
parent
9a64661f2c
commit
883e45577b
6 changed files with 77 additions and 6 deletions
|
|
@ -263,9 +263,11 @@ along with Geary; if not, write to the Free Software Foundation, Inc.,
|
|||
}
|
||||
}
|
||||
|
||||
public override void exiting(bool panicked) {
|
||||
public override bool exiting(bool panicked) {
|
||||
if (controller.main_window != null)
|
||||
controller.main_window.destroy();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public File get_user_data_directory() {
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ public class GearyController {
|
|||
private Geary.Conversation? last_deleted_conversation = null;
|
||||
private bool scan_in_progress = false;
|
||||
private int conversations_added_counter = 0;
|
||||
private Gee.LinkedList<ComposerWindow> composer_windows = new Gee.LinkedList<ComposerWindow>();
|
||||
|
||||
public GearyController() {
|
||||
// Setup actions.
|
||||
|
|
@ -80,6 +81,9 @@ public class GearyController {
|
|||
GearyApplication.instance.load_ui_file("accelerators.ui");
|
||||
GearyApplication.instance.config.display_preview_changed.connect(on_display_preview_changed);
|
||||
|
||||
// Listen for attempts to close the application.
|
||||
GearyApplication.instance.exiting.connect(on_application_exiting);
|
||||
|
||||
// Create the main window (must be done after creating actions.)
|
||||
main_window = new MainWindow();
|
||||
|
||||
|
|
@ -695,6 +699,15 @@ public class GearyController {
|
|||
old_cancellable.cancel();
|
||||
}
|
||||
|
||||
// We need to include the second parameter, or valac doesn't recognize the function as matching
|
||||
// YorbaApplication.exiting's signature.
|
||||
private bool on_application_exiting(YorbaApplication sender, bool panicked) {
|
||||
if (close_composition_windows())
|
||||
return true;
|
||||
|
||||
return sender.cancel_exit();
|
||||
}
|
||||
|
||||
public void on_quit() {
|
||||
GearyApplication.instance.exit();
|
||||
}
|
||||
|
|
@ -911,14 +924,41 @@ public class GearyController {
|
|||
}
|
||||
}
|
||||
|
||||
private bool close_composition_windows() {
|
||||
// We want to allow the user to cancel a quit when they have unsent text.
|
||||
|
||||
// We are modifying the list as we go, so we can't simply iterate through it.
|
||||
while (composer_windows.size > 0) {
|
||||
ComposerWindow composer_window = composer_windows.first();
|
||||
if (!composer_window.should_close())
|
||||
return false;
|
||||
|
||||
// This will remove composer_window from composer_windows.
|
||||
// See GearyController.on_composer_window_destroy.
|
||||
composer_window.destroy();
|
||||
}
|
||||
|
||||
// If we deleted all composer windows without the user cancelling, we can exit.
|
||||
return true;
|
||||
}
|
||||
|
||||
private void create_compose_window(Geary.ComposedEmail? prefill = null) {
|
||||
ComposerWindow w = new ComposerWindow(prefill);
|
||||
w.set_position(Gtk.WindowPosition.CENTER);
|
||||
w.send.connect(on_send);
|
||||
|
||||
// We want to keep track of the open composer windows, so we can allow the user to cancel
|
||||
// an exit without losing their data.
|
||||
composer_windows.add(w);
|
||||
w.destroy.connect(on_composer_window_destroy);
|
||||
|
||||
w.show_all();
|
||||
}
|
||||
|
||||
private void on_composer_window_destroy(Gtk.Widget sender) {
|
||||
composer_windows.remove((ComposerWindow) sender);
|
||||
}
|
||||
|
||||
private void on_new_message() {
|
||||
create_compose_window();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -345,7 +345,7 @@ public class ComposerWindow : Gtk.Window {
|
|||
base.show_all();
|
||||
}
|
||||
|
||||
private bool should_close() {
|
||||
public bool should_close() {
|
||||
// TODO: Check if the message was (automatically) saved
|
||||
if (editor.can_undo()) {
|
||||
var dialog = new Gtk.MessageDialog(this, 0,
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ public class MainWindow : Gtk.Window {
|
|||
pixbuf_list.append(IconFactory.instance.geary);
|
||||
set_default_icon_list(pixbuf_list);
|
||||
|
||||
delete_event.connect(on_delete_event);
|
||||
|
||||
create_layout();
|
||||
}
|
||||
|
||||
|
|
@ -62,9 +64,13 @@ public class MainWindow : Gtk.Window {
|
|||
GearyApplication.instance.config.messages_pane_position = messages_paned.get_position();
|
||||
}
|
||||
|
||||
base.destroy();
|
||||
}
|
||||
|
||||
private bool on_delete_event() {
|
||||
GearyApplication.instance.exit();
|
||||
|
||||
base.destroy();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool configure_event(Gdk.EventConfigure event) {
|
||||
|
|
|
|||
|
|
@ -42,8 +42,10 @@ public class PreferencesDialog : Object {
|
|||
}
|
||||
}
|
||||
|
||||
private void on_exit(bool panicked) {
|
||||
private bool on_exit(bool panicked) {
|
||||
dialog.destroy();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void on_autoselect_toggled() {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,14 @@ public abstract class YorbaApplication {
|
|||
public virtual signal void activate(string[] args) {
|
||||
}
|
||||
|
||||
public virtual signal void exiting(bool panicked) {
|
||||
/**
|
||||
* Signal that is activated when 'exit' is called, but before the application actually exits.
|
||||
*
|
||||
* To cancel an exit, a callback should return YorbaApplication.cancel_exit(). To procede with
|
||||
* an exit, a callback should return true.
|
||||
*/
|
||||
public virtual signal bool exiting(bool panicked) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -143,7 +150,12 @@ public abstract class YorbaApplication {
|
|||
this.exitcode = exitcode;
|
||||
|
||||
exiting_fired = true;
|
||||
exiting(false);
|
||||
if (!exiting(false)) {
|
||||
exiting_fired = false;
|
||||
this.exitcode = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Gtk.main_level() > 0)
|
||||
Gtk.main_quit();
|
||||
|
|
@ -151,6 +163,15 @@ public abstract class YorbaApplication {
|
|||
Posix.exit(exitcode);
|
||||
}
|
||||
|
||||
/**
|
||||
* A callback for GearyApplication.exiting should return cancel_exit() to prevent the
|
||||
* application from exiting.
|
||||
*/
|
||||
public bool cancel_exit() {
|
||||
Signal.stop_emission_by_name(this, "exiting");
|
||||
return false;
|
||||
}
|
||||
|
||||
// This call will fire "exiting" only if it's not already been fired and halt the application
|
||||
// in its tracks.
|
||||
public void panic() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue