[evolution/wip/webkit-composer: 70/372] Import GtkhtmlFace* classes as EEmoticon*



commit 0e222496eba787cd45c9177cefa8540993ac0e6b
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 6fc3ea1..c801325 100644
--- a/e-util/Makefile.am
+++ b/e-util/Makefile.am
@@ -177,6 +177,11 @@ evolution_util_include_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 \
@@ -425,6 +430,11 @@ libevolution_util_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 e1eb29c..e4ab1fc 100644
--- a/e-util/e-util.h
+++ b/e-util/e-util.h
@@ -91,6 +91,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]