gtkhtml r9060 - in trunk: . components/editor gtkhtml



Author: mbarnes
Date: Tue Dec  9 18:30:21 2008
New Revision: 9060
URL: http://svn.gnome.org/viewvc/gtkhtml?rev=9060&view=rev

Log:
2008-12-09  Matthew Barnes  <mbarnes redhat com>

	** Fixes bug #563841

	* configure.in:
	Bump gnome_icon_theme_minimum_version to 2.22.0 to pick up
	new "face" icons.

	* gtkhtml/htmlengine-edit-cut-and-paste.c (use_pictograms):
	Refresh the pictogram parser.  Use standard icon names for
	smiley faces and recognize more of them.

	* components/editor/gtkhtml-editor.ui:
	Replace "insert-face-menu" with "insert-face" and also add
	it to the HTML toolbar.  "insert-face" is a GtkhtmlFaceAction,
	which appears as a GtkhtmlFaceChooserMenu or GtkhtmlFaceToolButton.

	* components/editor/gtkhtml-editor-actions.c:
	Rewrite the insert face action callbacks.  Only a single
	callback is needed now.  Selected face is obtained through
	gtkhtml_face_chooser_get_current_face().

	* components/editor/gtkhtml-face.c:
	* components/editor/gtkhtml-face.h:
	New boxed type for dealing with face icons.

	* components/editor/gtkhtml-face-chooser.c:
	* components/editor/gtkhtml-face-chooser.h:
	New interface for choosing a smiley face from a list or table.
	Patterned after GtkRecentChooser.

	* components/editor/gtkhtml-face-action.c:
	* components/editor/gtkhtml-face-action.h:
	New GtkAction subclass for choosing from a predefined set of
	smiley faces.  Patterned after GtkRecentAction.  Implements
	the GtkhtmlFaceChooser interface.

	* components/editor/gtkhtml-face-chooser-menu.c:
	* components/editor/gtkhtml-face-chooser-menu.h:
	New GtkMenu subclass implements of a menu of smiley faces.
	Implements the GtkhtmlFaceChooser interface.

	* components/editor/gtkhtml-face-tool-button.c:
	* components/editor/gtkhtml-face-tool-button.h:
	New GtkToggleToolButton subclass implements a popup smiley face
	palette for use in toolbars.  Implements the GtkhtmlFaceChooser
	interface.



Added:
   trunk/components/editor/gtkhtml-face-action.c
   trunk/components/editor/gtkhtml-face-action.h
   trunk/components/editor/gtkhtml-face-chooser-menu.c
   trunk/components/editor/gtkhtml-face-chooser-menu.h
   trunk/components/editor/gtkhtml-face-chooser.c
   trunk/components/editor/gtkhtml-face-chooser.h
   trunk/components/editor/gtkhtml-face-tool-button.c
   trunk/components/editor/gtkhtml-face-tool-button.h
   trunk/components/editor/gtkhtml-face.c
   trunk/components/editor/gtkhtml-face.h
Modified:
   trunk/ChangeLog
   trunk/components/editor/ChangeLog
   trunk/components/editor/Makefile.am
   trunk/components/editor/gtkhtml-editor-actions.c
   trunk/components/editor/gtkhtml-editor-private.h
   trunk/components/editor/gtkhtml-editor.ui
   trunk/configure.in
   trunk/gtkhtml/ChangeLog
   trunk/gtkhtml/htmlengine-edit-cut-and-paste.c

Modified: trunk/components/editor/Makefile.am
==============================================================================
--- trunk/components/editor/Makefile.am	(original)
+++ trunk/components/editor/Makefile.am	Tue Dec  9 18:30:21 2008
@@ -42,6 +42,16 @@
 	gtkhtml-editor-private.h		\
 	gtkhtml-editor-signals.c		\
 	gtkhtml-editor-signals.h		\
+	gtkhtml-face.c				\
+	gtkhtml-face.h				\
+	gtkhtml-face-action.c			\
+	gtkhtml-face-action.h			\
+	gtkhtml-face-chooser.c			\
+	gtkhtml-face-chooser.h			\
+	gtkhtml-face-chooser-menu.c		\
+	gtkhtml-face-chooser-menu.h		\
+	gtkhtml-face-tool-button.c		\
+	gtkhtml-face-tool-button.h		\
 	gtkhtml-spell-dialog.c			\
 	gtkhtml-spell-dialog.h			\
 	gtkhtml-spell-checker.c			\

Modified: trunk/components/editor/gtkhtml-editor-actions.c
==============================================================================
--- trunk/components/editor/gtkhtml-editor-actions.c	(original)
+++ trunk/components/editor/gtkhtml-editor-actions.c	Tue Dec  9 18:30:21 2008
@@ -213,43 +213,6 @@
 }
 
 static void
-insert_emoticon (GtkhtmlEditor *editor,
-                 GtkAction *action,
-                 const gchar *alt)
-{
-	GtkHTML *html;
-	HTMLObject *image;
-	GtkIconInfo *icon_info;
-	const gchar *filename;
-	gchar *icon_name;
-	gchar *uri = NULL;
-
-	html = gtkhtml_editor_get_html (editor);
-
-	g_object_get (action, "icon-name", &icon_name, NULL);
-	icon_info = gtk_icon_theme_lookup_icon (
-		gtk_icon_theme_get_default (), icon_name, 16, 0);
-	g_free (icon_name);
-	g_return_if_fail (icon_info != NULL);
-
-	filename = gtk_icon_info_get_filename (icon_info);
-	if (filename != NULL)
-		uri = g_filename_to_uri (filename, NULL, NULL);
-	gtk_icon_info_free (icon_info);
-	g_return_if_fail (uri != NULL);
-
-	image = html_image_new (
-		html_engine_get_image_factory (html->engine),
-		uri, NULL, NULL, -1, -1, FALSE, FALSE, 0, NULL,
-		HTML_VALIGN_MIDDLE, FALSE);
-	html_image_set_alt (HTML_IMAGE (image), alt);
-	html_engine_paste_object (
-		html->engine, image, html_object_get_length (image));
-
-	g_free (uri);
-}
-
-static void
 insert_text_file_response_cb (GtkFileChooser *file_chooser,
                               gint response,
                               GtkhtmlEditor *editor)
@@ -626,6 +589,47 @@
 }
 
 static void
+action_insert_face_cb (GtkhtmlFaceAction *action,
+                       GtkhtmlEditor *editor)
+{
+	GtkHTML *html;
+	HTMLObject *image;
+	GtkIconInfo *icon_info;
+	GtkIconTheme *icon_theme;
+	GtkhtmlFaceChooser *chooser;
+	GtkhtmlFace *face;
+	const gchar *filename;
+	gchar *uri = NULL;
+
+	html = gtkhtml_editor_get_html (editor);
+
+	chooser = GTKHTML_FACE_CHOOSER (action);
+	face = gtkhtml_face_chooser_get_current_face (chooser);
+	g_return_if_fail (face != NULL);
+
+	icon_theme = gtk_icon_theme_get_default ();
+	icon_info = gtk_icon_theme_lookup_icon (
+		icon_theme, face->icon_name, 16, 0);
+	g_return_if_fail (icon_info != NULL);
+
+	filename = gtk_icon_info_get_filename (icon_info);
+	if (filename != NULL)
+		uri = g_filename_to_uri (filename, NULL, NULL);
+	gtk_icon_info_free (icon_info);
+	g_return_if_fail (uri != NULL);
+
+	image = html_image_new (
+		html_engine_get_image_factory (html->engine),
+		uri, NULL, NULL, -1, -1, FALSE, FALSE, 0, NULL,
+		HTML_VALIGN_MIDDLE, FALSE);
+	html_image_set_alt (HTML_IMAGE (image), face->text_face);
+	html_engine_paste_object (
+		html->engine, image, html_object_get_length (image));
+
+	g_free (uri);
+}
+
+static void
 action_insert_html_file_cb (GtkToggleAction *action,
                             GtkhtmlEditor *editor)
 {
@@ -666,125 +670,6 @@
 }
 
 static void
