[evolution/webkit-composer: 36/130] Initial support for custom context menu



commit cb56fc8946405e66e4d3225086051bba157efd93
Author: Dan VrÃtil <dvratil redhat com>
Date:   Mon Aug 13 14:45:42 2012 +0200

    Initial support for custom context menu

 e-util/e-editor-widget.c |   41 +++++++++++
 e-util/e-editor-widget.h |    5 +-
 e-util/e-editor.c        |  166 ++++++++++++++++++++++++++++++++++++++++++++++
 e-util/e-editor.h        |    3 +
 4 files changed, 214 insertions(+), 1 deletions(-)
---
diff --git a/e-util/e-editor-widget.c b/e-util/e-editor-widget.c
index 6b648c0..eb6d78d 100644
--- a/e-util/e-editor-widget.c
+++ b/e-util/e-editor-widget.c
@@ -25,6 +25,7 @@
 #include "e-emoticon-chooser.h"
 
 #include <e-util/e-util.h>
+#include <e-util/e-marshal.h>
 #include <glib/gi18n-lib.h>
 #include <gdk/gdkkeysyms.h>
 
@@ -70,6 +71,13 @@ enum {
 	PROP_CAN_UNDO
 };
 
+enum {
+	POPUP_EVENT,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
 static WebKitDOMRange *
 editor_widget_get_dom_range (EEditorWidget *widget)
 {
@@ -152,6 +160,28 @@ editor_widget_selection_changed_cb (EEditorWidget *widget,
 	}
 }
 
+static gboolean
+editor_widget_button_press_event (GtkWidget *gtk_widget,
+				  GdkEventButton *event)
+{
+	gboolean event_handled;
+
+	if (event->button != 3) {
+		event_handled = FALSE;
+	} else {
+		g_signal_emit (
+			gtk_widget, signals[POPUP_EVENT],
+			0, event, &event_handled);
+	}
+
+	if (event_handled) {
+		return TRUE;
+	}
+
+	/* Chain up to parent implementation */
+	return GTK_WIDGET_CLASS (e_editor_widget_parent_class)->button_press_event (gtk_widget, event);
+}
+
 /* Based on original use_pictograms() from GtkHTML */
 static const gchar *emoticons_chars =
 	/*  0 */ "DO)(|/PQ*!"
@@ -476,6 +506,7 @@ e_editor_widget_class_init (EEditorWidgetClass *klass)
 	object_class->finalize = e_editor_widget_finalize;
 
 	widget_class = GTK_WIDGET_CLASS (klass);
+	widget_class->button_press_event = editor_widget_button_press_event;
 	widget_class->key_release_event = editor_widget_key_release_event;
 
 	klass->paste_clipboard_quoted = editor_widget_paste_clipboard_quoted;
@@ -585,6 +616,16 @@ e_editor_widget_class_init (EEditorWidgetClass *klass)
 			NULL,
 			FALSE,
 			G_PARAM_READABLE));
+
+	signals[POPUP_EVENT] = g_signal_new (
+		"popup-event",
+		G_TYPE_FROM_CLASS (klass),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (EEditorWidgetClass, popup_event),
+		g_signal_accumulator_true_handled, NULL,
+		e_marshal_BOOLEAN__BOXED,
+		G_TYPE_BOOLEAN, 1,
+		GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
 }
 
 static void
