]> git.0d.be Git - empathy.git/blobdiff - libempathy-gtk/empathy-smiley-manager.c
Center the 'smiley images' inside the menu items
[empathy.git] / libempathy-gtk / empathy-smiley-manager.c
index 9f7dd73c84d011eafdc1356bba0dbb9f62cf48f8..d63763d8933da270ef0fe846a95c1baaacd609ea 100644 (file)
  * 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 <dafydd.harries@collabora.co.uk>
  *          Xavier Claessens <xclaesse@gmail.com>
  */
 
-#include <config.h>
+#include "config.h"
+#include "empathy-smiley-manager.h"
 
-#include <string.h>
+#include <tp-account-widgets/tpaw-pixbuf-utils.h>
+#include <tp-account-widgets/tpaw-utils.h>
 
-#include <libempathy/empathy-utils.h>
-#include "empathy-smiley-manager.h"
 #include "empathy-ui-utils.h"
+#include "empathy-utils.h"
 
 typedef struct _SmileyManagerTree SmileyManagerTree;
 
@@ -37,9 +38,10 @@ 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);
@@ -55,6 +57,7 @@ smiley_manager_tree_new (gunichar c)
        tree->c = c;
        tree->pixbuf = NULL;
        tree->childrens = NULL;
+       tree->path = NULL;
 
        return tree;
 }
@@ -76,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);
 }
 
@@ -85,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);
 }
@@ -113,7 +109,7 @@ 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);
 }
 
@@ -200,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;
 
@@ -209,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
@@ -238,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 (!EMP_STR_EMPTY (icon_name));
-       g_return_if_fail (!EMP_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 (!EMP_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 *
@@ -365,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);
@@ -383,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);
 }
@@ -413,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);