-action_insert_face_angel_cb (GtkAction *action,
-                             GtkhtmlEditor *editor)
-{
-	insert_emoticon (editor, action, "O:-)");
-}
-
-static void
-action_insert_face_cool_cb (GtkAction *action,
-                            GtkhtmlEditor *editor)
-{
-	insert_emoticon (editor, action, "B-)");
-}
-
-static void
-action_insert_face_crying_cb (GtkAction *action,
-                              GtkhtmlEditor *editor)
-{
-	insert_emoticon (editor, action, ":'(");
-}
-
-static void
-action_insert_face_devilish_cb (GtkAction *action,
-                                GtkhtmlEditor *editor)
-{
-	insert_emoticon (editor, action, ">:-)");
-}
-
-static void
-action_insert_face_embarrassed_cb (GtkAction *action,
-                                   GtkhtmlEditor *editor)
-{
-	insert_emoticon (editor, action, ":\"-)");
-}
-
-static void
-action_insert_face_kiss_cb (GtkAction *action,
-                            GtkhtmlEditor *editor)
-{
-	insert_emoticon (editor, action, ":-*");
-}
-
-static void
-action_insert_face_monkey_cb (GtkAction *action,
-                              GtkhtmlEditor *editor)
-{
-	insert_emoticon (editor, action, ":-(|)");
-}
-
-static void
-action_insert_face_plain_cb (GtkAction *action,
-                             GtkhtmlEditor *editor)
-{
-	insert_emoticon (editor, action, ":-|");
-}
-
-static void
-action_insert_face_raspberry_cb (GtkAction *action,
-                                 GtkhtmlEditor *editor)
-{
-	insert_emoticon (editor, action, ":-P");
-}
-
-static void
-action_insert_face_sad_cb (GtkAction *action,
-                           GtkhtmlEditor *editor)
-{
-	insert_emoticon (editor, action, ":-(");
-}
-
-static void
-action_insert_face_smile_cb (GtkAction *action,
-                             GtkhtmlEditor *editor)
-{
-	insert_emoticon (editor, action, ":-)");
-}
-
-static void
-action_insert_face_smile_big_cb (GtkAction *action,
-                                 GtkhtmlEditor *editor)
-{
-	insert_emoticon (editor, action, ":-D");
-}
-
-static void
-action_insert_face_smirk_cb (GtkAction *action,
-                             GtkhtmlEditor *editor)
-{
-	insert_emoticon (editor, action, ":-!");
-}
-
-static void
-action_insert_face_surprise_cb (GtkAction *action,
-                                GtkhtmlEditor *editor)
-{
-	insert_emoticon (editor, action, ":-O");
-}
-
-static void
-action_insert_face_wink_cb (GtkAction *action,
-                            GtkhtmlEditor *editor)
-{
-	insert_emoticon (editor, action, ";-)");
-}
-
-static void
-action_insert_smiley_9_cb (GtkAction *action,
-                           GtkhtmlEditor *editor)
-{
-	insert_emoticon (editor, action, ":-/");
-}
-
-static void
-action_insert_smiley_26_cb (GtkAction *action,
-                            GtkhtmlEditor *editor)
-{
-	insert_emoticon (editor, action, ":-Q");
-}
-
-static void
 action_insert_table_cb (GtkAction *action,
                         GtkhtmlEditor *editor)
 {
@@ -1345,125 +1230,6 @@
 	  NULL,
 	  G_CALLBACK (action_insert_html_file_cb) },
 
-	{ "insert-face-angel",
-	  "face-angel",
-	  N_("_Angel"),
-	  NULL,
-	  NULL,
-	  G_CALLBACK (action_insert_face_angel_cb) },
-
-	{ "insert-face-cool",
-	  "face-cool",
-	  N_("_Cool"),
-	  NULL,
-	  NULL,
-	  G_CALLBACK (action_insert_face_cool_cb) },
-
-	{ "insert-face-crying",
-	  "face-crying",
-	  N_("Cr_ying"),
-	  NULL,
-	  NULL,
-	  G_CALLBACK (action_insert_face_crying_cb) },
-
-	{ "insert-face-devilish",
-	  "face-devilish",
-	  N_("_Devilish"),
-	  NULL,
-	  NULL,
-	  G_CALLBACK (action_insert_face_devilish_cb) },
-
-	{ "insert-face-embarrassed",
-	  "face-embarrassed",
-	  N_("_Embarrassed"),
-	  NULL,
-	  NULL,
-	  G_CALLBACK (action_insert_face_embarrassed_cb) },
-
-	{ "insert-face-kiss",
-	  "face-kiss",
-	  N_("_Kiss"),
-	  NULL,
-	  NULL,
-	  G_CALLBACK (action_insert_face_kiss_cb) },
-
-	{ "insert-face-monkey",
-	  "face-monkey",
-	  N_("_Monkey"),
-	  NULL,
-	  NULL,
-	  G_CALLBACK (action_insert_face_monkey_cb) },
-
-	{ "insert-face-plain",
-	  "face-plain",
-	  N_("_Indifferent"),
-	  NULL,
-	  NULL,
-	  G_CALLBACK (action_insert_face_plain_cb) },
-
-	{ "insert-face-raspberry",
-	  "face-raspberry",
-	  N_("_Tongue"),
-	  NULL,
-	  NULL,
-	  G_CALLBACK (action_insert_face_raspberry_cb) },
-
-	{ "insert-face-sad",
-	  "face-sad",
-	  N_("_Frown"),
-	  NULL,
-	  NULL,
-	  G_CALLBACK (action_insert_face_sad_cb) },
-
-	{ "insert-face-smile",
-	  "face-smile",
-	  N_("_Smile"),
-	  NULL,
-	  NULL,
-	  G_CALLBACK (action_insert_face_smile_cb) },
-
-	{ "insert-face-smile-big",
-	  "face-smile-big",
-	  N_("_Laughing"),
-	  NULL,
-	  NULL,
-	  G_CALLBACK (action_insert_face_smile_big_cb) },
-
-	{ "insert-face-smirk",
-	  "face-smirk",
-	  N_("Smi_rk"),
-	  NULL,
-	  NULL,
-	  G_CALLBACK (action_insert_face_smirk_cb) },
-
-	{ "insert-face-surprise",
-	  "face-surprise",
-	  N_("Sur_prised"),
-	  NULL,
-	  NULL,
-	  G_CALLBACK (action_insert_face_surprise_cb) },
-
-	{ "insert-face-wink",
-	  "face-wink",
-	  N_("_Wink"),
-	  NULL,
-	  NULL,
-	  G_CALLBACK (action_insert_face_wink_cb) },
-
-	{ "insert-smiley-9",
-	  "stock_smiley-9",
-	  N_("_Undecided"),
-	  NULL,
-	  NULL,
-	  G_CALLBACK (action_insert_smiley_9_cb) },
-
-	{ "insert-smiley-26",
-	  "stock_smiley-26",
-	  N_("S_ick"),
-	  NULL,
-	  NULL,
-	  G_CALLBACK (action_insert_smiley_26_cb) },
-
 	{ "insert-text-file",
 	  NULL,
 	  N_("Te_xt File..."),
@@ -1578,13 +1344,6 @@
 	  NULL,
 	  NULL },
 
