X-Git-Url: https://git.0d.be/?p=empathy.git;a=blobdiff_plain;f=libempathy-gtk%2Fempathy-smiley-manager.c;h=d63763d8933da270ef0fe846a95c1baaacd609ea;hp=4137db63d512b0bc45196ccfedd782a6798722c9;hb=05687495cc78fe906ebe760e0f1fccaffc768bb0;hpb=4782781b30c2884f07a7451da03eca3244c62b6f diff --git a/libempathy-gtk/empathy-smiley-manager.c b/libempathy-gtk/empathy-smiley-manager.c index 4137db63..d63763d8 100644 --- a/libempathy-gtk/empathy-smiley-manager.c +++ b/libempathy-gtk/empathy-smiley-manager.c @@ -15,18 +15,19 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Authors: Dafydd Harrie * Xavier Claessens */ -#include +#include "config.h" +#include "empathy-smiley-manager.h" -#include +#include +#include -#include -#include "empathy-smiley-manager.h" #include "empathy-ui-utils.h" +#include "empathy-utils.h" typedef struct _SmileyManagerTree SmileyManagerTree; @@ -37,13 +38,16 @@ typedef struct { } EmpathySmileyManagerPriv; struct _SmileyManagerTree { - gunichar c; - GdkPixbuf *pixbuf; - GSList *childrens; + gunichar c; + GdkPixbuf *pixbuf; + gchar *path; + GSList *childrens; }; G_DEFINE_TYPE (EmpathySmileyManager, empathy_smiley_manager, G_TYPE_OBJECT); +static EmpathySmileyManager *manager_singleton = NULL; + static SmileyManagerTree * smiley_manager_tree_new (gunichar c) { @@ -53,6 +57,7 @@ smiley_manager_tree_new (gunichar c) tree->c = c; tree->pixbuf = NULL; tree->childrens = NULL; + tree->path = NULL; return tree; } @@ -74,6 +79,7 @@ smiley_manager_tree_free (SmileyManagerTree *tree) g_object_unref (tree->pixbuf); } g_slist_free (tree->childrens); + g_free (tree->path); g_slice_free (SmileyManagerTree, tree); } @@ -83,24 +89,16 @@ smiley_new (GdkPixbuf *pixbuf, const gchar *str) EmpathySmiley *smiley; smiley = g_slice_new0 (EmpathySmiley); - if (pixbuf) { - smiley->pixbuf = g_object_ref (pixbuf); - } + smiley->pixbuf = g_object_ref (pixbuf); smiley->str = g_strdup (str); return smiley; } -void -empathy_smiley_free (EmpathySmiley *smiley) +static void +smiley_free (EmpathySmiley *smiley) { - if (!smiley) { - return; - } - - if (smiley->pixbuf) { - g_object_unref (smiley->pixbuf); - } + g_object_unref (smiley->pixbuf); g_free (smiley->str); g_slice_free (EmpathySmiley, smiley); } @@ -111,16 +109,37 @@ smiley_manager_finalize (GObject *object) EmpathySmileyManagerPriv *priv = GET_PRIV (object); smiley_manager_tree_free (priv->tree); - g_slist_foreach (priv->smileys, (GFunc) empathy_smiley_free, NULL); + g_slist_foreach (priv->smileys, (GFunc) smiley_free, NULL); g_slist_free (priv->smileys); } +static GObject * +smiley_manager_constructor (GType type, + guint n_props, + GObjectConstructParam *props) +{ + GObject *retval; + + if (manager_singleton) { + retval = g_object_ref (manager_singleton); + } else { + retval = G_OBJECT_CLASS (empathy_smiley_manager_parent_class)->constructor + (type, n_props, props); + + manager_singleton = EMPATHY_SMILEY_MANAGER (retval); + g_object_add_weak_pointer (retval, (gpointer) &manager_singleton); + } + + return retval; +} + static void empathy_smiley_manager_class_init (EmpathySmileyManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = smiley_manager_finalize; + object_class->constructor = smiley_manager_constructor; g_type_class_add_private (object_class, sizeof (EmpathySmileyManagerPriv)); } @@ -134,22 +153,14 @@ empathy_smiley_manager_init (EmpathySmileyManager *manager) manager->priv = priv; priv->tree = smiley_manager_tree_new ('\0'); priv->smileys = NULL; + + empathy_smiley_manager_load (manager); } EmpathySmileyManager * -empathy_smiley_manager_new (void) +empathy_smiley_manager_dup_singleton (void) { - static EmpathySmileyManager *manager = NULL; - - if (!manager) { - manager = g_object_new (EMPATHY_TYPE_SMILEY_MANAGER, NULL); - g_object_add_weak_pointer (G_OBJECT (manager), (gpointer) &manager); - empathy_smiley_manager_load (manager); - } else { - g_object_ref (manager); - } - - return manager; + return g_object_new (EMPATHY_TYPE_SMILEY_MANAGER, NULL); } static SmileyManagerTree * @@ -185,8 +196,9 @@ smiley_manager_tree_find_or_insert_child (SmileyManagerTree *tree, gunichar c) static void smiley_manager_tree_insert (SmileyManagerTree *tree, - GdkPixbuf *smiley, - const gchar *str) + GdkPixbuf *pixbuf, + const gchar *str, + const gchar *path) { SmileyManagerTree *child; @@ -194,27 +206,33 @@ smiley_manager_tree_insert (SmileyManagerTree *tree, str = g_utf8_next_char (str); if (*str) { - smiley_manager_tree_insert (child, smiley, str); + smiley_manager_tree_insert (child, pixbuf, str, path); return; } - child->pixbuf = g_object_ref (smiley); + child->pixbuf = g_object_ref (pixbuf); + child->path = g_strdup (path); } static void smiley_manager_add_valist (EmpathySmileyManager *manager, - GdkPixbuf *smiley, + GdkPixbuf *pixbuf, + const gchar *path, const gchar *first_str, va_list var_args) { EmpathySmileyManagerPriv *priv = GET_PRIV (manager); const gchar *str; + EmpathySmiley *smiley; for (str = first_str; str; str = va_arg (var_args, gchar*)) { - smiley_manager_tree_insert (priv->tree, smiley, str); + smiley_manager_tree_insert (priv->tree, pixbuf, str, path); } - priv->smileys = g_slist_prepend (priv->smileys, smiley_new (smiley, first_str)); + g_object_set_data_full (G_OBJECT (pixbuf), "smiley_str", + g_strdup (first_str), g_free); + smiley = smiley_new (pixbuf, first_str); + priv->smileys = g_slist_prepend (priv->smileys, smiley); } void @@ -223,120 +241,194 @@ empathy_smiley_manager_add (EmpathySmileyManager *manager, const gchar *first_str, ...) { - GdkPixbuf *smiley; + GdkPixbuf *pixbuf; va_list var_args; g_return_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager)); - g_return_if_fail (!G_STR_EMPTY (icon_name)); - g_return_if_fail (!G_STR_EMPTY (first_str)); + g_return_if_fail (!TPAW_STR_EMPTY (icon_name)); + g_return_if_fail (!TPAW_STR_EMPTY (first_str)); + + pixbuf = tpaw_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_MENU); + if (pixbuf) { + gchar *path; - smiley = empathy_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_MENU); - if (smiley) { va_start (var_args, first_str); - smiley_manager_add_valist (manager, smiley, first_str, var_args); + path = tpaw_filename_from_icon_name (icon_name, GTK_ICON_SIZE_MENU); + smiley_manager_add_valist (manager, pixbuf, path, first_str, var_args); va_end (var_args); - g_object_unref (smiley); + g_object_unref (pixbuf); + g_free (path); } } void -empathy_smiley_manager_add_from_pixbuf (EmpathySmileyManager *manager, - GdkPixbuf *smiley, - const gchar *first_str, - ...) +empathy_smiley_manager_load (EmpathySmileyManager *manager) { - va_list var_args; - g_return_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager)); - g_return_if_fail (GDK_IS_PIXBUF (smiley)); - g_return_if_fail (!G_STR_EMPTY (first_str)); - va_start (var_args, first_str); - smiley_manager_add_valist (manager, smiley, first_str, var_args); - va_end (var_args); + /* From fd.o icon-naming spec */ + + /* U+1F47C BABY ANGEL */ + empathy_smiley_manager_add (manager, "face-angel", "👼", "O:-)", "O:)", NULL); + /* U+1F620 ANGRY FACE */ + empathy_smiley_manager_add (manager, "face-angry", "😠", "X-(", ":@", NULL); + /* U+1F60E SMILING FACE WITH SUNGLASSES */ + empathy_smiley_manager_add (manager, "face-cool", "😎", "B-)", "B-|", NULL); + /* U+1F62D LOUDLY CRYING FACE */ + empathy_smiley_manager_add (manager, "face-crying", "😭", ":'(", NULL); + /* U+1F608 SMILING FACE WITH HORNS */ + empathy_smiley_manager_add (manager, "face-devilish", "😈", ">:-)", ">:)", NULL); + /* U+1F633 FLUSHED FACE */ + empathy_smiley_manager_add (manager, "face-embarrassed","😳", ":-[", ":[", ":-$", ":$", NULL); + /* no suitable character in unicode */ + empathy_smiley_manager_add (manager, "face-glasses", "8-)", NULL); + /* U+1F618 FACE THROWING A KISS */ + empathy_smiley_manager_add (manager, "face-kiss", "😘", ":-*", ":*", NULL); + /* U+1F604 SMILING FACE WITH OPEN MOUTH AND SMILING EYES" */ + empathy_smiley_manager_add (manager, "face-laugh", "😄", ":-))", ":))", NULL); + /* U+1F435 MONKEY */ + empathy_smiley_manager_add (manager, "face-monkey", "🐵", ":-(|)", ":(|)", NULL); + /* U+1F610 NEUTRAL FACE */ + empathy_smiley_manager_add (manager, "face-plain", "😐", ":-|", ":|", NULL); + /* U+1F61B FACE WITH STUCK-OUT TONGUE */ + empathy_smiley_manager_add (manager, "face-raspberry", "😛", ":-P", ":P", ":-p", ":p", NULL); + /* U+1F626 FROWING FACE WITH OPEN MOUTH */ + empathy_smiley_manager_add (manager, "face-sad", "😦", ":-(", ":(", NULL); + /* U+1F635 DIZZY FACE */ + empathy_smiley_manager_add (manager, "face-sick", "😵", ":-&", ":&", NULL); + /* U+1F603 SMILING FACE WITH OPEN MOUTH */ + empathy_smiley_manager_add (manager, "face-smile", "😃", ":-)", ":)", ":]", "=)", NULL); + /* U+1F601 GRINNING FACE WITH SMILING EYES */ + empathy_smiley_manager_add (manager, "face-smile-big", "😁", ":-D", ":D", ":-d", ":d", NULL); + /* U+1F60F SMIRKING FACE */ + empathy_smiley_manager_add (manager, "face-smirk", "😏", ":-!", ":!", NULL); + /* U+1F632 ASTONISHED FACE */ + empathy_smiley_manager_add (manager, "face-surprise", "😲", ":-O", ":O", ":-o", ":o", NULL); + /* U+1F62A SLEEPY FACE */ + empathy_smiley_manager_add (manager, "face-tired", "😪", "|-)", "|)", NULL); + /* U+1F615 CONFUSED FACE */ + empathy_smiley_manager_add (manager, "face-uncertain", "😕", ":-/", ":/", ":-\\", ":\\", NULL); + /* U+1F609 WINKING FACE */ + empathy_smiley_manager_add (manager, "face-wink", "😉", ";-)", ";)", NULL); + /* U+1F61F WORRIED FACE */ + empathy_smiley_manager_add (manager, "face-worried", "😟", ":-S", ":S", ":-s", ":s", NULL); + /* U+2764 HEAVY BLACK HEART */ + empathy_smiley_manager_add (manager, "emblem-favorite", "❤", "<3", NULL); +} + +static EmpathySmileyHit * +smiley_hit_new (SmileyManagerTree *tree, + guint start, + guint end) +{ + EmpathySmileyHit *hit; + + hit = g_slice_new (EmpathySmileyHit); + hit->pixbuf = tree->pixbuf; + hit->path = tree->path; + hit->start = start; + hit->end = end; + + return hit; } void -empathy_smiley_manager_load (EmpathySmileyManager *manager) +empathy_smiley_hit_free (EmpathySmileyHit *hit) { - g_return_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager)); + g_return_if_fail (hit != NULL); - /* From fd.o icon-naming spec */ - empathy_smiley_manager_add (manager, "face-angel", "O:-)", "O:)", NULL); - empathy_smiley_manager_add (manager, "face-cool", "B-)", "B)", NULL); - empathy_smiley_manager_add (manager, "face-crying", ":'(", NULL); - empathy_smiley_manager_add (manager, "face-devilish", ">:-)", ">:)", NULL); - empathy_smiley_manager_add (manager, "face-embarrassed",":-[", ":[", ":-$", ":$", NULL); - empathy_smiley_manager_add (manager, "face-kiss", ":-*", ":*", NULL); - empathy_smiley_manager_add (manager, "face-monkey", ":-(|)", ":(|)", NULL); - empathy_smiley_manager_add (manager, "face-plain", ":-|", ":|", NULL); - empathy_smiley_manager_add (manager, "face-raspberry", ":-P", ":P", ":-p", ":p", NULL); - empathy_smiley_manager_add (manager, "face-sad", ":-(", ":(", NULL); - empathy_smiley_manager_add (manager, "face-smile", ":-)", ":)", NULL); - empathy_smiley_manager_add (manager, "face-smile-big", ":-D", ":D", ":-d", ":d", NULL); - empathy_smiley_manager_add (manager, "face-smirk", ":-!", ":!", NULL); - empathy_smiley_manager_add (manager, "face-surprise", ":-O", ":O", NULL); - empathy_smiley_manager_add (manager, "face-wink", ";-)", ";)", NULL); + g_slice_free (EmpathySmileyHit, hit); } GSList * -empathy_smiley_manager_parse (EmpathySmileyManager *manager, - const gchar *text) +empathy_smiley_manager_parse_len (EmpathySmileyManager *manager, + const gchar *text, + gssize len) { EmpathySmileyManagerPriv *priv = GET_PRIV (manager); - EmpathySmiley *smiley; + EmpathySmileyHit *hit; + GSList *hits = NULL; SmileyManagerTree *cur_tree = priv->tree; - const gchar *t; - const gchar *cur_str = text; - GSList *smileys = NULL; + const gchar *cur_str; + const gchar *start = NULL; g_return_val_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager), NULL); g_return_val_if_fail (text != NULL, NULL); - for (t = text; *t; t = g_utf8_next_char (t)) { + /* If len is negative, parse the string until we find '\0' */ + if (len < 0) { + len = G_MAXSSIZE; + } + + /* Parse the len first bytes of text to find smileys. Each time a smiley + * is detected, append a EmpathySmileyHit struct to the returned list, + * containing the smiley pixbuf and the position of the text to be + * replaced by it. + * cur_str is a pointer in the text showing the current position + * of the parsing. It is always at the begining of an UTF-8 character, + * because we support unicode smileys! For example we could want to + * replace ™ by an image. */ + + for (cur_str = text; + *cur_str != '\0' && cur_str - text < len; + cur_str = g_utf8_next_char (cur_str)) { SmileyManagerTree *child; gunichar c; - - c = g_utf8_get_char (t); - child = smiley_manager_tree_find_child (cur_tree, c); - if (cur_tree == priv->tree) { - if (child) { - if (t > cur_str) { - smiley = smiley_new (NULL, g_strndup (cur_str, t - cur_str)); - smileys = g_slist_prepend (smileys, smiley); - } - cur_str = t; - cur_tree = child; - } - - continue; - } + c = g_utf8_get_char (cur_str); + child = smiley_manager_tree_find_child (cur_tree, c); + /* If we have a child it means c is part of a smiley */ if (child) { + if (cur_tree == priv->tree) { + /* c is the first char of some smileys, keep + * the begining position */ + start = cur_str; + } cur_tree = child; continue; } - smiley = smiley_new (cur_tree->pixbuf, g_strndup (cur_str, t - cur_str)); - smileys = g_slist_prepend (smileys, smiley); - if (cur_tree->pixbuf) { - cur_str = t; - cur_tree = smiley_manager_tree_find_child (priv->tree, c); + /* c is not part of a smiley. let's check if we found a smiley + * before it. */ + if (cur_tree->pixbuf != NULL) { + /* found! */ + hit = smiley_hit_new (cur_tree, start - text, + cur_str - text); + hits = g_slist_prepend (hits, hit); - if (!cur_tree) { + /* c was not part of this smiley, check if a new smiley + * start with it. */ + cur_tree = smiley_manager_tree_find_child (priv->tree, c); + if (cur_tree) { + start = cur_str; + } else { cur_tree = priv->tree; } - } else { - cur_str = t; + } else if (cur_tree != priv->tree) { + /* We searched a smiley starting at 'start' but we ended + * with no smiley. Look again starting from next char. + * + * For example ">:)" and ":(" are both valid smileys, + * when parsing text ">:(" we first see '>' which could + * be the start of a smiley. 'start' variable is set to + * that position and we parse next char which is ':' and + * is still potential smiley. Then we see '(' which is + * NOT part of the smiley, ">:(" does not exist, so we + * have to start again from ':' to find ":(" which is + * correct smiley. */ + cur_str = start; cur_tree = priv->tree; } } - smiley = smiley_new (cur_tree->pixbuf, g_strndup (cur_str, t - cur_str)); - smileys = g_slist_prepend (smileys, smiley); + /* Check if last char of the text was the end of a smiley */ + if (cur_tree->pixbuf != NULL) { + hit = smiley_hit_new (cur_tree, start - text, cur_str - text); + hits = g_slist_prepend (hits, hit); + } - return g_slist_reverse (smileys); + return g_slist_reverse (hits); } GSList * @@ -350,15 +442,15 @@ empathy_smiley_manager_get_all (EmpathySmileyManager *manager) typedef struct { EmpathySmileyManager *manager; EmpathySmiley *smiley; - EmpathySmileyMenuFunc func; - gpointer user_data; + EmpathySmileyMenuFunc func; + gpointer user_data; } ActivateData; static void smiley_menu_data_free (gpointer user_data, GClosure *closure) { - ActivateData *data = (ActivateData*) user_data; + ActivateData *data = (ActivateData *) user_data; g_object_unref (data->manager); g_slice_free (ActivateData, data); @@ -368,7 +460,7 @@ static void smiley_menu_activate_cb (GtkMenuItem *menuitem, gpointer user_data) { - ActivateData *data = (ActivateData*) user_data; + ActivateData *data = (ActivateData *) user_data; data->func (data->manager, data->smiley, data->user_data); } @@ -398,8 +490,10 @@ empathy_smiley_menu_new (EmpathySmileyManager *manager, smiley = l->data; image = gtk_image_new_from_pixbuf (smiley->pixbuf); - item = gtk_image_menu_item_new_with_label (""); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image); + item = gtk_image_menu_item_new (); + gtk_style_context_add_class (gtk_widget_get_style_context (item), + "empathy-smiley-menu-item"); + gtk_container_add (GTK_CONTAINER (item), image); gtk_menu_attach (GTK_MENU (menu), item, x, x + 1, y, y + 1);