[evolution/webkit-composer: 4/130] Import GtkhtmlFace* classes as EEmoticon*



commit 468f91acaed053d29cf575f1fe3b980ae8e790d7
Author: Dan VrÃtil <dvratil redhat com>
Date:   Mon Jul 23 14:34:15 2012 +0200

    Import GtkhtmlFace* classes as EEmoticon*
    
    GtkhtmlFaceAction => EEmoticonAction
    GtkhtmlFaceChooserMenu => EEmoticonChooserMenu
    GtkhtmlFaceChooser => EEmoticonChooser
    GtkhtmlToolButton => EEmoticonToolButton
    GtkhtmlFace => EEmoticon

 e-util/Makefile.am               |   10 +
 e-util/e-emoticon-action.c       |  295 ++++++++++++++++
 e-util/e-emoticon-action.h       |   69 ++++
 e-util/e-emoticon-chooser-menu.c |  206 +++++++++++
 e-util/e-emoticon-chooser-menu.h |   65 ++++
 e-util/e-emoticon-chooser.c      |  180 ++++++++++
 e-util/e-emoticon-chooser.h      |   69 ++++
 e-util/e-emoticon-tool-button.c  |  711 ++++++++++++++++++++++++++++++++++++++
 e-util/e-emoticon-tool-button.h  |   71 ++++
 e-util/e-emoticon.c              |   89 +++++
 e-util/e-emoticon.h              |   48 +++
 e-util/e-util.h                  |    5 +
 12 files changed, 1818 insertions(+), 0 deletions(-)
---
diff --git a/e-util/Makefile.am b/e-util/Makefile.am
index a47d29e..9390b89 100644
--- a/e-util/Makefile.am
+++ b/e-util/Makefile.am
@@ -171,6 +171,11 @@ eutilinclude_HEADERS =  \
 	e-dialog-widgets.h \
 	e-editor-selection.h \
 	e-editor-widget.h \
+	e-emoticon-action.h \
+	e-emoticon-chooser-menu.h \
+	e-emoticon-chooser.h \
+	e-emoticon-tool-button.h \
+	e-emoticon.h \
 	e-event.h \
 	e-file-request.h \
 	e-file-utils.h \
@@ -419,6 +424,11 @@ libeutil_la_SOURCES = \
 	e-dialog-widgets.c \
 	e-editor-selection.c \
 	e-editor-widget.c \
+	e-emoticon-action.c \
+	e-emoticon-chooser-menu.c \
+	e-emoticon-chooser.c \
+	e-emoticon-tool-button.c \
+	e-emoticon.c \
 	e-event.c \
 	e-file-request.c \
 	e-file-utils.c \
