Merge branch 'mjog/233-entry-undo' into 'mainline'
Application-wide GtkEntry undo support Closes #233 See merge request GNOME/geary!360
This commit is contained in:
commit
63f626e050
24 changed files with 836 additions and 295 deletions
|
|
@ -93,7 +93,8 @@
|
||||||
<description>
|
<description>
|
||||||
<p>Enhancements included in this release:</p>
|
<p>Enhancements included in this release:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Improved and pervasive undo support for email actions</li>
|
<li>Unlimited undo for all email actions such as archiving, marking</li>
|
||||||
|
<li>Undo support for all text entry fields, including the composer</li>
|
||||||
<li>App-wide notification preferences now handled by desktop</li>
|
<li>App-wide notification preferences now handled by desktop</li>
|
||||||
<li>Improved missing attachment detection in composer</li>
|
<li>Improved missing attachment detection in composer</li>
|
||||||
<li>Initial plugin system</li>
|
<li>Initial plugin system</li>
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,10 @@ src/client/application/geary-application.vala
|
||||||
src/client/application/goa-mediator.vala
|
src/client/application/goa-mediator.vala
|
||||||
src/client/application/main.vala
|
src/client/application/main.vala
|
||||||
src/client/application/secret-mediator.vala
|
src/client/application/secret-mediator.vala
|
||||||
|
src/client/client-action.vala
|
||||||
src/client/components/client-web-view.vala
|
src/client/components/client-web-view.vala
|
||||||
src/client/components/components-attachment-pane.vala
|
src/client/components/components-attachment-pane.vala
|
||||||
|
src/client/components/components-entry-undo.vala
|
||||||
src/client/components/components-in-app-notification.vala
|
src/client/components/components-in-app-notification.vala
|
||||||
src/client/components/components-inspector.vala
|
src/client/components/components-inspector.vala
|
||||||
src/client/components/components-placeholder-pane.vala
|
src/client/components/components-placeholder-pane.vala
|
||||||
|
|
|
||||||
|
|
@ -517,11 +517,19 @@ private abstract class Accounts.AddPaneRow<Value> :
|
||||||
private abstract class Accounts.EntryRow : AddPaneRow<Gtk.Entry> {
|
private abstract class Accounts.EntryRow : AddPaneRow<Gtk.Entry> {
|
||||||
|
|
||||||
|
|
||||||
protected EntryRow(string label, string? placeholder = null) {
|
private Components.EntryUndo undo;
|
||||||
|
|
||||||
|
|
||||||
|
protected EntryRow(string label,
|
||||||
|
string? initial_value = null,
|
||||||
|
string? placeholder = null) {
|
||||||
base(label, new Gtk.Entry());
|
base(label, new Gtk.Entry());
|
||||||
|
|
||||||
|
this.value.text = initial_value ?? "";
|
||||||
this.value.placeholder_text = placeholder ?? "";
|
this.value.placeholder_text = placeholder ?? "";
|
||||||
this.value.width_chars = 32;
|
this.value.width_chars = 32;
|
||||||
|
|
||||||
|
this.undo = new Components.EntryUndo(this.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool focus(Gtk.DirectionType direction) {
|
public override bool focus(Gtk.DirectionType direction) {
|
||||||
|
|
@ -548,12 +556,12 @@ private class Accounts.NameRow : EntryRow {
|
||||||
public NameRow(string default_name) {
|
public NameRow(string default_name) {
|
||||||
// Translators: Label for the person's actual name when adding
|
// Translators: Label for the person's actual name when adding
|
||||||
// an account
|
// an account
|
||||||
base(_("Your name"));
|
base(_("Your name"), default_name.strip());
|
||||||
this.validator = new Components.Validator(this.value);
|
this.validator = new Components.Validator(this.value);
|
||||||
if (default_name.strip() != "") {
|
if (this.value.text != "") {
|
||||||
// Set the text after hooking up the validator, so if the
|
// Validate if the string is non-empty so it will be good
|
||||||
// string is non-null it will already be valid
|
// to go from the start
|
||||||
this.value.set_text(default_name);
|
this.value.activate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -566,6 +574,7 @@ private class Accounts.EmailRow : EntryRow {
|
||||||
public EmailRow() {
|
public EmailRow() {
|
||||||
base(
|
base(
|
||||||
_("Email address"),
|
_("Email address"),
|
||||||
|
null,
|
||||||
// Translators: Placeholder for the default sender address
|
// Translators: Placeholder for the default sender address
|
||||||
// when adding an account
|
// when adding an account
|
||||||
_("person@example.com")
|
_("person@example.com")
|
||||||
|
|
@ -634,7 +643,7 @@ private class Accounts.HostnameRow : EntryRow {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
base(label, placeholder);
|
base(label, null, placeholder);
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
|
||||||
this.validator = new Components.NetworkAddressValidator(this.value, 0);
|
this.validator = new Components.NetworkAddressValidator(this.value, 0);
|
||||||
|
|
|
||||||
|
|
@ -264,6 +264,7 @@ internal class Accounts.EditorEditPane :
|
||||||
private class Accounts.DisplayNameRow : AccountRow<EditorEditPane,Gtk.Entry> {
|
private class Accounts.DisplayNameRow : AccountRow<EditorEditPane,Gtk.Entry> {
|
||||||
|
|
||||||
|
|
||||||
|
private Components.EntryUndo value_undo;
|
||||||
private Application.CommandStack commands;
|
private Application.CommandStack commands;
|
||||||
private GLib.Cancellable? cancellable;
|
private GLib.Cancellable? cancellable;
|
||||||
|
|
||||||
|
|
@ -284,12 +285,19 @@ private class Accounts.DisplayNameRow : AccountRow<EditorEditPane,Gtk.Entry> {
|
||||||
|
|
||||||
update();
|
update();
|
||||||
|
|
||||||
|
// Hook up after updating the value so the default value isn't
|
||||||
|
// undoable
|
||||||
|
this.value_undo = new Components.EntryUndo(this.value);
|
||||||
|
|
||||||
this.value.focus_out_event.connect(on_focus_out);
|
this.value.focus_out_event.connect(on_focus_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void update() {
|
public override void update() {
|
||||||
this.value.set_placeholder_text(this.account.primary_mailbox.address);
|
this.value.placeholder_text = this.account.primary_mailbox.address;
|
||||||
this.value.set_text(this.account.display_name);
|
// Only update if changed to avoid adding more undo edits
|
||||||
|
if (this.value.text != this.account.display_name) {
|
||||||
|
this.value.text = this.account.display_name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void commit() {
|
private void commit() {
|
||||||
|
|
@ -434,7 +442,9 @@ internal class Accounts.MailboxEditorPopover : EditorPopover {
|
||||||
|
|
||||||
|
|
||||||
private Gtk.Entry name_entry = new Gtk.Entry();
|
private Gtk.Entry name_entry = new Gtk.Entry();
|
||||||
|
private Components.EntryUndo name_undo;
|
||||||
private Gtk.Entry address_entry = new Gtk.Entry();
|
private Gtk.Entry address_entry = new Gtk.Entry();
|
||||||
|
private Components.EntryUndo address_undo;
|
||||||
private Components.EmailValidator address_validator;
|
private Components.EmailValidator address_validator;
|
||||||
private Gtk.Button remove_button;
|
private Gtk.Button remove_button;
|
||||||
|
|
||||||
|
|
@ -460,6 +470,8 @@ internal class Accounts.MailboxEditorPopover : EditorPopover {
|
||||||
this.name_entry.activate.connect(on_activate);
|
this.name_entry.activate.connect(on_activate);
|
||||||
this.name_entry.show();
|
this.name_entry.show();
|
||||||
|
|
||||||
|
this.name_undo = new Components.EntryUndo(this.name_entry);
|
||||||
|
|
||||||
this.address_entry.input_purpose = Gtk.InputPurpose.EMAIL;
|
this.address_entry.input_purpose = Gtk.InputPurpose.EMAIL;
|
||||||
this.address_entry.set_text(address ?? "");
|
this.address_entry.set_text(address ?? "");
|
||||||
this.address_entry.set_placeholder_text(
|
this.address_entry.set_placeholder_text(
|
||||||
|
|
@ -473,6 +485,8 @@ internal class Accounts.MailboxEditorPopover : EditorPopover {
|
||||||
this.address_entry.activate.connect(on_activate);
|
this.address_entry.activate.connect(on_activate);
|
||||||
this.address_entry.show();
|
this.address_entry.show();
|
||||||
|
|
||||||
|
this.address_undo = new Components.EntryUndo(this.address_entry);
|
||||||
|
|
||||||
this.address_validator =
|
this.address_validator =
|
||||||
new Components.EmailValidator(this.address_entry);
|
new Components.EmailValidator(this.address_entry);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -245,7 +245,7 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane {
|
||||||
if (command.executed_label != null) {
|
if (command.executed_label != null) {
|
||||||
Components.InAppNotification ian =
|
Components.InAppNotification ian =
|
||||||
new Components.InAppNotification(command.executed_label);
|
new Components.InAppNotification(command.executed_label);
|
||||||
ian.set_button(_("Undo"), "win." + GearyApplication.ACTION_UNDO);
|
ian.set_button(_("Undo"), Action.Edit.prefix(Action.Edit.UNDO));
|
||||||
this.editor.add_notification(ian);
|
this.editor.add_notification(ian);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -254,7 +254,7 @@ internal class Accounts.EditorListPane : Gtk.Grid, EditorPane, CommandPane {
|
||||||
if (command.undone_label != null) {
|
if (command.undone_label != null) {
|
||||||
Components.InAppNotification ian =
|
Components.InAppNotification ian =
|
||||||
new Components.InAppNotification(command.undone_label);
|
new Components.InAppNotification(command.undone_label);
|
||||||
ian.set_button(_("Redo"), "win." + GearyApplication.ACTION_REDO);
|
ian.set_button(_("Redo"), Action.Edit.prefix(Action.Edit.REDO));
|
||||||
this.editor.add_notification(ian);
|
this.editor.add_notification(ian);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -711,6 +711,7 @@ private class Accounts.ServiceHostRow :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Components.EntryUndo value_undo;
|
||||||
private Application.CommandStack commands;
|
private Application.CommandStack commands;
|
||||||
private GLib.Cancellable? cancellable;
|
private GLib.Cancellable? cancellable;
|
||||||
|
|
||||||
|
|
@ -741,9 +742,11 @@ private class Accounts.ServiceHostRow :
|
||||||
this.validator = new Components.NetworkAddressValidator(this.value);
|
this.validator = new Components.NetworkAddressValidator(this.value);
|
||||||
|
|
||||||
// Update after the validator is wired up to ensure the value
|
// Update after the validator is wired up to ensure the value
|
||||||
// is validated
|
// is validated, wire up undo after updating so the default
|
||||||
|
// value isn't undoable.
|
||||||
setup_validator();
|
setup_validator();
|
||||||
update();
|
update();
|
||||||
|
this.value_undo = new Components.EntryUndo(this.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void update() {
|
public override void update() {
|
||||||
|
|
@ -862,6 +865,7 @@ private class Accounts.ServiceLoginRow :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Components.EntryUndo value_undo;
|
||||||
private Application.CommandStack commands;
|
private Application.CommandStack commands;
|
||||||
private GLib.Cancellable? cancellable;
|
private GLib.Cancellable? cancellable;
|
||||||
private ServicePasswordRow? password_row;
|
private ServicePasswordRow? password_row;
|
||||||
|
|
@ -894,9 +898,11 @@ private class Accounts.ServiceLoginRow :
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update after the validator is wired up to ensure the value
|
// Update after the validator is wired up to ensure the value
|
||||||
// is validated
|
// is validated, wire up undo after updating so the default
|
||||||
update();
|
// value isn't undoable.
|
||||||
setup_validator();
|
setup_validator();
|
||||||
|
update();
|
||||||
|
this.value_undo = new Components.EntryUndo(this.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void update() {
|
public override void update() {
|
||||||
|
|
@ -983,6 +989,7 @@ private class Accounts.ServicePasswordRow :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Components.EntryUndo value_undo;
|
||||||
private Application.CommandStack commands;
|
private Application.CommandStack commands;
|
||||||
private GLib.Cancellable? cancellable;
|
private GLib.Cancellable? cancellable;
|
||||||
|
|
||||||
|
|
@ -1008,9 +1015,11 @@ private class Accounts.ServicePasswordRow :
|
||||||
this.validator = new Components.Validator(this.value);
|
this.validator = new Components.Validator(this.value);
|
||||||
|
|
||||||
// Update after the validator is wired up to ensure the value
|
// Update after the validator is wired up to ensure the value
|
||||||
// is validated
|
// is validated, wire up undo after updating so the default
|
||||||
update();
|
// value isn't undoable.
|
||||||
setup_validator();
|
setup_validator();
|
||||||
|
update();
|
||||||
|
this.value_undo = new Components.EntryUndo(this.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void update() {
|
public override void update() {
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@
|
||||||
public class Accounts.Editor : Gtk.Dialog {
|
public class Accounts.Editor : Gtk.Dialog {
|
||||||
|
|
||||||
|
|
||||||
private const ActionEntry[] ACTION_ENTRIES = {
|
private const ActionEntry[] EDIT_ACTIONS = {
|
||||||
{ GearyApplication.ACTION_REDO, on_redo },
|
{ Action.Edit.REDO, on_redo },
|
||||||
{ GearyApplication.ACTION_UNDO, on_undo },
|
{ Action.Edit.UNDO, on_undo },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -40,7 +40,7 @@ public class Accounts.Editor : Gtk.Dialog {
|
||||||
get; private set;
|
get; private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SimpleActionGroup actions = new SimpleActionGroup();
|
private GLib.SimpleActionGroup edit_actions = new GLib.SimpleActionGroup();
|
||||||
|
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
private Gtk.Overlay notifications_pane;
|
private Gtk.Overlay notifications_pane;
|
||||||
|
|
@ -67,8 +67,8 @@ public class Accounts.Editor : Gtk.Dialog {
|
||||||
|
|
||||||
this.accounts = application.controller.account_manager;
|
this.accounts = application.controller.account_manager;
|
||||||
|
|
||||||
this.actions.add_action_entries(ACTION_ENTRIES, this);
|
this.edit_actions.add_action_entries(EDIT_ACTIONS, this);
|
||||||
insert_action_group("win", this.actions);
|
insert_action_group(Action.Edit.GROUP_NAME, this.edit_actions);
|
||||||
|
|
||||||
this.editor_list_pane = new EditorListPane(this);
|
this.editor_list_pane = new EditorListPane(this);
|
||||||
push(this.editor_list_pane);
|
push(this.editor_list_pane);
|
||||||
|
|
@ -227,8 +227,8 @@ public class Accounts.Editor : Gtk.Dialog {
|
||||||
can_redo = pane.commands.can_redo;
|
can_redo = pane.commands.can_redo;
|
||||||
}
|
}
|
||||||
|
|
||||||
get_action(GearyApplication.ACTION_UNDO).set_enabled(can_undo);
|
get_action(Action.Edit.UNDO).set_enabled(can_undo);
|
||||||
get_action(GearyApplication.ACTION_REDO).set_enabled(can_redo);
|
get_action(Action.Edit.REDO).set_enabled(can_redo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline EditorPane? get_current_pane() {
|
private inline EditorPane? get_current_pane() {
|
||||||
|
|
@ -236,7 +236,7 @@ public class Accounts.Editor : Gtk.Dialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline GLib.SimpleAction get_action(string name) {
|
private inline GLib.SimpleAction get_action(string name) {
|
||||||
return (GLib.SimpleAction) this.actions.lookup_action(name);
|
return (GLib.SimpleAction) this.edit_actions.lookup_action(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void on_undo() {
|
private void on_undo() {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ extern const string _PROFILE;
|
||||||
extern const string _VERSION;
|
extern const string _VERSION;
|
||||||
extern const string _REVNO;
|
extern const string _REVNO;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The interface between Geary and the desktop environment.
|
* The interface between Geary and the desktop environment.
|
||||||
*/
|
*/
|
||||||
|
|
@ -52,25 +53,6 @@ public class GearyApplication : Gtk.Application {
|
||||||
null
|
null
|
||||||
};
|
};
|
||||||
|
|
||||||
// Common window actions
|
|
||||||
public const string ACTION_CLOSE = "close";
|
|
||||||
public const string ACTION_COPY = "copy";
|
|
||||||
public const string ACTION_HELP_OVERLAY = "show-help-overlay";
|
|
||||||
public const string ACTION_REDO = "redo";
|
|
||||||
public const string ACTION_UNDO = "undo";
|
|
||||||
|
|
||||||
// App-wide actions
|
|
||||||
public const string ACTION_ABOUT = "about";
|
|
||||||
public const string ACTION_ACCOUNTS = "accounts";
|
|
||||||
public const string ACTION_COMPOSE = "compose";
|
|
||||||
public const string ACTION_INSPECT = "inspect";
|
|
||||||
public const string ACTION_HELP = "help";
|
|
||||||
public const string ACTION_MAILTO = "mailto";
|
|
||||||
public const string ACTION_PREFERENCES = "preferences";
|
|
||||||
public const string ACTION_SHOW_EMAIL = "show-email";
|
|
||||||
public const string ACTION_SHOW_FOLDER = "show-folder";
|
|
||||||
public const string ACTION_QUIT = "quit";
|
|
||||||
|
|
||||||
// Local-only command line options
|
// Local-only command line options
|
||||||
private const string OPTION_VERSION = "version";
|
private const string OPTION_VERSION = "version";
|
||||||
|
|
||||||
|
|
@ -90,16 +72,16 @@ public class GearyApplication : Gtk.Application {
|
||||||
private const string OPTION_REVOKE_CERTS = "revoke-certs";
|
private const string OPTION_REVOKE_CERTS = "revoke-certs";
|
||||||
|
|
||||||
private const ActionEntry[] ACTION_ENTRIES = {
|
private const ActionEntry[] ACTION_ENTRIES = {
|
||||||
{ACTION_ABOUT, on_activate_about},
|
{ Action.Application.ABOUT, on_activate_about},
|
||||||
{ACTION_ACCOUNTS, on_activate_accounts},
|
{ Action.Application.ACCOUNTS, on_activate_accounts},
|
||||||
{ACTION_COMPOSE, on_activate_compose},
|
{ Action.Application.COMPOSE, on_activate_compose},
|
||||||
{ACTION_HELP, on_activate_help},
|
{ Action.Application.HELP, on_activate_help},
|
||||||
{ACTION_INSPECT, on_activate_inspect},
|
{ Action.Application.INSPECT, on_activate_inspect},
|
||||||
{ACTION_MAILTO, on_activate_mailto, "s"},
|
{ Action.Application.MAILTO, on_activate_mailto, "s"},
|
||||||
{ACTION_PREFERENCES, on_activate_preferences},
|
{ Action.Application.PREFERENCES, on_activate_preferences},
|
||||||
{ACTION_QUIT, on_activate_quit},
|
{ Action.Application.QUIT, on_activate_quit},
|
||||||
{ACTION_SHOW_EMAIL, on_activate_show_email, "(svv)"},
|
{ Action.Application.SHOW_EMAIL, on_activate_show_email, "(svv)"},
|
||||||
{ACTION_SHOW_FOLDER, on_activate_show_folder, "(sv)"}
|
{ Action.Application.SHOW_FOLDER, on_activate_show_folder, "(sv)"}
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is also the order in which they are presented to the user,
|
// This is also the order in which they are presented to the user,
|
||||||
|
|
@ -435,21 +417,26 @@ public class GearyApplication : Gtk.Application {
|
||||||
Gtk.Window.set_default_icon_name(APP_ID);
|
Gtk.Window.set_default_icon_name(APP_ID);
|
||||||
|
|
||||||
// Application accels
|
// Application accels
|
||||||
add_app_accelerators(ACTION_COMPOSE, { "<Ctrl>N" });
|
add_app_accelerators(Action.Application.COMPOSE, { "<Ctrl>N" });
|
||||||
add_app_accelerators(ACTION_HELP, { "F1" });
|
add_app_accelerators(Action.Application.HELP, { "F1" });
|
||||||
add_app_accelerators(ACTION_INSPECT, { "<Alt><Shift>I" });
|
add_app_accelerators(Action.Application.INSPECT, { "<Alt><Shift>I" });
|
||||||
add_app_accelerators(ACTION_QUIT, { "<Ctrl>Q" });
|
add_app_accelerators(Action.Application.QUIT, { "<Ctrl>Q" });
|
||||||
|
|
||||||
// Common window accels
|
// Common window accels
|
||||||
add_window_accelerators(ACTION_CLOSE, { "<Ctrl>W" });
|
add_window_accelerators(Action.Window.CLOSE, { "<Ctrl>W" });
|
||||||
add_window_accelerators(ACTION_COPY, { "<Ctrl>C" });
|
add_window_accelerators(
|
||||||
add_window_accelerators(ACTION_HELP_OVERLAY, { "<Ctrl>F1", "<Ctrl>question" });
|
Action.Window.SHORTCUT_HELP, { "<Ctrl>F1", "<Ctrl>question" }
|
||||||
add_window_accelerators(ACTION_REDO, { "<Ctrl><Shift>Z" });
|
);
|
||||||
add_window_accelerators(ACTION_UNDO, { "<Ctrl>Z" });
|
|
||||||
|
|
||||||
MainWindow.add_window_accelerators(this);
|
// Common edit accels
|
||||||
ComposerWidget.add_window_accelerators(this);
|
add_edit_accelerators(Action.Edit.COPY, { "<Ctrl>C" });
|
||||||
Components.Inspector.add_window_accelerators(this);
|
add_edit_accelerators(Action.Edit.REDO, { "<Ctrl><Shift>Z" });
|
||||||
|
add_edit_accelerators(Action.Edit.UNDO, { "<Ctrl>Z" });
|
||||||
|
|
||||||
|
MainWindow.add_accelerators(this);
|
||||||
|
ComposerWidget.add_accelerators(this);
|
||||||
|
Components.Inspector.add_accelerators(this);
|
||||||
|
Dialogs.ProblemDetailsDialog.add_accelerators(this);
|
||||||
|
|
||||||
if (this.is_background_service) {
|
if (this.is_background_service) {
|
||||||
// Since command_line won't be called below if running as
|
// Since command_line won't be called below if running as
|
||||||
|
|
@ -494,7 +481,18 @@ public class GearyApplication : Gtk.Application {
|
||||||
public void add_window_accelerators(string action,
|
public void add_window_accelerators(string action,
|
||||||
string[] accelerators,
|
string[] accelerators,
|
||||||
Variant? param = null) {
|
Variant? param = null) {
|
||||||
string name = "win." + action;
|
string name = Action.Window.prefix(action);
|
||||||
|
string[] all_accel = get_accels_for_action(name);
|
||||||
|
foreach (string accel in accelerators) {
|
||||||
|
all_accel += accel;
|
||||||
|
}
|
||||||
|
set_accels_for_action(name, all_accel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add_edit_accelerators(string action,
|
||||||
|
string[] accelerators,
|
||||||
|
Variant? param = null) {
|
||||||
|
string name = Action.Edit.prefix(action);
|
||||||
string[] all_accel = get_accels_for_action(name);
|
string[] all_accel = get_accels_for_action(name);
|
||||||
foreach (string accel in accelerators) {
|
foreach (string accel in accelerators) {
|
||||||
all_accel += accel;
|
all_accel += accel;
|
||||||
|
|
@ -875,13 +873,10 @@ public class GearyApplication : Gtk.Application {
|
||||||
foreach (string arg in args) {
|
foreach (string arg in args) {
|
||||||
// the only acceptable arguments are mailto:'s
|
// the only acceptable arguments are mailto:'s
|
||||||
if (arg == MAILTO_URI_SCHEME_PREFIX) {
|
if (arg == MAILTO_URI_SCHEME_PREFIX) {
|
||||||
activate_action(GearyApplication.ACTION_COMPOSE, null);
|
activate_action(Action.Application.COMPOSE, null);
|
||||||
activated = true;
|
activated = true;
|
||||||
} else if (arg.down().has_prefix(MAILTO_URI_SCHEME_PREFIX)) {
|
} else if (arg.down().has_prefix(MAILTO_URI_SCHEME_PREFIX)) {
|
||||||
activate_action(
|
activate_action(Action.Application.MAILTO, new GLib.Variant.string(arg));
|
||||||
GearyApplication.ACTION_MAILTO,
|
|
||||||
new GLib.Variant.string(arg)
|
|
||||||
);
|
|
||||||
activated = true;
|
activated = true;
|
||||||
} else {
|
} else {
|
||||||
command_line.printerr("%s: ", this.binary);
|
command_line.printerr("%s: ", this.binary);
|
||||||
|
|
|
||||||
73
src/client/client-action.vala
Normal file
73
src/client/client-action.vala
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 Michael Gratton <mike@vee.net>
|
||||||
|
*
|
||||||
|
* This software is licensed under the GNU Lesser General Public License
|
||||||
|
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Common client GAction and action group names */
|
||||||
|
namespace Action {
|
||||||
|
|
||||||
|
|
||||||
|
/** Common application GAction names. */
|
||||||
|
namespace Application {
|
||||||
|
|
||||||
|
/** Application GAction group name */
|
||||||
|
public const string GROUP_NAME = "app";
|
||||||
|
|
||||||
|
public const string ABOUT = "about";
|
||||||
|
public const string ACCOUNTS = "accounts";
|
||||||
|
public const string COMPOSE = "compose";
|
||||||
|
public const string INSPECT = "inspect";
|
||||||
|
public const string HELP = "help";
|
||||||
|
public const string MAILTO = "mailto";
|
||||||
|
public const string PREFERENCES = "preferences";
|
||||||
|
public const string SHOW_EMAIL = "show-email";
|
||||||
|
public const string SHOW_FOLDER = "show-folder";
|
||||||
|
public const string QUIT = "quit";
|
||||||
|
|
||||||
|
|
||||||
|
/** Returns the given action name prefixed with the group name. */
|
||||||
|
public string prefix(string action_name) {
|
||||||
|
return GROUP_NAME + "." + action_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Common window GAction names. */
|
||||||
|
namespace Window {
|
||||||
|
|
||||||
|
/** Window GAction group name */
|
||||||
|
public const string GROUP_NAME = "win";
|
||||||
|
|
||||||
|
public const string CLOSE = "close";
|
||||||
|
public const string SHORTCUT_HELP = "show-help-overlay";
|
||||||
|
|
||||||
|
|
||||||
|
/** Returns the given action name prefixed with the group name. */
|
||||||
|
public string prefix(string action_name) {
|
||||||
|
return GROUP_NAME + "." + action_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Common editing GAction names. */
|
||||||
|
namespace Edit {
|
||||||
|
|
||||||
|
/** Editing GAction group name */
|
||||||
|
public const string GROUP_NAME = "edt";
|
||||||
|
|
||||||
|
public const string COPY = "copy";
|
||||||
|
public const string REDO = "redo";
|
||||||
|
public const string UNDO = "undo";
|
||||||
|
|
||||||
|
|
||||||
|
/** Returns the given action name prefixed with the group name. */
|
||||||
|
public string prefix(string action_name) {
|
||||||
|
return GROUP_NAME + "." + action_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
333
src/client/components/components-entry-undo.vala
Normal file
333
src/client/components/components-entry-undo.vala
Normal file
|
|
@ -0,0 +1,333 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 Michael Gratton <mike@vee.net>
|
||||||
|
*
|
||||||
|
* This software is licensed under the GNU Lesser General Public License
|
||||||
|
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides per-GTK Entry undo and redo using a command stack.
|
||||||
|
*/
|
||||||
|
public class Components.EntryUndo : Geary.BaseObject {
|
||||||
|
|
||||||
|
|
||||||
|
private const ActionEntry[] EDIT_ACTIONS = {
|
||||||
|
{ Action.Edit.UNDO, on_undo },
|
||||||
|
{ Action.Edit.REDO, on_redo },
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
private enum EditType { NONE, INSERT, DELETE; }
|
||||||
|
|
||||||
|
|
||||||
|
private class EditCommand : Application.Command {
|
||||||
|
|
||||||
|
|
||||||
|
private weak EntryUndo manager;
|
||||||
|
private EditType edit;
|
||||||
|
private int position;
|
||||||
|
private string text;
|
||||||
|
|
||||||
|
|
||||||
|
public EditCommand(EntryUndo manager,
|
||||||
|
EditType edit,
|
||||||
|
int position,
|
||||||
|
string text) {
|
||||||
|
this.manager = manager;
|
||||||
|
this.edit = edit;
|
||||||
|
this.position = position;
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async void execute(GLib.Cancellable? cancellable)
|
||||||
|
throws GLib.Error {
|
||||||
|
// No-op, has already been executed
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async void undo(GLib.Cancellable? cancellable)
|
||||||
|
throws GLib.Error {
|
||||||
|
EntryUndo? manager = this.manager;
|
||||||
|
if (manager != null) {
|
||||||
|
manager.events_enabled = false;
|
||||||
|
switch (this.edit) {
|
||||||
|
case INSERT:
|
||||||
|
do_delete(manager.target);
|
||||||
|
break;
|
||||||
|
case DELETE:
|
||||||
|
do_insert(manager.target);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
manager.events_enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async void redo(GLib.Cancellable? cancellable)
|
||||||
|
throws GLib.Error {
|
||||||
|
EntryUndo? manager = this.manager;
|
||||||
|
if (manager != null) {
|
||||||
|
manager.events_enabled = false;
|
||||||
|
switch (this.edit) {
|
||||||
|
case INSERT:
|
||||||
|
do_insert(manager.target);
|
||||||
|
break;
|
||||||
|
case DELETE:
|
||||||
|
do_delete(manager.target);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
manager.events_enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void do_insert(Gtk.Entry target) {
|
||||||
|
int position = this.position;
|
||||||
|
target.insert_text(this.text, -1, ref position);
|
||||||
|
target.set_position(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void do_delete(Gtk.Entry target) {
|
||||||
|
target.delete_text(
|
||||||
|
this.position, this.position + this.text.char_count()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** The entry being managed */
|
||||||
|
public Gtk.Entry target { get; private set; }
|
||||||
|
|
||||||
|
private Application.CommandStack commands;
|
||||||
|
private EditType last_edit = NONE;
|
||||||
|
private int edit_start = 0;
|
||||||
|
private int edit_end = 0;
|
||||||
|
private GLib.StringBuilder edit_accumuluator = new GLib.StringBuilder();
|
||||||
|
|
||||||
|
private bool events_enabled = true;
|
||||||
|
|
||||||
|
private GLib.SimpleActionGroup edit_actions = new GLib.SimpleActionGroup();
|
||||||
|
|
||||||
|
|
||||||
|
public EntryUndo(Gtk.Entry target) {
|
||||||
|
this.edit_actions.add_action_entries(EDIT_ACTIONS, this);
|
||||||
|
|
||||||
|
this.target = target;
|
||||||
|
this.target.insert_action_group(Action.Edit.GROUP_NAME, this.edit_actions);
|
||||||
|
this.target.insert_text.connect(on_inserted);
|
||||||
|
this.target.delete_text.connect(on_deleted);
|
||||||
|
|
||||||
|
this.commands = new Application.CommandStack();
|
||||||
|
this.commands.executed.connect(this.update_command_actions);
|
||||||
|
this.commands.undone.connect(this.update_command_actions);
|
||||||
|
this.commands.redone.connect(this.update_command_actions);
|
||||||
|
}
|
||||||
|
|
||||||
|
~EntryUndo() {
|
||||||
|
this.target.insert_text.disconnect(on_inserted);
|
||||||
|
this.target.delete_text.disconnect(on_deleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Resets the editing stack for the target entry. */
|
||||||
|
public void reset() {
|
||||||
|
this.last_edit = NONE;
|
||||||
|
this.commands.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void execute(Application.Command command) {
|
||||||
|
bool complete = false;
|
||||||
|
this.commands.execute.begin(
|
||||||
|
command,
|
||||||
|
null,
|
||||||
|
(obj, res) => {
|
||||||
|
try {
|
||||||
|
this.commands.execute.end(res);
|
||||||
|
} catch (GLib.Error thrown) {
|
||||||
|
debug(
|
||||||
|
"Failed to execute entry edit command: %s",
|
||||||
|
thrown.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
complete = true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
while (!complete) {
|
||||||
|
Gtk.main_iteration();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void do_undo() {
|
||||||
|
flush_command();
|
||||||
|
bool complete = false;
|
||||||
|
this.commands.undo.begin(
|
||||||
|
null,
|
||||||
|
(obj, res) => {
|
||||||
|
try {
|
||||||
|
this.commands.undo.end(res);
|
||||||
|
} catch (GLib.Error thrown) {
|
||||||
|
debug(
|
||||||
|
"Failed to undo entry edit command: %s",
|
||||||
|
thrown.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
complete = true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
while (!complete) {
|
||||||
|
Gtk.main_iteration();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void do_redo() {
|
||||||
|
flush_command();
|
||||||
|
bool complete = false;
|
||||||
|
this.commands.redo.begin(
|
||||||
|
null,
|
||||||
|
(obj, res) => {
|
||||||
|
try {
|
||||||
|
this.commands.redo.end(res);
|
||||||
|
} catch (GLib.Error thrown) {
|
||||||
|
debug(
|
||||||
|
"Failed to undo entry edit command: %s",
|
||||||
|
thrown.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
complete = true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
while (!complete) {
|
||||||
|
Gtk.main_iteration();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void flush_command() {
|
||||||
|
EditCommand? command = extract_command();
|
||||||
|
if (command != null) {
|
||||||
|
execute(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private EditCommand? extract_command() {
|
||||||
|
EditCommand? command = null;
|
||||||
|
if (this.last_edit != NONE) {
|
||||||
|
command = new EditCommand(
|
||||||
|
this,
|
||||||
|
this.last_edit,
|
||||||
|
this.edit_start,
|
||||||
|
this.edit_accumuluator.str
|
||||||
|
);
|
||||||
|
this.edit_accumuluator.truncate();
|
||||||
|
}
|
||||||
|
this.last_edit = NONE;
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update_command_actions() {
|
||||||
|
((GLib.SimpleAction) this.edit_actions.lookup_action(Action.Edit.UNDO))
|
||||||
|
.set_enabled(this.commands.can_undo);
|
||||||
|
((GLib.SimpleAction) this.edit_actions.lookup_action(Action.Edit.REDO))
|
||||||
|
.set_enabled(this.commands.can_redo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void on_inserted(string inserted, int inserted_len, ref int pos) {
|
||||||
|
if (this.events_enabled) {
|
||||||
|
// Normalise to something useful
|
||||||
|
inserted_len = inserted.char_count();
|
||||||
|
|
||||||
|
bool is_non_trivial = inserted_len > 1;
|
||||||
|
bool insert_handled = false;
|
||||||
|
if (this.last_edit == DELETE) {
|
||||||
|
Application.Command? command = extract_command();
|
||||||
|
if (command != null &&
|
||||||
|
this.edit_start == pos &&
|
||||||
|
is_non_trivial) {
|
||||||
|
// Delete followed by a non-trivial insert at the
|
||||||
|
// same position indicates something was probably
|
||||||
|
// pasted/spellchecked/completed/etc, so execute
|
||||||
|
// together as a single command.
|
||||||
|
this.last_edit = INSERT;
|
||||||
|
this.edit_start = pos;
|
||||||
|
this.edit_accumuluator.append(inserted);
|
||||||
|
command = new Application.CommandSequence({
|
||||||
|
command, extract_command()
|
||||||
|
});
|
||||||
|
insert_handled = true;
|
||||||
|
}
|
||||||
|
if (command != null) {
|
||||||
|
execute(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!insert_handled) {
|
||||||
|
bool is_disjoint_edit = (
|
||||||
|
this.last_edit == INSERT && this.edit_end != pos
|
||||||
|
);
|
||||||
|
bool is_non_alpha_num = (
|
||||||
|
inserted_len == 1 && !inserted.get_char(0).isalnum()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Flush any existing edits if any of the special
|
||||||
|
// cases hold
|
||||||
|
if (is_disjoint_edit || is_non_alpha_num || is_non_trivial) {
|
||||||
|
flush_command();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.last_edit == NONE) {
|
||||||
|
this.last_edit = INSERT;
|
||||||
|
this.edit_start = pos;
|
||||||
|
this.edit_end = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.edit_end += inserted_len;
|
||||||
|
this.edit_accumuluator.append(inserted);
|
||||||
|
|
||||||
|
// Flush the new edit if we don't want to coalesce
|
||||||
|
// with subsequent inserts
|
||||||
|
if (is_non_alpha_num || is_non_trivial) {
|
||||||
|
flush_command();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void on_deleted(int start, int end) {
|
||||||
|
if (this.events_enabled) {
|
||||||
|
// Normalise value of end to be something useful if needed
|
||||||
|
string text = this.target.buffer.get_text();
|
||||||
|
if (end < 0) {
|
||||||
|
end = text.char_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't flush non-trivial deletes since we want to be
|
||||||
|
// able to combine them with non-trivial inserts for
|
||||||
|
// better handling of pasting/spell-checking
|
||||||
|
// replacement/etc.
|
||||||
|
bool is_disjoint_edit = (
|
||||||
|
this.last_edit == DELETE && this.edit_start != end
|
||||||
|
);
|
||||||
|
if (this.last_edit == INSERT || is_disjoint_edit) {
|
||||||
|
flush_command();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.last_edit == NONE) {
|
||||||
|
this.last_edit = DELETE;
|
||||||
|
this.edit_end = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.edit_start = start;
|
||||||
|
this.edit_accumuluator.prepend(
|
||||||
|
text.slice(
|
||||||
|
text.index_of_nth_char(start),
|
||||||
|
text.index_of_nth_char(end)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void on_undo() {
|
||||||
|
do_undo();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void on_redo() {
|
||||||
|
do_redo();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -28,16 +28,20 @@ public class Components.Inspector : Gtk.ApplicationWindow {
|
||||||
private const string ACTION_SEARCH_TOGGLE = "toggle-search";
|
private const string ACTION_SEARCH_TOGGLE = "toggle-search";
|
||||||
private const string ACTION_SEARCH_ACTIVATE = "activate-search";
|
private const string ACTION_SEARCH_ACTIVATE = "activate-search";
|
||||||
|
|
||||||
private const ActionEntry[] action_entries = {
|
private const ActionEntry[] EDIT_ACTIONS = {
|
||||||
{GearyApplication.ACTION_CLOSE, on_close },
|
{ Action.Edit.COPY, on_copy_clicked },
|
||||||
{GearyApplication.ACTION_COPY, on_copy_clicked },
|
|
||||||
{ACTION_CLOSE, on_close },
|
|
||||||
{ACTION_PLAY_TOGGLE, on_logs_play_toggled, null, "true" },
|
|
||||||
{ACTION_SEARCH_TOGGLE, on_logs_search_toggled, null, "false" },
|
|
||||||
{ACTION_SEARCH_ACTIVATE, on_logs_search_activated },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public static void add_window_accelerators(GearyApplication app) {
|
private const ActionEntry[] WINDOW_ACTIONS = {
|
||||||
|
{ Action.Window.CLOSE, on_close },
|
||||||
|
{ ACTION_CLOSE, on_close },
|
||||||
|
{ ACTION_PLAY_TOGGLE, on_logs_play_toggled, null, "true" },
|
||||||
|
{ ACTION_SEARCH_TOGGLE, on_logs_search_toggled, null, "false" },
|
||||||
|
{ ACTION_SEARCH_ACTIVATE, on_logs_search_activated },
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public static void add_accelerators(GearyApplication app) {
|
||||||
app.add_window_accelerators(ACTION_CLOSE, { "Escape" } );
|
app.add_window_accelerators(ACTION_CLOSE, { "Escape" } );
|
||||||
app.add_window_accelerators(ACTION_PLAY_TOGGLE, { "space" } );
|
app.add_window_accelerators(ACTION_PLAY_TOGGLE, { "space" } );
|
||||||
app.add_window_accelerators(ACTION_SEARCH_ACTIVATE, { "<Ctrl>F" } );
|
app.add_window_accelerators(ACTION_SEARCH_ACTIVATE, { "<Ctrl>F" } );
|
||||||
|
|
@ -67,7 +71,13 @@ public class Components.Inspector : Gtk.ApplicationWindow {
|
||||||
Object(application: application);
|
Object(application: application);
|
||||||
this.title = this.header_bar.title = _("Inspector");
|
this.title = this.header_bar.title = _("Inspector");
|
||||||
|
|
||||||
add_action_entries(Inspector.action_entries, this);
|
// Edit actions
|
||||||
|
GLib.SimpleActionGroup edit_actions = new GLib.SimpleActionGroup();
|
||||||
|
edit_actions.add_action_entries(EDIT_ACTIONS, this);
|
||||||
|
insert_action_group(Action.Edit.GROUP_NAME, edit_actions);
|
||||||
|
|
||||||
|
// Window actions
|
||||||
|
add_action_entries(WINDOW_ACTIONS, this);
|
||||||
|
|
||||||
this.log_pane = new InspectorLogView(application.config, null);
|
this.log_pane = new InspectorLogView(application.config, null);
|
||||||
this.log_pane.record_selection_changed.connect(
|
this.log_pane.record_selection_changed.connect(
|
||||||
|
|
|
||||||
|
|
@ -34,14 +34,13 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
|
||||||
public const string ACTION_TRASH_CONVERSATION = "trash-conversation";
|
public const string ACTION_TRASH_CONVERSATION = "trash-conversation";
|
||||||
public const string ACTION_ZOOM = "zoom";
|
public const string ACTION_ZOOM = "zoom";
|
||||||
|
|
||||||
private const int STATUS_BAR_HEIGHT = 18;
|
private const ActionEntry[] EDIT_ACTIONS = {
|
||||||
private const int UPDATE_UI_INTERVAL = 60;
|
{ Action.Edit.UNDO, on_undo },
|
||||||
private const int MIN_CONVERSATION_COUNT = 50;
|
{ Action.Edit.REDO, on_redo },
|
||||||
|
};
|
||||||
|
|
||||||
private const ActionEntry[] win_action_entries = {
|
private const ActionEntry[] WINDOW_ACTIONS = {
|
||||||
{ GearyApplication.ACTION_CLOSE, on_close },
|
{ Action.Window.CLOSE, on_close },
|
||||||
{ GearyApplication.ACTION_UNDO, on_undo },
|
|
||||||
{ GearyApplication.ACTION_REDO, on_redo },
|
|
||||||
|
|
||||||
{ ACTION_CONVERSATION_LIST, on_conversation_list },
|
{ ACTION_CONVERSATION_LIST, on_conversation_list },
|
||||||
{ ACTION_FIND_IN_CONVERSATION, on_find_in_conversation_action },
|
{ ACTION_FIND_IN_CONVERSATION, on_find_in_conversation_action },
|
||||||
|
|
@ -70,8 +69,12 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
|
||||||
{ ACTION_ZOOM, on_zoom, "s" },
|
{ ACTION_ZOOM, on_zoom, "s" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private const int STATUS_BAR_HEIGHT = 18;
|
||||||
|
private const int UPDATE_UI_INTERVAL = 60;
|
||||||
|
private const int MIN_CONVERSATION_COUNT = 50;
|
||||||
|
|
||||||
public static void add_window_accelerators(GearyApplication owner) {
|
|
||||||
|
public static void add_accelerators(GearyApplication owner) {
|
||||||
// Marking actions
|
// Marking actions
|
||||||
//
|
//
|
||||||
// Unread is the primary action, so it doesn't get the <Shift>
|
// Unread is the primary action, so it doesn't get the <Shift>
|
||||||
|
|
@ -209,6 +212,8 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
|
||||||
|
|
||||||
private Application.Controller.AccountContext? context = null;
|
private Application.Controller.AccountContext? context = null;
|
||||||
|
|
||||||
|
private GLib.SimpleActionGroup edit_actions = new GLib.SimpleActionGroup();
|
||||||
|
|
||||||
// Determines if the conversation viewer should autoselect on next
|
// Determines if the conversation viewer should autoselect on next
|
||||||
// load
|
// load
|
||||||
private bool previous_selection_was_interactive = false;
|
private bool previous_selection_was_interactive = false;
|
||||||
|
|
@ -277,7 +282,13 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
|
||||||
|
|
||||||
load_config(application.config);
|
load_config(application.config);
|
||||||
restore_saved_window_state();
|
restore_saved_window_state();
|
||||||
add_action_entries(win_action_entries, this);
|
|
||||||
|
// Edit actions
|
||||||
|
this.edit_actions.add_action_entries(EDIT_ACTIONS, this);
|
||||||
|
insert_action_group(Action.Edit.GROUP_NAME, this.edit_actions);
|
||||||
|
|
||||||
|
// Window actions
|
||||||
|
add_action_entries(MainWindow.WINDOW_ACTIONS, this);
|
||||||
|
|
||||||
set_styling();
|
set_styling();
|
||||||
setup_layout(application.config);
|
setup_layout(application.config);
|
||||||
|
|
@ -572,7 +583,7 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
|
||||||
new ComposerWindow(composer, this.application);
|
new ComposerWindow(composer, this.application);
|
||||||
} else {
|
} else {
|
||||||
this.conversation_viewer.do_compose(composer);
|
this.conversation_viewer.do_compose(composer);
|
||||||
get_action(ACTION_FIND_IN_CONVERSATION).set_enabled(false);
|
get_window_action(ACTION_FIND_IN_CONVERSATION).set_enabled(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -893,6 +904,8 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
|
||||||
* ConversationWebView instances, since none of them handle
|
* ConversationWebView instances, since none of them handle
|
||||||
* events.
|
* events.
|
||||||
*
|
*
|
||||||
|
* See also the note in EmailEntry::on_key_press.
|
||||||
|
*
|
||||||
* The work around here is completely override the default
|
* The work around here is completely override the default
|
||||||
* implementation to reverse it. So if something related to
|
* implementation to reverse it. So if something related to
|
||||||
* key handling breaks in the future, this might be a good
|
* key handling breaks in the future, this might be a good
|
||||||
|
|
@ -969,10 +982,10 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
|
||||||
|
|
||||||
private void update_command_actions() {
|
private void update_command_actions() {
|
||||||
Application.Controller.AccountContext? selected = this.context;
|
Application.Controller.AccountContext? selected = this.context;
|
||||||
get_action(GearyApplication.ACTION_UNDO).set_enabled(
|
get_edit_action(Action.Edit.UNDO).set_enabled(
|
||||||
selected != null && selected.commands.can_undo
|
selected != null && selected.commands.can_undo
|
||||||
);
|
);
|
||||||
get_action(GearyApplication.ACTION_REDO).set_enabled(
|
get_edit_action(Action.Edit.REDO).set_enabled(
|
||||||
selected != null && selected.commands.can_redo
|
selected != null && selected.commands.can_redo
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1379,7 +1392,7 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
|
||||||
bool sensitive = (count != NONE);
|
bool sensitive = (count != NONE);
|
||||||
bool multiple = (count == MULTIPLE);
|
bool multiple = (count == MULTIPLE);
|
||||||
|
|
||||||
get_action(ACTION_FIND_IN_CONVERSATION).set_enabled(
|
get_window_action(ACTION_FIND_IN_CONVERSATION).set_enabled(
|
||||||
sensitive && !multiple
|
sensitive && !multiple
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -1389,29 +1402,29 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
|
||||||
this.selected_folder != null &&
|
this.selected_folder != null &&
|
||||||
this.selected_folder.special_folder_type != DRAFTS
|
this.selected_folder.special_folder_type != DRAFTS
|
||||||
);
|
);
|
||||||
get_action(ACTION_REPLY_CONVERSATION).set_enabled(reply_sensitive);
|
get_window_action(ACTION_REPLY_CONVERSATION).set_enabled(reply_sensitive);
|
||||||
get_action(ACTION_REPLY_ALL_CONVERSATION).set_enabled(reply_sensitive);
|
get_window_action(ACTION_REPLY_ALL_CONVERSATION).set_enabled(reply_sensitive);
|
||||||
get_action(ACTION_FORWARD_CONVERSATION).set_enabled(reply_sensitive);
|
get_window_action(ACTION_FORWARD_CONVERSATION).set_enabled(reply_sensitive);
|
||||||
|
|
||||||
bool move_enabled = (
|
bool move_enabled = (
|
||||||
sensitive && (selected_folder is Geary.FolderSupport.Move)
|
sensitive && (selected_folder is Geary.FolderSupport.Move)
|
||||||
);
|
);
|
||||||
this.main_toolbar.move_message_button.set_sensitive(move_enabled);
|
this.main_toolbar.move_message_button.set_sensitive(move_enabled);
|
||||||
get_action(ACTION_SHOW_MOVE_MENU).set_enabled(move_enabled);
|
get_window_action(ACTION_SHOW_MOVE_MENU).set_enabled(move_enabled);
|
||||||
|
|
||||||
bool copy_enabled = (
|
bool copy_enabled = (
|
||||||
sensitive && (selected_folder is Geary.FolderSupport.Copy)
|
sensitive && (selected_folder is Geary.FolderSupport.Copy)
|
||||||
);
|
);
|
||||||
this.main_toolbar.copy_message_button.set_sensitive(copy_enabled);
|
this.main_toolbar.copy_message_button.set_sensitive(copy_enabled);
|
||||||
get_action(ACTION_SHOW_COPY_MENU).set_enabled(move_enabled);
|
get_window_action(ACTION_SHOW_COPY_MENU).set_enabled(move_enabled);
|
||||||
|
|
||||||
get_action(ACTION_ARCHIVE_CONVERSATION).set_enabled(
|
get_window_action(ACTION_ARCHIVE_CONVERSATION).set_enabled(
|
||||||
sensitive && (selected_folder is Geary.FolderSupport.Archive)
|
sensitive && (selected_folder is Geary.FolderSupport.Archive)
|
||||||
);
|
);
|
||||||
get_action(ACTION_TRASH_CONVERSATION).set_enabled(
|
get_window_action(ACTION_TRASH_CONVERSATION).set_enabled(
|
||||||
sensitive && this.selected_folder_supports_trash
|
sensitive && this.selected_folder_supports_trash
|
||||||
);
|
);
|
||||||
get_action(ACTION_DELETE_CONVERSATION).set_enabled(
|
get_window_action(ACTION_DELETE_CONVERSATION).set_enabled(
|
||||||
sensitive && (selected_folder is Geary.FolderSupport.Remove)
|
sensitive && (selected_folder is Geary.FolderSupport.Remove)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -1454,15 +1467,15 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
|
||||||
supported_operations.add_all(selected_operations.get_values());
|
supported_operations.add_all(selected_operations.get_values());
|
||||||
}
|
}
|
||||||
|
|
||||||
get_action(ACTION_SHOW_MARK_MENU).set_enabled(
|
get_window_action(ACTION_SHOW_MARK_MENU).set_enabled(
|
||||||
sensitive &&
|
sensitive &&
|
||||||
(typeof(Geary.FolderSupport.Mark) in supported_operations)
|
(typeof(Geary.FolderSupport.Mark) in supported_operations)
|
||||||
);
|
);
|
||||||
get_action(ACTION_SHOW_COPY_MENU).set_enabled(
|
get_window_action(ACTION_SHOW_COPY_MENU).set_enabled(
|
||||||
sensitive &&
|
sensitive &&
|
||||||
(supported_operations.contains(typeof(Geary.FolderSupport.Copy)))
|
(supported_operations.contains(typeof(Geary.FolderSupport.Copy)))
|
||||||
);
|
);
|
||||||
get_action(ACTION_SHOW_MOVE_MENU).set_enabled(
|
get_window_action(ACTION_SHOW_MOVE_MENU).set_enabled(
|
||||||
sensitive &&
|
sensitive &&
|
||||||
(supported_operations.contains(typeof(Geary.FolderSupport.Move)))
|
(supported_operations.contains(typeof(Geary.FolderSupport.Move)))
|
||||||
);
|
);
|
||||||
|
|
@ -1489,10 +1502,14 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SimpleAction get_action(string name) {
|
private SimpleAction get_window_action(string name) {
|
||||||
return (SimpleAction) lookup_action(name);
|
return (SimpleAction) lookup_action(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SimpleAction get_edit_action(string name) {
|
||||||
|
return (SimpleAction) this.edit_actions.lookup_action(name);
|
||||||
|
}
|
||||||
|
|
||||||
private void on_scan_completed(Geary.App.ConversationMonitor monitor) {
|
private void on_scan_completed(Geary.App.ConversationMonitor monitor) {
|
||||||
// Done scanning. Check if we have enough messages to fill
|
// Done scanning. Check if we have enough messages to fill
|
||||||
// the conversation list; if not, trigger a load_more();
|
// the conversation list; if not, trigger a load_more();
|
||||||
|
|
@ -1595,6 +1612,7 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void on_command_undo(Application.Command command) {
|
private void on_command_undo(Application.Command command) {
|
||||||
|
update_command_actions();
|
||||||
Application.EmailCommand? email = command as Application.EmailCommand;
|
Application.EmailCommand? email = command as Application.EmailCommand;
|
||||||
if (email != null) {
|
if (email != null) {
|
||||||
if (email.conversations.size > 1) {
|
if (email.conversations.size > 1) {
|
||||||
|
|
@ -1610,20 +1628,19 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
|
||||||
if (command.undone_label != null) {
|
if (command.undone_label != null) {
|
||||||
Components.InAppNotification ian =
|
Components.InAppNotification ian =
|
||||||
new Components.InAppNotification(command.undone_label);
|
new Components.InAppNotification(command.undone_label);
|
||||||
ian.set_button(_("Redo"), "win." + GearyApplication.ACTION_REDO);
|
ian.set_button(_("Redo"), Action.Edit.prefix(Action.Edit.REDO));
|
||||||
add_notification(ian);
|
add_notification(ian);
|
||||||
}
|
}
|
||||||
update_command_actions();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void on_command_redo(Application.Command command) {
|
private void on_command_redo(Application.Command command) {
|
||||||
|
update_command_actions();
|
||||||
if (command.executed_label != null) {
|
if (command.executed_label != null) {
|
||||||
Components.InAppNotification ian =
|
Components.InAppNotification ian =
|
||||||
new Components.InAppNotification(command.executed_label);
|
new Components.InAppNotification(command.executed_label);
|
||||||
ian.set_button(_("Undo"), "win." + GearyApplication.ACTION_UNDO);
|
ian.set_button(_("Undo"), Action.Edit.prefix(Action.Edit.UNDO));
|
||||||
add_notification(ian);
|
add_notification(ian);
|
||||||
}
|
}
|
||||||
update_command_actions();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void on_conversation_view_added(ConversationListBox list) {
|
private void on_conversation_view_added(ConversationListBox list) {
|
||||||
|
|
@ -1781,14 +1798,14 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
|
||||||
unstarred_selected = true;
|
unstarred_selected = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get_action(ACTION_MARK_AS_READ).set_enabled(unread_selected);
|
get_window_action(ACTION_MARK_AS_READ).set_enabled(unread_selected);
|
||||||
get_action(ACTION_MARK_AS_UNREAD).set_enabled(read_selected);
|
get_window_action(ACTION_MARK_AS_UNREAD).set_enabled(read_selected);
|
||||||
get_action(ACTION_MARK_AS_STARRED).set_enabled(unstarred_selected);
|
get_window_action(ACTION_MARK_AS_STARRED).set_enabled(unstarred_selected);
|
||||||
get_action(ACTION_MARK_AS_UNSTARRED).set_enabled(starred_selected);
|
get_window_action(ACTION_MARK_AS_UNSTARRED).set_enabled(starred_selected);
|
||||||
|
|
||||||
// If we're in Drafts/Outbox, we also shouldn't set a message as SPAM.
|
// If we're in Drafts/Outbox, we also shouldn't set a message as SPAM.
|
||||||
bool in_spam_folder = selected_folder.special_folder_type == Geary.SpecialFolderType.SPAM;
|
bool in_spam_folder = selected_folder.special_folder_type == Geary.SpecialFolderType.SPAM;
|
||||||
get_action(ACTION_TOGGLE_SPAM).set_enabled(!in_spam_folder &&
|
get_window_action(ACTION_TOGGLE_SPAM).set_enabled(!in_spam_folder &&
|
||||||
selected_folder.special_folder_type != Geary.SpecialFolderType.DRAFTS &&
|
selected_folder.special_folder_type != Geary.SpecialFolderType.DRAFTS &&
|
||||||
selected_folder.special_folder_type != Geary.SpecialFolderType.OUTBOX);
|
selected_folder.special_folder_type != Geary.SpecialFolderType.OUTBOX);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ public class SearchBar : Gtk.SearchBar {
|
||||||
public bool search_entry_has_focus { get { return search_entry.has_focus; } }
|
public bool search_entry_has_focus { get { return search_entry.has_focus; } }
|
||||||
|
|
||||||
private Gtk.SearchEntry search_entry = new Gtk.SearchEntry();
|
private Gtk.SearchEntry search_entry = new Gtk.SearchEntry();
|
||||||
|
private Components.EntryUndo search_undo;
|
||||||
private Geary.ProgressMonitor? search_upgrade_progress_monitor = null;
|
private Geary.ProgressMonitor? search_upgrade_progress_monitor = null;
|
||||||
private MonitoredProgressBar search_upgrade_progress_bar = new MonitoredProgressBar();
|
private MonitoredProgressBar search_upgrade_progress_bar = new MonitoredProgressBar();
|
||||||
private Geary.Account? current_account = null;
|
private Geary.Account? current_account = null;
|
||||||
|
|
@ -29,6 +30,10 @@ public class SearchBar : Gtk.SearchBar {
|
||||||
});
|
});
|
||||||
search_entry.has_focus = true;
|
search_entry.has_focus = true;
|
||||||
|
|
||||||
|
this.search_undo = new Components.EntryUndo(this.search_entry);
|
||||||
|
|
||||||
|
this.notify["search-mode-enabled"].connect(on_search_mode_changed);
|
||||||
|
|
||||||
// Search upgrade progress bar.
|
// Search upgrade progress bar.
|
||||||
search_upgrade_progress_bar.show_text = true;
|
search_upgrade_progress_bar.show_text = true;
|
||||||
search_upgrade_progress_bar.visible = false;
|
search_upgrade_progress_bar.visible = false;
|
||||||
|
|
@ -41,7 +46,7 @@ public class SearchBar : Gtk.SearchBar {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set_search_text(string text) {
|
public void set_search_text(string text) {
|
||||||
search_entry.text = text;
|
this.search_entry.text = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void give_search_focus() {
|
public void give_search_focus() {
|
||||||
|
|
@ -110,4 +115,9 @@ public class SearchBar : Gtk.SearchBar {
|
||||||
_("Search %s account").printf(current_account.information.display_name));
|
_("Search %s account").printf(current_account.information.display_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void on_search_mode_changed() {
|
||||||
|
if (!this.search_mode_enabled) {
|
||||||
|
this.search_undo.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,70 +90,71 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
|
||||||
|
|
||||||
// ACTION_INSERT_LINK and ACTION_REMOVE_FORMAT are missing from
|
// ACTION_INSERT_LINK and ACTION_REMOVE_FORMAT are missing from
|
||||||
// here since they are handled in update_selection_actions
|
// here since they are handled in update_selection_actions
|
||||||
private const string[] html_actions = {
|
private const string[] HTML_ACTIONS = {
|
||||||
ACTION_BOLD, ACTION_ITALIC, ACTION_UNDERLINE, ACTION_STRIKETHROUGH,
|
ACTION_BOLD, ACTION_ITALIC, ACTION_UNDERLINE, ACTION_STRIKETHROUGH,
|
||||||
ACTION_FONT_SIZE, ACTION_FONT_FAMILY, ACTION_COLOR, ACTION_JUSTIFY,
|
ACTION_FONT_SIZE, ACTION_FONT_FAMILY, ACTION_COLOR, ACTION_JUSTIFY,
|
||||||
ACTION_INSERT_IMAGE, ACTION_COPY_LINK,
|
ACTION_INSERT_IMAGE, ACTION_COPY_LINK,
|
||||||
ACTION_OLIST, ACTION_ULIST
|
ACTION_OLIST, ACTION_ULIST
|
||||||
};
|
};
|
||||||
|
|
||||||
private const ActionEntry[] editor_action_entries = {
|
private const ActionEntry[] EDITOR_ACTIONS = {
|
||||||
{GearyApplication.ACTION_UNDO, on_undo },
|
{ Action.Edit.COPY, on_copy },
|
||||||
{GearyApplication.ACTION_REDO, on_redo },
|
{ Action.Edit.REDO, on_redo },
|
||||||
{GearyApplication.ACTION_COPY, on_copy },
|
{ Action.Edit.UNDO, on_undo },
|
||||||
{ACTION_CUT, on_cut },
|
{ ACTION_BOLD, on_action, null, "false" },
|
||||||
{ACTION_COPY_LINK, on_copy_link },
|
{ ACTION_COLOR, on_select_color },
|
||||||
{ACTION_PASTE, on_paste },
|
{ ACTION_COPY_LINK, on_copy_link },
|
||||||
{ACTION_PASTE_WITHOUT_FORMATTING, on_paste_without_formatting },
|
{ ACTION_CUT, on_cut },
|
||||||
{ACTION_SELECT_ALL, on_select_all },
|
{ ACTION_FONT_FAMILY, on_font_family, "s", "'sans'" },
|
||||||
{ACTION_BOLD, on_action, null, "false" },
|
{ ACTION_FONT_SIZE, on_font_size, "s", "'medium'" },
|
||||||
{ACTION_ITALIC, on_action, null, "false" },
|
{ ACTION_INDENT, on_indent },
|
||||||
{ACTION_UNDERLINE, on_action, null, "false" },
|
{ ACTION_INSERT_IMAGE, on_insert_image },
|
||||||
{ACTION_STRIKETHROUGH, on_action, null, "false" },
|
{ ACTION_INSERT_LINK, on_insert_link },
|
||||||
{ACTION_FONT_SIZE, on_font_size, "s", "'medium'" },
|
{ ACTION_ITALIC, on_action, null, "false" },
|
||||||
{ACTION_FONT_FAMILY, on_font_family, "s", "'sans'" },
|
{ ACTION_JUSTIFY, on_justify, "s", "'left'" },
|
||||||
{ACTION_REMOVE_FORMAT, on_remove_format, null, "false" },
|
{ ACTION_OLIST, on_olist },
|
||||||
{ACTION_INDENT, on_indent },
|
{ ACTION_OUTDENT, on_action },
|
||||||
{ACTION_OLIST, on_olist },
|
{ ACTION_PASTE, on_paste },
|
||||||
{ACTION_ULIST, on_ulist },
|
{ ACTION_PASTE_WITHOUT_FORMATTING, on_paste_without_formatting },
|
||||||
{ACTION_OUTDENT, on_action },
|
{ ACTION_REMOVE_FORMAT, on_remove_format, null, "false" },
|
||||||
{ACTION_JUSTIFY, on_justify, "s", "'left'" },
|
{ ACTION_SELECT_ALL, on_select_all },
|
||||||
{ACTION_COLOR, on_select_color },
|
{ ACTION_STRIKETHROUGH, on_action, null, "false" },
|
||||||
{ACTION_INSERT_IMAGE, on_insert_image },
|
{ ACTION_ULIST, on_ulist },
|
||||||
{ACTION_INSERT_LINK, on_insert_link },
|
{ ACTION_UNDERLINE, on_action, null, "false" },
|
||||||
{ACTION_OPEN_INSPECTOR, on_open_inspector },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private const ActionEntry[] composer_action_entries = {
|
private const ActionEntry[] COMPOSER_ACTIONS = {
|
||||||
{GearyApplication.ACTION_CLOSE, on_close },
|
{ Action.Window.CLOSE, on_close },
|
||||||
{ACTION_CLOSE, on_close },
|
{ ACTION_ADD_ATTACHMENT, on_add_attachment },
|
||||||
{ACTION_ADD_ATTACHMENT, on_add_attachment },
|
{ ACTION_ADD_ORIGINAL_ATTACHMENTS, on_pending_attachments },
|
||||||
{ACTION_ADD_ORIGINAL_ATTACHMENTS, on_pending_attachments },
|
{ ACTION_CLOSE, on_close },
|
||||||
{ACTION_CLOSE_AND_DISCARD, on_close_and_discard },
|
{ ACTION_CLOSE_AND_DISCARD, on_close_and_discard },
|
||||||
{ACTION_CLOSE_AND_SAVE, on_close_and_save },
|
{ ACTION_CLOSE_AND_SAVE, on_close_and_save },
|
||||||
{ACTION_COMPOSE_AS_HTML, on_toggle_action, null, "true", on_compose_as_html_toggled },
|
{ ACTION_COMPOSE_AS_HTML, on_toggle_action, null, "true", on_compose_as_html_toggled },
|
||||||
{ACTION_DETACH, on_detach },
|
{ ACTION_DETACH, on_detach },
|
||||||
{ACTION_SELECT_DICTIONARY, on_select_dictionary },
|
{ ACTION_OPEN_INSPECTOR, on_open_inspector },
|
||||||
{ACTION_SEND, on_send },
|
{ ACTION_SELECT_DICTIONARY, on_select_dictionary },
|
||||||
{ACTION_SHOW_EXTENDED, on_toggle_action, null, "false", on_show_extended_toggled },
|
{ ACTION_SEND, on_send },
|
||||||
|
{ ACTION_SHOW_EXTENDED, on_toggle_action, null, "false", on_show_extended_toggled },
|
||||||
};
|
};
|
||||||
|
|
||||||
public static void add_window_accelerators(GearyApplication application) {
|
public static void add_accelerators(GearyApplication application) {
|
||||||
application.add_window_accelerators(ACTION_CLOSE, { "Escape" } );
|
application.add_window_accelerators(ACTION_CLOSE, { "Escape" } );
|
||||||
application.add_window_accelerators(ACTION_CUT, { "<Ctrl>x" } );
|
|
||||||
application.add_window_accelerators(ACTION_PASTE, { "<Ctrl>v" } );
|
|
||||||
application.add_window_accelerators(ACTION_PASTE_WITHOUT_FORMATTING, { "<Ctrl><Shift>v" } );
|
|
||||||
application.add_window_accelerators(ACTION_INSERT_IMAGE, { "<Ctrl>g" } );
|
|
||||||
application.add_window_accelerators(ACTION_INSERT_LINK, { "<Ctrl>l" } );
|
|
||||||
application.add_window_accelerators(ACTION_INDENT, { "<Ctrl>bracketright" } );
|
|
||||||
application.add_window_accelerators(ACTION_OUTDENT, { "<Ctrl>bracketleft" } );
|
|
||||||
application.add_window_accelerators(ACTION_REMOVE_FORMAT, { "<Ctrl>space" } );
|
|
||||||
application.add_window_accelerators(ACTION_BOLD, { "<Ctrl>b" } );
|
|
||||||
application.add_window_accelerators(ACTION_ITALIC, { "<Ctrl>i" } );
|
|
||||||
application.add_window_accelerators(ACTION_UNDERLINE, { "<Ctrl>u" } );
|
|
||||||
application.add_window_accelerators(ACTION_STRIKETHROUGH, { "<Ctrl>k" } );
|
|
||||||
application.add_window_accelerators(ACTION_ADD_ATTACHMENT, { "<Ctrl>t" } );
|
application.add_window_accelerators(ACTION_ADD_ATTACHMENT, { "<Ctrl>t" } );
|
||||||
application.add_window_accelerators(ACTION_DETACH, { "<Ctrl>d" } );
|
application.add_window_accelerators(ACTION_DETACH, { "<Ctrl>d" } );
|
||||||
|
|
||||||
|
application.add_edit_accelerators(ACTION_CUT, { "<Ctrl>x" } );
|
||||||
|
application.add_edit_accelerators(ACTION_PASTE, { "<Ctrl>v" } );
|
||||||
|
application.add_edit_accelerators(ACTION_PASTE_WITHOUT_FORMATTING, { "<Ctrl><Shift>v" } );
|
||||||
|
application.add_edit_accelerators(ACTION_INSERT_IMAGE, { "<Ctrl>g" } );
|
||||||
|
application.add_edit_accelerators(ACTION_INSERT_LINK, { "<Ctrl>l" } );
|
||||||
|
application.add_edit_accelerators(ACTION_INDENT, { "<Ctrl>bracketright" } );
|
||||||
|
application.add_edit_accelerators(ACTION_OUTDENT, { "<Ctrl>bracketleft" } );
|
||||||
|
application.add_edit_accelerators(ACTION_REMOVE_FORMAT, { "<Ctrl>space" } );
|
||||||
|
application.add_edit_accelerators(ACTION_BOLD, { "<Ctrl>b" } );
|
||||||
|
application.add_edit_accelerators(ACTION_ITALIC, { "<Ctrl>i" } );
|
||||||
|
application.add_edit_accelerators(ACTION_UNDERLINE, { "<Ctrl>u" } );
|
||||||
|
application.add_edit_accelerators(ACTION_STRIKETHROUGH, { "<Ctrl>k" } );
|
||||||
}
|
}
|
||||||
|
|
||||||
private const string DRAFT_SAVED_TEXT = _("Saved");
|
private const string DRAFT_SAVED_TEXT = _("Saved");
|
||||||
|
|
@ -268,30 +269,43 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
private Gtk.ComboBoxText from_multiple;
|
private Gtk.ComboBoxText from_multiple;
|
||||||
private Gee.ArrayList<FromAddressMap> from_list = new Gee.ArrayList<FromAddressMap>();
|
private Gee.ArrayList<FromAddressMap> from_list = new Gee.ArrayList<FromAddressMap>();
|
||||||
|
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
private Gtk.EventBox to_box;
|
private Gtk.EventBox to_box;
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
private Gtk.Label to_label;
|
private Gtk.Label to_label;
|
||||||
private EmailEntry to_entry;
|
private EmailEntry to_entry;
|
||||||
|
private Components.EntryUndo to_undo;
|
||||||
|
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
private Gtk.EventBox cc_box;
|
private Gtk.EventBox cc_box;
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
private Gtk.Label cc_label;
|
private Gtk.Label cc_label;
|
||||||
private EmailEntry cc_entry;
|
private EmailEntry cc_entry;
|
||||||
|
private Components.EntryUndo cc_undo;
|
||||||
|
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
private Gtk.EventBox bcc_box;
|
private Gtk.EventBox bcc_box;
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
private Gtk.Label bcc_label;
|
private Gtk.Label bcc_label;
|
||||||
private EmailEntry bcc_entry;
|
private EmailEntry bcc_entry;
|
||||||
|
private Components.EntryUndo bcc_undo;
|
||||||
|
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
private Gtk.EventBox reply_to_box;
|
private Gtk.EventBox reply_to_box;
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
private Gtk.Label reply_to_label;
|
private Gtk.Label reply_to_label;
|
||||||
private EmailEntry reply_to_entry;
|
private EmailEntry reply_to_entry;
|
||||||
|
private Components.EntryUndo reply_to_undo;
|
||||||
|
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
private Gtk.Label subject_label;
|
private Gtk.Label subject_label;
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
private Gtk.Entry subject_entry;
|
private Gtk.Entry subject_entry;
|
||||||
|
private Components.EntryUndo subject_undo;
|
||||||
|
private Gspell.Checker subject_spell_checker = new Gspell.Checker(null);
|
||||||
|
private Gspell.Entry subject_spell_entry;
|
||||||
|
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
private Gtk.Label message_overlay_label;
|
private Gtk.Label message_overlay_label;
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
|
|
@ -325,8 +339,8 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
private Gtk.Label info_label;
|
private Gtk.Label info_label;
|
||||||
|
|
||||||
private SimpleActionGroup composer_actions = new SimpleActionGroup();
|
private GLib.SimpleActionGroup composer_actions = new GLib.SimpleActionGroup();
|
||||||
private SimpleActionGroup editor_actions = new SimpleActionGroup();
|
private GLib.SimpleActionGroup editor_actions = new GLib.SimpleActionGroup();
|
||||||
|
|
||||||
private Menu html_menu;
|
private Menu html_menu;
|
||||||
private Menu plain_menu;
|
private Menu plain_menu;
|
||||||
|
|
@ -386,9 +400,6 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
|
||||||
get { return (ComposerContainer) parent; }
|
get { return (ComposerContainer) parent; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private Gspell.Checker subject_spell_checker = new Gspell.Checker(null);
|
|
||||||
private Gspell.Entry subject_spell_entry;
|
|
||||||
|
|
||||||
private GearyApplication application;
|
private GearyApplication application;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -454,23 +465,30 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
|
||||||
this.to_entry = new EmailEntry(this);
|
this.to_entry = new EmailEntry(this);
|
||||||
this.to_entry.changed.connect(on_envelope_changed);
|
this.to_entry.changed.connect(on_envelope_changed);
|
||||||
this.to_box.add(to_entry);
|
this.to_box.add(to_entry);
|
||||||
|
this.to_label.set_mnemonic_widget(this.to_entry);
|
||||||
|
this.to_undo = new Components.EntryUndo(this.to_entry);
|
||||||
|
|
||||||
this.cc_entry = new EmailEntry(this);
|
this.cc_entry = new EmailEntry(this);
|
||||||
this.cc_entry.changed.connect(on_envelope_changed);
|
this.cc_entry.changed.connect(on_envelope_changed);
|
||||||
this.cc_box.add(cc_entry);
|
this.cc_box.add(cc_entry);
|
||||||
|
this.cc_label.set_mnemonic_widget(this.cc_entry);
|
||||||
|
this.cc_undo = new Components.EntryUndo(this.cc_entry);
|
||||||
|
|
||||||
this.bcc_entry = new EmailEntry(this);
|
this.bcc_entry = new EmailEntry(this);
|
||||||
this.bcc_entry.changed.connect(on_envelope_changed);
|
this.bcc_entry.changed.connect(on_envelope_changed);
|
||||||
this.bcc_box.add(bcc_entry);
|
this.bcc_box.add(bcc_entry);
|
||||||
|
this.bcc_label.set_mnemonic_widget(this.bcc_entry);
|
||||||
|
this.bcc_undo = new Components.EntryUndo(this.bcc_entry);
|
||||||
|
|
||||||
this.reply_to_entry = new EmailEntry(this);
|
this.reply_to_entry = new EmailEntry(this);
|
||||||
this.reply_to_entry.changed.connect(on_envelope_changed);
|
this.reply_to_entry.changed.connect(on_envelope_changed);
|
||||||
this.reply_to_box.add(reply_to_entry);
|
this.reply_to_box.add(reply_to_entry);
|
||||||
|
|
||||||
this.to_label.set_mnemonic_widget(this.to_entry);
|
|
||||||
this.cc_label.set_mnemonic_widget(this.cc_entry);
|
|
||||||
this.bcc_label.set_mnemonic_widget(this.bcc_entry);
|
|
||||||
this.reply_to_label.set_mnemonic_widget(this.reply_to_entry);
|
this.reply_to_label.set_mnemonic_widget(this.reply_to_entry);
|
||||||
|
this.reply_to_undo = new Components.EntryUndo(this.reply_to_entry);
|
||||||
|
|
||||||
this.to_entry.margin_top = this.cc_entry.margin_top = this.bcc_entry.margin_top = this.reply_to_entry.margin_top = 6;
|
this.to_entry.margin_top = this.cc_entry.margin_top = this.bcc_entry.margin_top = this.reply_to_entry.margin_top = 6;
|
||||||
|
|
||||||
|
this.subject_undo = new Components.EntryUndo(this.subject_entry);
|
||||||
this.subject_spell_entry = Gspell.Entry.get_from_gtk_entry(
|
this.subject_spell_entry = Gspell.Entry.get_from_gtk_entry(
|
||||||
this.subject_entry
|
this.subject_entry
|
||||||
);
|
);
|
||||||
|
|
@ -858,29 +876,23 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
|
||||||
// Initializes all actions and adds them to the action group
|
// Initializes all actions and adds them to the action group
|
||||||
private void initialize_actions() {
|
private void initialize_actions() {
|
||||||
// Composer actions
|
// Composer actions
|
||||||
this.composer_actions.add_action_entries(
|
this.composer_actions.add_action_entries(COMPOSER_ACTIONS, this);
|
||||||
ComposerWidget.composer_action_entries, this
|
// Main actions use the window prefix so they override main
|
||||||
);
|
// window actions. But for some reason, we can't use the same
|
||||||
// Main actions use 'win' prefix so they override main window
|
// prefix for the headerbar.
|
||||||
// action. But for some reason, we can't use the same prefix
|
insert_action_group(Action.Window.GROUP_NAME, this.composer_actions);
|
||||||
// for the headerbar.
|
|
||||||
insert_action_group("win", this.composer_actions);
|
|
||||||
this.header.insert_action_group("cmh", this.composer_actions);
|
this.header.insert_action_group("cmh", this.composer_actions);
|
||||||
|
|
||||||
// Editor actions - scoped to the editor only. Need to include
|
// Editor actions - scoped to the editor only.
|
||||||
// composer actions however since if not found in this group,
|
this.editor_actions.add_action_entries(EDITOR_ACTIONS, this);
|
||||||
// ancestors (including the composer's) will not be consulted.
|
this.editor_container.insert_action_group(
|
||||||
this.editor_actions.add_action_entries(
|
Action.Edit.GROUP_NAME, this.editor_actions
|
||||||
ComposerWidget.composer_action_entries, this
|
|
||||||
);
|
);
|
||||||
this.editor_actions.add_action_entries(
|
|
||||||
ComposerWidget.editor_action_entries, this
|
|
||||||
);
|
|
||||||
this.editor_container.insert_action_group("win", this.editor_actions);
|
|
||||||
|
|
||||||
SimpleActionGroup[] composer_action_entries_users
|
GLib.SimpleActionGroup[] composer_action_entries_users = {
|
||||||
= {this.editor_actions, this.composer_actions};
|
this.editor_actions, this.composer_actions
|
||||||
foreach (SimpleActionGroup entries_users in composer_action_entries_users) {
|
};
|
||||||
|
foreach (var entries_users in composer_action_entries_users) {
|
||||||
entries_users.change_action_state(ACTION_SHOW_EXTENDED, false);
|
entries_users.change_action_state(ACTION_SHOW_EXTENDED, false);
|
||||||
entries_users.change_action_state(
|
entries_users.change_action_state(
|
||||||
ACTION_COMPOSE_AS_HTML, this.application.config.compose_as_html
|
ACTION_COMPOSE_AS_HTML, this.application.config.compose_as_html
|
||||||
|
|
@ -888,8 +900,8 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
get_action(ACTION_CLOSE_AND_SAVE).set_enabled(false);
|
get_action(ACTION_CLOSE_AND_SAVE).set_enabled(false);
|
||||||
get_action(GearyApplication.ACTION_UNDO).set_enabled(false);
|
get_action(Action.Edit.UNDO).set_enabled(false);
|
||||||
get_action(GearyApplication.ACTION_REDO).set_enabled(false);
|
get_action(Action.Edit.REDO).set_enabled(false);
|
||||||
|
|
||||||
update_cursor_actions();
|
update_cursor_actions();
|
||||||
}
|
}
|
||||||
|
|
@ -897,7 +909,7 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
|
||||||
private void update_cursor_actions() {
|
private void update_cursor_actions() {
|
||||||
bool has_selection = this.editor.has_selection;
|
bool has_selection = this.editor.has_selection;
|
||||||
get_action(ACTION_CUT).set_enabled(has_selection);
|
get_action(ACTION_CUT).set_enabled(has_selection);
|
||||||
get_action(GearyApplication.ACTION_COPY).set_enabled(has_selection);
|
get_action(Action.Edit.COPY).set_enabled(has_selection);
|
||||||
|
|
||||||
get_action(ACTION_INSERT_LINK).set_enabled(
|
get_action(ACTION_INSERT_LINK).set_enabled(
|
||||||
this.editor.is_rich_text && (has_selection || this.cursor_url != null)
|
this.editor.is_rich_text && (has_selection || this.cursor_url != null)
|
||||||
|
|
@ -1896,7 +1908,7 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
|
||||||
bool compose_as_html = new_state.get_boolean();
|
bool compose_as_html = new_state.get_boolean();
|
||||||
action.set_state(compose_as_html);
|
action.set_state(compose_as_html);
|
||||||
|
|
||||||
foreach (string html_action in html_actions)
|
foreach (string html_action in HTML_ACTIONS)
|
||||||
get_action(html_action).set_enabled(compose_as_html);
|
get_action(html_action).set_enabled(compose_as_html);
|
||||||
|
|
||||||
update_cursor_actions();
|
update_cursor_actions();
|
||||||
|
|
@ -2345,8 +2357,8 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void on_command_state_changed(bool can_undo, bool can_redo) {
|
private void on_command_state_changed(bool can_undo, bool can_redo) {
|
||||||
get_action(GearyApplication.ACTION_UNDO).set_enabled(can_undo);
|
get_action(Action.Edit.UNDO).set_enabled(can_undo);
|
||||||
get_action(GearyApplication.ACTION_REDO).set_enabled(can_redo);
|
get_action(Action.Edit.REDO).set_enabled(can_redo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void on_editor_content_loaded() {
|
private void on_editor_content_loaded() {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ public class ContactEntryCompletion : Gtk.EntryCompletion, Geary.BaseInterface {
|
||||||
private string current_key = "";
|
private string current_key = "";
|
||||||
|
|
||||||
// List of (possibly incomplete) email addresses in the entry.
|
// List of (possibly incomplete) email addresses in the entry.
|
||||||
private string[] email_addresses = {};
|
private Gee.ArrayList<string> address_parts = new Gee.ArrayList<string>();
|
||||||
|
|
||||||
// Index of the email address the cursor is currently at
|
// Index of the email address the cursor is currently at
|
||||||
private int cursor_at_address = -1;
|
private int cursor_at_address = -1;
|
||||||
|
|
@ -98,10 +98,11 @@ public class ContactEntryCompletion : Gtk.EntryCompletion, Geary.BaseInterface {
|
||||||
model.clear();
|
model.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void trigger_selection() {
|
public void trigger_selection() {
|
||||||
if (last_iter != null) {
|
if (this.last_iter != null) {
|
||||||
on_match_selected(model, last_iter);
|
insert_address_at_cursor(this.last_iter);
|
||||||
last_iter = null;
|
this.last_iter = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,7 +111,7 @@ public class ContactEntryCompletion : Gtk.EntryCompletion, Geary.BaseInterface {
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
this.current_key = "";
|
this.current_key = "";
|
||||||
this.cursor_at_address = -1;
|
this.cursor_at_address = -1;
|
||||||
this.email_addresses = {};
|
this.address_parts.clear();
|
||||||
|
|
||||||
string text = entry.get_text();
|
string text = entry.get_text();
|
||||||
int cursor_pos = entry.get_position();
|
int cursor_pos = entry.get_position();
|
||||||
|
|
@ -123,7 +124,7 @@ public class ContactEntryCompletion : Gtk.EntryCompletion, Geary.BaseInterface {
|
||||||
while (text.get_next_char(ref next_idx, out c)) {
|
while (text.get_next_char(ref next_idx, out c)) {
|
||||||
if (current_char == cursor_pos) {
|
if (current_char == cursor_pos) {
|
||||||
this.current_key = text.slice(start_idx, next_idx).strip();
|
this.current_key = text.slice(start_idx, next_idx).strip();
|
||||||
this.cursor_at_address = this.email_addresses.length;
|
this.cursor_at_address = this.address_parts.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
|
@ -131,7 +132,7 @@ public class ContactEntryCompletion : Gtk.EntryCompletion, Geary.BaseInterface {
|
||||||
if (!in_quote) {
|
if (!in_quote) {
|
||||||
// Don't include the comma in the address
|
// Don't include the comma in the address
|
||||||
string address = text.slice(start_idx, next_idx -1);
|
string address = text.slice(start_idx, next_idx -1);
|
||||||
this.email_addresses += address.strip();
|
this.address_parts.add(address);
|
||||||
// Don't include it in the next one, either
|
// Don't include it in the next one, either
|
||||||
start_idx = next_idx;
|
start_idx = next_idx;
|
||||||
}
|
}
|
||||||
|
|
@ -147,12 +148,66 @@ public class ContactEntryCompletion : Gtk.EntryCompletion, Geary.BaseInterface {
|
||||||
|
|
||||||
// Add any remaining text after the last comma
|
// Add any remaining text after the last comma
|
||||||
string address = text.substring(start_idx);
|
string address = text.substring(start_idx);
|
||||||
this.email_addresses += address.strip();
|
this.address_parts.add(address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void search_contacts(string query,
|
private void insert_address_at_cursor(Gtk.TreeIter iter) {
|
||||||
GLib.Cancellable? cancellable) {
|
Gtk.Entry? entry = get_entry() as Gtk.Entry;
|
||||||
|
if (entry != null) {
|
||||||
|
// Take care to do a delete then an insert here so that
|
||||||
|
// Component.EntryUndo can combine the two into a single
|
||||||
|
// undoable command
|
||||||
|
int start_char = this.address_parts.slice(
|
||||||
|
0, this.cursor_at_address
|
||||||
|
).fold<int>(
|
||||||
|
// address parts don't contain commas, so need to add
|
||||||
|
// an char width for it
|
||||||
|
(a, chars) => a.char_count() + chars + 1, 0
|
||||||
|
);
|
||||||
|
int end_char = (
|
||||||
|
start_char +
|
||||||
|
this.address_parts[this.cursor_at_address].char_count()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Format and use the selected address
|
||||||
|
GLib.Value value;
|
||||||
|
this.model.get_value(iter, Column.MAILBOX, out value);
|
||||||
|
Geary.RFC822.MailboxAddress mailbox =
|
||||||
|
(Geary.RFC822.MailboxAddress) value.get_object();
|
||||||
|
string formatted = mailbox.to_full_display();
|
||||||
|
if (this.cursor_at_address != 0) {
|
||||||
|
// This isn't the first address, so add some
|
||||||
|
// whitespace to pad it out
|
||||||
|
formatted = " " + formatted;
|
||||||
|
}
|
||||||
|
this.address_parts[this.cursor_at_address] = formatted;
|
||||||
|
|
||||||
|
// Update the entry text
|
||||||
|
entry.delete_text(start_char, end_char);
|
||||||
|
entry.insert_text(
|
||||||
|
formatted, formatted.char_count(), ref start_char
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update the entry cursor position. The previous call
|
||||||
|
// updates the start so just use that, but add extra space
|
||||||
|
// for the comma and any white space at the start of the
|
||||||
|
// next address.
|
||||||
|
++start_char;
|
||||||
|
string? next_address = (
|
||||||
|
this.cursor_at_address + 1 < this.address_parts.size
|
||||||
|
? this.address_parts[this.cursor_at_address + 1]
|
||||||
|
: ""
|
||||||
|
);
|
||||||
|
for (int i = 0; i < next_address.length && next_address[i] == ' '; i++) {
|
||||||
|
++start_char;
|
||||||
|
}
|
||||||
|
entry.set_position(start_char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void search_contacts(string query,
|
||||||
|
GLib.Cancellable? cancellable) {
|
||||||
Gee.Collection<Application.Contact>? results = null;
|
Gee.Collection<Application.Contact>? results = null;
|
||||||
try {
|
try {
|
||||||
results = yield this.contacts.search(
|
results = yield this.contacts.search(
|
||||||
|
|
@ -283,37 +338,7 @@ public class ContactEntryCompletion : Gtk.EntryCompletion, Geary.BaseInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool on_match_selected(Gtk.TreeModel model, Gtk.TreeIter iter) {
|
private bool on_match_selected(Gtk.TreeModel model, Gtk.TreeIter iter) {
|
||||||
Gtk.Entry? entry = get_entry() as Gtk.Entry;
|
insert_address_at_cursor(iter);
|
||||||
if (entry != null) {
|
|
||||||
// Update the address
|
|
||||||
GLib.Value value;
|
|
||||||
model.get_value(iter, Column.MAILBOX, out value);
|
|
||||||
Geary.RFC822.MailboxAddress mailbox =
|
|
||||||
(Geary.RFC822.MailboxAddress) value.get_object();
|
|
||||||
this.email_addresses[this.cursor_at_address] =
|
|
||||||
mailbox.to_full_display();
|
|
||||||
|
|
||||||
// Update the entry text
|
|
||||||
bool current_is_last = (
|
|
||||||
this.cursor_at_address == this.email_addresses.length - 1
|
|
||||||
);
|
|
||||||
int new_cursor_pos = -1;
|
|
||||||
GLib.StringBuilder text = new GLib.StringBuilder();
|
|
||||||
int i = 0;
|
|
||||||
while (i < this.email_addresses.length) {
|
|
||||||
text.append(this.email_addresses[i]);
|
|
||||||
if (i == this.cursor_at_address) {
|
|
||||||
new_cursor_pos = text.str.char_count();
|
|
||||||
}
|
|
||||||
|
|
||||||
i++;
|
|
||||||
if (i != this.email_addresses.length || current_is_last) {
|
|
||||||
text.append(", ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
entry.text = text.str;
|
|
||||||
entry.set_position(current_is_last ? -1 : new_cursor_pos);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,13 +81,26 @@ public class EmailEntry : Gtk.Entry {
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool on_key_press(Gtk.Widget widget, Gdk.EventKey event) {
|
private bool on_key_press(Gtk.Widget widget, Gdk.EventKey event) {
|
||||||
|
bool ret = Gdk.EVENT_PROPAGATE;
|
||||||
if (event.keyval == Gdk.Key.Tab) {
|
if (event.keyval == Gdk.Key.Tab) {
|
||||||
((ContactEntryCompletion) get_completion()).trigger_selection();
|
ContactEntryCompletion? completion = (
|
||||||
composer.child_focus(Gtk.DirectionType.TAB_FORWARD);
|
get_completion() as ContactEntryCompletion
|
||||||
return true;
|
);
|
||||||
|
if (completion != null) {
|
||||||
|
completion.trigger_selection();
|
||||||
|
composer.child_focus(Gtk.DirectionType.TAB_FORWARD);
|
||||||
|
ret = Gdk.EVENT_STOP;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Keyboard shortcuts for undo/redo won't work when the
|
||||||
|
// completion UI is visible unless we explicitly check for
|
||||||
|
// them there. This may be related to the
|
||||||
|
// single-key-shortcut handling hack in the MainWindow.
|
||||||
|
Gtk.Window? window = get_toplevel() as Gtk.Window;
|
||||||
|
if (window != null) {
|
||||||
|
ret = window.activate_key(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ public class ConversationViewer : Gtk.Stack, Geary.BaseInterface {
|
||||||
|
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
internal Gtk.SearchEntry conversation_find_entry;
|
internal Gtk.SearchEntry conversation_find_entry;
|
||||||
|
private Components.EntryUndo conversation_find_undo;
|
||||||
|
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
private Gtk.Button conversation_find_next;
|
private Gtk.Button conversation_find_next;
|
||||||
|
|
@ -126,6 +127,10 @@ public class ConversationViewer : Gtk.Stack, Geary.BaseInterface {
|
||||||
);
|
);
|
||||||
this.empty_search_page.add(empty_search);
|
this.empty_search_page.add(empty_search);
|
||||||
|
|
||||||
|
this.conversation_find_undo = new Components.EntryUndo(
|
||||||
|
this.conversation_find_entry
|
||||||
|
);
|
||||||
|
|
||||||
// XXX GTK+ Bug 778190 workaround
|
// XXX GTK+ Bug 778190 workaround
|
||||||
new_conversation_scroller();
|
new_conversation_scroller();
|
||||||
|
|
||||||
|
|
@ -431,6 +436,7 @@ public class ConversationViewer : Gtk.Stack, Geary.BaseInterface {
|
||||||
this.current_list.conversation.base_folder
|
this.current_list.conversation.base_folder
|
||||||
as Geary.SearchFolder
|
as Geary.SearchFolder
|
||||||
);
|
);
|
||||||
|
this.conversation_find_undo.reset();
|
||||||
if (search_folder != null) {
|
if (search_folder != null) {
|
||||||
Geary.SearchQuery? search_query = search_folder.search_query;
|
Geary.SearchQuery? search_query = search_folder.search_query;
|
||||||
if (search_query != null) {
|
if (search_query != null) {
|
||||||
|
|
|
||||||
|
|
@ -16,15 +16,18 @@ public class Dialogs.ProblemDetailsDialog : Hdy.Dialog {
|
||||||
private const string ACTION_SEARCH_TOGGLE = "toggle-search";
|
private const string ACTION_SEARCH_TOGGLE = "toggle-search";
|
||||||
private const string ACTION_SEARCH_ACTIVATE = "activate-search";
|
private const string ACTION_SEARCH_ACTIVATE = "activate-search";
|
||||||
|
|
||||||
private const ActionEntry[] action_entries = {
|
private const ActionEntry[] EDIT_ACTIONS = {
|
||||||
{GearyApplication.ACTION_CLOSE, on_close },
|
{ Action.Edit.COPY, on_copy_clicked },
|
||||||
{GearyApplication.ACTION_COPY, on_copy_clicked },
|
|
||||||
{ACTION_CLOSE, on_close },
|
|
||||||
{ACTION_SEARCH_TOGGLE, on_logs_search_toggled, null, "false" },
|
|
||||||
{ACTION_SEARCH_ACTIVATE, on_logs_search_activated },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public static void add_window_accelerators(GearyApplication app) {
|
private const ActionEntry[] WINDOW_ACTIONS = {
|
||||||
|
{ Action.Window.CLOSE, on_close },
|
||||||
|
{ ACTION_CLOSE, on_close },
|
||||||
|
{ ACTION_SEARCH_TOGGLE, on_logs_search_toggled, null, "false" },
|
||||||
|
{ ACTION_SEARCH_ACTIVATE, on_logs_search_activated },
|
||||||
|
};
|
||||||
|
|
||||||
|
public static void add_accelerators(GearyApplication app) {
|
||||||
app.add_window_accelerators(ACTION_CLOSE, { "Escape" } );
|
app.add_window_accelerators(ACTION_CLOSE, { "Escape" } );
|
||||||
app.add_window_accelerators(ACTION_SEARCH_ACTIVATE, { "<Ctrl>F" } );
|
app.add_window_accelerators(ACTION_SEARCH_ACTIVATE, { "<Ctrl>F" } );
|
||||||
}
|
}
|
||||||
|
|
@ -64,9 +67,15 @@ public class Dialogs.ProblemDetailsDialog : Hdy.Dialog {
|
||||||
this.account = (account_report != null) ? account_report.account : null;
|
this.account = (account_report != null) ? account_report.account : null;
|
||||||
this.service = (service_report != null) ? service_report.service : null;
|
this.service = (service_report != null) ? service_report.service : null;
|
||||||
|
|
||||||
GLib.SimpleActionGroup actions = new GLib.SimpleActionGroup();
|
// Edit actions
|
||||||
actions.add_action_entries(ProblemDetailsDialog.action_entries, this);
|
GLib.SimpleActionGroup edit_actions = new GLib.SimpleActionGroup();
|
||||||
insert_action_group("win", actions);
|
edit_actions.add_action_entries(EDIT_ACTIONS, this);
|
||||||
|
insert_action_group(Action.Edit.GROUP_NAME, edit_actions);
|
||||||
|
|
||||||
|
// Window actions
|
||||||
|
GLib.SimpleActionGroup window_actions = new GLib.SimpleActionGroup();
|
||||||
|
window_actions.add_action_entries(WINDOW_ACTIONS, this);
|
||||||
|
insert_action_group(Action.Window.GROUP_NAME, window_actions);
|
||||||
|
|
||||||
this.error_pane = new Components.InspectorErrorView(
|
this.error_pane = new Components.InspectorErrorView(
|
||||||
error, account, service
|
error, account, service
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,11 @@ geary_client_vala_sources = files(
|
||||||
'accounts/accounts-signature-web-view.vala',
|
'accounts/accounts-signature-web-view.vala',
|
||||||
'accounts/accounts-manager.vala',
|
'accounts/accounts-manager.vala',
|
||||||
|
|
||||||
|
'client-action.vala',
|
||||||
|
|
||||||
'components/client-web-view.vala',
|
'components/client-web-view.vala',
|
||||||
'components/components-attachment-pane.vala',
|
'components/components-attachment-pane.vala',
|
||||||
|
'components/components-entry-undo.vala',
|
||||||
'components/components-inspector.vala',
|
'components/components-inspector.vala',
|
||||||
'components/components-in-app-notification.vala',
|
'components/components-in-app-notification.vala',
|
||||||
'components/components-inspector-error-view.vala',
|
'components/components-inspector-error-view.vala',
|
||||||
|
|
|
||||||
|
|
@ -149,9 +149,9 @@ public class Plugin.DesktopNotifications : Notification {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
action = GearyApplication.ACTION_SHOW_FOLDER;
|
action = Action.Application.SHOW_FOLDER;
|
||||||
} else {
|
} else {
|
||||||
action = GearyApplication.ACTION_SHOW_EMAIL;
|
action = Action.Application.SHOW_EMAIL;
|
||||||
target_param += new GLib.Variant.variant(id.to_variant());
|
target_param += new GLib.Variant.variant(id.to_variant());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -159,7 +159,7 @@ public class Plugin.DesktopNotifications : Notification {
|
||||||
ARRIVED_ID,
|
ARRIVED_ID,
|
||||||
summary,
|
summary,
|
||||||
body,
|
body,
|
||||||
"app." + action,
|
Action.Application.prefix(action),
|
||||||
new GLib.Variant.tuple(target_param)
|
new GLib.Variant.tuple(target_param)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="receives_default">True</property>
|
<property name="receives_default">True</property>
|
||||||
<property name="action_name">win.undo</property>
|
<property name="action_name">edt.undo</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage">
|
<object class="GtkImage">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@
|
||||||
<property name="receives_default">True</property>
|
<property name="receives_default">True</property>
|
||||||
<property name="tooltip_text" translatable="yes"
|
<property name="tooltip_text" translatable="yes"
|
||||||
comments="Tooltip for inspector button">Copy to clipboard</property>
|
comments="Tooltip for inspector button">Copy to clipboard</property>
|
||||||
<property name="action_name">win.copy</property>
|
<property name="action_name">edt.copy</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage">
|
<object class="GtkImage">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
|
|
||||||
|
|
@ -5,41 +5,41 @@
|
||||||
<section>
|
<section>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">S_ans Serif</attribute>
|
<attribute name="label" translatable="yes">S_ans Serif</attribute>
|
||||||
<attribute name="action">win.font-family</attribute>
|
<attribute name="action">edt.font-family</attribute>
|
||||||
<attribute name="target">sans</attribute>
|
<attribute name="target">sans</attribute>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">S_erif</attribute>
|
<attribute name="label" translatable="yes">S_erif</attribute>
|
||||||
<attribute name="action">win.font-family</attribute>
|
<attribute name="action">edt.font-family</attribute>
|
||||||
<attribute name="target">serif</attribute>
|
<attribute name="target">serif</attribute>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">_Fixed Width</attribute>
|
<attribute name="label" translatable="yes">_Fixed Width</attribute>
|
||||||
<attribute name="action">win.font-family</attribute>
|
<attribute name="action">edt.font-family</attribute>
|
||||||
<attribute name="target">monospace</attribute>
|
<attribute name="target">monospace</attribute>
|
||||||
</item>
|
</item>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">_Small</attribute>
|
<attribute name="label" translatable="yes">_Small</attribute>
|
||||||
<attribute name="action">win.font-size</attribute>
|
<attribute name="action">edt.font-size</attribute>
|
||||||
<attribute name="target">small</attribute>
|
<attribute name="target">small</attribute>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">_Medium</attribute>
|
<attribute name="label" translatable="yes">_Medium</attribute>
|
||||||
<attribute name="action">win.font-size</attribute>
|
<attribute name="action">edt.font-size</attribute>
|
||||||
<attribute name="target">medium</attribute>
|
<attribute name="target">medium</attribute>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">Lar_ge</attribute>
|
<attribute name="label" translatable="yes">Lar_ge</attribute>
|
||||||
<attribute name="action">win.font-size</attribute>
|
<attribute name="action">edt.font-size</attribute>
|
||||||
<attribute name="target">large</attribute>
|
<attribute name="target">large</attribute>
|
||||||
</item>
|
</item>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">C_olor</attribute>
|
<attribute name="label" translatable="yes">C_olor</attribute>
|
||||||
<attribute name="action">win.color</attribute>
|
<attribute name="action">edt.color</attribute>
|
||||||
</item>
|
</item>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
|
|
@ -76,49 +76,49 @@
|
||||||
<section>
|
<section>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">_Undo</attribute>
|
<attribute name="label" translatable="yes">_Undo</attribute>
|
||||||
<attribute name="action">win.undo</attribute>
|
<attribute name="action">edt.undo</attribute>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">_Redo</attribute>
|
<attribute name="label" translatable="yes">_Redo</attribute>
|
||||||
<attribute name="action">win.redo</attribute>
|
<attribute name="action">edt.redo</attribute>
|
||||||
</item>
|
</item>
|
||||||
</section>
|
</section>
|
||||||
<section id="context_menu_rich_text">
|
<section id="context_menu_rich_text">
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">Cu_t</attribute>
|
<attribute name="label" translatable="yes">Cu_t</attribute>
|
||||||
<attribute name="action">win.cut</attribute>
|
<attribute name="action">edt.cut</attribute>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">_Copy</attribute>
|
<attribute name="label" translatable="yes">_Copy</attribute>
|
||||||
<attribute name="action">win.copy</attribute>
|
<attribute name="action">edt.copy</attribute>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">_Paste</attribute>
|
<attribute name="label" translatable="yes">_Paste</attribute>
|
||||||
<attribute name="action">win.paste</attribute>
|
<attribute name="action">edt.paste</attribute>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes" context="Clipboard paste as plain text">Paste _Without Formatting</attribute>
|
<attribute name="label" translatable="yes" context="Clipboard paste as plain text">Paste _Without Formatting</attribute>
|
||||||
<attribute name="action">win.paste-without-formatting</attribute>
|
<attribute name="action">edt.paste-without-formatting</attribute>
|
||||||
</item>
|
</item>
|
||||||
</section>
|
</section>
|
||||||
<section id="context_menu_plain_text">
|
<section id="context_menu_plain_text">
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">Cu_t</attribute>
|
<attribute name="label" translatable="yes">Cu_t</attribute>
|
||||||
<attribute name="action">win.cut</attribute>
|
<attribute name="action">edt.cut</attribute>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">_Copy</attribute>
|
<attribute name="label" translatable="yes">_Copy</attribute>
|
||||||
<attribute name="action">win.copy</attribute>
|
<attribute name="action">edt.copy</attribute>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">_Paste</attribute>
|
<attribute name="label" translatable="yes">_Paste</attribute>
|
||||||
<attribute name="action">win.paste</attribute>
|
<attribute name="action">edt.paste</attribute>
|
||||||
</item>
|
</item>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label" translatable="yes">Select _All</attribute>
|
<attribute name="label" translatable="yes">Select _All</attribute>
|
||||||
<attribute name="action">win.select-all</attribute>
|
<attribute name="action">edt.select-all</attribute>
|
||||||
</item>
|
</item>
|
||||||
</section>
|
</section>
|
||||||
<section id="context_menu_webkit_text_entry"/>
|
<section id="context_menu_webkit_text_entry"/>
|
||||||
|
|
|
||||||
|
|
@ -351,7 +351,7 @@
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
<property name="receives_default">False</property>
|
<property name="receives_default">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Undo last edit (Ctrl+Z)</property>
|
<property name="tooltip_text" translatable="yes">Undo last edit (Ctrl+Z)</property>
|
||||||
<property name="action_name">win.undo</property>
|
<property name="action_name">edt.undo</property>
|
||||||
<property name="always_show_image">True</property>
|
<property name="always_show_image">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage">
|
<object class="GtkImage">
|
||||||
|
|
@ -375,7 +375,7 @@
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
<property name="receives_default">False</property>
|
<property name="receives_default">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Redo last edit (Ctrl+Shift+Z)</property>
|
<property name="tooltip_text" translatable="yes">Redo last edit (Ctrl+Shift+Z)</property>
|
||||||
<property name="action_name">win.redo</property>
|
<property name="action_name">edt.redo</property>
|
||||||
<property name="always_show_image">True</property>
|
<property name="always_show_image">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage">
|
<object class="GtkImage">
|
||||||
|
|
@ -413,7 +413,7 @@
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
<property name="receives_default">False</property>
|
<property name="receives_default">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Bold (Ctrl+B)</property>
|
<property name="tooltip_text" translatable="yes">Bold (Ctrl+B)</property>
|
||||||
<property name="action_name">win.bold</property>
|
<property name="action_name">edt.bold</property>
|
||||||
<property name="always_show_image">True</property>
|
<property name="always_show_image">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage" id="bold_image">
|
<object class="GtkImage" id="bold_image">
|
||||||
|
|
@ -437,7 +437,7 @@
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
<property name="receives_default">False</property>
|
<property name="receives_default">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Italic (Ctrl+I)</property>
|
<property name="tooltip_text" translatable="yes">Italic (Ctrl+I)</property>
|
||||||
<property name="action_name">win.italic</property>
|
<property name="action_name">edt.italic</property>
|
||||||
<property name="always_show_image">True</property>
|
<property name="always_show_image">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage" id="italics_image">
|
<object class="GtkImage" id="italics_image">
|
||||||
|
|
@ -461,7 +461,7 @@
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
<property name="receives_default">False</property>
|
<property name="receives_default">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Underline (Ctrl+U)</property>
|
<property name="tooltip_text" translatable="yes">Underline (Ctrl+U)</property>
|
||||||
<property name="action_name">win.underline</property>
|
<property name="action_name">edt.underline</property>
|
||||||
<property name="always_show_image">True</property>
|
<property name="always_show_image">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage" id="underline_image">
|
<object class="GtkImage" id="underline_image">
|
||||||
|
|
@ -485,7 +485,7 @@
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
<property name="receives_default">False</property>
|
<property name="receives_default">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Strikethrough (Ctrl+K)</property>
|
<property name="tooltip_text" translatable="yes">Strikethrough (Ctrl+K)</property>
|
||||||
<property name="action_name">win.strikethrough</property>
|
<property name="action_name">edt.strikethrough</property>
|
||||||
<property name="always_show_image">True</property>
|
<property name="always_show_image">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage" id="strikethrough_image">
|
<object class="GtkImage" id="strikethrough_image">
|
||||||
|
|
@ -523,7 +523,7 @@
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
<property name="receives_default">False</property>
|
<property name="receives_default">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Insert unordered list</property>
|
<property name="tooltip_text" translatable="yes">Insert unordered list</property>
|
||||||
<property name="action_name">win.ulist</property>
|
<property name="action_name">edt.ulist</property>
|
||||||
<property name="always_show_image">True</property>
|
<property name="always_show_image">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage" id="ulist_image">
|
<object class="GtkImage" id="ulist_image">
|
||||||
|
|
@ -547,7 +547,7 @@
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
<property name="receives_default">False</property>
|
<property name="receives_default">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Insert ordered list</property>
|
<property name="tooltip_text" translatable="yes">Insert ordered list</property>
|
||||||
<property name="action_name">win.olist</property>
|
<property name="action_name">edt.olist</property>
|
||||||
<property name="always_show_image">True</property>
|
<property name="always_show_image">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage" id="olist_image">
|
<object class="GtkImage" id="olist_image">
|
||||||
|
|
@ -585,7 +585,7 @@
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
<property name="receives_default">False</property>
|
<property name="receives_default">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Quote text (Ctrl+])</property>
|
<property name="tooltip_text" translatable="yes">Quote text (Ctrl+])</property>
|
||||||
<property name="action_name">win.indent</property>
|
<property name="action_name">edt.indent</property>
|
||||||
<property name="always_show_image">True</property>
|
<property name="always_show_image">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage" id="indent_image">
|
<object class="GtkImage" id="indent_image">
|
||||||
|
|
@ -609,7 +609,7 @@
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
<property name="receives_default">False</property>
|
<property name="receives_default">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Unquote text (Ctrl+[)</property>
|
<property name="tooltip_text" translatable="yes">Unquote text (Ctrl+[)</property>
|
||||||
<property name="action_name">win.outdent</property>
|
<property name="action_name">edt.outdent</property>
|
||||||
<property name="always_show_image">True</property>
|
<property name="always_show_image">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage" id="outdent_image">
|
<object class="GtkImage" id="outdent_image">
|
||||||
|
|
@ -647,7 +647,7 @@
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
<property name="receives_default">False</property>
|
<property name="receives_default">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Insert or update selection link (Ctrl+L)</property>
|
<property name="tooltip_text" translatable="yes">Insert or update selection link (Ctrl+L)</property>
|
||||||
<property name="action_name">win.insert-link</property>
|
<property name="action_name">edt.insert-link</property>
|
||||||
<property name="always_show_image">True</property>
|
<property name="always_show_image">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage" id="insert_link_image">
|
<object class="GtkImage" id="insert_link_image">
|
||||||
|
|
@ -671,7 +671,7 @@
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
<property name="receives_default">False</property>
|
<property name="receives_default">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Insert an image (Ctrl+G)</property>
|
<property name="tooltip_text" translatable="yes">Insert an image (Ctrl+G)</property>
|
||||||
<property name="action_name">win.insert-image</property>
|
<property name="action_name">edt.insert-image</property>
|
||||||
<property name="always_show_image">True</property>
|
<property name="always_show_image">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage">
|
<object class="GtkImage">
|
||||||
|
|
@ -705,7 +705,7 @@
|
||||||
<property name="focus_on_click">False</property>
|
<property name="focus_on_click">False</property>
|
||||||
<property name="receives_default">False</property>
|
<property name="receives_default">False</property>
|
||||||
<property name="tooltip_text" translatable="yes">Remove selection formatting (Ctrl+Space)</property>
|
<property name="tooltip_text" translatable="yes">Remove selection formatting (Ctrl+Space)</property>
|
||||||
<property name="action_name">win.remove-format</property>
|
<property name="action_name">edt.remove-format</property>
|
||||||
<property name="always_show_image">True</property>
|
<property name="always_show_image">True</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkImage" id="remove_format_image">
|
<object class="GtkImage" id="remove_format_image">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue