From 114ed09dda27bca0f34f978054f6d59ef6e7ffab Mon Sep 17 00:00:00 2001 From: Niels De Graef Date: Mon, 15 May 2017 23:21:32 +0200 Subject: [PATCH 01/11] Start the final part of bug 713991. * Ported Gtk.Action to GLib.Action in the GearyController. * Removed Gtk.AccelGroups (handled through Gtk.Application now). * Got rid of Gtk.UiManager (now all is done through Gtk.Builder). * Throw away workaround for conflicting Gtk.Actions in ComposerContainer. * Aggregate zoom in/out/normal into one parameterized zoom action. Signed-off-by: Niels De Graef --- po/POTFILES.in | 4 +- src/client/application/geary-application.vala | 40 -- src/client/application/geary-controller.vala | 594 ++++++------------ src/client/components/folder-popover.vala | 2 + src/client/components/main-toolbar.vala | 206 +++--- src/client/components/main-window.vala | 6 +- src/client/composer/composer-container.vala | 14 - .../conversation-list-view.vala | 64 +- src/client/util/util-gtk.vala | 19 - ui/CMakeLists.txt | 4 +- ui/accelerators.ui | 17 - ui/main-toolbar-menus.ui | 40 ++ ui/main-toolbar.ui | 29 +- ui/toolbar_empty_menu.ui | 7 - ui/toolbar_mark_menu.ui | 10 - 15 files changed, 388 insertions(+), 668 deletions(-) delete mode 100644 ui/accelerators.ui create mode 100644 ui/main-toolbar-menus.ui delete mode 100644 ui/toolbar_empty_menu.ui delete mode 100644 ui/toolbar_mark_menu.ui diff --git a/po/POTFILES.in b/po/POTFILES.in index 345dcbff..dea321b7 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -384,7 +384,6 @@ src/engine/util/util-time.vala src/engine/util/util-timeout-manager.vala src/engine/util/util-trillian.vala src/mailer/main.vala -[type: gettext/glade]ui/accelerators.ui [type: gettext/glade]ui/account_cannot_remove.glade [type: gettext/glade]ui/account_list.glade [type: gettext/glade]ui/account_spinner.glade @@ -407,10 +406,9 @@ src/mailer/main.vala [type: gettext/glade]ui/gtk/menus.ui [type: gettext/glade]ui/login.glade [type: gettext/glade]ui/main-toolbar.ui +[type: gettext/glade]ui/main-toolbar-menus.ui [type: gettext/glade]ui/main-window.ui [type: gettext/glade]ui/password-dialog.glade [type: gettext/glade]ui/preferences-dialog.ui [type: gettext/glade]ui/remove_confirm.glade -[type: gettext/glade]ui/toolbar_empty_menu.ui -[type: gettext/glade]ui/toolbar_mark_menu.ui [type: gettext/glade]ui/upgrade_dialog.glade diff --git a/src/client/application/geary-application.vala b/src/client/application/geary-application.vala index 857cffbc..8c51c3e1 100644 --- a/src/client/application/geary-application.vala +++ b/src/client/application/geary-application.vala @@ -113,14 +113,6 @@ public class GearyApplication : Gtk.Application { get { return Args.hidden_startup || this.config.startup_notifications; } } - public Gtk.ActionGroup actions { - get; private set; default = new Gtk.ActionGroup("GearyActionGroup"); - } - - public Gtk.UIManager ui_manager { - get; private set; default = new Gtk.UIManager(); - } - private string bin; private File exec_dir; private bool exiting_fired = false; @@ -260,14 +252,6 @@ public class GearyApplication : Gtk.Application { is_destroyed = true; } - // NOTE: This assert()'s if the Gtk.Action is not present in the default action group - public Gtk.Action get_action(string name) { - Gtk.Action? action = actions.get_action(name); - assert(action != null); - - return action; - } - public File get_user_data_directory() { return File.new_for_path(Environment.get_user_data_dir()).get_child("geary"); } @@ -350,30 +334,6 @@ public class GearyApplication : Gtk.Application { return GioUtil.create_builder(name); } - /** - * Loads a GResource as a string. - * - * @deprecated Use {@link GioUtil.read_resource} instead. - */ - [Deprecated] - public string read_resource(string name) throws Error { - return GioUtil.read_resource(name); - } - - /** - * Loads a UI GResource into the UI manager. - */ - [Deprecated] - public void load_ui_resource(string name) { - try { - this.ui_manager.add_ui_from_resource("/org/gnome/Geary/" + name); - } catch(GLib.Error error) { - critical("Unable to load \"%s\" for Gtk.UIManager: %s".printf( - name, error.message - )); - } - } - /** * Displays a URI on the current active window, if any. */ diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala index b2635306..040dede0 100644 --- a/src/client/application/geary-controller.vala +++ b/src/client/application/geary-controller.vala @@ -21,67 +21,40 @@ extern bool gcr_trust_remove_pinned_certificate(Gcr.Certificate cert, string pur */ public class GearyController : Geary.BaseObject { // Named actions. - // - // NOTE: Some actions with accelerators need to also be added to ui/accelerators.ui - public const string ACTION_NEW_MESSAGE = "GearyNewMessage"; - public const string ACTION_REPLY_TO_MESSAGE = "GearyReplyToMessage"; - public const string ACTION_REPLY_ALL_MESSAGE = "GearyReplyAllMessage"; - public const string ACTION_FORWARD_MESSAGE = "GearyForwardMessage"; - public const string ACTION_ARCHIVE_CONVERSATION = "GearyArchiveConversation"; - public const string ACTION_TRASH_CONVERSATION = "GearyTrashConversation"; - public const string ACTION_DELETE_CONVERSATION = "GearyDeleteConversation"; - public const string ACTION_EMPTY_SPAM = "GearyEmptySpam"; - public const string ACTION_EMPTY_TRASH = "GearyEmptyTrash"; - public const string ACTION_UNDO = "GearyUndo"; - public const string ACTION_FIND_IN_CONVERSATION = "GearyFindInConversation"; - public const string ACTION_ZOOM_IN = "GearyZoomIn"; - public const string ACTION_ZOOM_OUT = "GearyZoomOut"; - public const string ACTION_ZOOM_NORMAL = "GearyZoomNormal"; - public const string ACTION_MARK_AS_MENU = "GearyMarkAsMenuButton"; - public const string ACTION_MARK_AS_READ = "GearyMarkAsRead"; - public const string ACTION_MARK_AS_UNREAD = "GearyMarkAsUnread"; - public const string ACTION_MARK_AS_STARRED = "GearyMarkAsStarred"; - public const string ACTION_MARK_AS_UNSTARRED = "GearyMarkAsUnStarred"; - public const string ACTION_MARK_AS_SPAM = "GearyMarkAsSpam"; - public const string ACTION_COPY_MENU = "GearyCopyMenuButton"; - public const string ACTION_MOVE_MENU = "GearyMoveMenuButton"; - public const string ACTION_SEARCH = "GearySearch"; - public const string ACTION_CONVERSATION_LIST = "GearyConversationList"; - public const string ACTION_TOGGLE_SEARCH = "GearyToggleSearch"; - public const string ACTION_TOGGLE_FIND = "GearyToggleFind"; + public const string ACTION_NEW_MESSAGE = "new-message"; + public const string ACTION_REPLY_TO_MESSAGE = "reply-to-message"; + public const string ACTION_REPLY_ALL_MESSAGE = "reply-all-message"; + public const string ACTION_FORWARD_MESSAGE = "forward-message"; + public const string ACTION_ARCHIVE_CONVERSATION = "archive-conv"; + public const string ACTION_TRASH_CONVERSATION = "trash-conv"; + public const string ACTION_DELETE_CONVERSATION = "delete-conv"; + public const string ACTION_EMPTY_SPAM = "empty-spam"; + public const string ACTION_EMPTY_TRASH = "empty-trash"; + public const string ACTION_UNDO = "undo"; + public const string ACTION_FIND_IN_CONVERSATION = "conv-find"; + public const string ACTION_ZOOM = "zoom"; + public const string ACTION_SHOW_MARK_MENU = "mark-message-menu"; + public const string ACTION_MARK_AS_READ = "mark-message-read"; + public const string ACTION_MARK_AS_UNREAD = "mark-message-unread"; + public const string ACTION_MARK_AS_STARRED = "mark-message-starred"; + public const string ACTION_MARK_AS_UNSTARRED = "mark-message-unstarred"; + public const string ACTION_MARK_AS_SPAM = "mark-message-spam"; + public const string ACTION_MARK_AS_NOT_SPAM = "mark-message-not-spam"; + public const string ACTION_COPY_MENU = "show-copy-menu"; + public const string ACTION_MOVE_MENU = "show-move-menu"; + public const string ACTION_SEARCH = "search-conv"; + public const string ACTION_CONVERSATION_LIST = "focus-conv-list"; + public const string ACTION_TOGGLE_SEARCH = "toggle-search"; + public const string ACTION_TOGGLE_FIND = "toggle-find"; + // Properties public const string PROP_CURRENT_CONVERSATION ="current-conversations"; - + public const string PROP_SELECTED_CONVERSATIONS ="selected-conversations"; + public const int MIN_CONVERSATION_COUNT = 50; - - private const string DELETE_CONVERSATION_LABEL = _("Delete conversation"); - private const string DELETE_CONVERSATION_TOOLTIP_SINGLE = _("Delete conversation (Shift+Delete)"); - private const string DELETE_CONVERSATION_TOOLTIP_MULTIPLE = _("Delete conversations (Shift+Delete)"); - private const string DELETE_CONVERSATION_ICON_NAME = "edit-delete-symbolic"; - - // This refers to the action ("move email to the trash"), not the Trash folder itself - private const string TRASH_CONVERSATION_TOOLTIP_SINGLE = _("Move conversation to Trash (Delete, Backspace)"); - private const string TRASH_CONVERSATION_TOOLTIP_MULTIPLE = _("Move conversations to Trash (Delete, Backspace)"); - private const string TRASH_CONVERSATION_ICON_NAME = "user-trash-symbolic"; - - // This refers to the action ("archive an email"), not the Archive folder itself - private const string ARCHIVE_CONVERSATION_LABEL = _("_Archive"); - private const string ARCHIVE_CONVERSATION_TOOLTIP_SINGLE = _("Archive conversation (A)"); - private const string ARCHIVE_CONVERSATION_TOOLTIP_MULTIPLE = _("Archive conversations (A)"); - private const string ARCHIVE_CONVERSATION_ICON_NAME = "mail-archive-symbolic"; - - private const string MARK_AS_SPAM_LABEL = _("Mark as S_pam"); - private const string MARK_AS_NOT_SPAM_LABEL = _("Mark as not S_pam"); - - private const string MARK_MESSAGE_MENU_TOOLTIP_SINGLE = _("Mark conversation"); - private const string MARK_MESSAGE_MENU_TOOLTIP_MULTIPLE = _("Mark conversations"); - private const string LABEL_MESSAGE_TOOLTIP_SINGLE = _("Add label to conversation"); - private const string LABEL_MESSAGE_TOOLTIP_MULTIPLE = _("Add label to conversations"); - private const string MOVE_MESSAGE_TOOLTIP_SINGLE = _("Move conversation"); - private const string MOVE_MESSAGE_TOOLTIP_MULTIPLE = _("Move conversations"); - + private const int SELECT_FOLDER_TIMEOUT_USEC = 100 * 1000; - + private const string PROP_ATTEMPT_OPEN_ACCOUNT = "attempt-open-account"; public weak GearyApplication application { get; private set; } // circular ref @@ -126,10 +99,39 @@ public class GearyController : Geary.BaseObject { private Geary.Nonblocking.Mutex untrusted_host_prompt_mutex = new Geary.Nonblocking.Mutex(); private Gee.HashSet validating_endpoints = new Gee.HashSet(); private Geary.Revokable? revokable = null; - + // List of windows we're waiting to close before Geary closes. private Gee.List waiting_to_close = new Gee.ArrayList(); - + + private const ActionEntry[] win_action_entries = { + {ACTION_NEW_MESSAGE, on_new_message }, + {ACTION_CONVERSATION_LIST, on_conversation_list }, + {ACTION_FIND_IN_CONVERSATION, on_find_in_conversation_action }, + {ACTION_SEARCH, on_search_activated }, + {ACTION_EMPTY_SPAM, on_empty_spam }, + {ACTION_EMPTY_TRASH, on_empty_trash }, + {ACTION_UNDO, on_revoke }, + // Message actions + {ACTION_REPLY_TO_MESSAGE, on_reply_to_message_action }, + {ACTION_REPLY_ALL_MESSAGE, on_reply_all_message_action }, + {ACTION_FORWARD_MESSAGE, on_forward_message_action }, + {ACTION_ARCHIVE_CONVERSATION, on_archive_conversation }, + {ACTION_TRASH_CONVERSATION, on_trash_conversation }, + {ACTION_DELETE_CONVERSATION, on_delete_conversation }, + {ACTION_COPY_MENU, on_show_copy_menu }, + {ACTION_MOVE_MENU, on_show_move_menu }, + // Message marking actions + {ACTION_SHOW_MARK_MENU, on_show_mark_menu }, + {ACTION_MARK_AS_READ, on_mark_as_read }, + {ACTION_MARK_AS_UNREAD, on_mark_as_unread }, + {ACTION_MARK_AS_STARRED, on_mark_as_starred }, + {ACTION_MARK_AS_UNSTARRED, on_mark_as_unstarred }, + {ACTION_MARK_AS_SPAM, on_mark_as_spam_toggle }, + {ACTION_MARK_AS_NOT_SPAM, on_mark_as_spam_toggle }, + // Message viewer + {ACTION_ZOOM, on_zoom, "s" }, + }; + /** * Fired when the currently selected account has changed. */ @@ -176,10 +178,6 @@ public class GearyController : Geary.BaseObject { apply_app_menu_fix(); - // Setup actions. - setup_actions(); - this.application.load_ui_resource("accelerators.ui"); - // Listen for attempts to close the application. this.application.exiting.connect(on_application_exiting); @@ -224,9 +222,11 @@ public class GearyController : Geary.BaseObject { main_window = new MainWindow(this.application); main_window.on_shift_key.connect(on_shift_key); main_window.notify["has-toplevel-focus"].connect(on_has_toplevel_focus); - + + setup_actions(); + enable_message_buttons(false); - + Geary.Engine.instance.account_available.connect(on_account_available); Geary.Engine.instance.account_unavailable.connect(on_account_unavailable); Geary.Engine.instance.untrusted_host.connect(on_untrusted_host); @@ -414,11 +414,6 @@ public class GearyController : Geary.BaseObject { } } - private void add_accelerator(string accelerator, string action) { - GtkUtil.add_accelerator(this.application.ui_manager, this.application.actions, - accelerator, action); - } - // Fix for clients having both: // * disabled Gtk/ShellShowsAppMenu setting // * no 'menu' setting in Gtk/DecorationLayout @@ -428,13 +423,10 @@ public class GearyController : Geary.BaseObject { if (settings == null) { warning("Couldn't fetch Gtk default settings"); - return ; + return; } - string? decoration_layout = settings.gtk_decoration_layout; - - if (decoration_layout == null) decoration_layout = ""; - + string decoration_layout = settings.gtk_decoration_layout ?? ""; if (!decoration_layout.contains("menu")) { string prefix = "menu:"; if (decoration_layout.contains(":")) { @@ -444,177 +436,35 @@ public class GearyController : Geary.BaseObject { } } - private Gtk.ActionEntry[] create_actions() { - Gtk.ActionEntry[] entries = new Gtk.ActionEntry[0]; - - Gtk.ActionEntry mark_menu = { ACTION_MARK_AS_MENU, null, TRANSLATABLE, null, _("Mark conversation"), - on_show_mark_menu }; - mark_menu.label = _("_Mark as…"); - mark_menu.tooltip = MARK_MESSAGE_MENU_TOOLTIP_SINGLE; - entries += mark_menu; - - Gtk.ActionEntry mark_read = { ACTION_MARK_AS_READ, "mail-mark-read", TRANSLATABLE, "I", - null, on_mark_as_read }; - mark_read.label = _("Mark as _Read"); - entries += mark_read; - add_accelerator("I", ACTION_MARK_AS_READ); - - Gtk.ActionEntry mark_unread = { ACTION_MARK_AS_UNREAD, "mail-mark-unread", TRANSLATABLE, - "U", null, on_mark_as_unread }; - mark_unread.label = _("Mark as _Unread"); - entries += mark_unread; - add_accelerator("U", ACTION_MARK_AS_UNREAD); - - Gtk.ActionEntry mark_starred = { ACTION_MARK_AS_STARRED, "star-symbolic", TRANSLATABLE, "S", null, - on_mark_as_starred }; - mark_starred.label = _("_Star"); - entries += mark_starred; - - Gtk.ActionEntry mark_unstarred = { ACTION_MARK_AS_UNSTARRED, "non-starred", TRANSLATABLE, "D", - null, on_mark_as_unstarred }; - mark_unstarred.label = _("U_nstar"); - entries += mark_unstarred; - - Gtk.ActionEntry mark_spam = { ACTION_MARK_AS_SPAM, null, TRANSLATABLE, "J", null, - on_mark_as_spam }; - mark_spam.label = MARK_AS_SPAM_LABEL; - entries += mark_spam; - add_accelerator("exclam", ACTION_MARK_AS_SPAM); // Exclamation mark (!) - - Gtk.ActionEntry copy_menu = { ACTION_COPY_MENU, null, TRANSLATABLE, "L", - _("Add label"), null }; - copy_menu.label = _("_Label"); - entries += copy_menu; - - Gtk.ActionEntry move_menu = { ACTION_MOVE_MENU, null, TRANSLATABLE, "M", _("Move conversation"), null }; - move_menu.label = _("_Move"); - entries += move_menu; - - Gtk.ActionEntry new_message = { ACTION_NEW_MESSAGE, null, null, "N", - _("Compose new message (Ctrl+N, N)"), on_new_message }; - entries += new_message; - add_accelerator("N", ACTION_NEW_MESSAGE); - - Gtk.ActionEntry reply_to_message = { ACTION_REPLY_TO_MESSAGE, null, _("_Reply"), "R", - _("Reply (Ctrl+R, R)"), on_reply_to_message_action }; - entries += reply_to_message; - add_accelerator("R", ACTION_REPLY_TO_MESSAGE); - - Gtk.ActionEntry reply_all_message = { ACTION_REPLY_ALL_MESSAGE, null, _("R_eply All"), - "R", _("Reply all (Ctrl+Shift+R, Shift+R)"), - on_reply_all_message_action }; - entries += reply_all_message; - add_accelerator("R", ACTION_REPLY_ALL_MESSAGE); - - Gtk.ActionEntry forward_message = { ACTION_FORWARD_MESSAGE, null, _("_Forward"), "L", - _("Forward (Ctrl+L, F)"), on_forward_message_action }; - entries += forward_message; - add_accelerator("F", ACTION_FORWARD_MESSAGE); - - Gtk.ActionEntry find_in_conversation = { ACTION_FIND_IN_CONVERSATION, null, null, "F", - null, on_find_in_conversation_action }; - entries += find_in_conversation; - add_accelerator("slash", ACTION_FIND_IN_CONVERSATION); - - Gtk.ActionEntry archive_conversation = { ACTION_ARCHIVE_CONVERSATION, ARCHIVE_CONVERSATION_ICON_NAME, - ARCHIVE_CONVERSATION_LABEL, "A", null, on_archive_conversation }; - archive_conversation.tooltip = ARCHIVE_CONVERSATION_TOOLTIP_SINGLE; - entries += archive_conversation; - - // although this action changes according to the account's capabilities, set to Delete - // until they're known so the "translatable" string doesn't first appear - Gtk.ActionEntry trash_conversation = { ACTION_TRASH_CONVERSATION, TRASH_CONVERSATION_ICON_NAME, - null, "Delete", null, on_trash_conversation }; - trash_conversation.tooltip = TRASH_CONVERSATION_TOOLTIP_SINGLE; - entries += trash_conversation; - add_accelerator("BackSpace", ACTION_TRASH_CONVERSATION); - - Gtk.ActionEntry delete_conversation = { ACTION_DELETE_CONVERSATION, DELETE_CONVERSATION_ICON_NAME, - null, "Delete", null, on_delete_conversation }; - delete_conversation.label = DELETE_CONVERSATION_LABEL; - delete_conversation.tooltip = DELETE_CONVERSATION_TOOLTIP_SINGLE; - entries += delete_conversation; - add_accelerator("BackSpace", ACTION_DELETE_CONVERSATION); - - Gtk.ActionEntry empty_spam = { ACTION_EMPTY_SPAM, null, null, null, null, on_empty_spam }; - empty_spam.label = _("Empty _Spam…"); - entries += empty_spam; - - Gtk.ActionEntry empty_trash = { ACTION_EMPTY_TRASH, null, null, null, null, on_empty_trash }; - empty_trash.label = _("Empty _Trash…"); - entries += empty_trash; - - Gtk.ActionEntry undo = { ACTION_UNDO, "edit-undo-symbolic", null, "Z", null, on_revoke }; - entries += undo; - - Gtk.ActionEntry zoom_in = { ACTION_ZOOM_IN, null, null, "equal", - null, on_zoom_in }; - entries += zoom_in; - add_accelerator("equal", ACTION_ZOOM_IN); - - Gtk.ActionEntry zoom_out = { ACTION_ZOOM_OUT, null, null, "minus", - null, on_zoom_out }; - entries += zoom_out; - add_accelerator("minus", ACTION_ZOOM_OUT); - - Gtk.ActionEntry zoom_normal = { ACTION_ZOOM_NORMAL, null, null, "0", - null, on_zoom_normal }; - entries += zoom_normal; - add_accelerator("0", ACTION_ZOOM_NORMAL); - - Gtk.ActionEntry search = { - ACTION_SEARCH, null, null, "S", null, - () => { show_search_bar(); } - }; - entries += search; - - Gtk.ActionEntry conversation_list = { ACTION_CONVERSATION_LIST, null, null, "B", null, on_conversation_list }; - entries += conversation_list; - - // No callback is connected, since we bind the toggle button to the search bar visibility - Gtk.ActionEntry toggle_search = { ACTION_TOGGLE_SEARCH, null, null, null, - _("Toggle search bar"), null }; - entries += toggle_search; - - // No callback is connected, since we bind the toggle button to the find bar visibility - Gtk.ActionEntry toggle_find = { ACTION_TOGGLE_FIND, null, null, null, - _("Toggle find bar"), null }; - entries += toggle_find; - - return entries; - } - - private Gtk.ToggleActionEntry[] create_toggle_actions() { - Gtk.ToggleActionEntry[] entries = new Gtk.ToggleActionEntry[0]; - - return entries; - } - private void setup_actions() { - const string[] important_actions = { - ACTION_NEW_MESSAGE, - ACTION_REPLY_TO_MESSAGE, - ACTION_REPLY_ALL_MESSAGE, - ACTION_FORWARD_MESSAGE, - ACTION_ARCHIVE_CONVERSATION, - ACTION_TRASH_CONVERSATION, - ACTION_DELETE_CONVERSATION, - }; - Gtk.ActionGroup action_group = this.application.actions; + this.main_window.add_action_entries(win_action_entries, this); - Gtk.ActionEntry[] action_entries = create_actions(); - action_group.add_actions(action_entries, this); - foreach (Gtk.ActionEntry e in action_entries) { - Gtk.Action action = action_group.get_action(e.name); - assert(action != null); - - if (e.name in important_actions) - action.is_important = true; - } + add_window_accelerators(ACTION_MARK_AS_READ, { "I", "I" }); + add_window_accelerators(ACTION_MARK_AS_UNREAD, { "U", "U" }); + add_window_accelerators(ACTION_MARK_AS_STARRED, { "S" }); + add_window_accelerators(ACTION_MARK_AS_UNSTARRED, { "D" }); + add_window_accelerators(ACTION_MARK_AS_SPAM, { "J", "exclam" }); // Exclamation mark (!) + add_window_accelerators(ACTION_MARK_AS_NOT_SPAM, { "J", "exclam" }); + add_window_accelerators(ACTION_COPY_MENU, { "L" }); + add_window_accelerators(ACTION_MOVE_MENU, { "M" }); + add_window_accelerators(ACTION_NEW_MESSAGE, { "N", "N" }); + add_window_accelerators(ACTION_REPLY_TO_MESSAGE, { "R", "R" }); + add_window_accelerators(ACTION_REPLY_ALL_MESSAGE, { "R", "R" }); + add_window_accelerators(ACTION_FORWARD_MESSAGE, { "L", "F" }); + add_window_accelerators(ACTION_FIND_IN_CONVERSATION, { "F", "slash" }); + add_window_accelerators(ACTION_ARCHIVE_CONVERSATION, { "A" }); + add_window_accelerators(ACTION_TRASH_CONVERSATION, { "Delete", "BackSpace" }); + add_window_accelerators(ACTION_DELETE_CONVERSATION, { "Delete", "BackSpace" }); + add_window_accelerators(ACTION_UNDO, { "Z" }); + add_window_accelerators(ACTION_ZOOM+("('in')"), { "equal", "equal" }); + add_window_accelerators(ACTION_ZOOM+("('out')"), { "minus", "minus" }); + add_window_accelerators(ACTION_ZOOM+("('normal')"), { "0", "0" }); + add_window_accelerators(ACTION_SEARCH, { "S" }); + add_window_accelerators(ACTION_CONVERSATION_LIST, { "B" }); + } - Gtk.ToggleActionEntry[] toggle_action_entries = create_toggle_actions(); - action_group.add_toggle_actions(toggle_action_entries, this); - this.application.ui_manager.insert_action_group(action_group, 0); + private void add_window_accelerators(string action, string[] accelerators, Variant? param = null) { + this.application.set_accels_for_action("win."+action, accelerators); } private void open_account(Geary.Account account) { @@ -1330,11 +1180,9 @@ public class GearyController : Geary.BaseObject { // Update widgets and such to match capabilities of the current folder ... sensitivity is handled // by other utility methods private void update_ui() { - update_tooltips(); - main_window.main_toolbar.update_trash_button( - current_folder_supports_trash() || - !(current_folder is Geary.FolderSupport.Remove) - ); + main_window.main_toolbar.selected_conversations = this.selected_conversations.size; + main_window.main_toolbar.show_trash_button = current_folder_supports_trash() || + !(current_folder is Geary.FolderSupport.Remove); } private void on_folder_selected(Geary.Folder? folder) { @@ -1346,9 +1194,7 @@ public class GearyController : Geary.BaseObject { folder_selected(null); } else if (folder != this.current_folder) { this.main_window.conversation_viewer.show_loading(); - this.application.get_action( - ACTION_FIND_IN_CONVERSATION - ).set_sensitive(false); + get_window_action(ACTION_FIND_IN_CONVERSATION).set_enabled(false); enable_message_buttons(false); // To prevent the user from selecting folders too quickly, @@ -1531,7 +1377,7 @@ public class GearyController : Geary.BaseObject { private void on_indicator_activated_composer(uint32 timestamp) { on_indicator_activated_application(timestamp); - on_new_message(); + on_new_message(null); } private void on_indicator_activated_inbox(Geary.Folder folder, uint32 timestamp) { @@ -1554,9 +1400,7 @@ public class GearyController : Geary.BaseObject { private void on_conversations_selected(Gee.Set selected) { this.selected_conversations = selected; - this.application.get_action( - ACTION_FIND_IN_CONVERSATION - ).set_sensitive(false); + get_window_action(ACTION_FIND_IN_CONVERSATION).set_enabled(false); ConversationViewer viewer = this.main_window.conversation_viewer; if (this.current_folder != null && !viewer.is_composer_visible) { switch(selected.size) { @@ -1575,9 +1419,7 @@ public class GearyController : Geary.BaseObject { try { viewer.load_conversation.end(ret); enable_message_buttons(true); - this.application.get_action( - ACTION_FIND_IN_CONVERSATION - ).set_sensitive(true); + get_window_action(ACTION_FIND_IN_CONVERSATION).set_enabled(true); } catch (Error err) { debug("Unable to load conversation: %s", err.message); @@ -1754,8 +1596,9 @@ public class GearyController : Geary.BaseObject { private void on_shift_key(bool pressed) { if (main_window != null && main_window.main_toolbar != null && current_account != null && current_folder != null) { - main_window.main_toolbar.update_trash_button( - (!pressed && current_folder_supports_trash()) || !(current_folder is Geary.FolderSupport.Remove)); + main_window.main_toolbar.show_trash_button = + (!pressed && current_folder_supports_trash()) || + !(current_folder is Geary.FolderSupport.Remove); } } @@ -1832,30 +1675,19 @@ public class GearyController : Geary.BaseObject { unstarred_selected = true; } } - var actions = this.application.actions; - actions.get_action(ACTION_MARK_AS_READ).set_visible(unread_selected); - actions.get_action(ACTION_MARK_AS_UNREAD).set_visible(read_selected); - actions.get_action(ACTION_MARK_AS_STARRED).set_visible(unstarred_selected); - actions.get_action(ACTION_MARK_AS_UNSTARRED).set_visible(starred_selected); - - if (current_folder.special_folder_type != Geary.SpecialFolderType.DRAFTS && - current_folder.special_folder_type != Geary.SpecialFolderType.OUTBOX) { - if (current_folder.special_folder_type == Geary.SpecialFolderType.SPAM) { - // We're in the spam folder. - actions.get_action(ACTION_MARK_AS_SPAM).sensitive = true; - actions.get_action(ACTION_MARK_AS_SPAM).label = MARK_AS_NOT_SPAM_LABEL; - } else { - // We're not in the spam folder, but we are in a folder that allows mark-as-spam. - actions.get_action(ACTION_MARK_AS_SPAM).sensitive = true; - actions.get_action(ACTION_MARK_AS_SPAM).label = MARK_AS_SPAM_LABEL; - } - } else { - // We're in Drafts/Outbox, so gray-out the option. - actions.get_action(ACTION_MARK_AS_SPAM).sensitive = false; - actions.get_action(ACTION_MARK_AS_SPAM).label = MARK_AS_SPAM_LABEL; - } + get_window_action(ACTION_MARK_AS_READ).set_enabled(unread_selected); + get_window_action(ACTION_MARK_AS_UNREAD).set_enabled(read_selected); + get_window_action(ACTION_MARK_AS_STARRED).set_enabled(unstarred_selected); + get_window_action(ACTION_MARK_AS_UNSTARRED).set_enabled(starred_selected); + + bool in_spam_folder = current_folder.special_folder_type == Geary.SpecialFolderType.SPAM; + get_window_action(ACTION_MARK_AS_NOT_SPAM).set_enabled(in_spam_folder); + // If we're in Drafts/Outbox, we also shouldn't set a message as SPAM. + get_window_action(ACTION_MARK_AS_SPAM).set_enabled(!in_spam_folder && + current_folder.special_folder_type != Geary.SpecialFolderType.DRAFTS && + current_folder.special_folder_type != Geary.SpecialFolderType.OUTBOX); } - + private void on_visible_conversations_changed(Gee.Set visible) { clear_new_messages("on_visible_conversations_changed", visible); } @@ -1900,11 +1732,11 @@ public class GearyController : Geary.BaseObject { Geary.EmailFlags? flags_to_add, Geary.EmailFlags? flags_to_remove) { mark_email(emails, flags_to_add, flags_to_remove); } - - private void on_mark_as_read() { + + private void on_mark_as_read(SimpleAction action) { Geary.EmailFlags flags = new Geary.EmailFlags(); flags.add(Geary.EmailFlags.UNREAD); - + Gee.ArrayList ids = get_selected_email_ids(false); mark_email(ids, null, flags); @@ -1916,10 +1748,10 @@ public class GearyController : Geary.BaseObject { } } - private void on_mark_as_unread() { + private void on_mark_as_unread(SimpleAction action) { Geary.EmailFlags flags = new Geary.EmailFlags(); flags.add(Geary.EmailFlags.UNREAD); - + Gee.ArrayList ids = get_selected_email_ids(true); mark_email(ids, flags, null); @@ -1931,19 +1763,27 @@ public class GearyController : Geary.BaseObject { } } - private void on_mark_as_starred() { + private void on_mark_as_starred(SimpleAction action) { Geary.EmailFlags flags = new Geary.EmailFlags(); flags.add(Geary.EmailFlags.FLAGGED); mark_email(get_selected_email_ids(true), flags, null); } - private void on_mark_as_unstarred() { + private void on_mark_as_unstarred(SimpleAction action) { Geary.EmailFlags flags = new Geary.EmailFlags(); flags.add(Geary.EmailFlags.FLAGGED); mark_email(get_selected_email_ids(false), null, flags); } - - private async void mark_as_spam_async(Cancellable? cancellable) { + + private void on_show_move_menu(SimpleAction? action) { + this.main_window.main_toolbar.copy_message_button.clicked(); + } + + private void on_show_copy_menu(SimpleAction? action) { + this.main_window.main_toolbar.move_message_button.clicked(); + } + + private async void mark_as_spam_toggle_async(Cancellable? cancellable) { Geary.Folder? destination_folder = null; if (current_folder.special_folder_type != Geary.SpecialFolderType.SPAM) { // Move to spam folder. @@ -1961,15 +1801,15 @@ public class GearyController : Geary.BaseObject { debug("Error getting inbox folder: %s", e.message); } } - + if (destination_folder != null) on_move_conversation(destination_folder); } - - private void on_mark_as_spam() { - mark_as_spam_async.begin(null); + + private void on_mark_as_spam_toggle(SimpleAction action) { + mark_as_spam_toggle_async.begin(null); } - + private void copy_email(Gee.Collection ids, Geary.FolderPath destination) { if (ids.size > 0) { @@ -2284,9 +2124,7 @@ public class GearyController : Geary.BaseObject { if (widget.state == ComposerWidget.ComposerState.NEW || widget.state == ComposerWidget.ComposerState.PANED) { main_window.conversation_viewer.do_compose(widget); - this.application.get_action( - ACTION_FIND_IN_CONVERSATION - ).set_sensitive(false); + get_window_action(ACTION_FIND_IN_CONVERSATION).set_enabled(false); } else { main_window.conversation_viewer.do_compose_embedded( widget, @@ -2401,7 +2239,7 @@ public class GearyController : Geary.BaseObject { } } - private void on_new_message() { + private void on_new_message(SimpleAction? action) { create_compose_widget(ComposerWidget.ComposeType.NEW_MESSAGE); } @@ -2409,7 +2247,7 @@ public class GearyController : Geary.BaseObject { create_reply_forward_widget(ComposerWidget.ComposeType.REPLY, target_view); } - private void on_reply_to_message_action() { + private void on_reply_to_message_action(SimpleAction action) { create_reply_forward_widget(ComposerWidget.ComposeType.REPLY, null); } @@ -2417,7 +2255,7 @@ public class GearyController : Geary.BaseObject { create_reply_forward_widget(ComposerWidget.ComposeType.REPLY_ALL, target_view); } - private void on_reply_all_message_action() { + private void on_reply_all_message_action(SimpleAction action) { create_reply_forward_widget(ComposerWidget.ComposeType.REPLY_ALL, null); } @@ -2425,37 +2263,41 @@ public class GearyController : Geary.BaseObject { create_reply_forward_widget(ComposerWidget.ComposeType.FORWARD, target_view); } - private void on_forward_message_action() { + private void on_forward_message_action(SimpleAction action) { create_reply_forward_widget(ComposerWidget.ComposeType.FORWARD, null); } - private void on_find_in_conversation_action() { + private void on_find_in_conversation_action(SimpleAction action) { this.main_window.conversation_viewer.conversation_find_bar.set_search_mode(true); } - private void on_archive_conversation() { + private void on_search_activated(SimpleAction action) { + show_search_bar(); + } + + private void on_archive_conversation(SimpleAction action) { archive_or_delete_selection_async.begin(true, false, cancellable_folder, on_archive_or_delete_selection_finished); } - - private void on_trash_conversation() { + + private void on_trash_conversation(SimpleAction action) { archive_or_delete_selection_async.begin(false, true, cancellable_folder, on_archive_or_delete_selection_finished); } - - private void on_delete_conversation() { + + private void on_delete_conversation(SimpleAction action) { archive_or_delete_selection_async.begin(false, false, cancellable_folder, on_archive_or_delete_selection_finished); } - - private void on_empty_spam() { + + private void on_empty_spam(SimpleAction action) { on_empty_trash_or_spam(Geary.SpecialFolderType.SPAM); } - - private void on_empty_trash() { + + private void on_empty_trash(SimpleAction action) { on_empty_trash_or_spam(Geary.SpecialFolderType.TRASH); } - + private void on_empty_trash_or_spam(Geary.SpecialFolderType special_folder_type) { // Account must be in place, must have the specified special folder type, and that folder // must support Empty in order for this command to proceed @@ -2636,17 +2478,18 @@ public class GearyController : Geary.BaseObject { revokable.committed.connect(on_revokable_committed); } - Gtk.Action undo_action = this.application.get_action(ACTION_UNDO); - undo_action.tooltip = (revokable != null && description != null) ? description : _("Undo (Ctrl+Z)"); + if (revokable != null && description != null) + this.main_window.main_toolbar.undo_tooltip = description; + else + this.main_window.main_toolbar.undo_tooltip = _("Undo (Ctrl+Z)"); update_revokable_action(); } - + private void update_revokable_action() { - Gtk.Action undo_action = this.application.get_action(ACTION_UNDO); - undo_action.sensitive = revokable != null && revokable.valid && !revokable.in_process; + get_window_action(ACTION_UNDO).set_enabled(this.revokable != null && this.revokable.valid && !this.revokable.in_process); } - + private void on_revokable_valid_changed() { // remove revokable if it goes invalid if (revokable != null && !revokable.valid) @@ -2658,10 +2501,9 @@ public class GearyController : Geary.BaseObject { return; // use existing description - Gtk.Action undo_action = this.application.get_action(ACTION_UNDO); - save_revokable(committed_revokable, undo_action.tooltip); + save_revokable(committed_revokable, this.main_window.main_toolbar.undo_tooltip); } - + private void on_revoke() { if (revokable != null && revokable.valid) revokable.revoke_async.begin(null, on_revoke_completed); @@ -2681,27 +2523,16 @@ public class GearyController : Geary.BaseObject { } } - private void on_zoom_in() { - ConversationListBox? view = - main_window.conversation_viewer.current_list; - if (view != null) { - view.zoom_in(); - } - } - - private void on_zoom_out() { - ConversationListBox? view = - main_window.conversation_viewer.current_list; - if (view != null) { - view.zoom_out(); - } - } - - private void on_zoom_normal() { - ConversationListBox? view = - main_window.conversation_viewer.current_list; - if (view != null) { - view.zoom_reset(); + private void on_zoom(SimpleAction action, Variant? parameter) { + ConversationListBox? view = main_window.conversation_viewer.current_list; + if (view != null && parameter != null) { + string zoom_action = parameter.get_string(); + if (zoom_action == "in") + view.zoom_in(); + else if (zoom_action == "out") + view.zoom_out(); + else + view.zoom_reset(); } } @@ -2775,24 +2606,24 @@ public class GearyController : Geary.BaseObject { } } + private SimpleAction get_window_action(string action_name) { + return (SimpleAction) this.main_window.lookup_action(action_name); + } + // Disables all single-message buttons and enables all multi-message buttons. public void enable_multiple_message_buttons() { - update_tooltips(); + main_window.main_toolbar.selected_conversations = this.selected_conversations.size; // Single message only buttons. - this.application.actions.get_action(ACTION_REPLY_TO_MESSAGE).sensitive = false; - this.application.actions.get_action(ACTION_REPLY_ALL_MESSAGE).sensitive = false; - this.application.actions.get_action(ACTION_FORWARD_MESSAGE).sensitive = false; + get_window_action(ACTION_REPLY_TO_MESSAGE).set_enabled(false); + get_window_action(ACTION_REPLY_ALL_MESSAGE).set_enabled(false); + get_window_action(ACTION_FORWARD_MESSAGE).set_enabled(false); // Mutliple message buttons. - this.application.actions.get_action(ACTION_MOVE_MENU).sensitive = - (current_folder is Geary.FolderSupport.Move); - this.application.actions.get_action(ACTION_ARCHIVE_CONVERSATION).sensitive = - (current_folder is Geary.FolderSupport.Archive); - this.application.actions.get_action(ACTION_TRASH_CONVERSATION).sensitive = - current_folder_supports_trash(); - this.application.actions.get_action(ACTION_DELETE_CONVERSATION).sensitive = - (current_folder is Geary.FolderSupport.Remove); + get_window_action(ACTION_MOVE_MENU).set_enabled(current_folder is Geary.FolderSupport.Move); + get_window_action(ACTION_ARCHIVE_CONVERSATION).set_enabled(current_folder is Geary.FolderSupport.Archive); + get_window_action(ACTION_TRASH_CONVERSATION).set_enabled(current_folder_supports_trash()); + get_window_action(ACTION_DELETE_CONVERSATION).set_enabled(current_folder is Geary.FolderSupport.Remove); cancel_context_dependent_buttons(); enable_context_dependent_buttons_async.begin(true, cancellable_context_dependent_buttons); @@ -2800,29 +2631,25 @@ public class GearyController : Geary.BaseObject { // Enables or disables the message buttons on the toolbar. public void enable_message_buttons(bool sensitive) { - update_tooltips(); - + main_window.main_toolbar.selected_conversations = this.selected_conversations.size; + // No reply/forward in drafts folder. bool respond_sensitive = sensitive; if (current_folder != null && current_folder.special_folder_type == Geary.SpecialFolderType.DRAFTS) respond_sensitive = false; - this.application.actions.get_action(ACTION_REPLY_TO_MESSAGE).sensitive = respond_sensitive; - this.application.actions.get_action(ACTION_REPLY_ALL_MESSAGE).sensitive = respond_sensitive; - this.application.actions.get_action(ACTION_FORWARD_MESSAGE).sensitive = respond_sensitive; - this.application.actions.get_action(ACTION_MOVE_MENU).sensitive = - sensitive && (current_folder is Geary.FolderSupport.Move); - this.application.actions.get_action(ACTION_ARCHIVE_CONVERSATION).sensitive = sensitive - && (current_folder is Geary.FolderSupport.Archive); - this.application.actions.get_action(ACTION_TRASH_CONVERSATION).sensitive = sensitive - && current_folder_supports_trash(); - this.application.actions.get_action(ACTION_DELETE_CONVERSATION).sensitive = sensitive - && (current_folder is Geary.FolderSupport.Remove); + get_window_action(ACTION_REPLY_TO_MESSAGE).set_enabled(respond_sensitive); + get_window_action(ACTION_REPLY_ALL_MESSAGE).set_enabled(respond_sensitive); + get_window_action(ACTION_FORWARD_MESSAGE).set_enabled(respond_sensitive); + get_window_action(ACTION_MOVE_MENU).set_enabled(sensitive && (current_folder is Geary.FolderSupport.Move)); + get_window_action(ACTION_ARCHIVE_CONVERSATION).set_enabled(sensitive && (current_folder is Geary.FolderSupport.Archive)); + get_window_action(ACTION_TRASH_CONVERSATION).set_enabled(sensitive && current_folder_supports_trash()); + get_window_action(ACTION_DELETE_CONVERSATION).set_enabled(sensitive && (current_folder is Geary.FolderSupport.Remove)); cancel_context_dependent_buttons(); enable_context_dependent_buttons_async.begin(sensitive, cancellable_context_dependent_buttons); } - + private async void enable_context_dependent_buttons_async(bool sensitive, Cancellable? cancellable) { Gee.MultiMap? selected_operations = null; try { @@ -2846,29 +2673,8 @@ public class GearyController : Geary.BaseObject { if (selected_operations != null) supported_operations.add_all(selected_operations.get_values()); - this.application.actions.get_action(ACTION_MARK_AS_MENU).sensitive = - sensitive && (supported_operations.contains(typeof(Geary.FolderSupport.Mark))); - this.application.actions.get_action(ACTION_COPY_MENU).sensitive = - sensitive && (supported_operations.contains(typeof(Geary.FolderSupport.Copy))); - } - - // Updates tooltip text depending on number of conversations selected. - private void update_tooltips() { - bool single = selected_conversations.size == 1; - - this.application.actions.get_action(ACTION_MARK_AS_MENU).tooltip = single ? - MARK_MESSAGE_MENU_TOOLTIP_SINGLE : MARK_MESSAGE_MENU_TOOLTIP_MULTIPLE; - this.application.actions.get_action(ACTION_COPY_MENU).tooltip = single ? - LABEL_MESSAGE_TOOLTIP_SINGLE : LABEL_MESSAGE_TOOLTIP_MULTIPLE; - this.application.actions.get_action(ACTION_MOVE_MENU).tooltip = single ? - MOVE_MESSAGE_TOOLTIP_SINGLE : MOVE_MESSAGE_TOOLTIP_MULTIPLE; - - this.application.actions.get_action(ACTION_ARCHIVE_CONVERSATION).tooltip = single ? - ARCHIVE_CONVERSATION_TOOLTIP_SINGLE : ARCHIVE_CONVERSATION_TOOLTIP_MULTIPLE; - this.application.actions.get_action(ACTION_TRASH_CONVERSATION).tooltip = single ? - TRASH_CONVERSATION_TOOLTIP_SINGLE : TRASH_CONVERSATION_TOOLTIP_MULTIPLE; - this.application.actions.get_action(ACTION_DELETE_CONVERSATION).tooltip = single ? - DELETE_CONVERSATION_TOOLTIP_SINGLE : DELETE_CONVERSATION_TOOLTIP_MULTIPLE; + get_window_action(ACTION_SHOW_MARK_MENU).set_enabled(sensitive && (typeof(Geary.FolderSupport.Mark) in supported_operations)); + get_window_action(ACTION_COPY_MENU).set_enabled(sensitive && (supported_operations.contains(typeof(Geary.FolderSupport.Copy)))); } // Returns a list of composer windows for an account, or null if none. diff --git a/src/client/components/folder-popover.vala b/src/client/components/folder-popover.vala index 9b377c8f..99326abf 100644 --- a/src/client/components/folder-popover.vala +++ b/src/client/components/folder-popover.vala @@ -79,6 +79,8 @@ public class FolderPopover : Gtk.Popover { label.set_halign(Gtk.Align.START); row.add(label); + row.show_all(); + return row; } diff --git a/src/client/components/main-toolbar.vala b/src/client/components/main-toolbar.vala index 3c351137..67fc5d5c 100644 --- a/src/client/components/main-toolbar.vala +++ b/src/client/components/main-toolbar.vala @@ -1,4 +1,4 @@ -/* Copyright 2016 Software Freedom Conservancy Inc. +/* Copyright 2017 Software Freedom Conservancy Inc. * * This software is licensed under the GNU Lesser General Public License * (version 2.1 or later). See the COPYING file in this distribution. @@ -7,24 +7,35 @@ // Draws the main toolbar. [GtkTemplate (ui = "/org/gnome/Geary/main-toolbar.ui")] public class MainToolbar : Gtk.Box { - private Gtk.ActionGroup action_group; - public FolderPopover copy_folder_menu { get; private set; default = new FolderPopover(); } - public FolderPopover move_folder_menu { get; private set; default = new FolderPopover(); } + // How wide the left pane should be. Auto-synced with our settings + public int left_pane_width { get; set; } + // Used to form the title of the folder header public string account { get; set; } public string folder { get; set; } + // Close button settings public bool show_close_button { get; set; default = false; } public bool show_close_button_left { get; private set; default = true; } public bool show_close_button_right { get; private set; default = true; } + // Search and find bar public bool search_open { get; set; default = false; } public bool find_open { get; set; default = false; } - public int left_pane_width { get; set; } + // Copy and Move popovers + public FolderPopover copy_folder_menu { get; private set; default = new FolderPopover(); } + public FolderPopover move_folder_menu { get; private set; default = new FolderPopover(); } + // How many conversations are selected right now. Should automatically be updated. + public int selected_conversations { get; set; } + // Whether to show the trash or the delete button + public bool show_trash_button { get; set; default = true; } + // The tooltip of the Undo-button + public string undo_tooltip { + owned get { return this.undo_button.tooltip_text; } + set { this.undo_button.tooltip_text = value; } + } // Folder header elements [GtkChild] private Gtk.HeaderBar folder_header; [GtkChild] - private Gtk.Button compose_new_message_button; - [GtkChild] private Gtk.MenuButton empty_menu_button; [GtkChild] private Gtk.ToggleButton search_conversations_button; @@ -34,90 +45,85 @@ public class MainToolbar : Gtk.Box { [GtkChild] private Gtk.HeaderBar conversation_header; [GtkChild] - private Gtk.Button reply_sender_button; - [GtkChild] - private Gtk.Button reply_all_button; - [GtkChild] - private Gtk.Button forward_button; - [GtkChild] private Gtk.MenuButton mark_message_button; [GtkChild] - private Gtk.MenuButton copy_message_button; + public Gtk.MenuButton copy_message_button; [GtkChild] - private Gtk.MenuButton move_message_button; + public Gtk.MenuButton move_message_button; [GtkChild] private Gtk.Button archive_button; [GtkChild] private Gtk.Button trash_delete_button; [GtkChild] - private Gtk.Button undo_button; - [GtkChild] private Gtk.ToggleButton find_button; - public MainToolbar(Configuration config) { - this.action_group = GearyApplication.instance.actions; + // Other + [GtkChild] + private Gtk.Button undo_button; + // Load these at construction time + private Gtk.Image trash_image = new Gtk.Image.from_icon_name("user-trash-symbolic", Gtk.IconSize.MENU); + private Gtk.Image delete_image = new Gtk.Image.from_icon_name("edit-delete-symbolic", Gtk.IconSize.MENU); + + // Tooltips + private const string DELETE_CONVERSATION_TOOLTIP_SINGLE = _("Delete conversation (Shift+Delete)"); + private const string DELETE_CONVERSATION_TOOLTIP_MULTIPLE = _("Delete conversations (Shift+Delete)"); + private const string TRASH_CONVERSATION_TOOLTIP_SINGLE = _("Move conversation to Trash (Delete, Backspace)"); + private const string TRASH_CONVERSATION_TOOLTIP_MULTIPLE = _("Move conversations to Trash (Delete, Backspace)"); + private const string ARCHIVE_CONVERSATION_TOOLTIP_SINGLE = _("Archive conversation (A)"); + private const string ARCHIVE_CONVERSATION_TOOLTIP_MULTIPLE = _("Archive conversations (A)"); + private const string MARK_MESSAGE_MENU_TOOLTIP_SINGLE = _("Mark conversation"); + private const string MARK_MESSAGE_MENU_TOOLTIP_MULTIPLE = _("Mark conversations"); + private const string LABEL_MESSAGE_TOOLTIP_SINGLE = _("Add label to conversation"); + private const string LABEL_MESSAGE_TOOLTIP_MULTIPLE = _("Add label to conversations"); + private const string MOVE_MESSAGE_TOOLTIP_SINGLE = _("Move conversation"); + private const string MOVE_MESSAGE_TOOLTIP_MULTIPLE = _("Move conversations"); + + public MainToolbar(Configuration config) { // Instead of putting a separator between the two headerbars, as other applications do, // we put a separator at the right end of the left headerbar. This greatly improves // the appearance under the Ambiance theme (see bug #746171). To get this separator to // line up with the handle of the pane, we need to extend the width of the left-hand // headerbar a bit. Six pixels is right both for Adwaita and Ambiance. - GearyApplication.instance.config.bind(Configuration.MESSAGES_PANE_POSITION_KEY, - this, "left-pane-width", SettingsBindFlags.GET); - this.bind_property("left-pane-width", folder_header, "width-request", + config.bind(Configuration.MESSAGES_PANE_POSITION_KEY, this, "left-pane-width", + SettingsBindFlags.GET); + this.bind_property("left-pane-width", this.folder_header, "width-request", BindingFlags.SYNC_CREATE, (binding, source_value, ref target_value) => { target_value = left_pane_width + 6; return true; }); if (config.desktop_environment != Configuration.DesktopEnvironment.UNITY) { - this.bind_property("account", folder_header, "title", BindingFlags.SYNC_CREATE); - this.bind_property("folder", folder_header, "subtitle", BindingFlags.SYNC_CREATE); + this.bind_property("account", this.folder_header, "title", BindingFlags.SYNC_CREATE); + this.bind_property("folder", this.folder_header, "subtitle", BindingFlags.SYNC_CREATE); } - this.bind_property("show-close-button-left", folder_header, "show-close-button", + this.bind_property("show-close-button-left", this.folder_header, "show-close-button", BindingFlags.SYNC_CREATE); - this.bind_property("show-close-button-right", conversation_header, "show-close-button", + this.bind_property("show-close-button-right", this.conversation_header, "show-close-button", BindingFlags.SYNC_CREATE); // Assemble the empty/mark menus - GearyApplication.instance.load_ui_resource("toolbar_empty_menu.ui"); - Gtk.Menu empty_menu = (Gtk.Menu) GearyApplication.instance.ui_manager.get_widget("/ui/ToolbarEmptyMenu"); - GearyApplication.instance.load_ui_resource("toolbar_mark_menu.ui"); - Gtk.Menu mark_menu = (Gtk.Menu) GearyApplication.instance.ui_manager.get_widget("/ui/ToolbarMarkMenu"); + Gtk.Builder builder = new Gtk.Builder.from_resource("/org/gnome/Geary/main-toolbar-menus.ui"); + MenuModel empty_menu = (MenuModel) builder.get_object("empty_menu"); + MenuModel mark_menu = (MenuModel) builder.get_object("mark_message_menu"); // Setup folder header elements - setup_button(compose_new_message_button, GearyController.ACTION_NEW_MESSAGE); - empty_menu_button.popup = empty_menu; - - setup_button(search_conversations_button, GearyController.ACTION_TOGGLE_SEARCH); - this.bind_property("search-open", search_conversations_button, "active", + this.empty_menu_button.popover = new Gtk.Popover.from_model(null, empty_menu); + this.bind_property("search-open", this.search_conversations_button, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); // Setup conversation header elements - setup_button(reply_sender_button, GearyController.ACTION_REPLY_TO_MESSAGE); - setup_button(reply_all_button, GearyController.ACTION_REPLY_ALL_MESSAGE); - setup_button(forward_button, GearyController.ACTION_FORWARD_MESSAGE); + this.notify["selected-conversations"].connect(() => update_conversation_buttons()); + this.notify["show-trash-button"].connect(() => update_conversation_buttons()); + this.mark_message_button.popover = new Gtk.Popover.from_model(null, mark_menu); + this.copy_message_button.popover = copy_folder_menu; + this.move_message_button.popover = move_folder_menu; - setup_menu_button(mark_message_button, mark_menu, GearyController.ACTION_MARK_AS_MENU); - setup_popover_button(copy_message_button, copy_folder_menu, GearyController.ACTION_COPY_MENU); - setup_popover_button(move_message_button, move_folder_menu, GearyController.ACTION_MOVE_MENU); - - setup_button(archive_button, GearyController.ACTION_ARCHIVE_CONVERSATION, true); - setup_button(trash_delete_button, GearyController.ACTION_TRASH_CONVERSATION); - setup_button(undo_button, GearyController.ACTION_UNDO); - - setup_button(find_button, GearyController.ACTION_TOGGLE_FIND); - this.bind_property("find-open", find_button, "active", + this.bind_property("find-open", this.find_button, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); Gtk.Settings.get_default().notify["gtk-decoration-layout"].connect(set_window_buttons); - realize.connect(set_window_buttons); - } - - public void update_trash_button(bool is_trash) { - string action_name = (is_trash ? GearyController.ACTION_TRASH_CONVERSATION - : GearyController.ACTION_DELETE_CONVERSATION); - setup_button(trash_delete_button, action_name, false); + this.realize.connect(set_window_buttons); } public void set_conversation_header(Gtk.HeaderBar header) { @@ -152,71 +158,33 @@ public class MainToolbar : Gtk.Box { conversation_header.decoration_layout = ":" + buttons[1]; } - private void setup_button(Gtk.Button b, string action_name, bool show_label = false) { - Gtk.Action related_action = action_group.get_action(action_name); - b.focus_on_click = false; - b.use_underline = true; - b.tooltip_text = related_action.tooltip; - related_action.notify["tooltip"].connect(() => { b.tooltip_text = related_action.tooltip; }); - b.related_action = related_action; + // Updates tooltip text depending on number of conversations selected. + private void update_conversation_buttons() { + this.mark_message_button.tooltip_text = ngettext(MARK_MESSAGE_MENU_TOOLTIP_SINGLE, + MARK_MESSAGE_MENU_TOOLTIP_MULTIPLE, + this.selected_conversations); + this.copy_message_button.tooltip_text = ngettext(LABEL_MESSAGE_TOOLTIP_SINGLE, + LABEL_MESSAGE_TOOLTIP_MULTIPLE, + this.selected_conversations); + this.move_message_button.tooltip_text = ngettext(MOVE_MESSAGE_TOOLTIP_SINGLE, + MOVE_MESSAGE_TOOLTIP_MULTIPLE, + this.selected_conversations); + this.archive_button.tooltip_text = ngettext(ARCHIVE_CONVERSATION_TOOLTIP_SINGLE, + ARCHIVE_CONVERSATION_TOOLTIP_MULTIPLE, + this.selected_conversations); - // Load icon by name with this fallback order: specified icon name, the action's icon name, - // the action's stock ID ... although stock IDs are being deprecated, that's how we specify - // the icon in the GtkActionEntry (also being deprecated) and GTK+ 3.14 doesn't support that - // any longer - string? icon_to_load = b.related_action.icon_name; - if (icon_to_load == null) - icon_to_load = b.related_action.stock_id; - - // set pixel size to force GTK+ to load our images from our installed directory, not the theme - // directory - if (icon_to_load != null) { - Gtk.Image image = new Gtk.Image.from_icon_name(icon_to_load, Gtk.IconSize.MENU); - image.set_pixel_size(16); - b.image = image; - } - - b.always_show_image = true; - - if (show_label) - b.label = related_action.label; - else - b.label = null; - } - - /** - * Given an icon, menu, and action, creates a button that triggers the menu and the action. - */ - private void setup_menu_button(Gtk.MenuButton b, Gtk.Menu menu, string action_name) { - setup_button(b, action_name); - menu.foreach(GtkUtil.show_menuitem_accel_labels); - b.popup = menu; - - if (b.related_action != null) { - b.related_action.activate.connect(() => { - b.clicked(); - }); - // Null out the action since by connecting it to clicked - // above, invoking would cause an infinite loop otherwise. - b.related_action = null; - } - } - - /** - * Given an icon, popover, and action, creates a button that triggers the popover and the action. - */ - private void setup_popover_button(Gtk.MenuButton b, Gtk.Popover popover, string action_name) { - setup_button(b, action_name); - b.popover = popover; - b.clicked.connect(() => popover.show_all()); - - if (b.related_action != null) { - b.related_action.activate.connect(() => { - b.clicked(); - }); - // Null out the action since by connecting it to clicked - // above, invoking would cause an infinite loop otherwise. - b.related_action = null; + if (this.show_trash_button) { + this.trash_delete_button.action_name = "win."+GearyController.ACTION_TRASH_CONVERSATION; + this.trash_delete_button.image = trash_image; + this.trash_delete_button.tooltip_text = ngettext(TRASH_CONVERSATION_TOOLTIP_SINGLE, + TRASH_CONVERSATION_TOOLTIP_MULTIPLE, + this.selected_conversations); + } else { + this.trash_delete_button.action_name = "win."+GearyController.ACTION_DELETE_CONVERSATION; + this.trash_delete_button.image = delete_image; + this.trash_delete_button.tooltip_text = ngettext(DELETE_CONVERSATION_TOOLTIP_SINGLE, + DELETE_CONVERSATION_TOOLTIP_MULTIPLE, + this.selected_conversations); } } } diff --git a/src/client/components/main-window.vala b/src/client/components/main-window.vala index d0171071..13fedf7c 100644 --- a/src/client/components/main-window.vala +++ b/src/client/components/main-window.vala @@ -29,7 +29,7 @@ public class MainWindow : Gtk.ApplicationWindow { public FolderList.Tree folder_list { get; private set; default = new FolderList.Tree(); } public MainToolbar main_toolbar { get; private set; } public SearchBar search_bar { get; private set; default = new SearchBar(); } - public ConversationListView conversation_list_view { get; private set; default = new ConversationListView(); } + public ConversationListView conversation_list_view { get; private set; } public ConversationViewer conversation_viewer { get; private set; default = new ConversationViewer(); } public StatusBar status_bar { get; private set; default = new StatusBar(); } private MonitoredSpinner spinner = new MonitoredSpinner(); @@ -61,8 +61,6 @@ public class MainWindow : Gtk.ApplicationWindow { load_config(application.config); restore_saved_window_state(); - add_accel_group(application.ui_manager.get_accel_group()); - application.controller.notify[GearyController.PROP_CURRENT_CONVERSATION] .connect(on_conversation_monitor_changed); application.controller.folder_selected.connect(on_folder_selected); @@ -163,6 +161,8 @@ public class MainWindow : Gtk.ApplicationWindow { } private void setup_layout(Configuration config) { + // ConversationListView + this.conversation_list_view = new ConversationListView(this); // Toolbar this.main_toolbar = new MainToolbar(config); this.main_toolbar.bind_property("search-open", this.search_bar, "search-mode-enabled", diff --git a/src/client/composer/composer-container.vala b/src/client/composer/composer-container.vala index da82724d..ba831e64 100644 --- a/src/client/composer/composer-container.vala +++ b/src/client/composer/composer-container.vala @@ -12,12 +12,6 @@ public interface ComposerContainer { // The ComposerWidget-child. internal abstract ComposerWidget composer { get; set; } - // Workaround to retrieve all Gtk.Actions with conflicting accelerators - protected const string[] conflicting_actions = { - GearyController.ACTION_MARK_AS_UNREAD, - GearyController.ACTION_FORWARD_MESSAGE - }; - // We use old_accelerators to keep track of the accelerators we temporarily disabled. protected abstract Gee.MultiMap? old_accelerators { get; set; } @@ -80,10 +74,6 @@ public interface ComposerContainer { } } - // Very stupid workaround while we still use Gtk.Actions in the GearyController - foreach (string conflicting_action in conflicting_actions) - app.actions.get_action(conflicting_action).disconnect_accelerator(); - // Now add our actions to the window and their accelerators foreach (string action in ComposerWidget.action_accelerators.get_keys()) { this.top_window.add_action(composer.get_action(action)); @@ -99,10 +89,6 @@ public interface ComposerContainer { foreach (string action in ComposerWidget.action_accelerators.get_keys()) GearyApplication.instance.set_accels_for_action("win." + action, {}); - // Very stupid workaround while we still use Gtk.Actions in the GearyController - foreach (string conflicting_action in conflicting_actions) - GearyApplication.instance.actions.get_action(conflicting_action).connect_accelerator(); - foreach (string action in old_accelerators.get_keys()) foreach (string accelerator in this.old_accelerators[action]) restore_conflicting_accelerator(action, accelerator); diff --git a/src/client/conversation-list/conversation-list-view.vala b/src/client/conversation-list/conversation-list-view.vala index bad27448..abacb34e 100644 --- a/src/client/conversation-list/conversation-list-view.vala +++ b/src/client/conversation-list/conversation-list-view.vala @@ -6,9 +6,12 @@ public class ConversationListView : Gtk.TreeView { const int LOAD_MORE_HEIGHT = 100; - + + // Used to be able to refer to the action names of the MainWindow + private MainWindow main_window; + private bool enable_load_more = true; - + // Used to avoid repeated calls to load_more(). Contains the last "upper" bound of the // scroll adjustment seen at the call to load_more(). private double last_upper = -1.0; @@ -16,7 +19,6 @@ public class ConversationListView : Gtk.TreeView { private Geary.App.ConversationMonitor? conversation_monitor; private Gee.Set? current_visible_conversations = null; private Geary.Scheduler.Scheduled? scheduled_update_visible_conversations = null; - private Gtk.Menu? context_menu = null; private Gee.Set selected = new Gee.HashSet(); private Geary.IdleManager selection_update; private bool suppress_selection = false; @@ -36,9 +38,10 @@ public class ConversationListView : Gtk.TreeView { public signal void visible_conversations_changed(Gee.Set visible); - public ConversationListView() { + public ConversationListView(MainWindow parent) { set_show_expanders(false); set_headers_visible(false); + this.main_window = parent; append_column(create_column(ConversationListStore.Column.CONVERSATION_DATA, new ConversationListCellRenderer(), ConversationListStore.Column.CONVERSATION_DATA.to_string(), @@ -281,47 +284,36 @@ public class ConversationListView : Gtk.TreeView { if (event.button == 3 && event.type == Gdk.EventType.BUTTON_PRESS) { Geary.App.Conversation conversation = get_model().get_conversation_at_path(path); - - string?[] action_names = {}; - action_names += GearyController.ACTION_DELETE_CONVERSATION; - + + Menu context_menu_model = new Menu(); + context_menu_model.append(_("Delete conversation"), "win."+GearyController.ACTION_DELETE_CONVERSATION); + if (conversation.is_unread()) - action_names += GearyController.ACTION_MARK_AS_READ; - + context_menu_model.append(_("Mark as _Read"), "win."+GearyController.ACTION_MARK_AS_READ); + if (conversation.has_any_read_message()) - action_names += GearyController.ACTION_MARK_AS_UNREAD; - + context_menu_model.append(_("Mark as _Unread"), "win."+GearyController.ACTION_MARK_AS_UNREAD); + if (conversation.is_flagged()) - action_names += GearyController.ACTION_MARK_AS_UNSTARRED; + context_menu_model.append(_("U_nstar"), "win."+GearyController.ACTION_MARK_AS_UNSTARRED); else - action_names += GearyController.ACTION_MARK_AS_STARRED; - - // treat null as separator - action_names += null; - action_names += GearyController.ACTION_REPLY_TO_MESSAGE; - action_names += GearyController.ACTION_REPLY_ALL_MESSAGE; - action_names += GearyController.ACTION_FORWARD_MESSAGE; - - context_menu = new Gtk.Menu(); - foreach (string? action_name in action_names) { - if (action_name == null) { - context_menu.add(new Gtk.SeparatorMenuItem()); - - continue; - } - - Gtk.Action? menu_action = GearyApplication.instance.actions.get_action(action_name); - if (menu_action != null) - context_menu.add(menu_action.create_menu_item()); - } - + context_menu_model.append(_("_Star"), "win."+GearyController.ACTION_MARK_AS_STARRED); + + Menu actions_section = new Menu(); + actions_section.append(_("_Reply"), "win."+GearyController.ACTION_REPLY_TO_MESSAGE); + actions_section.append(_("R_eply All"), "win."+GearyController.ACTION_REPLY_ALL_MESSAGE); + actions_section.append(_("_Forward"), "win."+GearyController.ACTION_FORWARD_MESSAGE); + context_menu_model.append_section(null, actions_section); + + Gtk.Menu context_menu = new Gtk.Menu.from_model(context_menu_model); + context_menu.insert_action_group("win", this.main_window); context_menu.show_all(); context_menu.popup(null, null, null, event.button, event.time); - + // When the conversation under the mouse is selected, stop event propagation return get_selection().path_is_selected(path); } - + return false; } diff --git a/src/client/util/util-gtk.vala b/src/client/util/util-gtk.vala index 84fcabed..489d217e 100644 --- a/src/client/util/util-gtk.vala +++ b/src/client/util/util-gtk.vala @@ -35,25 +35,6 @@ public void add_proxy_menu(Gtk.ToolItem tool_item, string label, Gtk.Menu proxy_ }); } -public void add_accelerator(Gtk.UIManager ui_manager, Gtk.ActionGroup action_group, - string accelerator, string action) { - // Parse the accelerator. - uint key = 0; - Gdk.ModifierType modifiers = 0; - Gtk.accelerator_parse(accelerator, out key, out modifiers); - if (key == 0) { - debug("Failed to parse accelerator '%s'", accelerator); - return; - } - - // Connect the accelerator to the action. - ui_manager.get_accel_group().connect(key, modifiers, Gtk.AccelFlags.VISIBLE, - (group, obj, key, modifiers) => { - action_group.get_action(action).activate(); - return true; - }); -} - public void show_menuitem_accel_labels(Gtk.Widget widget) { Gtk.MenuItem? item = widget as Gtk.MenuItem; if (item == null) { diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index dc410911..05a74e74 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -1,6 +1,5 @@ set(RESOURCE_LIST - STRIPBLANKS "accelerators.ui" STRIPBLANKS "account_cannot_remove.glade" STRIPBLANKS "account_list.glade" STRIPBLANKS "account_spinner.glade" @@ -29,12 +28,11 @@ set(RESOURCE_LIST STRIPBLANKS "gtk/menus.ui" STRIPBLANKS "login.glade" STRIPBLANKS "main-toolbar.ui" + STRIPBLANKS "main-toolbar-menus.ui" STRIPBLANKS "main-window.ui" STRIPBLANKS "password-dialog.glade" STRIPBLANKS "preferences-dialog.ui" STRIPBLANKS "remove_confirm.glade" - STRIPBLANKS "toolbar_empty_menu.ui" - STRIPBLANKS "toolbar_mark_menu.ui" STRIPBLANKS "upgrade_dialog.glade" "geary.css" ) diff --git a/ui/accelerators.ui b/ui/accelerators.ui deleted file mode 100644 index 470696b5..00000000 --- a/ui/accelerators.ui +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/ui/main-toolbar-menus.ui b/ui/main-toolbar-menus.ui new file mode 100644 index 00000000..dea30ccb --- /dev/null +++ b/ui/main-toolbar-menus.ui @@ -0,0 +1,40 @@ + + + + + Empty _Spam… + win.empty-spam + + + Empty _Trash… + win.empty-trash + + + + + + Mark as _Read + win.mark-message-read + + + Mark as _Unread + win.mark-message-unread + + + _Star + win.mark-message-starred + + + U_nstar + win.mark-message-unstarred + + + Mark as S_pam + win.mark-message-spam + + + Mark as not S_pam + win.mark-message-not-spam + + + diff --git a/ui/main-toolbar.ui b/ui/main-toolbar.ui index 074710c6..48cd8a03 100644 --- a/ui/main-toolbar.ui +++ b/ui/main-toolbar.ui @@ -20,6 +20,7 @@ True False True + win.new-message text-editor-symbolic @@ -43,6 +44,7 @@ True False True + Toggle search bar preferences-system-search-symbolic @@ -94,6 +96,8 @@ True False True + Reply + win.reply-to-message mail-reply-sender-symbolic @@ -107,6 +111,8 @@ True False True + Reply All + win.reply-all-message mail-reply-all-symbolic @@ -120,6 +126,8 @@ True False True + Forward + win.forward-message mail-forward-symbolic @@ -143,6 +151,7 @@ True False True + win.mark-message-menu marker-symbolic @@ -151,7 +160,7 @@ - + True True False @@ -164,7 +173,7 @@ - + True True False @@ -184,6 +193,7 @@ True False True + Toggle find bar preferences-system-search-symbolic @@ -200,9 +210,10 @@ True False True + win.undo - preferences-system-search-symbolic + edit-undo-symbolic @@ -224,6 +235,12 @@ True False True + win.archive-conv + + + mail-archive-symbolic + + @@ -232,6 +249,12 @@ True False True + win.trash-conv + + + user-trash-symbolic + + diff --git a/ui/toolbar_empty_menu.ui b/ui/toolbar_empty_menu.ui deleted file mode 100644 index cfc89bb5..00000000 --- a/ui/toolbar_empty_menu.ui +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/ui/toolbar_mark_menu.ui b/ui/toolbar_mark_menu.ui deleted file mode 100644 index 662b27b2..00000000 --- a/ui/toolbar_mark_menu.ui +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - From 992e019b29a53ad4ef243310f6346a409409132b Mon Sep 17 00:00:00 2001 From: Michael James Gratton Date: Mon, 16 Oct 2017 08:37:36 +1030 Subject: [PATCH 02/11] Restore Archive toolbar label. * ui/main-toolbar.ui: Update min GTK version, add label to Archive button. --- ui/main-toolbar.ui | 161 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 122 insertions(+), 39 deletions(-) diff --git a/ui/main-toolbar.ui b/ui/main-toolbar.ui index 48cd8a03..9fa65e85 100644 --- a/ui/main-toolbar.ui +++ b/ui/main-toolbar.ui @@ -1,28 +1,30 @@ + - + + + True + False + mail-archive-symbolic + From 610b2e3130546e3ff41933273ef048841aba271a Mon Sep 17 00:00:00 2001 From: Michael James Gratton Date: Tue, 17 Oct 2017 18:08:56 +1030 Subject: [PATCH 03/11] Fix move and copy menu button ids in the main toolbar. --- ui/main-toolbar.ui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/main-toolbar.ui b/ui/main-toolbar.ui index 9fa65e85..030f59cc 100644 --- a/ui/main-toolbar.ui +++ b/ui/main-toolbar.ui @@ -198,7 +198,7 @@ - + True True False @@ -218,7 +218,7 @@ - + True True False From 7d15b746aab0cfe031b7fd7ba0e03546b6001916 Mon Sep 17 00:00:00 2001 From: Niels De Graef Date: Sun, 21 May 2017 20:42:58 +0200 Subject: [PATCH 04/11] Use a horizontal icon row for message actions, bug 782931. Signed-off-by: Niels De Graef --- ui/conversation-email-menus.ui | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/conversation-email-menus.ui b/ui/conversation-email-menus.ui index 32c7ddee..a06f3c58 100644 --- a/ui/conversation-email-menus.ui +++ b/ui/conversation-email-menus.ui @@ -3,17 +3,21 @@
+ horizontal-buttons _Reply eml.reply_sender + mail-reply-sender-symbolic Reply to _All eml.reply_all + mail-reply-all-symbolic _Forward eml.forward + mail-forward-symbolic
From bf7c2ec454e9f0220a0050e4f3d2e372242e35c7 Mon Sep 17 00:00:00 2001 From: Niels De Graef Date: Thu, 28 Sep 2017 12:35:45 +0200 Subject: [PATCH 05/11] Use https:// by default for links Signed-off-by: Niels De Graef --- src/client/composer/composer-widget.vala | 2 +- ui/composer-link-popover.ui | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala index 509ab5b9..b12ff01d 100644 --- a/src/client/composer/composer-widget.vala +++ b/src/client/composer/composer-widget.vala @@ -2272,7 +2272,7 @@ public class ComposerWidget : Gtk.EventBox { private void on_insert_link(SimpleAction action, Variant? param) { ComposerLinkPopover.Type type = ComposerLinkPopover.Type.NEW_LINK; - string url = "http://"; + string url = "https://"; if (this.cursor_url != null) { type = ComposerLinkPopover.Type.EXISTING_LINK; url = this.cursor_url; diff --git a/ui/composer-link-popover.ui b/ui/composer-link-popover.ui index 24d71d26..56bb7572 100644 --- a/ui/composer-link-popover.ui +++ b/ui/composer-link-popover.ui @@ -23,7 +23,7 @@ 40 False False - http:// + https:// url From ff755c1f7d52bb2e4ebd7b64d1c1fe26f18552d4 Mon Sep 17 00:00:00 2001 From: Niels De Graef Date: Fri, 23 Dec 2016 00:02:09 +0100 Subject: [PATCH 06/11] Replace intltool with gettext. Bug 771643. Signed-off-by: Niels De Graef --- CMakeLists.txt | 13 +++- INSTALL | 6 +- cmake/FindIntltool.cmake | 44 ----------- cmake/{Gettext.cmake => FindXGettext.cmake} | 84 ++++++++++++--------- debian/control | 1 - desktop/CMakeLists.txt | 51 ++++++++----- desktop/geary-attach.contract.in | 4 +- desktop/geary-autostart.desktop.in | 10 +-- desktop/org.gnome.Geary.appdata.xml.in | 28 +++---- desktop/org.gnome.Geary.desktop.in | 12 +-- po/POTFILES.in | 59 +++++++-------- 11 files changed, 150 insertions(+), 162 deletions(-) delete mode 100644 cmake/FindIntltool.cmake rename cmake/{Gettext.cmake => FindXGettext.cmake} (57%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3199468c..67bfeb77 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,12 +137,17 @@ endif() find_package(Git QUIET) -# intl -include(Gettext) +# xgettext +include(FindXGettext) +SET(MIN_XGETTEXT_VERSION "0.19.8") if (XGETTEXT_FOUND) - message(STATUS "xgettext found") + if (XGETTEXT_VERSION VERSION_LESS MIN_XGETTEXT_VERSION) + message (FATAL_ERROR "xgettext found, but version is ${XGETTEXT_VERSION} (minimum version required is ${MIN_XGETTEXT_VERSION}).") + else () + message(STATUS "xgettext found, version ${XGETTEXT_VERSION}") + endif () else () - message(STATUS "xgettext not found") + message (FATAL_ERROR "xgettext not found") endif () # GResources diff --git a/INSTALL b/INSTALL index bd3b503b..7af6aa1f 100644 --- a/INSTALL +++ b/INSTALL @@ -22,7 +22,7 @@ * WebKitGTK+ 2.10 * Vala 0.26 - With a full GObject introspection repository, intltool, cmake, + With a full GObject introspection repository, cmake, desktop-file-validate, and xml2po. Vala's vapigen must be installed as well. @@ -65,7 +65,7 @@ Fedora 23 and later ships with the correct versions of the required libraries. Install them by running this command: - $ sudo dnf install vala gobject-introspection-devel intltool cmake \ + $ sudo yum install vala gobject-introspection-devel cmake \ desktop-file-utils gnome-doc-utils libcanberra-devel libgee-devel \ glib2-devel gmime-devel gtk3-devel libnotify-devel sqlite-devel \ webkitgtk4-devel libsecret-devel libxml2-devel vala-tools \ @@ -84,7 +84,7 @@ Install them by running this command: - $ sudo apt-get install valac libgirepository1.0-dev intltool \ + $ sudo apt-get install valac libgirepository1.0-dev \ cmake desktop-file-utils gnome-doc-utils libcanberra-dev \ libgee-0.8-dev libglib2.0-dev libgmime-2.6-dev libgtk-3-dev \ libsecret-1-dev libxml2-dev libnotify-dev libsqlite3-dev \ diff --git a/cmake/FindIntltool.cmake b/cmake/FindIntltool.cmake deleted file mode 100644 index fb710557..00000000 --- a/cmake/FindIntltool.cmake +++ /dev/null @@ -1,44 +0,0 @@ -# FindIntltool.cmake -# -# Jim Nelson -# Copyright 2016 Software Freedom Conservancy Inc. - -find_program (INTLTOOL_MERGE_EXECUTABLE intltool-merge) - -if (INTLTOOL_MERGE_EXECUTABLE) - set (INTLTOOL_MERGE_FOUND TRUE) -else (INTLTOOL_MERGE_EXECUTABLE) - set (INTLTOOL_MERGE_FOUND FALSE) -endif (INTLTOOL_MERGE_EXECUTABLE) - -if (INTLTOOL_MERGE_FOUND) - macro (INTLTOOL_MERGE_APPDATA appstream_name po_dir) - add_custom_target (${appstream_name}.in ALL - ${INTLTOOL_MERGE_EXECUTABLE} --xml-style ${CMAKE_SOURCE_DIR}/${po_dir} - ${CMAKE_CURRENT_SOURCE_DIR}/${appstream_name}.in ${appstream_name} - ) - install (FILES ${CMAKE_CURRENT_BINARY_DIR}/${appstream_name} DESTINATION ${CMAKE_INSTALL_PREFIX}/share/metainfo) - endmacro (INTLTOOL_MERGE_APPDATA appstream_name po_dir) - macro (INTLTOOL_MERGE_DESKTOP desktop_id po_dir) - add_custom_target (org.gnome.Geary.desktop ALL - ${INTLTOOL_MERGE_EXECUTABLE} --desktop-style ${CMAKE_SOURCE_DIR}/${po_dir} - ${CMAKE_CURRENT_SOURCE_DIR}/${desktop_id}.in ${desktop_id} - ) - install (FILES ${CMAKE_CURRENT_BINARY_DIR}/org.gnome.Geary.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications) - endmacro (INTLTOOL_MERGE_DESKTOP desktop_id po_dir) - macro (INTLTOOL_MERGE_AUTOSTART_DESKTOP desktop_id po_dir) - add_custom_target (geary-autostart.desktop ALL - ${INTLTOOL_MERGE_EXECUTABLE} --desktop-style ${CMAKE_SOURCE_DIR}/${po_dir} - ${CMAKE_CURRENT_SOURCE_DIR}/${desktop_id}.in ${desktop_id} - ) - install (FILES ${CMAKE_CURRENT_BINARY_DIR}/geary-autostart.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications) - endmacro (INTLTOOL_MERGE_AUTOSTART_DESKTOP desktop_id po_dir) - macro (INTLTOOL_MERGE_CONTRACT desktop_id po_dir) - add_custom_target (geary-attach.contract ALL - ${INTLTOOL_MERGE_EXECUTABLE} --desktop-style ${CMAKE_SOURCE_DIR}/${po_dir} - ${CMAKE_CURRENT_SOURCE_DIR}/${desktop_id}.in ${desktop_id} - ) - install (FILES ${CMAKE_CURRENT_BINARY_DIR}/geary-attach.contract DESTINATION ${CMAKE_INSTALL_PREFIX}/share/contractor) - endmacro (INTLTOOL_MERGE_CONTRACT desktop_id po_dir) -endif (INTLTOOL_MERGE_FOUND) - diff --git a/cmake/Gettext.cmake b/cmake/FindXGettext.cmake similarity index 57% rename from cmake/Gettext.cmake rename to cmake/FindXGettext.cmake index 3247b3b5..c6437f85 100644 --- a/cmake/Gettext.cmake +++ b/cmake/FindXGettext.cmake @@ -21,6 +21,7 @@ # GETTEXT_FOUND: True if gettext has been found. # XGETTEXT_EXECUTABLE: the full path to the xgettext. # XGETTEXT_FOUND: True if xgettext has been found. +# XGETTEXT_VERSION: The xgettext version. # #=================================================================== # Macros: @@ -55,66 +56,81 @@ SET(XGETTEXT_OPTIONS_DEFAULT --language=C --keyword=_ --keyword=N_ --keyword=C_:1c,2 --keyword=NC_:1c,2 -s --escape --add-comments="/" --package-name=${PROJECT_NAME} --package-version=${VERSION}) +# Check for gettext IF (GETTEXT_MSGMERGE_EXECUTABLE AND GETTEXT_MSGFMT_EXECUTABLE AND GETTEXT_MSGCAT_EXECUTABLE) SET(GETTEXT_FOUND TRUE) ELSE (GETTEXT_MSGMERGE_EXECUTABLE AND GETTEXT_MSGFMT_EXECUTABLE) SET(GETTEXT_FOUND FALSE) IF (GetText_REQUIRED) - MESSAGE(FATAL_ERROR "GetText not found") + MESSAGE(FATAL_ERROR "GetText not found") ENDIF (GetText_REQUIRED) ENDIF (GETTEXT_MSGMERGE_EXECUTABLE AND GETTEXT_MSGFMT_EXECUTABLE AND GETTEXT_MSGCAT_EXECUTABLE) +# Check xgettext and the version IF(XGETTEXT_EXECUTABLE) SET(XGETTEXT_FOUND TRUE) + + # Liberally taken from + # https://github.com/boghison/mixcloudscope/blob/master/cmake/FindXGettext.cmake + execute_process(COMMAND ${XGETTEXT_EXECUTABLE} --version + OUTPUT_VARIABLE _xgettext_version + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_xgettext_version MATCHES "^xgettext \\(.*\\) [0-9]") + string( + REGEX REPLACE "^xgettext \\([^\\)]*\\) ([0-9\\.]+[^ \n]*).*" "\\1" + XGETTEXT_VERSION "${_xgettext_version}" + ) + endif() + unset(_xgettext_version) ELSE(XGETTEXT_EXECUTABLE) MESSAGE(STATUS "xgettext not found.") SET(XGETTTEXT_FOUND FALSE) ENDIF(XGETTEXT_EXECUTABLE) +# Set the default options if not specified IF(NOT DEFINED XGETTEXT_OPTIONS) SET(XGETTEXT_OPTIONS ${XGETTEXT_OPTIONS_DEFAULT}) ENDIF(NOT DEFINED XGETTEXT_OPTIONS) +# Add translations target IF(XGETTEXT_FOUND) MACRO(GETTEXT_CREATE_TRANSLATIONS _firstLang) - SET(_gmoFiles) - SET(_addToAll) - SET(_is_comment FALSE) + SET(_gmoFiles) + SET(_addToAll) + SET(_is_comment FALSE) - FOREACH (_currentLang ${_firstLang} ${ARGN}) - IF(_currentLang STREQUAL "ALL") - SET(_addToAll "ALL") - ELSEIF(_currentLang STREQUAL "COMMENT") - SET(_is_comment TRUE) - ELSEIF(_is_comment) - SET(_is_comment FALSE) - SET(_comment ${_currentLang}) - ELSE() - SET(_lang ${_currentLang}) - GET_FILENAME_COMPONENT(_absFile ${_currentLang}.po ABSOLUTE) - GET_FILENAME_COMPONENT(_abs_PATH ${_absFile} PATH) - SET(_gmoFile ${CMAKE_CURRENT_BINARY_DIR}/${_lang}.mo) + FOREACH (_currentLang ${_firstLang} ${ARGN}) + IF(_currentLang STREQUAL "ALL") + SET(_addToAll "ALL") + ELSEIF(_currentLang STREQUAL "COMMENT") + SET(_is_comment TRUE) + ELSEIF(_is_comment) + SET(_is_comment FALSE) + SET(_comment ${_currentLang}) + ELSE() + SET(_lang ${_currentLang}) + GET_FILENAME_COMPONENT(_absFile ${_currentLang}.po ABSOLUTE) + GET_FILENAME_COMPONENT(_abs_PATH ${_absFile} PATH) + SET(_gmoFile ${CMAKE_CURRENT_BINARY_DIR}/${_lang}.mo) - #MESSAGE("_absFile=${_absFile} _abs_PATH=${_abs_PATH} _lang=${_lang} curr_bin=${CMAKE_CURRENT_BINARY_DIR}") - ADD_CUSTOM_COMMAND( - OUTPUT ${_gmoFile} - COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} -o ${_gmoFile} ${_absFile} - DEPENDS ${_absFile} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - ) + #MESSAGE("_absFile=${_absFile} _abs_PATH=${_abs_PATH} _lang=${_lang} curr_bin=${CMAKE_CURRENT_BINARY_DIR}") + ADD_CUSTOM_COMMAND( + OUTPUT ${_gmoFile} + COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} -o ${_gmoFile} ${_absFile} + DEPENDS ${_absFile} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) INSTALL(FILES ${_gmoFile} DESTINATION share/locale/${_lang}/LC_MESSAGES RENAME ${GETTEXT_PACKAGE}.mo) - SET(_gmoFiles ${_gmoFiles} ${_gmoFile}) - ENDIF() - ENDFOREACH (_currentLang ) + SET(_gmoFiles ${_gmoFiles} ${_gmoFile}) + ENDIF() + ENDFOREACH (_currentLang ) - IF(DEFINED _comment) - ADD_CUSTOM_TARGET(translations ${_addToAll} DEPENDS ${_gmoFiles} COMMENT ${_comment}) - ELSE(DEFINED _comment) - ADD_CUSTOM_TARGET(translations ${_addToAll} DEPENDS ${_gmoFiles}) - ENDIF(DEFINED _comment) + IF(DEFINED _comment) + ADD_CUSTOM_TARGET(translations ${_addToAll} DEPENDS ${_gmoFiles} COMMENT ${_comment}) + ELSE(DEFINED _comment) + ADD_CUSTOM_TARGET(translations ${_addToAll} DEPENDS ${_gmoFiles}) + ENDIF(DEFINED _comment) ENDMACRO(GETTEXT_CREATE_TRANSLATIONS ) ENDIF(XGETTEXT_FOUND) - - diff --git a/debian/control b/debian/control index e68f86a7..61ffd315 100644 --- a/debian/control +++ b/debian/control @@ -18,7 +18,6 @@ Build-Depends: debhelper (>= 8), libsqlite3-dev (>= 3.7.4), libmessaging-menu-dev (>= 12.10.2), libunity-dev (>= 5.12.0), - intltool, libgirepository1.0-dev (>= 1.32.0), desktop-file-utils, gnome-doc-utils, diff --git a/desktop/CMakeLists.txt b/desktop/CMakeLists.txt index 927c9d41..6f0d0b37 100644 --- a/desktop/CMakeLists.txt +++ b/desktop/CMakeLists.txt @@ -2,24 +2,34 @@ # Build and install org.gnome.Geary.desktop # -include (FindIntltool) include (FindDesktopFileValidate) -if (INTLTOOL_MERGE_FOUND) - INTLTOOL_MERGE_APPDATA (org.gnome.Geary.appdata.xml po) - INTLTOOL_MERGE_DESKTOP (org.gnome.Geary.desktop po) - INTLTOOL_MERGE_AUTOSTART_DESKTOP (geary-autostart.desktop po) - if (DESKTOP_VALIDATE) - if (DESKTOP_FILE_VALIDATE_FOUND) - VALIDATE_DESKTOP_FILE (org.gnome.Geary.desktop) - VALIDATE_DESKTOP_FILE (geary-autostart.desktop) - else (DESKTOP_FILE_VALIDATE_FOUND) - message (FATAL_ERROR "desktop-file-validate must be installed to validate generated .desktop file") - endif (DESKTOP_FILE_VALIDATE_FOUND) - endif (DESKTOP_VALIDATE) -else (INTLTOOL_MERGE_FOUND) - message (FATAL_ERROR "intltool must be installed to generate .desktop file") -endif (INTLTOOL_MERGE_FOUND) +add_custom_target (org.gnome.Geary.appdata.xml ALL + ${GETTEXT_MSGFMT_EXECUTABLE} --xml -d '${CMAKE_SOURCE_DIR}/po' + --template '${CMAKE_CURRENT_SOURCE_DIR}/org.gnome.Geary.appdata.xml.in' -o org.gnome.Geary.appdata.xml +) +add_custom_target (org.gnome.Geary.desktop ALL + ${GETTEXT_MSGFMT_EXECUTABLE} --desktop -d '${CMAKE_SOURCE_DIR}/po' + --template '${CMAKE_CURRENT_SOURCE_DIR}/org.gnome.Geary.desktop.in' -o org.gnome.Geary.desktop +) +add_custom_target (geary-autostart.desktop ALL + ${GETTEXT_MSGFMT_EXECUTABLE} --desktop -d '${CMAKE_SOURCE_DIR}/po' + --template '${CMAKE_CURRENT_SOURCE_DIR}/geary-autostart.desktop.in' -o geary-autostart.desktop +) + +if (DESKTOP_VALIDATE) + if (DESKTOP_FILE_VALIDATE_FOUND) + VALIDATE_DESKTOP_FILE (org.gnome.Geary.desktop) + VALIDATE_DESKTOP_FILE (geary-autostart.desktop) + else (DESKTOP_FILE_VALIDATE_FOUND) + message (FATAL_ERROR "desktop-file-validate must be installed to validate generated .desktop file") + endif (DESKTOP_FILE_VALIDATE_FOUND) +endif (DESKTOP_VALIDATE) + +install (FILES ${CMAKE_CURRENT_BINARY_DIR}/org.gnome.Geary.appdata.xml DESTINATION ${CMAKE_INSTALL_PREFIX}/share/metainfo) +install (FILES ${CMAKE_CURRENT_BINARY_DIR}/org.gnome.Geary.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications) +install (FILES ${CMAKE_CURRENT_BINARY_DIR}/geary-autostart.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications) +install (FILES ${CMAKE_CURRENT_BINARY_DIR}/geary-attach.contract DESTINATION ${CMAKE_INSTALL_PREFIX}/share/contractor) # Optional: run update-desktop-database at install time. # (This has to happen after the org.gnome.Geary.desktop file is installed.) @@ -48,8 +58,11 @@ if (DISABLE_CONTRACT) message (STATUS "Install Contractor contract: OFF") else (DISABLE_CONTRACT) message (STATUS "Install Contractor contract: ON") - if (INTLTOOL_MERGE_FOUND) - INTLTOOL_MERGE_CONTRACT (geary-attach.contract po) + if (GETTEXT_FOUND) + add_custom_target (geary-attach.contract ALL + ${GETTEXT_MSGFMT_EXECUTABLE} --desktop -d '${CMAKE_SOURCE_DIR}/po' + --template '${CMAKE_CURRENT_SOURCE_DIR}/geary-attach.contract.in' -o geary-attach.contract + ) # Can't validate Contractor file since it isn't a valid Desktop # file according to desktop-file-validate from desktop-file-utils 0.22: @@ -63,6 +76,6 @@ else (DISABLE_CONTRACT) # VALIDATE_DESKTOP_FILE (geary-attach.contract) # endif (DESKTOP_FILE_VALIDATE_FOUND) # endif (DESKTOP_VALIDATE) - endif (INTLTOOL_MERGE_FOUND) + endif (GETTEXT_FOUND) install (PROGRAMS geary-attach DESTINATION bin) endif (DISABLE_CONTRACT) diff --git a/desktop/geary-attach.contract.in b/desktop/geary-attach.contract.in index 1ebd4f46..298c6e0a 100644 --- a/desktop/geary-attach.contract.in +++ b/desktop/geary-attach.contract.in @@ -1,6 +1,6 @@ [Contractor Entry] -_Name=Send by email +Name=Send by email Icon=mail-send -_Description=Send files using Geary +Description=Send files using Geary MimeType=!inode; Exec=geary-attach %F diff --git a/desktop/geary-autostart.desktop.in b/desktop/geary-autostart.desktop.in index c6d5a6d1..c0b6dd2f 100644 --- a/desktop/geary-autostart.desktop.in +++ b/desktop/geary-autostart.desktop.in @@ -1,9 +1,9 @@ [Desktop Entry] -_Name=Geary -_GenericName=Email -_X-GNOME-FullName=Geary Mail -_Comment=Send and receive email -_Keywords=Email;E-mail;Mail; +Name=Geary +GenericName=Email +X-GNOME-FullName=Geary Mail +Comment=Send and receive email +Keywords=Email;E-mail;Mail; Icon=geary TryExec=geary Exec=geary --hidden diff --git a/desktop/org.gnome.Geary.appdata.xml.in b/desktop/org.gnome.Geary.appdata.xml.in index 02f1109d..3012cfb7 100644 --- a/desktop/org.gnome.Geary.appdata.xml.in +++ b/desktop/org.gnome.Geary.appdata.xml.in @@ -8,29 +8,29 @@ geary-list@gnome.org - <_name>Geary + Geary - <_developer_name>Geary Development Team + Geary Development Team - <_summary>Send and receive email + Send and receive email - <_p> +