diff --git a/e-util/e-editor-widget.h b/e-util/e-editor-widget.h
index 487c1a0..1f08c4b 100644
--- a/e-util/e-editor-widget.h
+++ b/e-util/e-editor-widget.h
@@ -73,7 +73,10 @@ struct _EEditorWidget {
 struct _EEditorWidgetClass {
 	WebKitWebViewClass parent_class;
 
-	void	(*paste_clipboard_quoted)	(EEditorWidget *widget);
+	void		(*paste_clipboard_quoted)	(EEditorWidget *widget);
+
+	gboolean	(*popup_event)			(EEditorWidget *widget,
+							 GdkEventButton *event);
 };
 
 GType		e_editor_widget_get_type 	(void);
diff --git a/e-util/e-editor.c b/e-util/e-editor.c
index 238f166..892623f 100644
--- a/e-util/e-editor.c
+++ b/e-util/e-editor.c
@@ -21,6 +21,7 @@
 
 #include "e-editor.h"
 #include "e-editor-private.h"
+#include "e-editor-utils.h"
 
 #include <glib/gi18n-lib.h>
 
@@ -34,6 +35,157 @@ enum {
 	PROP_FILENAME
 };
 
+enum {
+	UPDATE_ACTIONS,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+editor_update_actions (EEditor *editor,
+		       GdkEventButton *event)
+{
+	WebKitWebView *webview;
+	WebKitHitTestResult *hit_test;
+	WebKitHitTestResultContext context;
+	WebKitDOMNode *node;
+	EEditorWidget *widget;
+	GtkUIManager *manager;
+	GtkActionGroup *action_group;
+	GList *list;
+	gboolean visible;
+	guint merge_id;
+
+	widget = e_editor_get_editor_widget (editor);
+	webview = WEBKIT_WEB_VIEW (widget);
+	manager = e_editor_get_ui_manager (editor);
+
+	/* Update context menu item visibility. */
+	hit_test = webkit_web_view_get_hit_test_result (webview, event);
+	g_object_get (
+		G_OBJECT (hit_test),
+		"context", &context,
+	        "inner-node", &node, NULL);
+
+	visible = (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE);
+	gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_IMAGE), visible);
+
+	visible = (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK);
+	gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_LINK), visible);
+
+	visible = (WEBKIT_DOM_IS_HTMLHR_ELEMENT (node));
+	gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_RULE), visible);
+
+	visible = (webkit_dom_node_get_node_type (node) == 3);
+	gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_TEXT), visible);
+
+	visible =
+		gtk_action_get_visible (ACTION (CONTEXT_PROPERTIES_IMAGE)) ||
+		gtk_action_get_visible (ACTION (CONTEXT_PROPERTIES_LINK)) ||
+		gtk_action_get_visible (ACTION (CONTEXT_PROPERTIES_TEXT));
+	gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_PARAGRAPH), visible);
+
+	/* Set to visible if any of these are true:
+	 *   - Selection is active and contains a link.
+	 *   - Cursor is on a link.
+	 *   - Cursor is on an image that has a URL or target.
+	 */
+	visible = (e_editor_dom_node_get_parent_element (
+			node, WEBKIT_TYPE_DOM_HTML_ANCHOR_ELEMENT) != NULL);
+	gtk_action_set_visible (ACTION (CONTEXT_REMOVE_LINK), visible);
+
+
+	visible = (e_editor_dom_node_get_parent_element (
+			node, WEBKIT_TYPE_DOM_HTML_TABLE_CELL_ELEMENT) != NULL);
+	gtk_action_set_visible (ACTION (CONTEXT_DELETE_CELL), visible);
+	gtk_action_set_visible (ACTION (CONTEXT_DELETE_COLUMN), visible);
+	gtk_action_set_visible (ACTION (CONTEXT_DELETE_ROW), visible);
+	gtk_action_set_visible (ACTION (CONTEXT_DELETE_TABLE), visible);
+	gtk_action_set_visible (ACTION (CONTEXT_INSERT_COLUMN_AFTER), visible);
+	gtk_action_set_visible (ACTION (CONTEXT_INSERT_COLUMN_BEFORE), visible);
+	gtk_action_set_visible (ACTION (CONTEXT_INSERT_ROW_ABOVE), visible);
+	gtk_action_set_visible (ACTION (CONTEXT_INSERT_ROW_BELOW), visible);
+	gtk_action_set_visible (ACTION (CONTEXT_INSERT_TABLE), visible);
+	gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_CELL), visible);
+
+	/* Note the |= (cursor must be in a table cell). */
+	visible |= (e_editor_dom_node_get_parent_element (
+			node, WEBKIT_TYPE_DOM_HTML_TABLE_ELEMENT) != NULL);
+	gtk_action_set_visible (ACTION (CONTEXT_PROPERTIES_TABLE), visible);
+
+	/********************** Spell Check Suggestions **********************/
+
+	/* FIXME WEBKIT No spellcheching for now
+	object = html->engine->cursor->object;
+	action_group = editor->priv->suggestion_actions;
+
+	// Remove the old content from the context menu.
+	merge_id = editor->priv->spell_suggestions_merge_id;
+	if (merge_id > 0) {
+		gtk_ui_manager_remove_ui (manager, merge_id);
+		editor->priv->spell_suggestions_merge_id = 0;
+	}
+
+	// Clear the action group for spelling suggestions.
+	list = gtk_action_group_list_actions (action_group);
+	while (list != NULL) {
+		GtkAction *action = list->data;
+
+		gtk_action_group_remove_action (action_group, action);
+		list = g_list_delete_link (list, list);
+	}
+
+	// Decide if we should show spell checking items.
+	visible =
+		!html_engine_is_selection_active (html->engine) &&
+		object != NULL && html_object_is_text (object) &&
+		!html_engine_spell_word_is_valid (html->engine);
+	action_group = editor->priv->spell_check_actions;
+	gtk_action_group_set_visible (action_group, visible);
+
+	// Exit early if spell checking items are invisible.
+	if (!visible)
+		return;
+
+	list = editor->priv->active_spell_checkers;
+	merge_id = gtk_ui_manager_new_merge_id (manager);
+	editor->priv->spell_suggestions_merge_id = merge_id;
+
+	// Handle a single active language as a special case.
+	if (g_list_length (list) == 1) {
+		editor_inline_spelling_suggestions (editor, list->data);
+		return;
+	}
+
+	// Add actions and context menu content for active languages
+	g_list_foreach (list, (GFunc) editor_spell_checkers_foreach, editor);
+	*/
+}
+
+static gboolean
+editor_show_popup (EEditor *editor,
+		   GdkEventButton *event,
+		   gpointer user_data)
+{
+	GtkWidget *menu;
+
+	menu = e_editor_get_managed_widget (editor, "/context-menu");
+
+	g_signal_emit(editor, signals[UPDATE_ACTIONS], 0, event);
+
+	if (event != NULL)
+		gtk_menu_popup (
+			GTK_MENU (menu), NULL, NULL, NULL,
+			user_data, event->button, event->time);
+	else
+		gtk_menu_popup (
+			GTK_MENU (menu), NULL, NULL, NULL,
+			user_data, 0, gtk_get_current_event_time ());
+
+	return TRUE;
+}
+
 static gchar *
 editor_find_ui_file (const gchar *basename)
 {
@@ -145,6 +297,8 @@ editor_constructed (GObject *object)
 	widget = GTK_WIDGET (e_editor_get_editor_widget (editor));
 	gtk_container_add (GTK_CONTAINER (priv->scrolled_window), widget);
 	gtk_widget_show (widget);
+	g_signal_connect_swapped (widget, "popup-event",
+		G_CALLBACK (editor_show_popup), editor);
 
 	/* Add some combo boxes to the "edit" toolbar. */
 
@@ -333,6 +487,8 @@ e_editor_class_init (EEditorClass *klass)
 	object_class->constructed = editor_constructed;
 	object_class->dispose = editor_dispose;
 
+	klass->update_actions = editor_update_actions;
+
 	g_object_class_install_property (
 		object_class,
 		PROP_FILENAME,
@@ -342,6 +498,16 @@ e_editor_class_init (EEditorClass *klass)
 		        NULL,
 		        NULL,
 		        G_PARAM_READWRITE));
+
+	signals[UPDATE_ACTIONS] = g_signal_new (
+		"update-actions",
+		G_TYPE_FROM_CLASS (klass),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (EEditorClass, update_actions),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__BOXED,
+		G_TYPE_NONE, 1,
+		GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
 }
 
 static void
diff --git a/e-util/e-editor.h b/e-util/e-editor.h
index 9b878eb..b39e6a1 100644
--- a/e-util/e-editor.h
+++ b/e-util/e-editor.h
@@ -58,6 +58,9 @@ struct _EEditor {
 
 struct _EEditorClass {
 	GtkBoxClass parent_class;
+
+	void		(*update_actions)	(EEditor *editor,
+						 GdkEventButton *event);
 };
 
 GType		e_editor_get_type		(void);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]