From 20db6e57ddf4c6feef7ef5deb80fc0c66daff319 Mon Sep 17 00:00:00 2001 From: Michael James Gratton Date: Wed, 4 Jan 2017 13:54:07 +1100 Subject: [PATCH] Re-enable custom composer context menu, add WK text entry items to it. * src/client/composer/composer-widget.vala (ComposerWidget::action_entries): Add an entry for the inspector so it can be loaded via the context model in the same way as other items. (ComposerWidget::context_menu_*): Keep track of a number of the context menu's sections, so we can selectively include them in the WK context menu. (ComposerWidget::on_context_menu): Re-implement to work wth the WK2 context menu model, which annoyingly doesn't even extend any of the GtkMenu infrastrcuture. * src/client/util/util-gtk.vala (menu_foreach): Pass the action target through to the loop's delegate, it's a bit more useful for dealing with the WK2 model. (add_g_menu_to_gtk_menu): Removed, WebKitContextMenu doesn't extend Gtk.Menu any longer so this is is no longer needed. * ui/composer-menus.ui: Reoganise to use standard composer action prefix. Duplicate rich and plain text clipboard sections so they can just be selectively enabled. Include dummy sections for WK items to specify where they are located in the menu, and a section for the inspector. --- src/client/composer/composer-widget.vala | 130 ++++++++++++++++------- src/client/util/util-gtk.vala | 51 ++------- ui/composer-menus.ui | 42 +++++--- 3 files changed, 129 insertions(+), 94 deletions(-) diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala index 9d3c2080..31de05ea 100644 --- a/src/client/composer/composer-widget.vala +++ b/src/client/composer/composer-widget.vala @@ -77,6 +77,7 @@ public class ComposerWidget : Gtk.EventBox { private const string ACTION_ADD_ATTACHMENT = "add-attachment"; private const string ACTION_ADD_ORIGINAL_ATTACHMENTS = "add-original-attachments"; private const string ACTION_SELECT_DICTIONARY = "select-dictionary"; + private const string ACTION_OPEN_INSPECTOR = "open_inspector"; private const string[] html_actions = { ACTION_BOLD, ACTION_ITALIC, ACTION_UNDERLINE, ACTION_STRIKETHROUGH, ACTION_FONT_SIZE, @@ -118,6 +119,7 @@ public class ComposerWidget : Gtk.EventBox { {ACTION_ADD_ATTACHMENT, on_add_attachment }, {ACTION_ADD_ORIGINAL_ATTACHMENTS, on_pending_attachments }, {ACTION_SELECT_DICTIONARY, on_select_dictionary }, + {ACTION_OPEN_INSPECTOR, on_open_inspector }, }; public static Gee.MultiMap action_accelerators = new Gee.HashMultiMap(); @@ -298,7 +300,13 @@ public class ComposerWidget : Gtk.EventBox { private Menu html_menu; private Menu plain_menu; + private Menu context_menu_model; + private Menu context_menu_rich_text; + private Menu context_menu_plain_text; + private Menu context_menu_webkit_spelling; + private Menu context_menu_webkit_text_entry; + private Menu context_menu_inspector; private SpellCheckPopover? spell_check_popover = null; private string? hover_url = null; @@ -401,6 +409,11 @@ public class ComposerWidget : Gtk.EventBox { this.html_menu = (Menu) builder.get_object("html_menu_model"); this.plain_menu = (Menu) builder.get_object("plain_menu_model"); this.context_menu_model = (Menu) builder.get_object("context_menu_model"); + this.context_menu_rich_text = (Menu) builder.get_object("context_menu_rich_text"); + this.context_menu_plain_text = (Menu) builder.get_object("context_menu_plain_text"); + this.context_menu_inspector = (Menu) builder.get_object("context_menu_inspector"); + this.context_menu_webkit_spelling = (Menu) builder.get_object("context_menu_webkit_spelling"); + this.context_menu_webkit_text_entry = (Menu) builder.get_object("context_menu_webkit_text_entry"); this.subject_entry.bind_property("text", this, "window-title", BindingFlags.SYNC_CREATE, (binding, source_value, ref target_value) => { @@ -1843,53 +1856,88 @@ public class ComposerWidget : Gtk.EventBox { } private bool on_context_menu(WebKit.WebView view, - WebKit.ContextMenu default_menu, + WebKit.ContextMenu context_menu, Gdk.Event event, WebKit.HitTestResult hit_test_result) { - // Gtk.Menu context_menu = (Gtk.Menu) default_menu; + // This is a three step process: + // 1. Work out what existing menu items exist that we want to keep + // 2. Clear the existing menu + // 3. Rebuild it based on our GMenu specification - // // Keep the spelling menu items - // foreach (weak Gtk.Widget child in context_menu.get_children()) { - // Gtk.MenuItem item = (Gtk.MenuItem) child; - // WebKit.ContextMenuAction action = WebKit.context_menu_item_get_action(item); + // Step 1. - // const WebKit.ContextMenuAction[] spelling_actions = { - // WebKit.ContextMenuAction.SPELLING_GUESS, - // WebKit.ContextMenuAction.IGNORE_SPELLING, - // WebKit.ContextMenuAction.LEARN_SPELLING - // }; + const WebKit.ContextMenuAction[] SPELLING_ACTIONS = { + WebKit.ContextMenuAction.SPELLING_GUESS, + WebKit.ContextMenuAction.NO_GUESSES_FOUND, + WebKit.ContextMenuAction.IGNORE_SPELLING, + WebKit.ContextMenuAction.IGNORE_GRAMMAR, + WebKit.ContextMenuAction.LEARN_SPELLING, + }; + const WebKit.ContextMenuAction[] TEXT_INPUT_ACTIONS = { + WebKit.ContextMenuAction.INPUT_METHODS, + WebKit.ContextMenuAction.UNICODE, + }; - // if (!(action in spelling_actions)) - // context_menu.remove(item); - // } + Gee.List existing_spelling = + new Gee.LinkedList(); + Gee.List existing_text_entry = + new Gee.LinkedList(); - // // Add our own Menu (but don't add formatting actions if they are disabled). - // context_menu.insert_action_group("cme", this.actions); - // GtkUtil.add_g_menu_to_gtk_menu(context_menu, context_menu_model, (label, detailed_action_name) => { - // string action_name; - // Variant? target; - // try { - // Action.parse_detailed_name(detailed_action_name, out action_name, out target); - // if ("." in action_name) // Remove possible prefixes - // action_name = action_name.split(".")[1]; - // } catch (GLib.Error e) { - // debug("Couldn't parse action \"%s\" in context menu".printf(detailed_action_name)); - // } - // return !(action_name in html_actions) || (this.actions.get_action_enabled(action_name)); - // }); + foreach (WebKit.ContextMenuItem item in context_menu.get_items()) { + if (item.get_stock_action() in SPELLING_ACTIONS) { + existing_spelling.add(item); + } else if (item.get_stock_action() in TEXT_INPUT_ACTIONS) { + existing_text_entry.add(item); + } + } - // if (Args.inspector) { - // Gtk.MenuItem inspect_item = new Gtk.MenuItem.with_mnemonic(_("_Inspect")); - // inspect_item.activate.connect(() => { - // this.editor.get_inspector().show(); - // }); - // context_menu.append(new Gtk.SeparatorMenuItem()); - // context_menu.append(inspect_item); - // } + // Step 2. - // context_menu.show_all(); - // update_actions(); - return false; + context_menu.remove_all(); + + // Step 3. + + GtkUtil.menu_foreach(context_menu_model, (label, name, target, section) => { + if (context_menu.last() != null) { + context_menu.append(new WebKit.ContextMenuItem.separator()); + } + + if (section == this.context_menu_webkit_spelling) { + foreach (WebKit.ContextMenuItem item in existing_spelling) + context_menu.append(item); + } else if (section == this.context_menu_webkit_text_entry) { + foreach (WebKit.ContextMenuItem item in existing_text_entry) + context_menu.append(item); + } else if (section == this.context_menu_rich_text) { + if (this.editor.is_rich_text) + append_menu_section(context_menu, section); + } else if (section == this.context_menu_plain_text) { + if (!this.editor.is_rich_text) + append_menu_section(context_menu, section); + } else if (section == this.context_menu_inspector) { + if (Args.inspector) + append_menu_section(context_menu, section); + } else { + append_menu_section(context_menu, section); + } + }); + + return Gdk.EVENT_PROPAGATE; + } + + private inline void append_menu_section(WebKit.ContextMenu context_menu, + Menu section) { + GtkUtil.menu_foreach(section, (label, name, target, section) => { + if ("." in name) + name = name.split(".")[1]; + + Gtk.Action action = new Gtk.Action(name, label, null, null); + action.set_sensitive(get_action(name).enabled); + action.activate.connect((action) => { + this.actions.activate_action(name, target); + }); + context_menu.append(new WebKit.ContextMenuItem(action)); + }); } private void on_select_dictionary(SimpleAction action, Variant? param) { @@ -2243,4 +2291,8 @@ public class ComposerWidget : Gtk.EventBox { link_dialog("http://"); } + private void on_open_inspector(SimpleAction action, Variant? param) { + this.editor.get_inspector().show(); + } + } diff --git a/src/client/util/util-gtk.vala b/src/client/util/util-gtk.vala index ae205e1b..8c7107cf 100644 --- a/src/client/util/util-gtk.vala +++ b/src/client/util/util-gtk.vala @@ -146,61 +146,26 @@ void menu_foreach(Menu menu, MenuForeachFunc foreach_func) { Variant? label = menu.get_item_attribute_value(i, Menu.ATTRIBUTE_LABEL, VariantType.STRING); Variant? action_name = menu.get_item_attribute_value(i, Menu.ATTRIBUTE_ACTION, VariantType.STRING); Variant? action_target = menu.get_item_attribute_value(i, Menu.ATTRIBUTE_TARGET, VariantType.STRING); - string detailed_action_name = null; - if (action_name != null) - detailed_action_name = Action.print_detailed_name(action_name.get_string(), action_target); // Check if the child is a section Menu? section = (Menu) menu.get_item_link(i, Menu.LINK_SECTION); // Callback - foreach_func((label != null) ? label.get_string() : null - , detailed_action_name - , section); + foreach_func((label != null) ? label.get_string() : null, + (action_name != null) ? action_name.get_string() : null, + action_target, + section); } } /* * Used for menu_foreach() + * @param id - The id if one was set * @param label - The label if one was set - * @param detailed_action_name - The action if it was set + * @param action_name - The action name, if set + * @param action_target - The action target, if set * @param section - If the item represents a section, this will return that section (or null otherwise) */ -delegate void MenuForeachFunc(string? label, string? detailed_action_name, Menu? section); - -/** - * Adds the entries of a GLib.Menu to a Gtk.Menu - * @param gtk_menu - The Gtk.Menu to add entries to. - * @param g_menu - The Menu whose entries should be added - * @param filter - If this function returns false for a menu item, it will not be added to the menu - */ -void add_g_menu_to_gtk_menu(Gtk.Menu gtk_menu, Menu g_menu, MenuItemFilterFunc filter) { - menu_foreach(g_menu, (label, action_name, section) => { - List children = gtk_menu.get_children(); - weak Gtk.Widget? last_child = (children.length() > 0) ? children.last().data : null; - - if (section != null) { - // Section. Set separators (without getting double separators) - if ((children.length() > 0) && ((last_child as Gtk.SeparatorMenuItem) == null)) - gtk_menu.add(new Gtk.SeparatorMenuItem()); - - add_g_menu_to_gtk_menu(gtk_menu, section, filter); - - if ((children.length() > 0) && ((last_child as Gtk.SeparatorMenuItem) == null)) - gtk_menu.add(new Gtk.SeparatorMenuItem()); - } else { - // Regular menu-item - if (filter(label, action_name) && label != null) { - Gtk.MenuItem item = new Gtk.MenuItem.with_mnemonic(label); - if (action_name != null) - item.set_detailed_action_name(action_name); - gtk_menu.add(item); - } - } - }); -} - -// Used for add_g_menu_to_gtk_menu() -delegate bool MenuItemFilterFunc(string? label, string? detailed_action_name); +delegate void MenuForeachFunc(string? label, string? action_name, Variant? target, Menu? section); } diff --git a/ui/composer-menus.ui b/ui/composer-menus.ui index 56f0a006..f9f7c64a 100644 --- a/ui/composer-menus.ui +++ b/ui/composer-menus.ui @@ -72,42 +72,60 @@ +
_Undo - cme.undo + cmp.undo _Redo - cme.redo + cmp.redo
-
+
Cu_t - cme.cut + cmp.cut _Copy - cme.copy - - - Copy _Link - cme.copy-link + cmp.copy _Paste - cme.paste + cmp.paste Paste _With Formatting - cme.paste-with-formatting + cmp.paste-with-formatting + +
+
+ + Cu_t + cmp.cut + + + _Copy + cmp.copy + + + _Paste + cmp.paste
Select _All - cme.select-all + cmp.select-all + +
+
+
+ + _Inspect… + cmp.open_inspector