Geary is an email application built around conversations, for the GNOME 3 desktop. It allows you to read, find and send email with a straightforward, modern interface. - - <_p> +

+

Conversations allow you to read a complete discussion without having to find and click from message to message. - - <_p>Geary’s features include: +

+

Geary’s features include:

    - <_li>Quick email account setup - <_li>Shows related messages together in conversations - <_li>Fast, full text and keyword search - <_li>Full-featured HTML and plain text message composer - <_li>Desktop notification of new mail - <_li>Compatible with GMail, Yahoo! Mail, Outlook.com and other IMAP servers +
  • Quick email account setup
  • +
  • Shows related messages together in conversations
  • +
  • Fast, full text and keyword search
  • +
  • Full-featured HTML and plain text message composer
  • +
  • Desktop notification of new mail
  • +
  • Compatible with GMail, Yahoo! Mail, Outlook.com and other IMAP servers
diff --git a/desktop/org.gnome.Geary.desktop.in b/desktop/org.gnome.Geary.desktop.in index 3935cbfb..62a4772e 100644 --- a/desktop/org.gnome.Geary.desktop.in +++ b/desktop/org.gnome.Geary.desktop.in @@ -1,10 +1,10 @@ [Desktop Entry] -_Name=Geary -_GenericName=Email -_X-GNOME-FullName=Geary Email -_Comment=Send and receive email +Name=Geary +GenericName=Email +X-GNOME-FullName=Geary Email +Comment=Send and receive email # Translators: These are desktop search terms. Do not translate semicolons, end line with a semicolon. -_Keywords=Mail;E-mail;IMAP;GMail;Yahoo;Hotmail;Outlook; +Keywords=Mail;E-mail;IMAP;GMail;Yahoo;Hotmail;Outlook; Icon=geary TryExec=geary Exec=geary %U @@ -16,5 +16,5 @@ StartupNotify=true Actions=Compose; [Desktop Action Compose] -_Name=Compose Message +Name=Compose Message Exec=geary mailto: diff --git a/po/POTFILES.in b/po/POTFILES.in index dea321b7..9f17fd59 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,8 +1,7 @@ -[encoding: UTF-8] desktop/org.gnome.Geary.appdata.xml.in desktop/org.gnome.Geary.desktop.in desktop/geary-autostart.desktop.in -[type: gettext/ini]desktop/geary-attach.contract.in +desktop/geary-attach.contract.in src/geary-version.vala.in src/client/accounts/account-dialog-account-list-pane.vala src/client/accounts/account-dialog-add-edit-pane.vala @@ -384,31 +383,31 @@ src/engine/util/util-time.vala src/engine/util/util-timeout-manager.vala src/engine/util/util-trillian.vala src/mailer/main.vala -[type: gettext/glade]ui/account_cannot_remove.glade -[type: gettext/glade]ui/account_list.glade -[type: gettext/glade]ui/account_spinner.glade -[type: gettext/glade]ui/certificate_warning_dialog.glade -[type: gettext/glade]ui/composer-headerbar.ui -[type: gettext/glade]ui/composer-link-popover.ui -[type: gettext/glade]ui/composer-menus.ui -[type: gettext/glade]ui/composer-widget.ui -[type: gettext/glade]ui/conversation-email.ui -[type: gettext/glade]ui/conversation-email-attachment-view.ui -[type: gettext/glade]ui/conversation-email-menus.ui -[type: gettext/glade]ui/conversation-message-menus.ui -[type: gettext/glade]ui/conversation-message.ui -[type: gettext/glade]ui/conversation-viewer.ui -[type: gettext/glade]ui/edit_alternate_emails.glade -[type: gettext/glade]ui/empty-placeholder.ui -[type: gettext/glade]ui/find_bar.glade -[type: gettext/glade]ui/folder-popover.ui -[type: gettext/glade]ui/gtk/help-overlay.ui -[type: gettext/glade]ui/gtk/menus.ui -[type: gettext/glade]ui/login.glade -[type: gettext/glade]ui/main-toolbar.ui -[type: gettext/glade]ui/main-toolbar-menus.ui -[type: gettext/glade]ui/main-window.ui -[type: gettext/glade]ui/password-dialog.glade -[type: gettext/glade]ui/preferences-dialog.ui -[type: gettext/glade]ui/remove_confirm.glade -[type: gettext/glade]ui/upgrade_dialog.glade +ui/account_cannot_remove.glade +ui/account_list.glade +ui/account_spinner.glade +ui/certificate_warning_dialog.glade +ui/composer-headerbar.ui +ui/composer-link-popover.ui +ui/composer-menus.ui +ui/composer-widget.ui +ui/conversation-email.ui +ui/conversation-email-attachment-view.ui +ui/conversation-email-menus.ui +ui/conversation-message-menus.ui +ui/conversation-message.ui +ui/conversation-viewer.ui +ui/edit_alternate_emails.glade +ui/empty-placeholder.ui +ui/find_bar.glade +ui/folder-popover.ui +ui/gtk/help-overlay.ui +ui/gtk/menus.ui +ui/login.glade +ui/main-toolbar.ui +ui/main-window.ui +ui/main-toolbar-menus.ui +ui/password-dialog.glade +ui/preferences-dialog.ui +ui/remove_confirm.glade +ui/upgrade_dialog.glade From fa1de0245e390334d8e21a1e7fc5a9c6486e26f3 Mon Sep 17 00:00:00 2001 From: Michael James Gratton Date: Thu, 26 Oct 2017 15:59:45 +1100 Subject: [PATCH 07/11] Add po/Makevars hint for Damned Lies (a.k.a. l10n.gnome.org). --- po/Makevars | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 po/Makevars diff --git a/po/Makevars b/po/Makevars new file mode 100644 index 00000000..bab9cb04 --- /dev/null +++ b/po/Makevars @@ -0,0 +1,79 @@ +# Makefile variables for PO directory in any package using GNU gettext. + +# Usually the message domain is the same as the package name. +DOMAIN = geary + +# These two variables depend on the location of this directory. +subdir = po +top_builddir = .. + +# These options get passed to xgettext. +# XXX Keep these in sync with CMakeLists.txt +XGETTEXT_OPTIONS = --from-code=UTF-8 --add-comments --keyword=_ --keyword=N_ --keyword=C_:1c,2 --keyword=NC_:1c,2 --keyword=g_dngettext:2,3 + +# This is the copyright holder that gets inserted into the header of the +# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding +# package. (Note that the msgstr strings, extracted from the package's +# sources, belong to the copyright holder of the package.) Translators are +# expected to transfer the copyright for their translations to this person +# or entity, or to disclaim their copyright. The empty string stands for +# the public domain; in this case the translators are expected to disclaim +# their copyright. +COPYRIGHT_HOLDER = Geary translation teams + +# This tells whether or not to prepend "GNU " prefix to the package +# name that gets inserted into the header of the $(DOMAIN).pot file. +# Possible values are "yes", "no", or empty. If it is empty, try to +# detect it automatically by scanning the files in $(top_srcdir) for +# "GNU packagename" string. +PACKAGE_GNU = no + +# This is the email address or URL to which the translators shall report +# bugs in the untranslated strings: +# - Strings which are not entire sentences, see the maintainer guidelines +# in the GNU gettext documentation, section 'Preparing Strings'. +# - Strings which use unclear terms or require additional context to be +# understood. +# - Strings which make invalid assumptions about notation of date, time or +# money. +# - Pluralisation problems. +# - Incorrect English spelling. +# - Incorrect formatting. +# It can be your email address, or a mailing list address where translators +# can write to without being subscribed, or the URL of a web page through +# which the translators can contact you. +MSGID_BUGS_ADDRESS = https://bugzilla.gnome.org/enter_bug.cgi?product=geary&keywords=I18N+L10N&component=internationalization + +# This is the list of locale categories, beyond LC_MESSAGES, for which the +# message catalogs shall be used. It is usually empty. +EXTRA_LOCALE_CATEGORIES = + +# This tells whether the $(DOMAIN).pot file contains messages with an 'msgctxt' +# context. Possible values are "yes" and "no". Set this to yes if the +# package uses functions taking also a message context, like pgettext(), or +# if in $(XGETTEXT_OPTIONS) you define keywords with a context argument. +USE_MSGCTXT = yes + +# These options get passed to msgmerge. +# Useful options are in particular: +# --previous to keep previous msgids of translated messages, +# --quiet to reduce the verbosity. +MSGMERGE_OPTIONS = + +# These options get passed to msginit. +# If you want to disable line wrapping when writing PO files, add +# --no-wrap to MSGMERGE_OPTIONS, XGETTEXT_OPTIONS, and +# MSGINIT_OPTIONS. +MSGINIT_OPTIONS = + +# This tells whether or not to regenerate a PO file when $(DOMAIN).pot +# has changed. Possible values are "yes" and "no". Set this to no if +# the POT file is checked in the repository and the version control +# program ignores timestamps. +PO_DEPENDS_ON_POT = no + +# This tells whether or not to forcibly update $(DOMAIN).pot and +# regenerate PO files on "make dist". Possible values are "yes" and +# "no". Set this to no if the POT file and PO files are maintained +# externally. +DIST_DEPENDS_ON_UPDATE_PO = no From 67daf4a048045d64e728305721b0e96f18b7cf10 Mon Sep 17 00:00:00 2001 From: Michael James Gratton Date: Thu, 26 Oct 2017 16:07:49 +1100 Subject: [PATCH 08/11] Re-enable top level cmake/Makefile "pot_file" target for po/geary.pot. --- Makefile.in | 2 +- cmake/FindXGettext.cmake | 29 ++++++++++++++++++++++++++++- po/CMakeLists.txt | 15 +++++++++++++-- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/Makefile.in b/Makefile.in index 61a97309..7d4b8084 100644 --- a/Makefile.in +++ b/Makefile.in @@ -24,7 +24,7 @@ uninstall: .PHONY: pot_file pot_file: @$(MAKE) -C $(BUILD_DIR) $@ - @cp build/src/geary.pot po + @cp build/po/geary.pot po .PHONY: clean clean: diff --git a/cmake/FindXGettext.cmake b/cmake/FindXGettext.cmake index c6437f85..a490d7e1 100644 --- a/cmake/FindXGettext.cmake +++ b/cmake/FindXGettext.cmake @@ -27,7 +27,6 @@ # Macros: # GETTEXT_CREATE_POT(potFile # [OPTION xgettext_options] -# SRC list_of_source_file_that_contains_msgid # ) # # Generate .pot file. @@ -94,6 +93,34 @@ ENDIF(NOT DEFINED XGETTEXT_OPTIONS) # Add translations target IF(XGETTEXT_FOUND) + MACRO(GETTEXT_CREATE_POT _potFile _pot_options ) + SET(_xgettext_options_list) + SET(_src_list) + SET(_src_list_abs) + FOREACH(_pot_option ${_pot_options} ${ARGN}) + IF(_pot_option STREQUAL "OPTION") + SET(_stage "OPTION") + ELSE(_pot_option STREQUAL "OPTION") + IF(_stage STREQUAL "OPTION") + SET(_xgettext_options_list ${_xgettext_options_list} ${_pot_option}) + ENDIF(_stage STREQUAL "OPTION") + ENDIF(_pot_option STREQUAL "OPTION") + ENDFOREACH(_pot_option ${_pot_options} ${ARGN}) + + IF (_xgettext_options_list) + SET(_xgettext_options ${_xgettext_options_list}) + ELSE(_xgettext_options_list) + SET(_xgettext_options ${XGETTEXT_OPTIONS}) + ENDIF(_xgettext_options_list) + + ADD_CUSTOM_TARGET(pot_file + COMMAND ${XGETTEXT_EXECUTABLE} ${_xgettext_options_list} -f po/POTFILES.in -o ${CMAKE_CURRENT_BINARY_DIR}/${_potFile} + DEPENDS "POTFILES.in" + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Extract translatable messages to ${_potFile}" + ) + ENDMACRO(GETTEXT_CREATE_POT _potFile _pot_options) + MACRO(GETTEXT_CREATE_TRANSLATIONS _firstLang) SET(_gmoFiles) SET(_addToAll) diff --git a/po/CMakeLists.txt b/po/CMakeLists.txt index 69ba6005..d6adb1b8 100644 --- a/po/CMakeLists.txt +++ b/po/CMakeLists.txt @@ -2,9 +2,20 @@ file(STRINGS "LINGUAS" TRANSLATED) IF (XGETTEXT_FOUND) + GETTEXT_CREATE_POT(geary.pot + OPTION + # XXX Keep these in sync with Makevars + --from-code=UTF-8 + --add-comments + --keyword=_ + --keyword=N_ + --keyword=C_:1c,2 + --keyword=NC_:1c,2 + --keyword=g_dngettext:2,3 + ) GETTEXT_CREATE_TRANSLATIONS(ALL ${TRANSLATED} - COMMENT "Creating translations.") + COMMENT "Creating translations." + ) ELSE () message(STATUS "xgettext not found") ENDIF() - From 49e33cb97916a500b9edec312ebb590e543e454d Mon Sep 17 00:00:00 2001 From: Michael James Gratton Date: Thu, 26 Oct 2017 16:21:46 +1100 Subject: [PATCH 09/11] Remove obsolete X-GNOME-FullName key from desktop files. --- desktop/geary-autostart.desktop.in | 1 - desktop/org.gnome.Geary.desktop.in | 1 - 2 files changed, 2 deletions(-) diff --git a/desktop/geary-autostart.desktop.in b/desktop/geary-autostart.desktop.in index c0b6dd2f..c91bbc8e 100644 --- a/desktop/geary-autostart.desktop.in +++ b/desktop/geary-autostart.desktop.in @@ -1,7 +1,6 @@ [Desktop Entry] Name=Geary GenericName=Email -X-GNOME-FullName=Geary Mail Comment=Send and receive email Keywords=Email;E-mail;Mail; Icon=geary diff --git a/desktop/org.gnome.Geary.desktop.in b/desktop/org.gnome.Geary.desktop.in index 62a4772e..d907aa7b 100644 --- a/desktop/org.gnome.Geary.desktop.in +++ b/desktop/org.gnome.Geary.desktop.in @@ -1,7 +1,6 @@ [Desktop Entry] Name=Geary GenericName=Email -X-GNOME-FullName=Geary Email Comment=Send and receive email # Translators: These are desktop search terms. Do not translate semicolons, end line with a semicolon. Keywords=Mail;E-mail;IMAP;GMail;Yahoo;Hotmail;Outlook; From bdbe1d6a288b9b2eb7bc427401ac4a443b58a8eb Mon Sep 17 00:00:00 2001 From: Michael James Gratton Date: Thu, 26 Oct 2017 17:07:36 +1100 Subject: [PATCH 10/11] Workaround xgettext not recognising Elementary Contractor files. We can't just simply run xgettext twice (once as normal, once with just the contractor file and passing "--desktop" as an arg, since l10n.gnome.org won't pick up the second pass and the contract will remain untranslated. So work around by renaming the contractor file such that it is recognised, then rename it back to normal when translating it. * desktop/geary-attach.contract.in: Renamed to desktop/geary-attach.contract.desktop.in. * desktop/CMakeLists.txt: Undo the rename when translating. * po/CMakeLists.txt, po/Makevars: Add a xgettext keyword for the contractor Description field. * po/POTFILES.in: Chase the file rename. --- desktop/CMakeLists.txt | 3 ++- ...ary-attach.contract.in => geary-attach.contract.desktop.in} | 0 po/CMakeLists.txt | 1 + po/Makevars | 2 +- po/POTFILES.in | 2 +- 5 files changed, 5 insertions(+), 3 deletions(-) rename desktop/{geary-attach.contract.in => geary-attach.contract.desktop.in} (100%) diff --git a/desktop/CMakeLists.txt b/desktop/CMakeLists.txt index 6f0d0b37..5ade054c 100644 --- a/desktop/CMakeLists.txt +++ b/desktop/CMakeLists.txt @@ -61,7 +61,8 @@ else (DISABLE_CONTRACT) if (GETTEXT_FOUND) add_custom_target (geary-attach.contract ALL ${GETTEXT_MSGFMT_EXECUTABLE} --desktop -d '${CMAKE_SOURCE_DIR}/po' - --template '${CMAKE_CURRENT_SOURCE_DIR}/geary-attach.contract.in' -o geary-attach.contract + --template '${CMAKE_CURRENT_SOURCE_DIR}/geary-attach.contract.desktop.in' -o geary-attach.contract + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/geary-attach.contract.desktop.in ) # Can't validate Contractor file since it isn't a valid Desktop diff --git a/desktop/geary-attach.contract.in b/desktop/geary-attach.contract.desktop.in similarity index 100% rename from desktop/geary-attach.contract.in rename to desktop/geary-attach.contract.desktop.in diff --git a/po/CMakeLists.txt b/po/CMakeLists.txt index d6adb1b8..88fddf45 100644 --- a/po/CMakeLists.txt +++ b/po/CMakeLists.txt @@ -12,6 +12,7 @@ IF (XGETTEXT_FOUND) --keyword=C_:1c,2 --keyword=NC_:1c,2 --keyword=g_dngettext:2,3 + --keyword=Description ) GETTEXT_CREATE_TRANSLATIONS(ALL ${TRANSLATED} COMMENT "Creating translations." diff --git a/po/Makevars b/po/Makevars index bab9cb04..b29f35ff 100644 --- a/po/Makevars +++ b/po/Makevars @@ -9,7 +9,7 @@ top_builddir = .. # These options get passed to xgettext. # XXX Keep these in sync with CMakeLists.txt -XGETTEXT_OPTIONS = --from-code=UTF-8 --add-comments --keyword=_ --keyword=N_ --keyword=C_:1c,2 --keyword=NC_:1c,2 --keyword=g_dngettext:2,3 +XGETTEXT_OPTIONS = --from-code=UTF-8 --add-comments --keyword=_ --keyword=N_ --keyword=C_:1c,2 --keyword=NC_:1c,2 --keyword=g_dngettext:2,3 --keyword=Description # This is the copyright holder that gets inserted into the header of the # $(DOMAIN).pot file. Set this to the copyright holder of the surrounding diff --git a/po/POTFILES.in b/po/POTFILES.in index 9f17fd59..c26838fc 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,7 +1,7 @@ desktop/org.gnome.Geary.appdata.xml.in desktop/org.gnome.Geary.desktop.in desktop/geary-autostart.desktop.in -desktop/geary-attach.contract.in +desktop/geary-attach.contract.desktop.in src/geary-version.vala.in src/client/accounts/account-dialog-account-list-pane.vala src/client/accounts/account-dialog-add-edit-pane.vala From 02376443979af7001c8044ffb57a7c612fb083f9 Mon Sep 17 00:00:00 2001 From: Michael James Gratton Date: Thu, 26 Oct 2017 17:08:29 +1100 Subject: [PATCH 11/11] Workaround a xgettext warning about a Vala verbatim comment. --- src/engine/util/util-js.vala | 16 ++++++++-------- test/engine/util-js-test.vala | 1 - 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/engine/util/util-js.vala b/src/engine/util/util-js.vala index 4d224297..4c5108eb 100644 --- a/src/engine/util/util-js.vala +++ b/src/engine/util/util-js.vala @@ -177,31 +177,31 @@ namespace Geary.JS { builder.append("\x00"); break; case '\'': - builder.append("""\'"""); + builder.append("\\\'"); break; case '"': - builder.append("""\""""); + builder.append("\\\""); break; case '\\': - builder.append("""\\"""); + builder.append("\\\\"); break; case '\n': - builder.append("""\n"""); + builder.append("\\n"); break; case '\r': - builder.append("""\r"""); + builder.append("\\r"); break; case '\x0b': builder.append("\x0b"); break; case '\t': - builder.append("""\t"""); + builder.append("\\t"); break; case '\b': - builder.append("""\b"""); + builder.append("\\b"); break; case '\f': - builder.append("""\f"""); + builder.append("\\f"); break; default: builder.append_unichar(c); diff --git a/test/engine/util-js-test.vala b/test/engine/util-js-test.vala index e9081058..eb087cc8 100644 --- a/test/engine/util-js-test.vala +++ b/test/engine/util-js-test.vala @@ -13,7 +13,6 @@ public class Geary.JS.Test : Gee.TestCase { } public void escape_string() { - print("\ndata: %s\n", Geary.JS.escape_string("\n")); assert(Geary.JS.escape_string("\n") == """\n"""); assert(Geary.JS.escape_string("\r") == """\r"""); assert(Geary.JS.escape_string("\t") == """\t""");