-	{ "insert-face-menu",
-	  NULL,
-	  N_("_Emoticon"),
-	  NULL,
-	  NULL,
-	  NULL },
-
 	{ "insert-menu",
 	  NULL,
 	  N_("_Insert"),
@@ -2291,6 +2050,7 @@
 void
 gtkhtml_editor_actions_init (GtkhtmlEditor *editor)
 {
+	GtkAction *action;
 	GtkActionGroup *action_group;
 	GtkUIManager *manager;
 	const gchar *domain;
@@ -2323,6 +2083,16 @@
 		G_CALLBACK (action_style_cb), editor);
 	gtk_ui_manager_insert_action_group (manager, action_group, 0);
 
+	/* Face Action */
+	action = gtkhtml_face_action_new (
+		"insert-face", _("_Emoticon"),
+		_("Insert Emoticon"), NULL);
+	g_object_set (action, "icon-name", "face-smile", NULL);
+	g_signal_connect (
+		action, "item-activated",
+		G_CALLBACK (action_insert_face_cb), editor);
+	gtk_action_group_add_action (action_group, action);
+
 	/* Core Actions (HTML only) */
 	action_group = editor->priv->html_actions;
 	gtk_action_group_set_translation_domain (action_group, domain);

Modified: trunk/components/editor/gtkhtml-editor-private.h
==============================================================================
--- trunk/components/editor/gtkhtml-editor-private.h	(original)
+++ trunk/components/editor/gtkhtml-editor-private.h	Tue Dec  9 18:30:21 2008
@@ -31,6 +31,8 @@
 #include "gtkhtml-color-palette.h"
 #include "gtkhtml-color-swatch.h"
 #include "gtkhtml-combo-box.h"
+#include "gtkhtml-face-action.h"
+#include "gtkhtml-face-chooser.h"
 #include "gtkhtml-spell-dialog.h"
 
 /* GtkHTML internals */

Modified: trunk/components/editor/gtkhtml-editor.ui
==============================================================================
--- trunk/components/editor/gtkhtml-editor.ui	(original)
+++ trunk/components/editor/gtkhtml-editor.ui	Tue Dec  9 18:30:21 2008
@@ -34,18 +34,7 @@
       <menuitem action='insert-table'/>
       <menuitem action='insert-text-file'/>
       <menuitem action='insert-html-file'/>
-      <menu action='insert-face-menu'>
-        <menuitem action='insert-face-smile'/>
-        <menuitem action='insert-face-surprise'/>
-        <menuitem action='insert-face-wink'/>
-        <menuitem action='insert-face-sad'/>
-        <menuitem action='insert-face-smile-big'/>
-        <menuitem action='insert-face-plain'/>
-        <menuitem action='insert-smiley-9'/>
-        <menuitem action='insert-face-raspberry'/>
-        <menuitem action='insert-face-crying'/>
-        <menuitem action='insert-smiley-26'/>
-      </menu>
+      <menuitem action='insert-face'/>
     </menu>
     <placeholder name='pre-format-menu'/>
     <menu action='format-menu'>
@@ -135,6 +124,7 @@
     <toolitem action='insert-link'/>
     <toolitem action='insert-rule'/>
     <toolitem action='insert-table'/>
+    <toolitem action='insert-face'/>
   </toolbar>
   <popup name='context-menu'>
     <placeholder name='context-spell-suggest'/>

Added: trunk/components/editor/gtkhtml-face-action.c
==============================================================================
--- (empty file)
+++ trunk/components/editor/gtkhtml-face-action.c	Tue Dec  9 18:30:21 2008
@@ -0,0 +1,300 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gtkhtml-face-action.c
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ *
+ * 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 "gtkhtml-face-action.h"
+
+#include "gtkhtml-face-chooser-menu.h"
+#include "gtkhtml-face-tool-button.h"
+
+#define GTKHTML_FACE_ACTION_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), GTKHTML_TYPE_FACE_ACTION, GtkhtmlFaceActionPrivate))
+
+struct _GtkhtmlFaceActionPrivate {
+	GList *choosers;
+	GtkhtmlFaceChooser *current_chooser;
+};
+
+enum {
+	PROP_0,
+	PROP_CURRENT_FACE
+};
+
+static gpointer parent_class;
+
+static void
+face_action_proxy_item_activated_cb (GtkhtmlFaceAction *action,
+                                     GtkhtmlFaceChooser *chooser)
+{
+	action->priv->current_chooser = chooser;
+
+	g_signal_emit_by_name (action, "item-activated");
+}
+
+static void
+face_action_set_property (GObject *object,
+                          guint property_id,
+                          const GValue *value,
+                          GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_CURRENT_FACE:
+			gtkhtml_face_chooser_set_current_face (
+				GTKHTML_FACE_CHOOSER (object),
+				g_value_get_boxed (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+face_action_get_property (GObject *object,
+                          guint property_id,
+                          GValue *value,
+                          GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_CURRENT_FACE:
+			g_value_set_boxed (
+				value, gtkhtml_face_chooser_get_current_face (
+				GTKHTML_FACE_CHOOSER (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+face_action_finalize (GObject *object)
+{
+	GtkhtmlFaceActionPrivate *priv;
+
+	priv = GTKHTML_FACE_ACTION_GET_PRIVATE (object);
+
+	g_list_free (priv->choosers);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+face_action_activate (GtkAction *action)
+{
+	GtkhtmlFaceActionPrivate *priv;
+
+	priv = GTKHTML_FACE_ACTION_GET_PRIVATE (action);
+
+	priv->current_chooser = NULL;
+}
+
+static GtkWidget *
+face_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 *
+face_action_create_tool_item (GtkAction *action)
+{
+	return GTK_WIDGET (gtkhtml_face_tool_button_new ());
+}
+
+static void
+face_action_connect_proxy (GtkAction *action,
+                           GtkWidget *proxy)
+{
+	GtkhtmlFaceActionPrivate *priv;
+
+	priv = GTKHTML_FACE_ACTION_GET_PRIVATE (action);
+
+	if (!GTKHTML_IS_FACE_CHOOSER (proxy))
+		goto chainup;
+
+	if (g_list_find (priv->choosers, proxy) != NULL)
+		goto chainup;
+
+	g_signal_connect_swapped (
+		proxy, "item-activated",
+		G_CALLBACK (face_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
+face_action_disconnect_proxy (GtkAction *action,
+                              GtkWidget *proxy)
+{
+	GtkhtmlFaceActionPrivate *priv;
+
+	priv = GTKHTML_FACE_ACTION_GET_PRIVATE (action);
+
+	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 *
+face_action_create_menu (GtkAction *action)
+{
+	GtkhtmlFaceActionPrivate *priv;
+	GtkWidget *widget;
+
+	priv = GTKHTML_FACE_ACTION_GET_PRIVATE (action);
+
+	widget = gtkhtml_face_chooser_menu_new ();
+
+	g_signal_connect_swapped (
+		widget, "item-activated",
+		G_CALLBACK (face_action_proxy_item_activated_cb), action);
+
+	priv->choosers = g_list_prepend (priv->choosers, widget);
+
+	return widget;
+}
+
+static GtkhtmlFace *
+face_action_get_current_face (GtkhtmlFaceChooser *chooser)
+{
+	GtkhtmlFaceActionPrivate *priv;
+	GtkhtmlFace *face = NULL;
+
+	priv = GTKHTML_FACE_ACTION_GET_PRIVATE (chooser);
+
+	if (priv->current_chooser != NULL)
+		face = gtkhtml_face_chooser_get_current_face (
+			priv->current_chooser);
+
+	return face;
+}
+
+static void
+face_action_set_current_face (GtkhtmlFaceChooser *chooser,
+                              GtkhtmlFace *face)
+{
+	GtkhtmlFaceActionPrivate *priv;
+	GList *iter;
+
+	priv = GTKHTML_FACE_ACTION_GET_PRIVATE (chooser);
+
+	for (iter = priv->choosers; iter != NULL; iter = iter->next) {
+		GtkhtmlFaceChooser *proxy_chooser = iter->data;
+
+		gtkhtml_face_chooser_set_current_face (proxy_chooser, face);
+	}
+}
+
+static void
+face_action_class_init (GtkhtmlFaceActionClass *class)
+{
+	GObjectClass *object_class;
+	GtkActionClass *action_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (GtkhtmlFaceAction));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = face_action_set_property;
+	object_class->get_property = face_action_get_property;
+	object_class->finalize = face_action_finalize;
+
+	action_class = GTK_ACTION_CLASS (class);
+	action_class->activate = face_action_activate;
+	action_class->create_menu_item = face_action_create_menu_item;
+	action_class->create_tool_item = face_action_create_tool_item;
+	action_class->connect_proxy = face_action_connect_proxy;
+	action_class->disconnect_proxy = face_action_disconnect_proxy;
+	action_class->create_menu = face_action_create_menu;
+
+	g_object_class_override_property (
+		object_class, PROP_CURRENT_FACE, "current-face");
+}
+
+static void
+face_action_iface_init (GtkhtmlFaceChooserIface *iface)
+{
+	iface->get_current_face = face_action_get_current_face;
+	iface->set_current_face = face_action_set_current_face;
+}
+
+static void
+face_action_init (GtkhtmlFaceAction *action)
+{
+	action->priv = GTKHTML_FACE_ACTION_GET_PRIVATE (action);
+}
+
+GType
+gtkhtml_face_action_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (GtkhtmlFaceActionClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) face_action_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (GtkhtmlFaceAction),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) face_action_init,
+			NULL   /* value_table */
+		};
+
+		static const GInterfaceInfo iface_info = {
+			(GInterfaceInitFunc) face_action_iface_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL  /* interface_data */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_ACTION, "GtkhtmlFaceAction", &type_info, 0);
+
+		g_type_add_interface_static (
+			type, GTKHTML_TYPE_FACE_CHOOSER, &iface_info);
+	}
+
+	return type;
+}
+
+GtkAction *
+gtkhtml_face_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 (
+		GTKHTML_TYPE_FACE_ACTION, "name", name, "label", label,
+		"tooltip", tooltip, "stock-id", stock_id, NULL);
+}

Added: trunk/components/editor/gtkhtml-face-action.h
==============================================================================
--- (empty file)
+++ trunk/components/editor/gtkhtml-face-action.h	Tue Dec  9 18:30:21 2008
@@ -0,0 +1,68 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gtkhtml-face-action.h
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ *
+ * 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.
+ */
+
+#ifndef GTKHTML_FACE_ACTION_H
+#define GTKHTML_FACE_ACTION_H
+
+#include "gtkhtml-editor-common.h"
+
+/* Standard GObject macros */
+#define GTKHTML_TYPE_FACE_ACTION \
+	(gtkhtml_face_action_get_type ())
+#define GTKHTML_FACE_ACTION(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), GTKHTML_TYPE_FACE_ACTION, GtkhtmlFaceAction))
+#define GTKHTML_FACE_ACTION_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), GTKHTML_TYPE_FACE_ACTION, GtkhtmlFaceActionClass))
+#define GTKHTML_IS_FACE_ACTION(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), GTKHTML_TYPE_FACE_ACTION))
+#define GTKHTML_IS_FACE_ACTION_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), GTKHTML_TYPE_FACE_ACTION))
+#define GTKHTML_FACE_ACTION_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), GTKHTML_TYPE_FACE_ACTION, GtkhtmlFaceActionClass))
+
+G_BEGIN_DECLS
+
+typedef struct _GtkhtmlFaceAction GtkhtmlFaceAction;
+typedef struct _GtkhtmlFaceActionClass GtkhtmlFaceActionClass;
+typedef struct _GtkhtmlFaceActionPrivate GtkhtmlFaceActionPrivate;
+
+struct _GtkhtmlFaceAction {
+	GtkAction parent;
+	GtkhtmlFaceActionPrivate *priv;
+};
+
+struct _GtkhtmlFaceActionClass {
+	GtkActionClass parent_class;
+};
+
+GType		gtkhtml_face_action_get_type	(void);
+GtkAction *	gtkhtml_face_action_new		(const gchar *name,
+						 const gchar *label,
+						 const gchar *tooltip,
+						 const gchar *stock_id);
+
+G_END_DECLS
+
+#endif /* GTKHTML_FACE_ACTION_H */

Added: trunk/components/editor/gtkhtml-face-chooser-menu.c
==============================================================================
--- (empty file)
+++ trunk/components/editor/gtkhtml-face-chooser-menu.c	Tue Dec  9 18:30:21 2008
@@ -0,0 +1,219 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gtkhtml-face-chooser-menu.c
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ *
+ * 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 "gtkhtml-face-chooser-menu.h"
+
+#define GTKHTML_FACE_CHOOSER_MENU_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), GTKHTML_TYPE_FACE_CHOOSER_MENU, GtkhtmlFaceChooserMenuPrivate))
+
+struct _GtkhtmlFaceChooserMenuPrivate {
+	gint dummy;
+};
+
+enum {
+	PROP_0,
+	PROP_CURRENT_FACE
+};
+
+static gpointer parent_class;
+
+static void
+face_chooser_menu_set_property (GObject *object,
+                                guint property_id,
+                                const GValue *value,
+                                GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_CURRENT_FACE:
+			gtkhtml_face_chooser_set_current_face (
+				GTKHTML_FACE_CHOOSER (object),
+				g_value_get_boxed (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+face_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,
+				gtkhtml_face_chooser_get_current_face (
+				GTKHTML_FACE_CHOOSER (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static GtkhtmlFace *
+face_chooser_menu_get_current_face (GtkhtmlFaceChooser *chooser)
+{
+	GtkhtmlFaceChooserMenuPrivate *priv;
+	GtkWidget *item;
+
+	priv = GTKHTML_FACE_CHOOSER_MENU_GET_PRIVATE (chooser);
+
+	item = gtk_menu_get_active (GTK_MENU (chooser));
+	if (item == NULL)
+		return NULL;
+
+	return g_object_get_data (G_OBJECT (item), "face");
+}
+
+static void
+face_chooser_menu_set_current_face (GtkhtmlFaceChooser *chooser,
+                                    GtkhtmlFace *face)
+{
+	GtkhtmlFaceChooserMenuPrivate *priv;
+	GList *list, *iter;
+
+	priv = GTKHTML_FACE_CHOOSER_MENU_GET_PRIVATE (chooser);
+
+	list = gtk_container_get_children (GTK_CONTAINER (chooser));
+
+	for (iter = list; iter != NULL; iter = iter->next) {
+		GtkWidget *item = iter->data;
+		GtkhtmlFace *candidate;
+
+		candidate = g_object_get_data (G_OBJECT (item), "face");
+		if (candidate == NULL)
+			continue;
+
+		if (gtkhtml_face_equal (face, candidate)) {
+			gtk_menu_shell_activate_item (
+				GTK_MENU_SHELL (chooser), item, TRUE);
+			break;
+		}
+	}
+
+	g_list_free (list);
+}
+
+static void
+face_chooser_menu_class_init (GtkhtmlFaceChooserMenuClass *class)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (GtkhtmlFaceChooserMenuPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = face_chooser_menu_set_property;
+	object_class->get_property = face_chooser_menu_get_property;
+
+	g_object_class_override_property (
+		object_class, PROP_CURRENT_FACE, "current-face");
+}
+
+static void
+face_chooser_menu_iface_init (GtkhtmlFaceChooserIface *iface)
+{
+	iface->get_current_face = face_chooser_menu_get_current_face;
+	iface->set_current_face = face_chooser_menu_set_current_face;
+}
+
+static void
+face_chooser_menu_init (GtkhtmlFaceChooserMenu *chooser_menu)
+{
+	GtkhtmlFaceChooser *chooser;
+	GList *list, *iter;
+
+	chooser_menu->priv =
+		GTKHTML_FACE_CHOOSER_MENU_GET_PRIVATE (chooser_menu);
+
+	chooser = GTKHTML_FACE_CHOOSER (chooser_menu);
+	list = gtkhtml_face_chooser_get_items (chooser);
+
+	for (iter = list; iter != NULL; iter = iter->next) {
+		GtkhtmlFace *face = iter->data;
+		GtkWidget *item;
+
+		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",
+			gtkhtml_face_copy (face),
+			(GDestroyNotify) gtkhtml_face_free);
+
+		g_signal_connect_swapped (
+			item, "activate",
+			G_CALLBACK (gtkhtml_face_chooser_item_activated),
+			chooser);
+
+		gtk_menu_shell_append (GTK_MENU_SHELL (chooser_menu), item);
+	}
+
+	g_list_free (list);
+}
+
+GType
+gtkhtml_face_chooser_menu_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (GtkhtmlFaceChooserMenuClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) face_chooser_menu_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (GtkhtmlFaceChooserMenu),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) face_chooser_menu_init,
+			NULL   /* value_table */
+		};
+
+		static const GInterfaceInfo iface_info = {
+			(GInterfaceInitFunc) face_chooser_menu_iface_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL  /* interface_data */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_MENU, "GtkhtmlFileChooserMenu",
+			&type_info, 0);
+
+		g_type_add_interface_static (
+			type, GTKHTML_TYPE_FACE_CHOOSER, &iface_info);
+	}
+
+	return type;
+}
+
+GtkWidget *
+gtkhtml_face_chooser_menu_new (void)
+{
+	return g_object_new (GTKHTML_TYPE_FACE_CHOOSER_MENU, NULL);
+}

Added: trunk/components/editor/gtkhtml-face-chooser-menu.h
==============================================================================
--- (empty file)
+++ trunk/components/editor/gtkhtml-face-chooser-menu.h	Tue Dec  9 18:30:21 2008
@@ -0,0 +1,66 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gtkhtml-face-chooser-menu.h
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ *
+ * 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.
+ */
+
+#ifndef GTKHTML_FACE_CHOOSER_MENU_H
+#define GTKHTML_FACE_CHOOSER_MENU_H
+
+#include "gtkhtml-editor-common.h"
+#include "gtkhtml-face-chooser.h"
+
+/* Standard GObject macros */
+#define GTKHTML_TYPE_FACE_CHOOSER_MENU \
+	(gtkhtml_face_chooser_menu_get_type ())
+#define GTKHTML_FACE_CHOOSER_MENU(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), GTKHTML_TYPE_FACE_CHOOSER_MENU, GtkhtmlFaceChooserMenu))
+#define GTKHTML_FACE_CHOOSER_MENU_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), GTKHTML_TYPE_FACE_CHOOSER_MENU, GtkhtmlFaceChooserMenuClass))
+#define GTKHTML_IS_FACE_CHOOSER_MENU(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), GTKHTML_TYPE_FACE_CHOOSER_MENU))
+#define GTKHTML_IS_FACE_CHOOSER_MENU_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), GTKHTML_TYPE_FACE_CHOOSER_MENU))
+#define GTKHTML_FACE_CHOOSER_MENU_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), GTKHTML_TYPE_FACE_CHOOSER_MENU, GtkhtmlFaceChooserMenuClass))
+
+G_BEGIN_DECLS
+
+typedef struct _GtkhtmlFaceChooserMenu GtkhtmlFaceChooserMenu;
+typedef struct _GtkhtmlFaceChooserMenuClass GtkhtmlFaceChooserMenuClass;
+typedef struct _GtkhtmlFaceChooserMenuPrivate GtkhtmlFaceChooserMenuPrivate;
+
+struct _GtkhtmlFaceChooserMenu {
+	GtkMenu parent;
+	GtkhtmlFaceChooserMenuPrivate *priv;
+};
+
+struct _GtkhtmlFaceChooserMenuClass {
+	GtkMenuClass parent_class;
+};
+
+GType		gtkhtml_face_chooser_menu_get_type	(void);
+GtkWidget *	gtkhtml_face_chooser_menu_new		(void);
+
+G_END_DECLS
+
+#endif /* GTKHTML_FACE_CHOOSER_MENU_H */

Added: trunk/components/editor/gtkhtml-face-chooser.c
==============================================================================
--- (empty file)
+++ trunk/components/editor/gtkhtml-face-chooser.c	Tue Dec  9 18:30:21 2008
@@ -0,0 +1,151 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gtkhtml-face-chooser.c
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ *
+ * 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 "gtkhtml-face-chooser.h"
+
+#include <glib/gi18n-lib.h>
+
+static GtkhtmlFace available_faces[] = {
+	{ N_("_Smile"),		"face-smile",		":-)"	},
+	{ N_("S_ad"),		"face-sad",		":-("	},
+	{ N_("_Wink"),		"face-wink",		";-)"	},
+	{ N_("Ton_gue"),	"face-raspberry",	":-P"	},
+	{ N_("Laug_h"),		"face-laugh",		":-))"	},
+	{ N_("_Plain"),		"face-plain",		":-|"	},
+	{ N_("Smi_rk"),		"face-smirk",		":-!"	},
+	{ N_("_Embarrassed"),	"face-embarrassed",	":\"-)"	},
+	{ N_("_Big Smile"),	"face-smile-big",	":-D"	},
+	{ N_("Uncer_tain"),	"face-uncertain",	":-/"	},
+	{ N_("S_urprise"),	"face-surprise",	":-O"	},
+	{ N_("W_orried"),	"face-worried",		":-S"	},
+	{ N_("_Kiss"),		"face-kiss",		":-*"	},
+	{ N_("A_ngry"),		"face-angry",		"X-("	},
+	{ N_("_Cool"),		"face-cool",		"B-)"	},
+	{ N_("Ange_l"),		"face-angel",		"O:-)"	},
+	{ N_("Cr_ying"),	"face-crying",		":'("	},
+	{ N_("S_ick"),		"face-sick",		":-Q"	},
+	{ N_("Tire_d"),		"face-tired",		"|-)"	},
+	{ N_("De_vilish"),	"face-devilish",	">:-)"	},
+	{ N_("_Monkey"),	"face-monkey",		":-(|)"	}
+};
+
+enum {
+	ITEM_ACTIVATED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+static void
+face_chooser_class_init (GtkhtmlFaceChooserIface *iface)
+{
+	g_object_interface_install_property (
+		iface,
+		g_param_spec_boxed (
+			"current-face",
+			"Current Face",
+			"Currently selected face",
+			GTKHTML_TYPE_FACE,
+			G_PARAM_READWRITE));
+
+	signals[ITEM_ACTIVATED] = g_signal_new (
+		"item-activated",
+		G_TYPE_FROM_INTERFACE (iface),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET (GtkhtmlFaceChooserIface, item_activated),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+}
+
+GType
+gtkhtml_face_chooser_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (GtkhtmlFaceChooserIface),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) face_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, "GtkhtmlFaceChooser", &type_info, 0);
+
+		g_type_interface_add_prerequisite (type, G_TYPE_OBJECT);
+	}
+
+	return type;
+}
+
+GtkhtmlFace *
+gtkhtml_face_chooser_get_current_face (GtkhtmlFaceChooser *chooser)
+{
+	GtkhtmlFaceChooserIface *iface;
+
+	g_return_val_if_fail (GTKHTML_IS_FACE_CHOOSER (chooser), NULL);
+
+	iface = GTKHTML_FACE_CHOOSER_GET_IFACE (chooser);
+	g_return_val_if_fail (iface->get_current_face != NULL, NULL);
+
+	return iface->get_current_face (chooser);
+}
+
+void
+gtkhtml_face_chooser_set_current_face (GtkhtmlFaceChooser *chooser,
+                                       GtkhtmlFace *face)
+{
+	GtkhtmlFaceChooserIface *iface;
+
+	g_return_if_fail (GTKHTML_IS_FACE_CHOOSER (chooser));
+
+	iface = GTKHTML_FACE_CHOOSER_GET_IFACE (chooser);
+	g_return_if_fail (iface->set_current_face != NULL);
+
+	iface->set_current_face (chooser, face);
+}
+
+void
+gtkhtml_face_chooser_item_activated (GtkhtmlFaceChooser *chooser)
+{
+	g_return_if_fail (GTKHTML_IS_FACE_CHOOSER (chooser));
+
+	g_signal_emit (chooser, signals[ITEM_ACTIVATED], 0);
+}
+
+GList *
+gtkhtml_face_chooser_get_items (GtkhtmlFaceChooser *chooser)
+{
+	GList *list = NULL;
+	gint ii;
+
+	for (ii = 0; ii < G_N_ELEMENTS (available_faces); ii++)
+		list = g_list_prepend (list, &available_faces[ii]);
+
+	return g_list_reverse (list);
+}

Added: trunk/components/editor/gtkhtml-face-chooser.h
==============================================================================
--- (empty file)
+++ trunk/components/editor/gtkhtml-face-chooser.h	Tue Dec  9 18:30:21 2008
@@ -0,0 +1,69 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gtkhtml-face-chooser.h
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ *
+ * 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.
+ */
+
+#ifndef GTKHTML_FACE_CHOOSER_H
+#define GTKHTML_FACE_CHOOSER_H
+
+#include "gtkhtml-editor-common.h"
+#include "gtkhtml-face.h"
+
+/* Standard GObject macros */
+#define GTKHTML_TYPE_FACE_CHOOSER \
+	(gtkhtml_face_chooser_get_type ())
+#define GTKHTML_FACE_CHOOSER(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), GTKHTML_TYPE_FACE_CHOOSER, GtkhtmlFaceChooser))
+#define GTKHTML_IS_FACE_CHOOSER(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), GTKHTML_TYPE_FACE_CHOOSER))
+#define GTKHTML_FACE_CHOOSER_GET_IFACE(obj) \
+	(G_TYPE_INSTANCE_GET_INTERFACE \
+	((obj), GTKHTML_TYPE_FACE_CHOOSER, GtkhtmlFaceChooserIface))
+
+G_BEGIN_DECLS
+
+typedef struct _GtkhtmlFaceChooser GtkhtmlFaceChooser;
+typedef struct _GtkhtmlFaceChooserIface GtkhtmlFaceChooserIface;
+
+struct _GtkhtmlFaceChooserIface {
+	GTypeInterface parent_iface;
+
+	/* Methods */
+	GtkhtmlFace *	(*get_current_face)	(GtkhtmlFaceChooser *chooser);
+	void		(*set_current_face)	(GtkhtmlFaceChooser *chooser,
+						 GtkhtmlFace *face);
+
+	/* Signals */
+	void		(*item_activated)	(GtkhtmlFaceChooser *chooser);
+};
+
+GType		gtkhtml_face_chooser_get_type	(void);
+GtkhtmlFace *	gtkhtml_face_chooser_get_current_face
+						(GtkhtmlFaceChooser *chooser);
+void		gtkhtml_face_chooser_set_current_face
+						(GtkhtmlFaceChooser *chooser,
+						 GtkhtmlFace *face);
+void		gtkhtml_face_chooser_item_activated
+						(GtkhtmlFaceChooser *chooser);
+GList *		gtkhtml_face_chooser_get_items	(GtkhtmlFaceChooser *chooser);
+
+G_END_DECLS
+
+#endif /* GTKHTML_FACE_CHOOSER_H */

Added: trunk/components/editor/gtkhtml-face-tool-button.c
==============================================================================
--- (empty file)
+++ trunk/components/editor/gtkhtml-face-tool-button.c	Tue Dec  9 18:30:21 2008
@@ -0,0 +1,675 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gtkhtml-face-tool-button.c
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ *
+ * 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 "gtkhtml-face-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 "gtkhtml-face-chooser.h"
+
+#define GTKHTML_FACE_TOOL_BUTTON_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), GTKHTML_TYPE_FACE_TOOL_BUTTON, GtkhtmlFaceToolButtonPrivate))
+
+/* XXX Should calculate this dynamically. */
+#define NUM_ROWS	7
+#define NUM_COLS	3
+
+enum {
+	PROP_0,
+	PROP_CURRENT_FACE,
+	PROP_POPUP_SHOWN
+};
+
+enum {
+	POPUP,
+	POPDOWN,
+	LAST_SIGNAL
+};
+
+struct _GtkhtmlFaceToolButtonPrivate {
+	GtkWidget *active_button;  /* not referenced */
+	GtkWidget *table;
+	GtkWidget *window;
+
+	guint popup_shown	: 1;
+	guint popup_in_progress	: 1;
+};
+
+static gpointer parent_class;
+static guint signals[LAST_SIGNAL];
+
+/* XXX Copied from _gtk_toolbar_elide_underscores() */
+static gchar *
+face_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
+face_tool_button_reposition_window (GtkhtmlFaceToolButton *button)
+{
+	GdkScreen *screen;
+	GdkWindow *window;
+	GdkRectangle monitor;
+	gint monitor_num;
+	gint x, y, width, height;
+
+	window = GTK_WIDGET (button)->window;
+	screen = gtk_widget_get_screen (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_NO_WINDOW (button)) {
+		x += GTK_WIDGET (button)->allocation.x;
+		y += GTK_WIDGET (button)->allocation.y;
+	}
+
+	width = button->priv->window->allocation.width;
+	height = button->priv->window->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
+face_tool_button_face_clicked_cb (GtkhtmlFaceToolButton *button,
+                                  GtkWidget *face_button)
+{
+	button->priv->active_button = face_button;
+	gtkhtml_face_tool_button_popdown (button);
+}
+
+static gboolean
+face_tool_button_face_release_event_cb (GtkhtmlFaceToolButton *button,
+                                        GdkEventButton *event,
+                                        GtkButton *face_button)
+{
+	if (GTK_WIDGET_STATE (button) != GTK_STATE_NORMAL)
+		gtk_button_clicked (face_button);
+
+	return FALSE;
+}
+
+static gboolean
+face_tool_button_button_release_event_cb (GtkhtmlFaceToolButton *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:
+	gtkhtml_face_tool_button_popdown (button);
+
+	return TRUE;
+}
+
+static void
+face_tool_button_child_show_cb (GtkhtmlFaceToolButton *button)
+{
+	button->priv->popup_shown = TRUE;
+	g_object_notify (G_OBJECT (button), "popup-shown");
+}
+
+static void
+face_tool_button_child_hide_cb (GtkhtmlFaceToolButton *button)
+{
+	button->priv->popup_shown = FALSE;
+	g_object_notify (G_OBJECT (button), "popup-shown");
+}
+
+static gboolean
+face_tool_button_child_key_press_event_cb (GtkhtmlFaceToolButton *button,
+                                           GdkEventKey *event)
+{
+	GtkWidget *window = button->priv->window;
+
+	if (!gtk_bindings_activate_event (GTK_OBJECT (window), event))
+		gtk_bindings_activate_event (GTK_OBJECT (button), event);
+
+	return TRUE;
+}
+
+static void
+face_tool_button_set_property (GObject *object,
+                               guint property_id,
+                               const GValue *value,
+                               GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_CURRENT_FACE:
+			gtkhtml_face_chooser_set_current_face (
+				GTKHTML_FACE_CHOOSER (object),
+				g_value_get_boxed (value));
+			return;
+
+		case PROP_POPUP_SHOWN:
+			if (g_value_get_boolean (value))
+				gtkhtml_face_tool_button_popup (
+					GTKHTML_FACE_TOOL_BUTTON (object));
+			else
+				gtkhtml_face_tool_button_popdown (
+					GTKHTML_FACE_TOOL_BUTTON (object));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+face_tool_button_get_property (GObject *object,
+                               guint property_id,
+                               GValue *value,
+                               GParamSpec *pspec)
+{
+	GtkhtmlFaceToolButtonPrivate *priv;
+
+	priv = GTKHTML_FACE_TOOL_BUTTON_GET_PRIVATE (object);
+
+	switch (property_id) {
+		case PROP_CURRENT_FACE:
+			g_value_set_boxed (
+				value,
+				gtkhtml_face_chooser_get_current_face (
+				GTKHTML_FACE_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
+face_tool_button_dispose (GObject *object)
+{
+	GtkhtmlFaceToolButtonPrivate *priv;
+
+	priv = GTKHTML_FACE_TOOL_BUTTON_GET_PRIVATE (object);
+
+	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
+face_tool_button_press_event (GtkWidget *widget,
+                              GdkEventButton *event)
+{
+	GtkhtmlFaceToolButton *button;
+	GtkToggleToolButton *toggle_button;
+	GtkWidget *event_widget;
+
+	button = GTKHTML_FACE_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;
+
+	gtkhtml_face_tool_button_popup (button);
+
+	button->priv->popup_in_progress = TRUE;
+
+	return TRUE;
+}
+
+static void
+face_tool_button_toggled (GtkToggleToolButton *button)
+{
+	if (gtk_toggle_tool_button_get_active (button))
+		gtkhtml_face_tool_button_popup (
+			GTKHTML_FACE_TOOL_BUTTON (button));
+	else
+		gtkhtml_face_tool_button_popdown (
+			GTKHTML_FACE_TOOL_BUTTON (button));
+}
+
+static void
+face_tool_button_popup (GtkhtmlFaceToolButton *button)
+{
+	GtkToggleToolButton *tool_button;
+	GdkWindow *window;
+	GdkGrabStatus status;
+
+	if (!GTK_WIDGET_REALIZED (button))
+		return;
+
+	if (button->priv->popup_shown)
+		return;
+
+	/* Position the window over the button. */
+	face_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 = button->priv->window->window;
+	status = gdk_pointer_grab (
+		window, TRUE,
+		GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+		GDK_POINTER_MOTION_MASK, NULL, NULL, GDK_CURRENT_TIME);
+	if (status == GDK_GRAB_SUCCESS) {
+		status = gdk_keyboard_grab (window, TRUE, GDK_CURRENT_TIME);
+		if (status != GDK_GRAB_SUCCESS)
+			gdk_display_pointer_ungrab (
+				gdk_drawable_get_display (window),
+				GDK_CURRENT_TIME);
+	}
+
+	if (status == GDK_GRAB_SUCCESS)
+		gtk_grab_add (button->priv->window);
+	else
+		gtk_widget_hide (button->priv->window);
+}
+
+static void
+face_tool_button_popdown (GtkhtmlFaceToolButton *button)
+{
+	GtkToggleToolButton *tool_button;
+
+	if (!GTK_WIDGET_REALIZED (button))
+		return;
+
+	if (!button->priv->popup_shown)
+		return;
+
+	/* Hide the pop-up. */
+	gtk_grab_remove (button->priv->window);
+	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);
+}
+
+static GtkhtmlFace *
+face_tool_button_get_current_face (GtkhtmlFaceChooser *chooser)
+{
+	GtkhtmlFaceToolButtonPrivate *priv;
+
+	priv = GTKHTML_FACE_TOOL_BUTTON_GET_PRIVATE (chooser);
+
+	if (priv->active_button == NULL)
+		return NULL;
+
+	return g_object_get_data (G_OBJECT (priv->active_button), "face");
+}
+
+static void
+face_tool_button_set_current_face (GtkhtmlFaceChooser *chooser,
+                                   GtkhtmlFace *face)
+{
+	GtkhtmlFaceToolButtonPrivate *priv;
+	GList *list, *iter;
+
+	priv = GTKHTML_FACE_TOOL_BUTTON_GET_PRIVATE (chooser);
+
+	list = gtk_container_get_children (GTK_CONTAINER (priv->table));
+
+	for (iter = list; iter != NULL; iter = iter->next) {
+		GtkWidget *item = iter->data;
+		GtkhtmlFace *candidate;
+
+		candidate = g_object_get_data (G_OBJECT (item), "face");
+		if (candidate == NULL)
+			continue;
+
+		if (gtkhtml_face_equal (face, candidate)) {
+			gtk_button_clicked (GTK_BUTTON (item));
+			break;
+		}
+	}
+
+	g_list_free (list);
+}
+
+static void
+face_tool_button_class_init (GtkhtmlFaceToolButtonClass *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 (GtkhtmlFaceToolButtonPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->set_property = face_tool_button_set_property;
+	object_class->get_property = face_tool_button_get_property;
+	object_class->dispose = face_tool_button_dispose;
+
+	widget_class = GTK_WIDGET_CLASS (class);
+	widget_class->button_press_event = face_tool_button_press_event;
+
+	toggle_tool_button_class = GTK_TOGGLE_TOOL_BUTTON_CLASS (class);
+	toggle_tool_button_class->toggled = face_tool_button_toggled;
+
+	class->popup = face_tool_button_popup;
+	class->popdown = face_tool_button_popdown;
+
+	g_object_class_override_property (
+		object_class, PROP_CURRENT_FACE, "current-face");
+
+	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 (GtkhtmlFaceToolButtonClass, 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 (GtkhtmlFaceToolButtonClass, popdown),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__VOID,
+		G_TYPE_NONE, 0);
+
+	gtk_binding_entry_add_signal (
+		gtk_binding_set_by_class (class),
+		GDK_Down, GDK_MOD1_MASK, "popup", 0);
+	gtk_binding_entry_add_signal (
+		gtk_binding_set_by_class (class),
+		GDK_KP_Down, GDK_MOD1_MASK, "popup", 0);
+
+	gtk_binding_entry_add_signal (
+		gtk_binding_set_by_class (class),
+		GDK_Up, GDK_MOD1_MASK, "popdown", 0);
+	gtk_binding_entry_add_signal (
+		gtk_binding_set_by_class (class),
+		GDK_KP_Up, GDK_MOD1_MASK, "popdown", 0);
+	gtk_binding_entry_add_signal (
+		gtk_binding_set_by_class (class),
+		GDK_Escape, 0, "popdown", 0);
+}
+
+static void
+face_tool_button_iface_init (GtkhtmlFaceChooserIface *iface)
+{
+	iface->get_current_face = face_tool_button_get_current_face;
+	iface->set_current_face = face_tool_button_set_current_face;
+}
+
+static void
+face_tool_button_init (GtkhtmlFaceToolButton *button)
+{
+	GtkhtmlFaceChooser *chooser;
+	GtkWidget *toplevel;
+	GtkWidget *container;
+	GtkWidget *widget;
+	GtkWidget *window;
+	GList *list, *iter;
+	gint ii;
+
+	button->priv = GTKHTML_FACE_TOOL_BUTTON_GET_PRIVATE (button);
+
+	/* 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_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 (face_tool_button_child_show_cb), button);
+	g_signal_connect_swapped (
+		window, "hide",
+		G_CALLBACK (face_tool_button_child_hide_cb), button);
+	g_signal_connect_swapped (
+		window, "button-release-event",
+		G_CALLBACK (face_tool_button_button_release_event_cb),
+		button);
+	g_signal_connect_swapped (
+		window, "key-press-event",
+		G_CALLBACK (face_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 = GTKHTML_FACE_CHOOSER (button);
+	list = gtkhtml_face_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++) {
+		GtkhtmlFace *face = iter->data;
+		guint left = ii % NUM_COLS;
+		guint top = ii / NUM_COLS;
+		gchar *tooltip;
+
+		tooltip = face_tool_button_elide_underscores (
+			gettext (face->label));
+
+		widget = gtk_button_new ();
+		gtk_button_set_image (
+			GTK_BUTTON (widget),
+			gtk_image_new_from_icon_name (
+			face->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), "face",
+			gtkhtml_face_copy (face),
+			(GDestroyNotify) gtkhtml_face_free);
+
+		g_signal_connect_swapped (
+			widget, "clicked",
+			G_CALLBACK (face_tool_button_face_clicked_cb),
+			button);
+
+		g_signal_connect_swapped (
+			widget, "clicked",
+			G_CALLBACK (gtkhtml_face_chooser_item_activated),
+			chooser);
+
+		g_signal_connect_swapped (
+			widget, "button-release-event",
+			G_CALLBACK (face_tool_button_face_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
+gtkhtml_face_tool_button_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (GtkhtmlFaceToolButtonClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) face_tool_button_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (GtkhtmlFaceToolButton),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) face_tool_button_init,
+			NULL   /* value_table */
+		};
+
+		static const GInterfaceInfo iface_info = {
+			(GInterfaceInitFunc) face_tool_button_iface_init,
+			(GInterfaceFinalizeFunc) NULL,
+			NULL  /* interface_data */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_TOGGLE_TOOL_BUTTON,
+			"GtkhtmlFaceToolButton", &type_info, 0);
+
+		g_type_add_interface_static (
+			type, GTKHTML_TYPE_FACE_CHOOSER, &iface_info);
+	}
+
+	return type;
+}
+
+GtkToolItem *
+gtkhtml_face_tool_button_new (void)
+{
+	return g_object_new (GTKHTML_TYPE_FACE_TOOL_BUTTON, NULL);
+}
+
+void
+gtkhtml_face_tool_button_popup (GtkhtmlFaceToolButton *button)
+{
+	g_return_if_fail (GTKHTML_IS_FACE_TOOL_BUTTON (button));
+
+	g_signal_emit (button, signals[POPUP], 0);
+}
+
+void
+gtkhtml_face_tool_button_popdown (GtkhtmlFaceToolButton *button)
+{
+	g_return_if_fail (GTKHTML_IS_FACE_TOOL_BUTTON (button));
+
+	g_signal_emit (button, signals[POPDOWN], 0);
+}

Added: trunk/components/editor/gtkhtml-face-tool-button.h
==============================================================================
--- (empty file)
+++ trunk/components/editor/gtkhtml-face-tool-button.h	Tue Dec  9 18:30:21 2008
@@ -0,0 +1,70 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gtkhtml-face-tool-button.h
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ *
+ * 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.
+ */
+
+#ifndef GTKHTML_FACE_TOOL_BUTTON_H
+#define GTKHTML_FACE_TOOL_BUTTON_H
+
+#include "gtkhtml-editor-common.h"
+
+/* Standard GObject macros */
+#define GTKHTML_TYPE_FACE_TOOL_BUTTON \
+	(gtkhtml_face_tool_button_get_type ())
+#define GTKHTML_FACE_TOOL_BUTTON(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), GTKHTML_TYPE_FACE_TOOL_BUTTON, GtkhtmlFaceToolButton))
+#define GTKHTML_FACE_TOOL_BUTTON_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), GTKHTML_TYPE_FACE_TOOL_BUTTON, GtkhtmlFaceToolButtonClass))
+#define GTKHTML_IS_FACE_TOOL_BUTTON(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), GTKHTML_TYPE_FACE_TOOL_BUTTON))
+#define GTKHTML_IS_FACE_TOOL_BUTTON_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), GTKHTML_TYPE_FACE_TOOL_BUTTON))
+#define GTKHTML_FACE_TOOL_BUTTON_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), GTKHTML_TYPE_FACE_TOOL_BUTTON, GtkhtmlFaceToolButtonClass))
+
+G_BEGIN_DECLS
+
+typedef struct _GtkhtmlFaceToolButton GtkhtmlFaceToolButton;
+typedef struct _GtkhtmlFaceToolButtonClass GtkhtmlFaceToolButtonClass;
+typedef struct _GtkhtmlFaceToolButtonPrivate GtkhtmlFaceToolButtonPrivate;
+
+struct _GtkhtmlFaceToolButton {
+	GtkToggleToolButton parent;
+	GtkhtmlFaceToolButtonPrivate *priv;
+};
+
+struct _GtkhtmlFaceToolButtonClass {
+	GtkToggleToolButtonClass parent_class;
+
+	void	(*popup)		(GtkhtmlFaceToolButton *button);
+	void	(*popdown)		(GtkhtmlFaceToolButton *button);
+};
+
+GType		gtkhtml_face_tool_button_get_type	(void);
+GtkToolItem *	gtkhtml_face_tool_button_new		(void);
+void		gtkhtml_face_tool_button_popup		(GtkhtmlFaceToolButton *button);
+void		gtkhtml_face_tool_button_popdown	(GtkhtmlFaceToolButton *button);
+
+G_END_DECLS
+
+#endif /* GTKHTML_FACE_TOOL_BUTTON_H */

Added: trunk/components/editor/gtkhtml-face.c
==============================================================================
--- (empty file)
+++ trunk/components/editor/gtkhtml-face.c	Tue Dec  9 18:30:21 2008
@@ -0,0 +1,88 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gtkhtml-face.h
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ *
+ * 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 "gtkhtml-face.h"
+
+static GtkhtmlFace *
+face_copy (GtkhtmlFace *face)
+{
+	GtkhtmlFace *copy;
+
+	copy = g_slice_new (GtkhtmlFace);
+	copy->label = g_strdup (face->label);
+	copy->icon_name = g_strdup (face->icon_name);
+	copy->text_face = g_strdup (face->text_face);
+
+	return copy;
+}
+
+static void
+face_free (GtkhtmlFace *face)
+{
+	g_free (face->label);
+	g_free (face->icon_name);
+	g_free (face->text_face);
+	g_slice_free (GtkhtmlFace, face);
+}
+
+GType
+gtkhtml_face_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0))
+		type = g_boxed_type_register_static (
+			"GtkhtmlFace",
+			(GBoxedCopyFunc) face_copy,
+			(GBoxedFreeFunc) face_free);
+
+	return type;
+}
+
+gboolean
+gtkhtml_face_equal (GtkhtmlFace *face_a,
+                    GtkhtmlFace *face_b)
+{
+	if (face_a == face_b)
+		return TRUE;
+
+	if (g_strcmp0 (face_a->label, face_b->label) != 0)
+		return FALSE;
+
+	if (g_strcmp0 (face_a->icon_name, face_b->icon_name) != 0)
+		return FALSE;
+
+	if (g_strcmp0 (face_a->text_face, face_b->text_face) != 0)
+		return FALSE;
+
+	return TRUE;
+}
+
+GtkhtmlFace *
+gtkhtml_face_copy (GtkhtmlFace *face)
+{
+	return g_boxed_copy (GTKHTML_TYPE_FACE, face);
+}
+
+void
+gtkhtml_face_free (GtkhtmlFace *face)
+{
+	g_boxed_free (GTKHTML_TYPE_FACE, face);
+}

Added: trunk/components/editor/gtkhtml-face.h
==============================================================================
--- (empty file)
+++ trunk/components/editor/gtkhtml-face.h	Tue Dec  9 18:30:21 2008
@@ -0,0 +1,47 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gtkhtml-face.h
+ *
+ * Copyright (C) 2008 Novell, Inc.
+ *
+ * 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.
+ */
+
+#ifndef GTKHTML_FACE_H
+#define GTKHTML_FACE_H
+
+#include "gtkhtml-editor-common.h"
+
+#define GTKHTML_TYPE_FACE \
+	(gtkhtml_face_get_type ())
+
+G_BEGIN_DECLS
+
+typedef struct _GtkhtmlFace GtkhtmlFace;
+
+struct _GtkhtmlFace {
+	gchar *label;
+	gchar *icon_name;
+	gchar *text_face;
+};
+
+GType		gtkhtml_face_get_type		(void);
+gboolean	gtkhtml_face_equal		(GtkhtmlFace *face_a,
+						 GtkhtmlFace *face_b);
+GtkhtmlFace *	gtkhtml_face_copy		(GtkhtmlFace *face);
+void		gtkhtml_face_free		(GtkhtmlFace *face);
+
+G_END_DECLS
+
+#endif /* GTKHTML_FACE_H */

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Tue Dec  9 18:30:21 2008
@@ -8,7 +8,7 @@
 # Required Package Versions
 m4_define([gtk_minimum_version], [2.12.0])
 m4_define([gail_minimum_version], [1.1.0])
-m4_define([gnome_icon_theme_minimum_version], [1.2.0])
+m4_define([gnome_icon_theme_minimum_version], [2.22.0])
 m4_define([libbonobo_minimum_version], [2.20.3])
 m4_define([libbonoboui_minimum_version], [2.2.4])
 m4_define([libglade_minimum_version], [2.0.0])

Modified: trunk/gtkhtml/htmlengine-edit-cut-and-paste.c
==============================================================================
--- trunk/gtkhtml/htmlengine-edit-cut-and-paste.c	(original)
+++ trunk/gtkhtml/htmlengine-edit-cut-and-paste.c	Tue Dec  9 18:30:21 2008
@@ -1266,21 +1266,46 @@
 	html_engine_thaw (e);
 }
 
-static char *picto_chars = "DO)(|/PQ\0:-\0:\0:-\0:\0:;=-\0:;\0:-~\0:\0:\0:-\0:\0:-\0:\0:-\0:\0:-\0:\0";
-static gint picto_states [] = { 9, 14, 19, 27, 35, 40, 45, 50, 0, -1, 12, 0, -1, 0, -2, 17, 0, -2, 0, -3, -4, -5, 24, 0, -3, -4, 0, -6, 31, 33, 0, -6, 0, -11, 0, -8, 38, 0, -8, 0, -9, 43, 0, -9, 0, -10, 48, 0, -10, 0, -12, 53, 0, -12, 0};
+static char *picto_chars =
+	/*  0 */ "DO)(|/PQ*!"
+	/* 10 */ "S\0:-\0:\0:-\0"
+	/* 20 */ ":\0:;=-\"\0:;"
+	/* 30 */ "B\"|\0:-'\0:X"
+	/* 40 */ "\0:\0:-\0:\0:-"
+	/* 50 */ "\0:\0:-\0:\0:-"
+	/* 60 */ "\0:\0:\0:-\0:\0"
+	/* 70 */ ":-\0:\0:-\0:\0";
+static gint picto_states [] = {
+	/*  0 */  12,  17,  22,  34,  43,  48,  53,  58,  65,  70,
+	/* 10 */  75,   0, -15,  15,   0, -15,   0, -17,  20,   0,
+	/* 20 */ -17,   0, -14, -20, -14,  28,  63,   0, -14, -20,
+	/* 30 */  -3,  63, -18,   0, -12,  38,  41,   0, -12,  -2,
+	/* 40 */   0,  -4,   0, -10,  46,   0, -10,   0, -19,  51,
+	/* 50 */   0, -19,   0, -11,  56,   0, -11,   0, -13,  61,
+	/* 60 */   0, -13,   0,  -6,   0,  68,  -7,   0,  -7,   0,
+	/* 70 */ -16,  73,   0, -16,   0, -21,  78,   0, -21,   0 };
 static gchar *picto_icon_names [] = {
-	"stock_smiley-6",
-	"stock_smiley-5",
-	"stock_smiley-1",
-	"stock_smiley-3",
-	"stock_smiley-2",
-	"stock_smiley-4",
-	"stock_smiley-7",  /* XXX No idea if this one is correct */
-	"stock_smiley-8",
-	"stock_smiley-9",
-	"stock_smiley-10",
-	"stock_smiley-11",
-	"stock_smiley-26",
+	"face-angel",
+	"face-angry",
+	"face-cool",
+	"face-crying",
+	"face-devilish",
+	"face-embarrassed",
+	"face-kiss",
+	"face-laugh",		/* not used */
+	"face-monkey",		/* not used */
+	"face-plain",
+	"face-raspberry",
+	"face-sad",
+	"face-sick",
+	"face-smile",
+	"face-smile-big",
+	"face-smirk",
+	"face-surprise",
+	"face-tired",
+	"face-uncertain",
+	"face-wink",
+	"face-worried"
 };
 
 static void
@@ -1311,6 +1336,18 @@
 		pos --;
 	}
 
+	/* Special case needed to recognize angel and devilish. */
+	if (pos > 0 && state == -14) {
+		uc = html_text_get_char (HTML_TEXT (e->cursor->object), pos - 1);
+		if (uc == 'O') {
+			state = -1;
+			pos--;
+		} else if (uc == '>') {
+			state = -5;
+			pos--;
+		}
+	}
+
 	if (state < 0) {
 		HTMLObject *image;
 		GtkIconInfo *icon_info;



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