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.
This commit is contained in:
Michael James Gratton 2017-01-04 13:54:07 +11:00
parent 159e6c9bae
commit 20db6e57dd
3 changed files with 129 additions and 94 deletions

View file

@ -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<string, string> action_accelerators = new Gee.HashMultiMap<string, string>();
@ -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<WebKit.ContextMenuItem> existing_spelling =
new Gee.LinkedList<WebKit.ContextMenuItem>();
Gee.List<WebKit.ContextMenuItem> existing_text_entry =
new Gee.LinkedList<WebKit.ContextMenuItem>();
// // 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();
}
}

View file

@ -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<weak Gtk.Widget> 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);
}

View file

@ -72,42 +72,60 @@
</menu>
<menu id="context_menu_model">
<section id="context_menu_webkit_spelling"/>
<section>
<item>
<attribute name="label" translatable="yes">_Undo</attribute>
<attribute name="action">cme.undo</attribute>
<attribute name="action">cmp.undo</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_Redo</attribute>
<attribute name="action">cme.redo</attribute>
<attribute name="action">cmp.redo</attribute>
</item>
</section>
<section>
<section id="context_menu_rich_text">
<item>
<attribute name="label" translatable="yes">Cu_t</attribute>
<attribute name="action">cme.cut</attribute>
<attribute name="action">cmp.cut</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_Copy</attribute>
<attribute name="action">cme.copy</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Copy _Link</attribute>
<attribute name="action">cme.copy-link</attribute>
<attribute name="action">cmp.copy</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_Paste</attribute>
<attribute name="action">cme.paste</attribute>
<attribute name="action">cmp.paste</attribute>
</item>
<item>
<attribute name="label" translatable="yes" context="Clipboard paste with rich text">Paste _With Formatting</attribute>
<attribute name="action">cme.paste-with-formatting</attribute>
<attribute name="action">cmp.paste-with-formatting</attribute>
</item>
</section>
<section id="context_menu_plain_text">
<item>
<attribute name="label" translatable="yes">Cu_t</attribute>
<attribute name="action">cmp.cut</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_Copy</attribute>
<attribute name="action">cmp.copy</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_Paste</attribute>
<attribute name="action">cmp.paste</attribute>
</item>
</section>
<section>
<item>
<attribute name="label" translatable="yes">Select _All</attribute>
<attribute name="action">cme.select-all</attribute>
<attribute name="action">cmp.select-all</attribute>
</item>
</section>
<section id="context_menu_webkit_text_entry"/>
<section id="context_menu_inspector">
<item>
<attribute name="label" translatable="yes">_Inspect…</attribute>
<attribute name="action">cmp.open_inspector</attribute>
</item>
</section>
</menu>