diff --git a/e-util/e-emoticon-action.c b/e-util/e-emoticon-action.c
new file mode 100644
index 0000000..7ff674c
--- /dev/null
+++ b/e-util/e-emoticon-action.c
@@ -0,0 +1,295 @@
+/* e-emoticon-action.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "e-emoticon-action.h"
+
+#include "e-emoticon-chooser.h"
+#include "e-emoticon-chooser-menu.h"
+#include "e-emoticon-tool-button.h"
+
+struct _EEmoticonActionPrivate {
+	GList *choosers;
+	EEmoticonChooser *current_chooser;
+};
+
+enum {
+	PROP_0,
+	PROP_CURRENT_FACE
+};
+
+static gpointer parent_class;
+
+static void
+emoticon_action_proxy_item_activated_cb (EEmoticonAction *action,
+					 EEmoticonChooser *chooser)
+{
+	action->priv->current_chooser = chooser;
+
+	g_signal_emit_by_name (action, "item-activated");
+}
+
+static void
+emoticon_action_set_property (GObject *object,
+			      guint property_id,
+			      const GValue *value,
+			      GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_CURRENT_FACE:
+			e_emoticon_chooser_set_current_emoticon (
+				E_EMOTICON_CHOOSER (object),
+				g_value_get_boxed (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+emoticon_action_get_property (GObject *object,
+			      guint property_id,
+			      GValue *value,
+			      GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_CURRENT_FACE:
+			g_value_set_boxed (
+				value, e_emoticon_chooser_get_current_emoticon (
+				E_EMOTICON_CHOOSER (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+emoticon_action_finalize (GObject *object)
+{
+	EEmoticonActionPrivate *priv;
+
+	priv = E_EMOTICON_ACTION (object)->priv;
+
+	g_list_free (priv->choosers);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+emoticon_action_activate (GtkAction *action)
+{
+	EEmoticonActionPrivate *priv;
+
+	priv = E_EMOTICON_ACTION (action)->priv;
+
+	priv->current_chooser = NULL;
+}
+
+static GtkWidget *
+emoticon_action_create_menu_item (GtkAction *action)
+{
+	GtkWidget *item;
+	GtkWidget *menu;
+
+	item = gtk_image_menu_item_new ();
+	menu = gtk_action_create_menu (action);
+	gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
+	gtk_widget_show (menu);
+
+	return item;
+}
+
+static GtkWidget *
+emoticon_action_create_tool_item (GtkAction *action)
+{
+	return GTK_WIDGET (e_emoticon_tool_button_new ());
+}
+
+static void
+emoticon_action_connect_proxy (GtkAction *action,
+			       GtkWidget *proxy)
+{
+	EEmoticonActionPrivate *priv;
+
+	priv = E_EMOTICON_ACTION (action)->priv;
+
+	if (!E_IS_EMOTICON_CHOOSER (proxy))
+		goto chainup;
+
+	if (g_list_find (priv->choosers, proxy) != NULL)
+		goto chainup;
+
+	g_signal_connect_swapped (
+		proxy, "item-activated",
+		G_CALLBACK (emoticon_action_proxy_item_activated_cb), action);
+
+chainup:
+	/* Chain up to parent's connect_proxy() method. */
+	GTK_ACTION_CLASS (parent_class)->connect_proxy (action, proxy);
+}
+
+static void
+emoticon_action_disconnect_proxy (GtkAction *action,
+				  GtkWidget *proxy)
+{
+	EEmoticonActionPrivate *priv;
+
+	priv = E_EMOTICON_ACTION (action)->priv;
+
+	priv->choosers = g_list_remove (priv->choosers, proxy);
+
+	/* Chain up to parent's disconnect_proxy() method. */
+	GTK_ACTION_CLASS (parent_class)->disconnect_proxy (action, proxy);
+}
+
+static GtkWidget *
+emoticon_action_create_menu (GtkAction *action)
+{
+	EEmoticonActionPrivate *priv;
+	GtkWidget *widget;
+
+	priv = E_EMOTICON_ACTION (action)->priv;
+
+	widget = e_emoticon_chooser_menu_new ();
+
+	g_signal_connect_swapped (
+		widget, "item-activated",
+		G_CALLBACK (emoticon_action_proxy_item_activated_cb), action);
+
+	priv->choosers = g_list_prepend (priv->choosers, widget);
+
+	return widget;
+}
+
+static EEmoticon *
+emoticon_action_get_current_emoticon (EEmoticonChooser *chooser)
+{
+	EEmoticonActionPrivate *priv;
+	EEmoticon *emoticon = NULL;
+
+	priv = E_EMOTICON_ACTION (chooser)->priv;
+
+	if (priv->current_chooser != NULL)
+		emoticon = e_emoticon_chooser_get_current_emoticon (
+			priv->current_chooser);
+
+	return emoticon;
+}
+
+static void
+emoticon_action_set_current_emoticon (EEmoticonChooser *chooser,
+				      EEmoticon *emoticon)
+{
+	EEmoticonActionPrivate *priv;
+	GList *iter;
+
+	priv = E_EMOTICON_ACTION (chooser)->priv;
+
+	for (iter = priv->choosers; iter != NULL; iter = iter->next) {
+		EEmoticonChooser *proxy_chooser = iter->data;
+
+		e_emoticon_chooser_set_current_emoticon (proxy_chooser, emoticon);
+	}
+}
+
+static void
+emoticon_action_class_init (EEmoticonActionClass *class)
+{
+	GObjectClass *object_class;
+	GtkActionClass *action_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (EEmoticonAction));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = emoticon_action_set_property;
+	object_class->get_property = emoticon_action_get_property;
+	object_class->finalize = emoticon_action_finalize;
+
+	action_class = GTK_ACTION_CLASS (class);
+	action_class->activate = emoticon_action_activate;
+	action_class->create_menu_item = emoticon_action_create_menu_item;
+	action_class->create_tool_item = emoticon_action_create_tool_item;
+	action_class->connect_proxy = emoticon_action_connect_proxy;
+	action_class->disconnect_proxy = emoticon_action_disconnect_proxy;
+	action_class->create_menu = emoticon_action_create_menu;
+
+	g_object_class_override_property (
+		object_class, PROP_CURRENT_FACE, "current-emoticon");
+}
+
+static void
+emoticon_action_iface_init (EEmoticonChooserIface *iface)
+{
+	iface->get_current_emoticon = emoticon_action_get_current_emoticon;
+	iface->set_current_emoticon = emoticon_action_set_current_emoticon;
+}
+
+static void
+emoticon_action_init (EEmoticonAction *action)
+{
+	action->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+		action, E_TYPE_EMOTICON_ACTION, EEmoticonActionPrivate);
+}
+
+GType
+e_emoticon_action_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EEmoticonActionClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) emoticon_action_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EEmoticonAction),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) emoticon_action_init,
+			NULL   /* value_table */
+		};
+
+		static const GInterfaceInfo iface_info = {
+			(GInterfaceInitFunc) emoticon_action_iface_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL  /* interemoticon_data */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_ACTION, "EEmoticonAction", &type_info, 0);
+
+		g_type_add_interface_static (
+			type, E_TYPE_EMOTICON_CHOOSER, &iface_info);
+	}
+
+	return type;
+}
+
+GtkAction *
+e_emoticon_action_new (const gchar *name,
+		       const gchar *label,
+		       const gchar *tooltip,
+		       const gchar *stock_id)
+{
+	g_return_val_if_fail (name != NULL, NULL);
+
+	return g_object_new (
+		E_TYPE_EMOTICON_ACTION, "name", name, "label", label,
+		"tooltip", tooltip, "stock-id", stock_id, NULL);
+}
diff --git a/e-util/e-emoticon-action.h b/e-util/e-emoticon-action.h
new file mode 100644
index 0000000..388b851
--- /dev/null
+++ b/e-util/e-emoticon-action.h
@@ -0,0 +1,69 @@
+/* e-emoticon-action.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_EMOTICON_ACTION_H
+#define E_EMOTICON_ACTION_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_EMOTICON_ACTION \
+	(e_emoticon_action_get_type ())
+#define E_EMOTICON_ACTION(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_EMOTICON_ACTION, EEmoticonAction))
+#define E_EMOTICON_ACTION_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_EMOTICON_ACTION, EEmoticonActionClass))
+#define E_IS_EMOTICON_ACTION(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_EMOTICON_ACTION))
+#define E_IS_EMOTICON_ACTION_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_EMOTICON_ACTION))
+#define E_EMOTICON_ACTION_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_EMOTICON_ACTION, EEmoticonActionClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EEmoticonAction EEmoticonAction;
+typedef struct _EEmoticonActionClass EEmoticonActionClass;
+typedef struct _EEmoticonActionPrivate EEmoticonActionPrivate;
+
+struct _EEmoticonAction {
+	GtkAction parent;
+	EEmoticonActionPrivate *priv;
+};
+
+struct _EEmoticonActionClass {
+	GtkActionClass parent_class;
+};
+
+GType		e_emoticon_action_get_type	(void);
+GtkAction *	e_emoticon_action_new		(const gchar *name,
+						 const gchar *label,
+						 const gchar *tooltip,
+						 const gchar *stock_id);
+
+G_END_DECLS
+
+#endif /* E_EMOTICON_ACTION_H */
diff --git a/e-util/e-emoticon-chooser-menu.c b/e-util/e-emoticon-chooser-menu.c
new file mode 100644
index 0000000..619bd21
--- /dev/null
+++ b/e-util/e-emoticon-chooser-menu.c
@@ -0,0 +1,206 @@
+/* e-emoticon-chooser-menu.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-emoticon-chooser-menu.h"
+#include "e-emoticon-chooser.h"
+
+#include <glib/gi18n-lib.h>
+
+enum {
+	PROP_0,
+	PROP_CURRENT_FACE
+};
+
+static gpointer parent_class;
+
+static void
+emoticon_chooser_menu_set_property (GObject *object,
+				    guint property_id,
+				    const GValue *value,
+				    GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_CURRENT_FACE:
+			e_emoticon_chooser_set_current_emoticon (
+				E_EMOTICON_CHOOSER (object),
+				g_value_get_boxed (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+emoticon_chooser_menu_get_property (GObject *object,
+				    guint property_id,
+				    GValue *value,
+				    GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_CURRENT_FACE:
+			g_value_set_boxed (
+				value,
+				e_emoticon_chooser_get_current_emoticon (
+				E_EMOTICON_CHOOSER (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static EEmoticon *
+emoticon_chooser_menu_get_current_emoticon (EEmoticonChooser *chooser)
+{
+	GtkWidget *item;
+
+	item = gtk_menu_get_active (GTK_MENU (chooser));
+	if (item == NULL)
+		return NULL;
+
+	return g_object_get_data (G_OBJECT (item), "emoticon");
+}
+
+static void
+emoticon_chooser_menu_set_current_emoticon (EEmoticonChooser *chooser,
+					    EEmoticon *emoticon)
+{
+	GList *list, *iter;
+
+	list = gtk_container_get_children (GTK_CONTAINER (chooser));
+
+	for (iter = list; iter != NULL; iter = iter->next) {
+		GtkWidget *item = iter->data;
+		EEmoticon *candidate;
+
+		candidate = g_object_get_data (G_OBJECT (item), "emoticon");
+		if (candidate == NULL)
+			continue;
+
+		if (e_emoticon_equal (emoticon, candidate)) {
+			gtk_menu_shell_activate_item (
+				GTK_MENU_SHELL (chooser), item, TRUE);
+			break;
+		}
+	}
+
+	g_list_free (list);
+}
+
+static void
+emoticon_chooser_menu_class_init (EEmoticonChooserMenuClass *class)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (class);
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = emoticon_chooser_menu_set_property;
+	object_class->get_property = emoticon_chooser_menu_get_property;
+
+	g_object_class_override_property (
+		object_class, PROP_CURRENT_FACE, "current-emoticon");
+}
+
+static void
+emoticon_chooser_menu_iface_init (EEmoticonChooserIface *iface)
+{
+	iface->get_current_emoticon = emoticon_chooser_menu_get_current_emoticon;
+	iface->set_current_emoticon = emoticon_chooser_menu_set_current_emoticon;
+}
+
+static void
+emoticon_chooser_menu_init (EEmoticonChooserMenu *chooser_menu)
+{
+	EEmoticonChooser *chooser;
+	GList *list, *iter;
+
+	chooser = E_EMOTICON_CHOOSER (chooser_menu);
+	list = e_emoticon_chooser_get_items (chooser);
+
+	for (iter = list; iter != NULL; iter = iter->next) {
+		EEmoticon *face = iter->data;
+		GtkWidget *item;
+
+		/* To keep translated strings in subclasses */
+		item = gtk_image_menu_item_new_with_mnemonic (_(face->label));
+		gtk_image_menu_item_set_image (
+			GTK_IMAGE_MENU_ITEM (item),
+			gtk_image_new_from_icon_name (
+			face->icon_name, GTK_ICON_SIZE_MENU));
+		gtk_widget_show (item);
+
+		g_object_set_data_full (
+			G_OBJECT (item), "face",
+			e_emoticon_copy (face),
+			(GDestroyNotify) e_emoticon_free);
+
+		g_signal_connect_swapped (
+			item, "activate",
+			G_CALLBACK (e_emoticon_chooser_item_activated),
+			chooser);
+
+		gtk_menu_shell_append (GTK_MENU_SHELL (chooser_menu), item);
+	}
+
+	g_list_free (list);
+}
+
+GType
+e_emoticon_chooser_menu_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EEmoticonChooserMenuClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) emoticon_chooser_menu_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EEmoticonChooserMenu),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) emoticon_chooser_menu_init,
+			NULL   /* value_table */
+		};
+
+		static const GInterfaceInfo iface_info = {
+			(GInterfaceInitFunc) emoticon_chooser_menu_iface_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL  /* interface_data */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_MENU, "EEmoticonChooserMenu",
+			&type_info, 0);
+
+		g_type_add_interface_static (
+			type, E_TYPE_EMOTICON_CHOOSER, &iface_info);
+	}
+
+	return type;
+}
+
+GtkWidget *
+e_emoticon_chooser_menu_new (void)
+{
+	return g_object_new (E_TYPE_EMOTICON_CHOOSER_MENU, NULL);
+}
diff --git a/e-util/e-emoticon-chooser-menu.h b/e-util/e-emoticon-chooser-menu.h
new file mode 100644
index 0000000..bbed9aa
--- /dev/null
+++ b/e-util/e-emoticon-chooser-menu.h
@@ -0,0 +1,65 @@
+/* e-emoticon-chooser-menu.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_EMOTICON_CHOOSER_MENU_H
+#define E_EMOTICON_CHOOSER_MENU_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_EMOTICON_CHOOSER_MENU \
+	(e_emoticon_chooser_menu_get_type ())
+#define E_EMOTICON_CHOOSER_MENU(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_EMOTICON_CHOOSER_MENU, EEmoticonChooserMenu))
+#define E_EMOTICON_CHOOSER_MENU_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_EMOTICON_CHOOSER_MENU, EEmoticonChooserMenuClass))
+#define E_IS_EMOTICON_CHOOSER_MENU(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_EMOTICON_CHOOSER_MENU))
+#define E_IS_EMOTICON_CHOOSER_MENU_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_EMOTICON_CHOOSER_MENU))
+#define E_EMOTICON_CHOOSER_MENU_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_EMOTICON_CHOOSER_MENU, EEmoticonChooserMenuClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EEmoticonChooserMenu EEmoticonChooserMenu;
+typedef struct _EEmoticonChooserMenuClass EEmoticonChooserMenuClass;
+typedef struct _EEmoticonChooserMenuPrivate EEmoticonChooserMenuPrivate;
+
+struct _EEmoticonChooserMenu {
+	GtkMenu parent;
+};
+
+struct _EEmoticonChooserMenuClass {
+	GtkMenuClass parent_class;
+};
+
+GType		e_emoticon_chooser_menu_get_type	(void);
+GtkWidget *	e_emoticon_chooser_menu_new		(void);
+
+G_END_DECLS
+
+#endif /* E_EMOTICON_CHOOSER_MENU_H */
diff --git a/e-util/e-emoticon-chooser.c b/e-util/e-emoticon-chooser.c
new file mode 100644
index 0000000..c8a92b0
--- /dev/null
+++ b/e-util/e-emoticon-chooser.c
@@ -0,0 +1,180 @@
+/* e-emoticon-chooser.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-emoticon-chooser.h"
+
+#include <glib/gi18n-lib.h>
+
+/* Constant version of EEMoticon. */
+typedef struct {
+	const gchar *label;
+	const gchar *icon_name;
+	const gchar *text_face;
+} ConstantEmoticon;
+
+static ConstantEmoticon available_emoticons[] = {
+	/* Translators: :-) */
+	{ N_("_Smile"),		"face-smile",		":-)"	},
+	/* Translators: :-( */
+	{ N_("S_ad"),		"face-sad",		":-("	},
+	/* Translators: ;-) */
+	{ N_("_Wink"),		"face-wink",		";-)"	},
+	/* Translators: :-P */
+	{ N_("Ton_gue"),	"face-raspberry",	":-P"	},
+	/* Translators: :-)) */
+	{ N_("Laug_h"),		"face-laugh",		":-))"	},
+	/* Translators: :-| */
+	{ N_("_Plain"),		"face-plain",		":-|"	},
+	/* Translators: :-! */
+	{ N_("Smi_rk"),		"face-smirk",		":-!"	},
+	/* Translators: :"-) */
+	{ N_("_Embarrassed"),	"face-embarrassed",	":\"-)"	},
+	/* Translators: :-D */
+	{ N_("_Big Smile"),	"face-smile-big",	":-D"	},
+	/* Translators: :-/ */
+	{ N_("Uncer_tain"),	"face-uncertain",	":-/"	},
+	/* Translators: :-O */
+	{ N_("S_urprise"),	"face-surprise",	":-O"	},
+	/* Translators: :-S */
+	{ N_("W_orried"),	"face-worried",		":-S"	},
+	/* Translators: :-* */
+	{ N_("_Kiss"),		"face-kiss",		":-*"	},
+	/* Translators: X-( */
+	{ N_("A_ngry"),		"face-angry",		"X-("	},
+	/* Translators: B-) */
+	{ N_("_Cool"),		"face-cool",		"B-)"	},
+	/* Translators: O:-) */
+	{ N_("Ange_l"),		"face-angel",		"O:-)"	},
+	/* Translators: :'( */
+	{ N_("Cr_ying"),	"face-crying",		":'("	},
+	/* Translators: :-Q */
+	{ N_("S_ick"),		"face-sick",		":-Q"	},
+	/* Translators: |-) */
+	{ N_("Tire_d"),		"face-tired",		"|-)"	},
+	/* Translators: >:-) */
+	{ N_("De_vilish"),	"face-devilish",	">:-)"	},
+	/* Translators: :-(|) */
+	{ N_("_Monkey"),	"face-monkey",		":-(|)"	}
+};
+
+enum {
+	ITEM_ACTIVATED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+static void
+emoticon_chooser_class_init (EEmoticonChooserIface *iface)
+{
+	g_object_interface_install_property (
+		iface,
+		g_param_spec_boxed (
+			"current-emoticon",
+			"Current Emoticon",
+			"Currently selected emoticon",
+			E_TYPE_EMOTICON,
+			G_PARAM_READWRITE));
+
+	signals[ITEM_ACTIVATED] = g_signal_new (
+		"item-activated",
+		G_TYPE_FROM_INTERFACE (iface),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (EEmoticonChooserIface, item_activated),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+}
+
+GType
+e_emoticon_chooser_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EEmoticonChooserIface),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) emoticon_chooser_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			0,     /* instance_size */
+			0,     /* n_preallocs */
+			NULL,  /* instance_init */
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			G_TYPE_INTERFACE, "EEmoticonChooser", &type_info, 0);
+
+		g_type_interface_add_prerequisite (type, G_TYPE_OBJECT);
+	}
+
+	return type;
+}
+
+EEmoticon *
+e_emoticon_chooser_get_current_emoticon (EEmoticonChooser *chooser)
+{
+	EEmoticonChooserIface *iface;
+
+	g_return_val_if_fail (E_IS_EMOTICON_CHOOSER (chooser), NULL);
+
+	iface = E_EMOTICON_CHOOSER_GET_IFACE (chooser);
+	g_return_val_if_fail (iface->get_current_emoticon != NULL, NULL);
+
+	return iface->get_current_emoticon (chooser);
+}
+
+void
+e_emoticon_chooser_set_current_emoticon (EEmoticonChooser *chooser,
+					 EEmoticon *emoticon)
+{
+	EEmoticonChooserIface *iface;
+
+	g_return_if_fail (E_IS_EMOTICON_CHOOSER (chooser));
+
+	iface = E_EMOTICON_CHOOSER_GET_IFACE (chooser);
+	g_return_if_fail (iface->set_current_emoticon != NULL);
+
+	iface->set_current_emoticon (chooser, emoticon);
+}
+
+void
+e_emoticon_chooser_item_activated (EEmoticonChooser *chooser)
+{
+	g_return_if_fail (E_IS_EMOTICON_CHOOSER (chooser));
+
+	g_signal_emit (chooser, signals[ITEM_ACTIVATED], 0);
+}
+
+GList *
+e_emoticon_chooser_get_items (EEmoticonChooser *chooser)
+{
+	GList *list = NULL;
+	gint ii;
+
+	for (ii = 0; ii < G_N_ELEMENTS (available_emoticons); ii++)
+		list = g_list_prepend (list, &available_emoticons[ii]);
+
+	return g_list_reverse (list);
+}
diff --git a/e-util/e-emoticon-chooser.h b/e-util/e-emoticon-chooser.h
new file mode 100644
index 0000000..7390da3
--- /dev/null
+++ b/e-util/e-emoticon-chooser.h
@@ -0,0 +1,69 @@
+/* e-emoticon-chooser.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_EMOTICON_CHOOSER_H
+#define E_EMOTICON_CHOOSER_H
+
+#include <e-util/e-emoticon.h>
+
+/* Standard GObject macros */
+#define E_TYPE_EMOTICON_CHOOSER \
+	(e_emoticon_chooser_get_type ())
+#define E_EMOTICON_CHOOSER(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_EMOTICON_CHOOSER, EEmoticonChooser))
+#define E_IS_EMOTICON_CHOOSER(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_EMOTICON_CHOOSER))
+#define E_EMOTICON_CHOOSER_GET_IFACE(obj) \
+	(G_TYPE_INSTANCE_GET_INTERFACE \
+	((obj), E_TYPE_EMOTICON_CHOOSER, EEmoticonChooserIface))
+
+G_BEGIN_DECLS
+
+typedef struct _EEmoticonChooser EEmoticonChooser;
+typedef struct _EEmoticonChooserIface EEmoticonChooserIface;
+
+struct _EEmoticonChooserIface {
+	GTypeInterface parent_iface;
+
+	/* Methods */
+	EEmoticon *	(*get_current_emoticon)	(EEmoticonChooser *chooser);
+	void		(*set_current_emoticon)	(EEmoticonChooser *chooser,
+						 EEmoticon *emoticon);
+
+	/* Signals */
+	void		(*item_activated)	(EEmoticonChooser *chooser);
+};
+
+GType		e_emoticon_chooser_get_type	(void);
+EEmoticon *	e_emoticon_chooser_get_current_emoticon
+						(EEmoticonChooser *chooser);
+void		e_emoticon_chooser_set_current_emoticon
+						(EEmoticonChooser *chooser,
+						 EEmoticon *emoticon);
+void		e_emoticon_chooser_item_activated
+						(EEmoticonChooser *chooser);
+GList *		e_emoticon_chooser_get_items	(EEmoticonChooser *chooser);
+
+G_END_DECLS
+
+#endif /* E_EMOTICON_CHOOSER_H */
diff --git a/e-util/e-emoticon-tool-button.c b/e-util/e-emoticon-tool-button.c
new file mode 100644
index 0000000..dc0d0dd
--- /dev/null
+++ b/e-util/e-emoticon-tool-button.c
@@ -0,0 +1,711 @@
+/* e-emoticon-tool-button.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-emoticon-tool-button.h"
+
+/* XXX The "button" aspects of this widget are based heavily on the
+ *     GtkComboBox tree-view implementation.  Consider splitting it
+ *     into a reusable "button-with-an-empty-window" widget. */
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "e-emoticon-chooser.h"
+
+/* XXX Should calculate this dynamically. */
+#define NUM_ROWS	7
+#define NUM_COLS	3
+
+enum {
+	PROP_0,
+	PROP_CURRENT_EMOTICON,
+	PROP_POPUP_SHOWN
+};
+
+enum {
+	POPUP,
+	POPDOWN,
+	LAST_SIGNAL
+};
+
+struct _EEmoticonToolButtonPrivate {
+	GtkWidget *active_button;  /* not referenced */
+	GtkWidget *table;
+	GtkWidget *window;
+
+	guint popup_shown	: 1;
+	guint popup_in_progress	: 1;
+	GdkDevice *grab_keyboard;
+	GdkDevice *grab_mouse;
+};
+
+static gpointer parent_class;
+static guint signals[LAST_SIGNAL];
+
+/* XXX Copied from _gtk_toolbar_elide_underscores() */
+static gchar *
+emoticon_tool_button_elide_underscores (const gchar *original)
+{
+	gchar *q, *result;
+	const gchar *p, *end;
+	gsize len;
+	gboolean last_underscore;
+
+	if (!original)
+		return NULL;
+
+	len = strlen (original);
+	q = result = g_malloc (len + 1);
+	last_underscore = FALSE;
+
+	end = original + len;
+	for (p = original; p < end; p++) {
+		if (!last_underscore && *p == '_')
+			last_underscore = TRUE;
+		else {
+			last_underscore = FALSE;
+			if (original + 2 <= p && p + 1 <= end &&
+				p[-2] == '(' && p[-1] == '_' &&
+				p[0] != '_' && p[1] == ')') {
+				q--;
+				*q = '\0';
+				p++;
+			} else
+				*q++ = *p;
+		}
+	}
+
+	if (last_underscore)
+		*q++ = '_';
+
+	*q = '\0';
+
+	return result;
+}
+
+static void
+emoticon_tool_button_reposition_window (EEmoticonToolButton *button)
+{
+	GdkScreen *screen;
+	GdkWindow *window;
+	GdkRectangle monitor;
+	GtkAllocation allocation;
+	gint monitor_num;
+	gint x, y, width, height;
+
+	screen = gtk_widget_get_screen (GTK_WIDGET (button));
+	window = gtk_widget_get_window (GTK_WIDGET (button));
+	monitor_num = gdk_screen_get_monitor_at_window (screen, window);
+	gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+	gdk_window_get_origin (window, &x, &y);
+
+	if (!gtk_widget_get_has_window (GTK_WIDGET (button))) {
+		gtk_widget_get_allocation (GTK_WIDGET (button), &allocation);
+		x += allocation.x;
+		y += allocation.y;
+	}
+
+	gtk_widget_get_allocation (button->priv->window, &allocation);
+	width = allocation.width;
+	height = allocation.height;
+
+	x = CLAMP (x, monitor.x, monitor.x + monitor.width - width);
+	y = CLAMP (y, monitor.y, monitor.y + monitor.height - height);
+
+	gtk_window_move (GTK_WINDOW (button->priv->window), x, y);
+}
+
+static void
+emoticon_tool_button_emoticon_clicked_cb (EEmoticonToolButton *button,
+					  GtkWidget *emoticon_button)
+{
+	button->priv->active_button = emoticon_button;
+	e_emoticon_tool_button_popdown (button);
+}
+
+static gboolean
+emoticon_tool_button_emoticon_release_event_cb (EEmoticonToolButton *button,
+						GdkEventButton *event,
+						GtkButton *emoticon_button)
+{
+	GtkStateType state;
+
+	state = gtk_widget_get_state (GTK_WIDGET (button));
+
+	if (state != GTK_STATE_NORMAL)
+		gtk_button_clicked (emoticon_button);
+
+	return FALSE;
+}
+
+static gboolean
+emoticon_tool_button_button_release_event_cb (EEmoticonToolButton *button,
+					      GdkEventButton *event)
+{
+	GtkToggleToolButton *tool_button;
+	GtkWidget *event_widget;
+	gboolean popup_in_progress;
+
+	tool_button = GTK_TOGGLE_TOOL_BUTTON (button);
+	event_widget = gtk_get_event_widget ((GdkEvent *) event);
+
+	popup_in_progress = button->priv->popup_in_progress;
+	button->priv->popup_in_progress = FALSE;
+
+	if (event_widget != GTK_WIDGET (button))
+		goto popdown;
+
+	if (popup_in_progress)
+		return FALSE;
+
+	if (gtk_toggle_tool_button_get_active (tool_button))
+		goto popdown;
+
+	return FALSE;
+
+popdown:
+	e_emoticon_tool_button_popdown (button);
+
+	return TRUE;
+}
+
+static void
+emoticon_tool_button_child_show_cb (EEmoticonToolButton *button)
+{
+	button->priv->popup_shown = TRUE;
+	g_object_notify (G_OBJECT (button), "popup-shown");
+}
+
+static void
+emoticon_tool_button_child_hide_cb (EEmoticonToolButton *button)
+{
+	button->priv->popup_shown = FALSE;
+	g_object_notify (G_OBJECT (button), "popup-shown");
+}
+
+static gboolean
+emoticon_tool_button_child_key_press_event_cb (EEmoticonToolButton *button,
+					       GdkEventKey *event)
+{
+	GtkWidget *window = button->priv->window;
+
+	if (!gtk_bindings_activate_event (G_OBJECT (window), event))
+		gtk_bindings_activate_event (G_OBJECT (button), event);
+
+	return TRUE;
+}
+
+static void
+emoticon_tool_button_set_property (GObject *object,
+				   guint property_id,
+				   const GValue *value,
+				   GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_CURRENT_EMOTICON:
+			e_emoticon_chooser_set_current_emoticon (
+				E_EMOTICON_CHOOSER (object),
+				g_value_get_boxed (value));
+			return;
+
+		case PROP_POPUP_SHOWN:
+			if (g_value_get_boolean (value))
+				e_emoticon_tool_button_popup (
+					E_EMOTICON_TOOL_BUTTON (object));
+			else
+				e_emoticon_tool_button_popdown (
+					E_EMOTICON_TOOL_BUTTON (object));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+emoticon_tool_button_get_property (GObject *object,
+				   guint property_id,
+				   GValue *value,
+				   GParamSpec *pspec)
+{
+	EEmoticonToolButtonPrivate *priv;
+
+	priv = E_EMOTICON_TOOL_BUTTON (object)->priv;
+
+	switch (property_id) {
+		case PROP_CURRENT_EMOTICON:
+			g_value_set_boxed (
+				value,
+				e_emoticon_chooser_get_current_emoticon (
+				E_EMOTICON_CHOOSER (object)));
+			return;
+
+		case PROP_POPUP_SHOWN:
+			g_value_set_boolean (value, priv->popup_shown);
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+emoticon_tool_button_dispose (GObject *object)
+{
+	EEmoticonToolButtonPrivate *priv;
+
+	priv = E_EMOTICON_TOOL_BUTTON (object)->priv;
+
+	if (priv->window != NULL) {
+		g_object_unref (priv->window);
+		priv->window = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static gboolean
+emoticon_tool_button_press_event (GtkWidget *widget,
+                              GdkEventButton *event)
+{
+	EEmoticonToolButton *button;
+	GtkToggleToolButton *toggle_button;
+	GtkWidget *event_widget;
+
+	button = E_EMOTICON_TOOL_BUTTON (widget);
+
+	event_widget = gtk_get_event_widget ((GdkEvent *) event);
+
+	if (event_widget == button->priv->window)
+		return TRUE;
+
+	if (event_widget != widget)
+		return FALSE;
+
+	toggle_button = GTK_TOGGLE_TOOL_BUTTON (widget);
+	if (gtk_toggle_tool_button_get_active (toggle_button))
+		return FALSE;
+
+	e_emoticon_tool_button_popup (button);
+
+	button->priv->popup_in_progress = TRUE;
+
+	return TRUE;
+}
+
+static void
+emoticon_tool_button_toggled (GtkToggleToolButton *button)
+{
+	if (gtk_toggle_tool_button_get_active (button))
+		e_emoticon_tool_button_popup (
+			E_EMOTICON_TOOL_BUTTON (button));
+	else
+		e_emoticon_tool_button_popdown (
+			E_EMOTICON_TOOL_BUTTON (button));
+}
+
+static void
+emoticon_tool_button_popup (EEmoticonToolButton *button)
+{
+	GtkToggleToolButton *tool_button;
+	GdkWindow *window;
+	gboolean grab_status;
+	GdkDevice *device, *mouse, *keyboard;
+	guint32 activate_time;
+
+	device = gtk_get_current_event_device ();
+	g_return_if_fail (device != NULL);
+
+	if (!gtk_widget_get_realized (GTK_WIDGET (button)))
+		return;
+
+	if (button->priv->popup_shown)
+		return;
+
+	activate_time = gtk_get_current_event_time ();
+	if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD) {
+		keyboard = device;
+		mouse = gdk_device_get_associated_device (device);
+	} else {
+		keyboard = gdk_device_get_associated_device (device);
+		mouse = device;
+	}
+
+	/* Position the window over the button. */
+	emoticon_tool_button_reposition_window (button);
+
+	/* Show the pop-up. */
+	gtk_widget_show (button->priv->window);
+	gtk_widget_grab_focus (button->priv->window);
+
+	/* Activate the tool button. */
+	tool_button = GTK_TOGGLE_TOOL_BUTTON (button);
+	gtk_toggle_tool_button_set_active (tool_button, TRUE);
+
+	/* Try to grab the pointer and keyboard. */
+	window = gtk_widget_get_window (button->priv->window);
+	grab_status = !keyboard ||
+		gdk_device_grab (keyboard, window,
+			GDK_OWNERSHIP_WINDOW, TRUE,
+			GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+			NULL, activate_time) == GDK_GRAB_SUCCESS;
+	if (grab_status) {
+		grab_status = !mouse ||
+			gdk_device_grab (mouse, window,
+				GDK_OWNERSHIP_WINDOW, TRUE,
+				GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK,
+				NULL, activate_time) == GDK_GRAB_SUCCESS;
+		if (!grab_status && keyboard)
+			gdk_device_ungrab (keyboard, activate_time);
+	}
+
+	if (grab_status) {
+		gtk_device_grab_add (button->priv->window, mouse, TRUE);
+		button->priv->grab_keyboard = keyboard;
+		button->priv->grab_mouse = mouse;
+	} else {
+		gtk_widget_hide (button->priv->window);
+	}
+}
+
+static void
+emoticon_tool_button_popdown (EEmoticonToolButton *button)
+{
+	GtkToggleToolButton *tool_button;
+
+	if (!gtk_widget_get_realized (GTK_WIDGET (button)))
+		return;
+
+	if (!button->priv->popup_shown)
+		return;
+
+	/* Hide the pop-up. */
+	gtk_device_grab_remove (button->priv->window, button->priv->grab_mouse);
+	gtk_widget_hide (button->priv->window);
+
+	/* Deactivate the tool button. */
+	tool_button = GTK_TOGGLE_TOOL_BUTTON (button);
+	gtk_toggle_tool_button_set_active (tool_button, FALSE);
+
+	if (button->priv->grab_keyboard)
+		gdk_device_ungrab (button->priv->grab_keyboard, GDK_CURRENT_TIME);
+	if (button->priv->grab_mouse)
+		gdk_device_ungrab (button->priv->grab_mouse, GDK_CURRENT_TIME);
+
+	button->priv->grab_keyboard = NULL;
+	button->priv->grab_mouse = NULL;
+}
+
+static EEmoticon *
+emoticon_tool_button_get_current_emoticon (EEmoticonChooser *chooser)
+{
+	EEmoticonToolButtonPrivate *priv;
+
+	priv = E_EMOTICON_TOOL_BUTTON (chooser)->priv;
+
+	if (priv->active_button == NULL)
+		return NULL;
+
+	return g_object_get_data (G_OBJECT (priv->active_button), "emoticon");
+}
+
+static void
+emoticon_tool_button_set_current_emoticon (EEmoticonChooser *chooser,
+					   EEmoticon *emoticon)
+{
+	EEmoticonToolButtonPrivate *priv;
+	GList *list, *iter;
+
+	priv = E_EMOTICON_TOOL_BUTTON (chooser)->priv;
+
+	list = gtk_container_get_children (GTK_CONTAINER (priv->table));
+
+	for (iter = list; iter != NULL; iter = iter->next) {
+		GtkWidget *item = iter->data;
+		EEmoticon *candidate;
+
+		candidate = g_object_get_data (G_OBJECT (item), "emoticon");
+		if (candidate == NULL)
+			continue;
+
+		if (e_emoticon_equal (emoticon, candidate)) {
+			gtk_button_clicked (GTK_BUTTON (item));
+			break;
+		}
+	}
+
+	g_list_free (list);
+}
+
+static void
+emoticon_tool_button_class_init (EEmoticonToolButtonClass *class)
+{
+	GObjectClass *object_class;
+	GtkWidgetClass *widget_class;
+	GtkToggleToolButtonClass *toggle_tool_button_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (EEmoticonToolButtonPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = emoticon_tool_button_set_property;
+	object_class->get_property = emoticon_tool_button_get_property;
+	object_class->dispose = emoticon_tool_button_dispose;
+
+	widget_class = GTK_WIDGET_CLASS (class);
+	widget_class->button_press_event = emoticon_tool_button_press_event;
+
+	toggle_tool_button_class = GTK_TOGGLE_TOOL_BUTTON_CLASS (class);
+	toggle_tool_button_class->toggled = emoticon_tool_button_toggled;
+
+	class->popup = emoticon_tool_button_popup;
+	class->popdown = emoticon_tool_button_popdown;
+
+	g_object_class_override_property (
+		object_class, PROP_CURRENT_EMOTICON, "current-emoticon");
+
+	g_object_class_install_property (
+		object_class,
+		PROP_POPUP_SHOWN,
+		g_param_spec_boolean (
+			"popup-shown",
+			"Popup Shown",
+			"Whether the button's dropdown is shown",
+			FALSE,
+			G_PARAM_READWRITE));
+
+	signals[POPUP] = g_signal_new (
+		"popup",
+		G_OBJECT_CLASS_TYPE (class),
+		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		G_STRUCT_OFFSET (EEmoticonToolButtonClass, popup),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+
+	signals[POPDOWN] = g_signal_new (
+		"popdown",
+		G_OBJECT_CLASS_TYPE (class),
+		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		G_STRUCT_OFFSET (EEmoticonToolButtonClass, popdown),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+
+	gtk_binding_entry_add_signal (
+		gtk_binding_set_by_class (class),
+		GDK_KEY_Down, GDK_MOD1_MASK, "popup", 0);
+	gtk_binding_entry_add_signal (
+		gtk_binding_set_by_class (class),
+		GDK_KEY_KP_Down, GDK_MOD1_MASK, "popup", 0);
+
+	gtk_binding_entry_add_signal (
+		gtk_binding_set_by_class (class),
+		GDK_KEY_Up, GDK_MOD1_MASK, "popdown", 0);
+	gtk_binding_entry_add_signal (
+		gtk_binding_set_by_class (class),
+		GDK_KEY_KP_Up, GDK_MOD1_MASK, "popdown", 0);
+	gtk_binding_entry_add_signal (
+		gtk_binding_set_by_class (class),
+		GDK_KEY_Escape, 0, "popdown", 0);
+}
+
+static void
+emoticon_tool_button_iemoticon_init (EEmoticonChooserIface *iemoticon)
+{
+	iemoticon->get_current_emoticon = emoticon_tool_button_get_current_emoticon;
+	iemoticon->set_current_emoticon = emoticon_tool_button_set_current_emoticon;
+}
+
+static void
+emoticon_tool_button_init (EEmoticonToolButton *button)
+{
+	EEmoticonChooser *chooser;
+	GtkWidget *toplevel;
+	GtkWidget *container;
+	GtkWidget *widget;
+	GtkWidget *window;
+	GList *list, *iter;
+	gint ii;
+
+	button->priv = G_TYPE_INSTANCE_GET_PRIVATE (
+		button, E_TYPE_EMOTICON_TOOL_BUTTON,
+		EEmoticonToolButtonPrivate);
+
+	/* Build the pop-up window. */
+
+	window = gtk_window_new (GTK_WINDOW_POPUP);
+	toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
+	gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
+	gtk_window_set_type_hint (
+		GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_COMBO);
+	if (gtk_widget_is_toplevel (toplevel)) {
+		gtk_window_group_add_window (
+			gtk_window_get_group (GTK_WINDOW (toplevel)),
+			GTK_WINDOW (window));
+		gtk_window_set_transient_for (
+			GTK_WINDOW (window), GTK_WINDOW (toplevel));
+	}
+	button->priv->window = g_object_ref (window);
+
+	g_signal_connect_swapped (
+		window, "show",
+		G_CALLBACK (emoticon_tool_button_child_show_cb), button);
+	g_signal_connect_swapped (
+		window, "hide",
+		G_CALLBACK (emoticon_tool_button_child_hide_cb), button);
+	g_signal_connect_swapped (
+		window, "button-release-event",
+		G_CALLBACK (emoticon_tool_button_button_release_event_cb),
+		button);
+	g_signal_connect_swapped (
+		window, "key-press-event",
+		G_CALLBACK (emoticon_tool_button_child_key_press_event_cb),
+		button);
+
+	/* Build the pop-up window contents. */
+
+	widget = gtk_frame_new (NULL);
+	gtk_frame_set_shadow_type (GTK_FRAME (widget), GTK_SHADOW_OUT);
+	gtk_container_add (GTK_CONTAINER (window), widget);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	widget = gtk_table_new (NUM_ROWS, NUM_COLS, TRUE);
+	gtk_table_set_row_spacings (GTK_TABLE (widget), 0);
+	gtk_table_set_col_spacings (GTK_TABLE (widget), 0);
+	gtk_container_add (GTK_CONTAINER (container), widget);
+	button->priv->table = g_object_ref (widget);
+	gtk_widget_show (widget);
+
+	container = widget;
+
+	chooser = E_EMOTICON_CHOOSER (button);
+	list = e_emoticon_chooser_get_items (chooser);
+	g_assert (g_list_length (list) <= NUM_ROWS * NUM_COLS);
+
+	for (iter = list, ii = 0; iter != NULL; iter = iter->next, ii++) {
+		EEmoticon *emoticon = iter->data;
+		guint left = ii % NUM_COLS;
+		guint top = ii / NUM_COLS;
+		gchar *tooltip;
+
+		tooltip = emoticon_tool_button_elide_underscores (
+			gettext (emoticon->label));
+
+		widget = gtk_button_new ();
+		gtk_button_set_image (
+			GTK_BUTTON (widget),
+			gtk_image_new_from_icon_name (
+			emoticon->icon_name, GTK_ICON_SIZE_BUTTON));
+		gtk_button_set_relief (GTK_BUTTON (widget), GTK_RELIEF_NONE);
+		gtk_widget_set_tooltip_text (widget, tooltip);
+		gtk_widget_show (widget);
+
+		g_object_set_data_full (
+			G_OBJECT (widget), "emoticon",
+			e_emoticon_copy (emoticon),
+			(GDestroyNotify) e_emoticon_free);
+
+		g_signal_connect_swapped (
+			widget, "clicked",
+			G_CALLBACK (emoticon_tool_button_emoticon_clicked_cb),
+			button);
+
+		g_signal_connect_swapped (
+			widget, "clicked",
+			G_CALLBACK (e_emoticon_chooser_item_activated),
+			chooser);
+
+		g_signal_connect_swapped (
+			widget, "button-release-event",
+			G_CALLBACK (emoticon_tool_button_emoticon_release_event_cb),
+			button);
+
+		gtk_table_attach (
+			GTK_TABLE (container), widget,
+			left, left + 1, top, top + 1, 0, 0, 0, 0);
+
+		g_free (tooltip);
+	}
+
+	g_list_free (list);
+}
+
+GType
+e_emoticon_tool_button_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (EEmoticonToolButtonClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) emoticon_tool_button_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (EEmoticonToolButton),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) emoticon_tool_button_init,
+			NULL   /* value_table */
+		};
+
+		static const GInterfaceInfo iemoticon_info = {
+			(GInterfaceInitFunc) emoticon_tool_button_iemoticon_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL  /* interemoticon_data */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_TOGGLE_TOOL_BUTTON,
+			"EEmoticonToolButton", &type_info, 0);
+
+		g_type_add_interface_static (
+			type, E_TYPE_EMOTICON_CHOOSER, &iemoticon_info);
+	}
+
+	return type;
+}
+
+GtkToolItem *
+e_emoticon_tool_button_new (void)
+{
+	return g_object_new (E_TYPE_EMOTICON_TOOL_BUTTON, NULL);
+}
+
+void
+e_emoticon_tool_button_popup (EEmoticonToolButton *button)
+{
+	g_return_if_fail (E_IS_EMOTICON_TOOL_BUTTON (button));
+
+	g_signal_emit (button, signals[POPUP], 0);
+}
+
+void
+e_emoticon_tool_button_popdown (EEmoticonToolButton *button)
+{
+	g_return_if_fail (E_IS_EMOTICON_TOOL_BUTTON (button));
+
+	g_signal_emit (button, signals[POPDOWN], 0);
+}
diff --git a/e-util/e-emoticon-tool-button.h b/e-util/e-emoticon-tool-button.h
new file mode 100644
index 0000000..6c1273e
--- /dev/null
+++ b/e-util/e-emoticon-tool-button.h
@@ -0,0 +1,71 @@
+/* e-emoticon-tool-button.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_EMOTICON_TOOL_BUTTON_H
+#define E_EMOTICON_TOOL_BUTTON_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_EMOTICON_TOOL_BUTTON \
+	(e_emoticon_tool_button_get_type ())
+#define E_EMOTICON_TOOL_BUTTON(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_EMOTICON_TOOL_BUTTON, EEmoticonToolButton))
+#define E_EMOTICON_TOOL_BUTTON_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_EMOTICON_TOOL_BUTTON, EEmoticonToolButtonClass))
+#define E_IS_EMOTICON_TOOL_BUTTON(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_EMOTICON_TOOL_BUTTON))
+#define E_IS_EMOTICON_TOOL_BUTTON_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_EMOTICON_TOOL_BUTTON))
+#define E_EMOTICON_TOOL_BUTTON_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_EMOTICON_TOOL_BUTTON, EEmoticonToolButtonClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EEmoticonToolButton EEmoticonToolButton;
+typedef struct _EEmoticonToolButtonClass EEmoticonToolButtonClass;
+typedef struct _EEmoticonToolButtonPrivate EEmoticonToolButtonPrivate;
+
+struct _EEmoticonToolButton {
+	GtkToggleToolButton parent;
+	EEmoticonToolButtonPrivate *priv;
+};
+
+struct _EEmoticonToolButtonClass {
+	GtkToggleToolButtonClass parent_class;
+
+	void	(*popup)		(EEmoticonToolButton *button);
+	void	(*popdown)		(EEmoticonToolButton *button);
+};
+
+GType		e_emoticon_tool_button_get_type	(void);
+GtkToolItem *	e_emoticon_tool_button_new	(void);
+void		e_emoticon_tool_button_popup	(EEmoticonToolButton *button);
+void		e_emoticon_tool_button_popdown	(EEmoticonToolButton *button);
+
+G_END_DECLS
+
+#endif /* E_EMOTICON_TOOL_BUTTON_H */
diff --git a/e-util/e-emoticon.c b/e-util/e-emoticon.c
new file mode 100644
index 0000000..1d394e6
--- /dev/null
+++ b/e-util/e-emoticon.c
@@ -0,0 +1,89 @@
+/* e-emoticon.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "e-emoticon.h"
+
+static EEmoticon *
+emoticon_copy (EEmoticon *emoticon)
+{
+	EEmoticon *copy;
+
+	copy = g_slice_new (EEmoticon);
+	copy->label = g_strdup (emoticon->label);
+	copy->icon_name = g_strdup (emoticon->icon_name);
+	copy->text_face = g_strdup (emoticon->text_face);
+
+	return copy;
+}
+
+static void
+emoticon_free (EEmoticon *emoticon)
+{
+	g_free (emoticon->label);
+	g_free (emoticon->icon_name);
+	g_free (emoticon->text_face);
+	g_slice_free (EEmoticon, emoticon);
+}
+
+GType
+e_emoticon_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0))
+		type = g_boxed_type_register_static (
+			"EEmoticon",
+			(GBoxedCopyFunc) emoticon_copy,
+			(GBoxedFreeFunc) emoticon_free);
+
+	return type;
+}
+
+gboolean
+e_emoticon_equal (EEmoticon *emoticon_a,
+		  EEmoticon *emoticon_b)
+{
+	if (((emoticon_a == NULL) && (emoticon_b != NULL)) ||
+	    ((emoticon_a != NULL) && (emoticon_b == NULL)))
+		return FALSE;
+
+	if (emoticon_a == emoticon_b)
+		return TRUE;
+
+	if (g_strcmp0 (emoticon_a->label, emoticon_b->label) != 0)
+		return FALSE;
+
+	if (g_strcmp0 (emoticon_a->icon_name, emoticon_b->icon_name) != 0)
+		return FALSE;
+
+	if (g_strcmp0 (emoticon_a->text_face, emoticon_b->text_face) != 0)
+		return FALSE;
+
+	return TRUE;
+}
+
+EEmoticon *
+e_emoticon_copy (EEmoticon *emoticon)
+{
+	return g_boxed_copy (E_TYPE_EMOTICON, emoticon);
+}
+
+void
+e_emoticon_free (EEmoticon *emoticon)
+{
+	g_boxed_free (E_TYPE_EMOTICON, emoticon);
+}
diff --git a/e-util/e-emoticon.h b/e-util/e-emoticon.h
new file mode 100644
index 0000000..cf270ef
--- /dev/null
+++ b/e-util/e-emoticon.h
@@ -0,0 +1,48 @@
+/* e-emoticon.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_EMOTICON_H
+#define E_EMOTICON_H
+
+#include <glib-object.h>
+
+#define E_TYPE_EMOTICON \
+	(e_emoticon_get_type ())
+
+G_BEGIN_DECLS
+
+typedef struct _EEmoticon EEmoticon;
+
+struct _EEmoticon {
+	gchar *label;
+	gchar *icon_name;
+	gchar *text_face;
+};
+
+GType		e_emoticon_get_type		(void);
+gboolean	e_emoticon_equal		(EEmoticon *emoticon_a,
+						 EEmoticon *emoticon_b);
+EEmoticon *	e_emoticon_copy			(EEmoticon *emoticon);
+void		e_emoticon_free			(EEmoticon *emoticon);
+
+G_END_DECLS
+
+#endif /* E_EMOTICON_H */
diff --git a/e-util/e-util.h b/e-util/e-util.h
index 002ead7..a94e654 100644
--- a/e-util/e-util.h
+++ b/e-util/e-util.h
@@ -90,6 +90,11 @@
 #include <e-util/e-dialog-widgets.h>
 #include <e-util/e-editor-selection.h>
 #include <e-util/e-editor-widget.h>
+#include <e-util/e-emoticon-action.h>
+#include <e-util/e-emoticon-chooser-menu.h>
+#include <e-util/e-emoticon-chooser.h>
+#include <e-util/e-emoticon-tool-button.h>
+#include <e-util/e-emoticon.h>
 #include <e-util/e-event.h>
 #include <e-util/e-file-request.h>
 #include <e-util/e-file-utils.h>



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