diff options
Diffstat (limited to 'libempathy-gtk')
-rw-r--r-- | libempathy-gtk/Makefile.am | 33 | ||||
-rw-r--r-- | libempathy-gtk/empathy-avatar-chooser.c | 4 | ||||
-rw-r--r-- | libempathy-gtk/empathy-chat-text-view.c | 28 | ||||
-rw-r--r-- | libempathy-gtk/empathy-chat.c | 2 | ||||
-rw-r--r-- | libempathy-gtk/empathy-conf.h | 1 | ||||
-rw-r--r-- | libempathy-gtk/empathy-location-manager.c | 10 | ||||
-rw-r--r-- | libempathy-gtk/empathy-profile-chooser.c | 425 | ||||
-rw-r--r-- | libempathy-gtk/empathy-profile-chooser.h | 51 | ||||
-rw-r--r-- | libempathy-gtk/empathy-smiley-manager.c | 83 | ||||
-rw-r--r-- | libempathy-gtk/empathy-smiley-manager.h | 9 | ||||
-rw-r--r-- | libempathy-gtk/empathy-theme-adium.c | 831 | ||||
-rw-r--r-- | libempathy-gtk/empathy-theme-adium.h | 56 | ||||
-rw-r--r-- | libempathy-gtk/empathy-theme-manager.c | 55 | ||||
-rw-r--r-- | libempathy-gtk/empathy-theme.c | 408 | ||||
-rw-r--r-- | libempathy-gtk/empathy-ui-utils.c | 23 | ||||
-rw-r--r-- | libempathy-gtk/empathy-ui-utils.h | 9 |
16 files changed, 1360 insertions, 668 deletions
diff --git a/libempathy-gtk/Makefile.am b/libempathy-gtk/Makefile.am index 80c9a792d..2a4438e57 100644 --- a/libempathy-gtk/Makefile.am +++ b/libempathy-gtk/Makefile.am @@ -10,6 +10,7 @@ AM_CPPFLAGS = \ $(ENCHANT_CFLAGS) \ $(LIBCHAMPLAIN_CFLAGS) \ $(GEOCLUE_CFLAGS) \ + $(WEBKIT_CFLAGS) \ $(WARN_CFLAGS) \ $(DISABLE_DEPRECATED) @@ -73,6 +74,7 @@ libempathy_gtk_la_LIBADD = \ $(ENCHANT_LIBS) \ $(LIBCHAMPLAIN_LIBS) \ $(GEOCLUE_LIBS) \ + $(WEBKIT_LIBS) \ $(top_builddir)/libempathy/libempathy.la libempathy_gtk_la_LDFLAGS = \ @@ -118,14 +120,6 @@ libempathy_gtk_headers = \ empathy-theme-manager.h \ empathy-ui-utils.h -if HAVE_GEOCLUE -libempathy_gtk_handwritten_source += \ - empathy-location-manager.c - -libempathy_gtk_headers += \ - empathy-location-manager.h -endif - check_c_sources = \ $(libempathy_gtk_handwritten_source) \ $(libempathy_gtk_headers) @@ -218,6 +212,29 @@ pkgconfig_DATA = libempathy-gtk.pc EXTRA_DIST = \ $(ui_DATA) +if HAVE_GEOCLUE +libempathy_gtk_handwritten_source += \ + empathy-location-manager.c + +libempathy_gtk_headers += \ + empathy-location-manager.h +else +EXTRA_DIST += \ + empathy-location-manager.c \ + empathy-location-manager.h +endif + +if HAVE_WEBKIT +libempathy_gtk_handwritten_source += \ + empathy-theme-adium.c +libempathy_gtk_headers += \ + empathy-theme-adium.h +else +EXTRA_DIST += \ + empathy-theme-adium.c \ + empathy-theme-adium.h +endif + CLEANFILES = \ $(BUILT_SOURCES) \ stamp-empathy-gtk-enum-types.h diff --git a/libempathy-gtk/empathy-avatar-chooser.c b/libempathy-gtk/empathy-avatar-chooser.c index 0f4311e07..8c005a029 100644 --- a/libempathy-gtk/empathy-avatar-chooser.c +++ b/libempathy-gtk/empathy-avatar-chooser.c @@ -555,7 +555,7 @@ avatar_chooser_maybe_convert_and_scale (EmpathyAvatarChooser *chooser, /* Takes ownership of new_mime_type and converted_image_data */ avatar = empathy_avatar_new (converted_image_data, - converted_image_size, new_mime_type, NULL); + converted_image_size, new_mime_type, NULL, NULL); return avatar; } @@ -598,7 +598,7 @@ avatar_chooser_set_image_from_data (EmpathyAvatarChooser *chooser, } /* avatar takes ownership of data and mime_type */ - avatar = empathy_avatar_new (data, size, mime_type, NULL); + avatar = empathy_avatar_new (data, size, mime_type, NULL, NULL); avatar_chooser_set_image (chooser, avatar, pixbuf, set_locally); } diff --git a/libempathy-gtk/empathy-chat-text-view.c b/libempathy-gtk/empathy-chat-text-view.c index 8874b7ace..95e67b714 100644 --- a/libempathy-gtk/empathy-chat-text-view.c +++ b/libempathy-gtk/empathy-chat-text-view.c @@ -231,12 +231,6 @@ chat_text_view_notify_system_font_cb (EmpathyConf *conf, } static void -chat_text_view_clear_view_cb (GtkMenuItem *menuitem, EmpathyChatTextView *view) -{ - empathy_chat_view_clear (EMPATHY_CHAT_VIEW (view)); -} - -static void chat_text_view_open_address_cb (GtkMenuItem *menuitem, const gchar *url) { empathy_url_show (GTK_WIDGET (menuitem), url); @@ -256,8 +250,8 @@ chat_text_view_copy_address_cb (GtkMenuItem *menuitem, const gchar *url) static void chat_text_view_populate_popup (EmpathyChatTextView *view, - GtkMenu *menu, - gpointer user_data) + GtkMenu *menu, + gpointer user_data) { EmpathyChatTextViewPriv *priv; GtkTextTagTable *table; @@ -271,7 +265,7 @@ chat_text_view_populate_popup (EmpathyChatTextView *view, /* Clear menu item */ if (gtk_text_buffer_get_char_count (priv->buffer) > 0) { - item = gtk_menu_item_new (); + item = gtk_separator_menu_item_new (); gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); gtk_widget_show (item); @@ -279,10 +273,9 @@ chat_text_view_populate_popup (EmpathyChatTextView *view, gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); gtk_widget_show (item); - g_signal_connect (item, - "activate", - G_CALLBACK (chat_text_view_clear_view_cb), - view); + g_signal_connect_swapped (item, "activate", + G_CALLBACK (empathy_chat_view_clear), + view); } /* Link context menu items */ @@ -316,21 +309,19 @@ chat_text_view_populate_popup (EmpathyChatTextView *view, "url", str, (GDestroyNotify) g_free); - item = gtk_menu_item_new (); + item = gtk_separator_menu_item_new (); gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); gtk_widget_show (item); item = gtk_menu_item_new_with_mnemonic (_("_Copy Link Address")); - g_signal_connect (item, - "activate", + g_signal_connect (item, "activate", G_CALLBACK (chat_text_view_copy_address_cb), str); gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); gtk_widget_show (item); item = gtk_menu_item_new_with_mnemonic (_("_Open Link")); - g_signal_connect (item, - "activate", + g_signal_connect (item, "activate", G_CALLBACK (chat_text_view_open_address_cb), str); gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); @@ -576,6 +567,7 @@ chat_text_view_finalize (GObject *object) if (priv->scroll_timeout) { g_source_remove (priv->scroll_timeout); } + g_object_unref (priv->smiley_manager); G_OBJECT_CLASS (empathy_chat_text_view_parent_class)->finalize (object); } diff --git a/libempathy-gtk/empathy-chat.c b/libempathy-gtk/empathy-chat.c index 248fd116d..0fb77f24c 100644 --- a/libempathy-gtk/empathy-chat.c +++ b/libempathy-gtk/empathy-chat.c @@ -1217,7 +1217,7 @@ chat_set_show_contacts (EmpathyChat *chat, gboolean show) store = empathy_contact_list_store_new (EMPATHY_CONTACT_LIST (priv->tp_chat)); priv->contact_list_view = GTK_WIDGET (empathy_contact_list_view_new (store, - EMPATHY_CONTACT_LIST_FEATURE_NONE, + EMPATHY_CONTACT_LIST_FEATURE_CONTACT_TOOLTIP, EMPATHY_CONTACT_FEATURE_CHAT | EMPATHY_CONTACT_FEATURE_CALL | EMPATHY_CONTACT_FEATURE_LOG | diff --git a/libempathy-gtk/empathy-conf.h b/libempathy-gtk/empathy-conf.h index 07026df5c..47949139e 100644 --- a/libempathy-gtk/empathy-conf.h +++ b/libempathy-gtk/empathy-conf.h @@ -60,6 +60,7 @@ struct _EmpathyConfClass { #define EMPATHY_PREFS_POPUPS_WHEN_AVAILABLE EMPATHY_PREFS_PATH "/notifications/popups_when_available" #define EMPATHY_PREFS_CHAT_SHOW_SMILEYS EMPATHY_PREFS_PATH "/conversation/graphical_smileys" #define EMPATHY_PREFS_CHAT_THEME EMPATHY_PREFS_PATH "/conversation/theme" +#define EMPATHY_PREFS_CHAT_ADIUM_PATH EMPATHY_PREFS_PATH "/conversation/adium_path" #define EMPATHY_PREFS_CHAT_SPELL_CHECKER_LANGUAGES EMPATHY_PREFS_PATH "/conversation/spell_checker_languages" #define EMPATHY_PREFS_CHAT_SPELL_CHECKER_ENABLED EMPATHY_PREFS_PATH "/conversation/spell_checker_enabled" #define EMPATHY_PREFS_CHAT_NICK_COMPLETION_CHAR EMPATHY_PREFS_PATH "/conversation/nick_completion_char" diff --git a/libempathy-gtk/empathy-location-manager.c b/libempathy-gtk/empathy-location-manager.c index fb804315c..1b76e2e1d 100644 --- a/libempathy-gtk/empathy-location-manager.c +++ b/libempathy-gtk/empathy-location-manager.c @@ -302,7 +302,10 @@ address_changed_cb (GeoclueAddress *address, g_hash_table_remove (priv->location, EMPATHY_LOCATION_POSTAL_CODE); if (g_hash_table_size (details) == 0) - return; + { + DEBUG ("\t - (Empty)"); + return; + } g_hash_table_iter_init (&iter, details); while (g_hash_table_iter_next (&iter, &key, &value)) @@ -449,8 +452,11 @@ update_resources (EmpathyLocationManager *location_manager) DEBUG ("Updating resources %d", priv->resources); + /* As per Geoclue bug #15126, using NONE results in no address + * being found as geoclue-manual report an empty address with + * accuracy = NONE */ if (!geoclue_master_client_set_requirements (priv->gc_client, - GEOCLUE_ACCURACY_LEVEL_NONE, 0, TRUE, priv->resources, + GEOCLUE_ACCURACY_LEVEL_COUNTRY, 0, TRUE, priv->resources, NULL)) { DEBUG ("set_requirements failed"); diff --git a/libempathy-gtk/empathy-profile-chooser.c b/libempathy-gtk/empathy-profile-chooser.c index 14965741c..b3cbf90d0 100644 --- a/libempathy-gtk/empathy-profile-chooser.c +++ b/libempathy-gtk/empathy-profile-chooser.c @@ -1,6 +1,6 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ /* - * Copyright (C) 2007-2008 Collabora Ltd. + * Copyright (C) 2007-2009 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,6 +17,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: Xavier Claessens <xclaesse@gmail.com> + * Jonny Lamb <jonny.lamb@collabora.co.uk> */ #include <config.h> @@ -27,6 +28,8 @@ #include <libmissioncontrol/mc-profile.h> #include <libmissioncontrol/mc-protocol.h> +#include <libempathy/empathy-utils.h> + #include "empathy-profile-chooser.h" #include "empathy-ui-utils.h" @@ -34,112 +37,277 @@ * SECTION:empathy-profile-chooser * @title: EmpathyProfileChooser * @short_description: A widget used to choose from a list of profiles - * @include: libempathy-gtk/empathy-account-chooser.h + * @include: libempathy-gtk/empathy-profile-chooser.h * - * #EmpathyProfileChooser is a widget which provides a chooser of available + * #EmpathyProfileChooser is a widget which extends #GtkComboBox to provides a + * chooser of available profiles. + */ + +/** + * EmpathyProfileChooser: + * @parent: parent object + * + * Widget which extends #GtkComboBox to provide a chooser of available * profiles. */ -enum { - COL_ICON, - COL_LABEL, - COL_PROFILE, - COL_COUNT +#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyProfileChooser) +typedef struct +{ + GtkListStore *store; + gboolean dispose_run; +} EmpathyProfileChooserPriv; + +enum +{ + COL_ICON, + COL_LABEL, + COL_PROFILE, + COL_COUNT }; +G_DEFINE_TYPE (EmpathyProfileChooser, empathy_profile_chooser, + GTK_TYPE_COMBO_BOX); + +static gint +profile_chooser_sort_profile_value (McProfile *profile) +{ + guint i; + const gchar *profile_name; + const gchar *names[] = { + "jabber", + "salut", + "gtalk", + NULL + }; + + profile_name = mc_profile_get_unique_name (profile); + + for (i = 0 ; names[i]; i++) + { + if (strcmp (profile_name, names[i]) == 0) + return i; + } + + return i; +} + +static gint +profile_chooser_sort_func (GtkTreeModel *model, + GtkTreeIter *iter_a, + GtkTreeIter *iter_b, + gpointer user_data) +{ + McProfile *profile_a; + McProfile *profile_b; + gint cmp; + + gtk_tree_model_get (model, iter_a, + COL_PROFILE, &profile_a, + -1); + gtk_tree_model_get (model, iter_b, + COL_PROFILE, &profile_b, + -1); + + cmp = profile_chooser_sort_profile_value (profile_a); + cmp -= profile_chooser_sort_profile_value (profile_b); + if (cmp == 0) + { + cmp = strcmp (mc_profile_get_display_name (profile_a), + mc_profile_get_display_name (profile_b)); + } + + g_object_unref (profile_a); + g_object_unref (profile_b); + + return cmp; +} + +static void +profile_chooser_constructed (GObject *object) +{ + EmpathyProfileChooser *profile_chooser; + EmpathyProfileChooserPriv *priv; + + GList *profiles, *l, *seen; + GtkCellRenderer *renderer; + GtkTreeIter iter; + gboolean iter_set = FALSE; + McManager *btf_cm; + + priv = GET_PRIV (object); + profile_chooser = EMPATHY_PROFILE_CHOOSER (object); + + /* set up combo box with new store */ + priv->store = gtk_list_store_new (COL_COUNT, + G_TYPE_STRING, /* Icon name */ + G_TYPE_STRING, /* Label */ + MC_TYPE_PROFILE); /* Profile */ + + gtk_combo_box_set_model (GTK_COMBO_BOX (object), + GTK_TREE_MODEL (priv->store)); + + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (object), renderer, FALSE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (object), renderer, + "icon-name", COL_ICON, + NULL); + g_object_set (renderer, "stock-size", GTK_ICON_SIZE_BUTTON, NULL); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (object), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (object), renderer, + "text", COL_LABEL, + NULL); + + btf_cm = mc_manager_lookup ("butterfly"); + profiles = mc_profiles_list (); + seen = NULL; + for (l = profiles; l; l = g_list_next (l)) + { + McProfile *profile; + McProtocol *protocol; + const gchar *unique_name; + + profile = l->data; + + /* Check if the CM is installed, otherwise skip that profile. + * Workaround SF bug #1688779 */ + protocol = mc_profile_get_protocol (profile); + if (!protocol) + continue; + + g_object_unref (protocol); + + /* Skip MSN-Haze if we have butterfly */ + unique_name = mc_profile_get_unique_name (profile); + if (btf_cm && strcmp (unique_name, "msn-haze") == 0) + continue; + + if (g_list_find_custom (seen, unique_name, (GCompareFunc) strcmp)) + continue; + + seen = g_list_append (seen, (char *) unique_name); + + gtk_list_store_insert_with_values (priv->store, &iter, 0, + COL_ICON, mc_profile_get_icon_name (profile), + COL_LABEL, mc_profile_get_display_name (profile), + COL_PROFILE, profile, + -1); + iter_set = TRUE; + } + + g_list_free (seen); + + if (btf_cm) + g_object_unref (btf_cm); + + /* Set the profile sort function */ + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->store), + COL_PROFILE, + profile_chooser_sort_func, + NULL, NULL); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->store), + COL_PROFILE, + GTK_SORT_ASCENDING); + + if (iter_set) + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (object), &iter); + + mc_profiles_free_list (profiles); + + if (G_OBJECT_CLASS (empathy_profile_chooser_parent_class)->constructed) + G_OBJECT_CLASS (empathy_profile_chooser_parent_class)->constructed (object); +} + +static void +empathy_profile_chooser_init (EmpathyProfileChooser *profile_chooser) +{ + EmpathyProfileChooserPriv *priv = + G_TYPE_INSTANCE_GET_PRIVATE (profile_chooser, + EMPATHY_TYPE_PROFILE_CHOOSER, EmpathyProfileChooserPriv); + + priv->dispose_run = FALSE; + + profile_chooser->priv = priv; +} + +static void +profile_chooser_dispose (GObject *object) +{ + EmpathyProfileChooser *profile_chooser = EMPATHY_PROFILE_CHOOSER (object); + EmpathyProfileChooserPriv *priv = GET_PRIV (profile_chooser); + + if (priv->dispose_run) + return; + + priv->dispose_run = TRUE; + + if (priv->store) + { + g_object_unref (priv->store); + priv->store = NULL; + } + + (G_OBJECT_CLASS (empathy_profile_chooser_parent_class)->dispose) (object); +} + +static void +empathy_profile_chooser_class_init (EmpathyProfileChooserClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructed = profile_chooser_constructed; + object_class->dispose = profile_chooser_dispose; + + g_type_class_add_private (object_class, sizeof (EmpathyProfileChooserPriv)); +} + /** * empathy_profile_chooser_dup_selected: - * @widget: an #EmpathyProfileChooser + * @profile_chooser: an #EmpathyProfileChooser * - * Returns a new reference to the selected #McProfile in @widget. The returned - * #McProfile should be unrefed with g_object_unref() when finished with. + * Returns a new reference to the selected #McProfile in @profile_chooser. The + * returned #McProfile should be unrefed with g_object_unref() when finished + * with. * * Return value: a new reference to the selected #McProfile */ McProfile * -empathy_profile_chooser_dup_selected (GtkWidget *widget) +empathy_profile_chooser_dup_selected (EmpathyProfileChooser *profile_chooser) { - GtkTreeModel *model; - GtkTreeIter iter; - McProfile *profile = NULL; - - model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget)); - if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter)) { - gtk_tree_model_get (model, &iter, - COL_PROFILE, &profile, - -1); - } - - return profile; + EmpathyProfileChooserPriv *priv = GET_PRIV (profile_chooser); + GtkTreeIter iter; + McProfile *profile = NULL; + + g_return_val_if_fail (EMPATHY_IS_PROFILE_CHOOSER (profile_chooser), NULL); + + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (profile_chooser), &iter)) + { + gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter, + COL_PROFILE, &profile, + -1); + } + + return profile; } /** * empathy_profile_chooser_n_profiles: - * @widget: an #EmpathyProfileChooser + * @profile_chooser: an #EmpathyProfileChooser * - * Returns the number of profiles in @widget. + * Returns the number of profiles in @profile_chooser. * - * Return value: the number of profiles in @widget + * Return value: the number of profiles in @profile_chooser */ gint -empathy_profile_chooser_n_profiles (GtkWidget *widget) +empathy_profile_chooser_n_profiles (EmpathyProfileChooser *profile_chooser) { - GtkTreeModel *model; + EmpathyProfileChooserPriv *priv = GET_PRIV (profile_chooser); - model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget)); - - return gtk_tree_model_iter_n_children (model, NULL); -} + g_return_val_if_fail (EMPATHY_IS_PROFILE_CHOOSER (profile_chooser), 0); -static gint -profile_chooser_sort_profile_value (McProfile *profile) -{ - guint i; - const gchar *profile_name; - const gchar *names[] = {"jabber", - "salut", - "gtalk", - NULL}; - - profile_name = mc_profile_get_unique_name (profile); - - for (i = 0 ; names[i]; i++) { - if (strcmp (profile_name, names[i]) == 0) { - return i; - } - } - - return i; -} - -static gint -profile_chooser_sort_func (GtkTreeModel *model, - GtkTreeIter *iter_a, - GtkTreeIter *iter_b, - gpointer user_data) -{ - McProfile *profile_a; - McProfile *profile_b; - gint cmp; - - gtk_tree_model_get (model, iter_a, - COL_PROFILE, &profile_a, - -1); - gtk_tree_model_get (model, iter_b, - COL_PROFILE, &profile_b, - -1); - - cmp = profile_chooser_sort_profile_value (profile_a); - cmp -= profile_chooser_sort_profile_value (profile_b); - if (cmp == 0) { - cmp = strcmp (mc_profile_get_display_name (profile_a), - mc_profile_get_display_name (profile_b)); - } - - g_object_unref (profile_a); - g_object_unref (profile_b); - - return cmp; + return gtk_tree_model_iter_n_children (GTK_TREE_MODEL (priv->store), NULL); } /** @@ -152,94 +320,5 @@ profile_chooser_sort_func (GtkTreeModel *model, GtkWidget * empathy_profile_chooser_new (void) { - GList *profiles, *l, *seen; - GtkListStore *store; - GtkCellRenderer *renderer; - GtkWidget *combo_box; - GtkTreeIter iter; - gboolean iter_set = FALSE; - McManager *btf_cm; - - /* set up combo box with new store */ - store = gtk_list_store_new (COL_COUNT, - G_TYPE_STRING, /* Icon name */ - G_TYPE_STRING, /* Label */ - MC_TYPE_PROFILE); /* Profile */ - combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store)); - - - renderer = gtk_cell_renderer_pixbuf_new (); - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, FALSE); - gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer, - "icon-name", COL_ICON, - NULL); - g_object_set (renderer, "stock-size", GTK_ICON_SIZE_BUTTON, NULL); - - renderer = gtk_cell_renderer_text_new (); - gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE); - gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer, - "text", COL_LABEL, - NULL); - - btf_cm = mc_manager_lookup ("butterfly"); - profiles = mc_profiles_list (); - seen = NULL; - for (l = profiles; l; l = l->next) { - McProfile *profile; - McProtocol *protocol; - const gchar *unique_name; - - profile = l->data; - - /* Check if the CM is installed, otherwise skip that profile. - * Workaround SF bug #1688779 */ - protocol = mc_profile_get_protocol (profile); - if (!protocol) { - continue; - } - g_object_unref (protocol); - - /* Skip MSN-Haze if we have butterfly */ - unique_name = mc_profile_get_unique_name (profile); - if (btf_cm && strcmp (unique_name, "msn-haze") == 0) { - continue; - } - - if (g_list_find_custom (seen, unique_name, (GCompareFunc) strcmp)) { - continue; - } - seen = g_list_append (seen, (char *) unique_name); - - gtk_list_store_insert_with_values (store, &iter, 0, - COL_ICON, mc_profile_get_icon_name (profile), - COL_LABEL, mc_profile_get_display_name (profile), - COL_PROFILE, profile, - -1); - iter_set = TRUE; - } - - g_list_free (seen); - - if (btf_cm) { - g_object_unref (btf_cm); - } - - /* Set the profile sort function */ - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store), - COL_PROFILE, - profile_chooser_sort_func, - NULL, NULL); - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), - COL_PROFILE, - GTK_SORT_ASCENDING); - - if (iter_set) { - gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo_box), &iter); - } - - mc_profiles_free_list (profiles); - g_object_unref (store); - - return combo_box; + return GTK_WIDGET (g_object_new (EMPATHY_TYPE_PROFILE_CHOOSER, NULL)); } - diff --git a/libempathy-gtk/empathy-profile-chooser.h b/libempathy-gtk/empathy-profile-chooser.h index 74c761cc4..37d7241a9 100644 --- a/libempathy-gtk/empathy-profile-chooser.h +++ b/libempathy-gtk/empathy-profile-chooser.h @@ -1,6 +1,6 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ /* - * Copyright (C) 2007-2008 Collabora Ltd. + * Copyright (C) 2007-2009 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,18 +17,53 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: Xavier Claessens <xclaesse@gmail.com> + * Jonny Lamb <jonny.lamb@collabora.co.uk */ -#ifndef __EMPATHY_PROTOCOL_CHOOSER_H__ -#define __EMPATHY_PROTOCOL_CHOOSER_H__ +#ifndef __EMPATHY_PROFILE_CHOOSER_H__ +#define __EMPATHY_PROFILE_CHOOSER_H__ + +#include <glib-object.h> +#include <gtk/gtk.h> #include <libmissioncontrol/mc-profile.h> G_BEGIN_DECLS -GtkWidget * empathy_profile_chooser_new (void); -McProfile * empathy_profile_chooser_dup_selected (GtkWidget *widget); -gint empathy_profile_chooser_n_profiles (GtkWidget *widget); +#define EMPATHY_TYPE_PROFILE_CHOOSER (empathy_profile_chooser_get_type ()) +#define EMPATHY_PROFILE_CHOOSER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), \ + EMPATHY_TYPE_PROFILE_CHOOSER, EmpathyProfileChooser)) +#define EMPATHY_PROFILE_CHOOSER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), \ + EMPATHY_TYPE_PROFILE_CHOOSER, EmpathyProfileChooserClass)) +#define EMPATHY_IS_PROFILE_CHOOSER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), \ + EMPATHY_TYPE_PROFILE_CHOOSER)) +#define EMPATHY_IS_PROFILE_CHOOSER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), \ + EMPATHY_TYPE_PROFILE_CHOOSER)) +#define EMPATHY_PROFILE_CHOOSER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), \ + EMPATHY_TYPE_PROFILE_CHOOSER, EmpathyProfileChooserClass)) + +typedef struct _EmpathyProfileChooser EmpathyProfileChooser; +typedef struct _EmpathyProfileChooserClass EmpathyProfileChooserClass; + +struct _EmpathyProfileChooser +{ + GtkComboBox parent; + + /*<private>*/ + gpointer priv; +}; + +struct _EmpathyProfileChooserClass +{ + GtkComboBoxClass parent_class; +}; + +GType empathy_profile_chooser_get_type (void) G_GNUC_CONST; +GtkWidget * empathy_profile_chooser_new (void); +McProfile * empathy_profile_chooser_dup_selected ( + EmpathyProfileChooser *profile_chooser); +gint empathy_profile_chooser_n_profiles ( + EmpathyProfileChooser *profile_chooser); G_END_DECLS -#endif /* __EMPATHY_PROTOCOL_CHOOSER_H__ */ +#endif /* __EMPATHY_PROFILE_CHOOSER_H__ */ diff --git a/libempathy-gtk/empathy-smiley-manager.c b/libempathy-gtk/empathy-smiley-manager.c index a729a2929..c504d4cdb 100644 --- a/libempathy-gtk/empathy-smiley-manager.c +++ b/libempathy-gtk/empathy-smiley-manager.c @@ -37,9 +37,10 @@ typedef struct { } EmpathySmileyManagerPriv; struct _SmileyManagerTree { - gunichar c; - GdkPixbuf *pixbuf; - GSList *childrens; + gunichar c; + GdkPixbuf *pixbuf; + const gchar *path; + GSList *childrens; }; G_DEFINE_TYPE (EmpathySmileyManager, empathy_smiley_manager, G_TYPE_OBJECT); @@ -55,6 +56,7 @@ smiley_manager_tree_new (gunichar c) tree->c = c; tree->pixbuf = NULL; tree->childrens = NULL; + tree->path = NULL; return tree; } @@ -81,7 +83,7 @@ smiley_manager_tree_free (SmileyManagerTree *tree) /* Note: This function takes the ownership of str */ static EmpathySmiley * -smiley_new (GdkPixbuf *pixbuf, gchar *str) +smiley_new (GdkPixbuf *pixbuf, gchar *str, const gchar *path) { EmpathySmiley *smiley; @@ -90,6 +92,7 @@ smiley_new (GdkPixbuf *pixbuf, gchar *str) smiley->pixbuf = g_object_ref (pixbuf); } smiley->str = str; + smiley->path = path; return smiley; } @@ -112,9 +115,16 @@ static void smiley_manager_finalize (GObject *object) { EmpathySmileyManagerPriv *priv = GET_PRIV (object); + GSList *l; smiley_manager_tree_free (priv->tree); - g_slist_foreach (priv->smileys, (GFunc) empathy_smiley_free, NULL); + for (l = priv->smileys; l; l = l->next) { + EmpathySmiley *smiley = l->data; + + /* The smiley got the ownership of the path */ + g_free ((gchar*) smiley->path); + empathy_smiley_free (smiley); + } g_slist_free (priv->smileys); } @@ -201,8 +211,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; @@ -210,28 +221,32 @@ 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 = path; } static void smiley_manager_add_valist (EmpathySmileyManager *manager, - GdkPixbuf *smiley, + GdkPixbuf *pixbuf, + 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, g_strdup (first_str))); + /* We give the ownership of path to the smiley */ + smiley = smiley_new (pixbuf, g_strdup (first_str), path); + priv->smileys = g_slist_prepend (priv->smileys, smiley); } void @@ -240,40 +255,26 @@ 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)); - smiley = empathy_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_MENU); - if (smiley) { + pixbuf = empathy_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_MENU); + if (pixbuf) { + gchar *path; + va_start (var_args, first_str); - smiley_manager_add_valist (manager, smiley, first_str, var_args); + path = empathy_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); } } void -empathy_smiley_manager_add_from_pixbuf (EmpathySmileyManager *manager, - GdkPixbuf *smiley, - const gchar *first_str, - ...) -{ - 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); -} - -void empathy_smiley_manager_load (EmpathySmileyManager *manager) { g_return_if_fail (EMPATHY_IS_SMILEY_MANAGER (manager)); @@ -320,7 +321,9 @@ empathy_smiley_manager_parse (EmpathySmileyManager *manager, if (cur_tree == priv->tree) { if (child) { if (t > cur_str) { - smiley = smiley_new (NULL, g_strndup (cur_str, t - cur_str)); + smiley = smiley_new (NULL, + g_strndup (cur_str, t - cur_str), + NULL); smileys = g_slist_prepend (smileys, smiley); } cur_str = t; @@ -335,7 +338,9 @@ empathy_smiley_manager_parse (EmpathySmileyManager *manager, continue; } - smiley = smiley_new (cur_tree->pixbuf, g_strndup (cur_str, t - cur_str)); + smiley = smiley_new (cur_tree->pixbuf, + g_strndup (cur_str, t - cur_str), + cur_tree->path); smileys = g_slist_prepend (smileys, smiley); if (cur_tree->pixbuf) { cur_str = t; @@ -350,7 +355,9 @@ empathy_smiley_manager_parse (EmpathySmileyManager *manager, } } - smiley = smiley_new (cur_tree->pixbuf, g_strndup (cur_str, t - cur_str)); + smiley = smiley_new (cur_tree->pixbuf, + g_strndup (cur_str, t - cur_str), + cur_tree->path); smileys = g_slist_prepend (smileys, smiley); return g_slist_reverse (smileys); diff --git a/libempathy-gtk/empathy-smiley-manager.h b/libempathy-gtk/empathy-smiley-manager.h index 5eaf4b283..dc7428c3b 100644 --- a/libempathy-gtk/empathy-smiley-manager.h +++ b/libempathy-gtk/empathy-smiley-manager.h @@ -48,8 +48,9 @@ struct _EmpathySmileyManagerClass { }; typedef struct { - GdkPixbuf *pixbuf; - gchar *str; + GdkPixbuf *pixbuf; + gchar *str; + const gchar *path; } EmpathySmiley; typedef void (*EmpathySmileyMenuFunc) (EmpathySmileyManager *manager, @@ -63,10 +64,6 @@ void empathy_smiley_manager_add (EmpathySmileyManag const gchar *icon_name, const gchar *first_str, ...); -void empathy_smiley_manager_add_from_pixbuf (EmpathySmileyManager *manager, - GdkPixbuf *smiley, - const gchar *first_str, - ...); GSList * empathy_smiley_manager_get_all (EmpathySmileyManager *manager); GSList * empathy_smiley_manager_parse (EmpathySmileyManager *manager, const gchar *text); diff --git a/libempathy-gtk/empathy-theme-adium.c b/libempathy-gtk/empathy-theme-adium.c new file mode 100644 index 000000000..1f0c043aa --- /dev/null +++ b/libempathy-gtk/empathy-theme-adium.c @@ -0,0 +1,831 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008-2009 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * 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: Xavier Claessens <xclaesse@gmail.com> + */ + +#include "config.h" + +#include <string.h> +#include <glib/gi18n.h> + +#include <webkit/webkitnetworkrequest.h> + +#include <libempathy/empathy-time.h> +#include <libempathy/empathy-utils.h> + +#include "empathy-theme-adium.h" +#include "empathy-smiley-manager.h" +#include "empathy-conf.h" +#include "empathy-ui-utils.h" + +#define DEBUG_FLAG EMPATHY_DEBUG_CHAT +#include <libempathy/empathy-debug.h> + +#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyThemeAdium) + +typedef struct { + EmpathySmileyManager *smiley_manager; + EmpathyContact *last_contact; + gboolean page_loaded; + GList *message_queue; + gchar *path; + gchar *default_avatar_filename; + gchar *template_html; + gchar *basedir; + gchar *in_content_html; + gsize in_content_len; + gchar *in_nextcontent_html; + gsize in_nextcontent_len; + gchar *out_content_html; + gsize out_content_len; + gchar *out_nextcontent_html; + gsize out_nextcontent_len; + gchar *status_html; + gsize status_len; +} EmpathyThemeAdiumPriv; + +static void theme_adium_iface_init (EmpathyChatViewIface *iface); + +enum { + PROP_0, + PROP_PATH, +}; + +G_DEFINE_TYPE_WITH_CODE (EmpathyThemeAdium, empathy_theme_adium, + WEBKIT_TYPE_WEB_VIEW, + G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CHAT_VIEW, + theme_adium_iface_init)); + +static void +theme_adium_load (EmpathyThemeAdium *theme) +{ + EmpathyThemeAdiumPriv *priv = GET_PRIV (theme); + gchar *file; + gchar *template_html = NULL; + gsize template_len; + GString *string; + gchar **strv = NULL; + gchar *css_path; + guint len = 0; + guint i = 0; + gchar *basedir_uri; + + priv->basedir = g_strconcat (priv->path, G_DIR_SEPARATOR_S "Contents" G_DIR_SEPARATOR_S "Resources" G_DIR_SEPARATOR_S, NULL); + basedir_uri = g_strconcat ("file://", priv->basedir, NULL); + + /* Load html files */ + file = g_build_filename (priv->basedir, "Incoming", "Content.html", NULL); + g_file_get_contents (file, &priv->in_content_html, &priv->in_content_len, NULL); + g_free (file); + + file = g_build_filename (priv->basedir, "Incoming", "NextContent.html", NULL); + g_file_get_contents (file, &priv->in_nextcontent_html, &priv->in_nextcontent_len, NULL); + g_free (file); + + file = g_build_filename (priv->basedir, "Outgoing", "Content.html", NULL); + g_file_get_contents (file, &priv->out_content_html, &priv->out_content_len, NULL); + g_free (file); + + file = g_build_filename (priv->basedir, "Outgoing", "NextContent.html", NULL); + g_file_get_contents (file, &priv->out_nextcontent_html, &priv->out_nextcontent_len, NULL); + g_free (file); + + file = g_build_filename (priv->basedir, "Status.html", NULL); + g_file_get_contents (file, &priv->status_html, &priv->status_len, NULL); + g_free (file); + + css_path = g_build_filename (priv->basedir, "main.css", NULL); + + /* There is 2 formats for Template.html: The old one has 4 parameters, + * the new one has 5 parameters. */ + file = g_build_filename (priv->basedir, "Template.html", NULL); + if (g_file_get_contents (file, &template_html, &template_len, NULL)) { + strv = g_strsplit (template_html, "%@", -1); + len = g_strv_length (strv); + } + g_free (file); + + if (len != 5 && len != 6) { + /* Either the theme has no template or it don't have the good + * number of parameters. Fallback to use our own template. */ + g_free (template_html); + g_strfreev (strv); + + file = empathy_file_lookup ("Template.html", "data"); + g_file_get_contents (file, &template_html, &template_len, NULL); + g_free (file); + strv = g_strsplit (template_html, "%@", -1); + len = g_strv_length (strv); + } + + /* Replace %@ with the needed information in the template html. */ + string = g_string_sized_new (template_len); + g_string_append (string, strv[i++]); + g_string_append (string, priv->basedir); + g_string_append (string, strv[i++]); + if (len == 6) { + /* We include main.css by default */ + g_string_append_printf (string, "@import url(\"%s\");", css_path); + g_string_append (string, strv[i++]); + /* FIXME: We should set the variant css here */ + g_string_append (string, ""); + } else { + /* FIXME: We should set main.css OR the variant css */ + g_string_append (string, css_path); + } + g_string_append (string, strv[i++]); + g_string_append (string, ""); /* We don't want header */ + g_string_append (string, strv[i++]); + g_string_append (string, ""); /* FIXME: We don't support footer yet */ + g_string_append (string, strv[i++]); + priv->template_html = g_string_free (string, FALSE); + + /* Load the template */ + webkit_web_view_load_html_string (WEBKIT_WEB_VIEW (theme), + priv->template_html, basedir_uri); + + g_free (basedir_uri); + g_free (template_html); + g_free (css_path); + g_strfreev (strv); +} + +static WebKitNavigationResponse +theme_adium_navigation_requested_cb (WebKitWebView *view, + WebKitWebFrame *frame, + WebKitNetworkRequest *request, + gpointer user_data) +{ + const gchar *uri; + + uri = webkit_network_request_get_uri (request); + empathy_url_show (GTK_WIDGET (view), uri); + + return WEBKIT_NAVIGATION_RESPONSE_IGNORE; +} + +static void +theme_adium_populate_popup_cb (WebKitWebView *view, + GtkMenu *menu, + gpointer user_data) +{ + GtkWidget *item; + + /* Remove default menu items */ + gtk_container_foreach (GTK_CONTAINER (menu), + (GtkCallback) gtk_widget_destroy, NULL); + + /* Select all item */ + item = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL); + gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + + g_signal_connect_swapped (item, "activate", + G_CALLBACK (webkit_web_view_select_all), + view); + + /* Copy menu item */ + if (webkit_web_view_can_copy_clipboard (view)) { + item = gtk_image_menu_item_new_from_stock (GTK_STOCK_COPY, NULL); + gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + + g_signal_connect_swapped (item, "activate", + G_CALLBACK (webkit_web_view_copy_clipboard), + view); + } + + /* Clear menu item */ + item = gtk_separator_menu_item_new (); + gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + + item = gtk_image_menu_item_new_from_stock (GTK_STOCK_CLEAR, NULL); + gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + + g_signal_connect_swapped (item, "activate", + G_CALLBACK (empathy_chat_view_clear), + view); + + /* FIXME: Add open_link and copy_link when those bugs are fixed: + * https://bugs.webkit.org/show_bug.cgi?id=16092 + * https://bugs.webkit.org/show_bug.cgi?id=16562 + */ +} + +static gchar * +theme_adium_parse_body (EmpathyThemeAdium *theme, + const gchar *text) +{ + EmpathyThemeAdiumPriv *priv = GET_PRIV (theme); + gboolean use_smileys = FALSE; + GSList *smileys, *l; + GString *string; + gint i; + GRegex *uri_regex; + GMatchInfo *match_info; + gboolean match; + gchar *ret = NULL; + gint prev; + + empathy_conf_get_bool (empathy_conf_get (), + EMPATHY_PREFS_CHAT_SHOW_SMILEYS, + &use_smileys); + + if (use_smileys) { + /* Replace smileys by a <img/> tag */ + string = g_string_sized_new (strlen (text)); + smileys = empathy_smiley_manager_parse (priv->smiley_manager, text); + for (l = smileys; l; l = l->next) { + EmpathySmiley *smiley; + + smiley = l->data; + if (smiley->path) { + g_string_append_printf (string, + "<abbr title='%s'><img src=\"%s\"/ alt=\"%s\"/></abbr>", + smiley->str, smiley->path, smiley->str); + } else { + gchar *str; + + str = g_markup_escape_text (smiley->str, -1); + g_string_append (string, str); + g_free (str); + } + empathy_smiley_free (smiley); + } + g_slist_free (smileys); + + g_free (ret); + text = ret = g_string_free (string, FALSE); + } + + /* Add <a href></a> arround links */ + uri_regex = empathy_uri_regex_dup_singleton (); + match = g_regex_match (uri_regex, text, 0, &match_info); + if (match) { + gint last = 0; + gint s = 0, e = 0; + + string = g_string_sized_new (strlen (text)); + do { + g_match_info_fetch_pos (match_info, 0, &s, &e); + + if (s > last) { + /* Append the text between last link (or the + * start of the message) and this link */ + g_string_append_len (string, text + last, s - last); + } + + /* Append the link inside <a href=""></a> tag */ + g_string_append (string, "<a href=\""); + g_string_append_len (string, text + s, e - s); + g_string_append (string, "\">"); + g_string_append_len (string, text + s, e - s); + g_string_append (string, "</a>"); + + last = e; + } while (g_match_info_next (match_info, NULL)); + + if (e < strlen (text)) { + /* Append the text after the last link */ + g_string_append_len (string, text + e, strlen (text) - e); + } + + g_free (ret); + text = ret = g_string_free (string, FALSE); + } + g_match_info_free (match_info); + g_regex_unref (uri_regex); + + /* Replace \n by <br/> */ + string = NULL; + prev = 0; + for (i = 0; text[i] != '\0'; i++) { + if (text[i] == '\n') { + if (!string ) { + string = g_string_sized_new (strlen (text)); + } + g_string_append_len (string, text + prev, i - prev); + g_string_append (string, "<br/>"); + prev = i + 1; + } + } + if (string) { + g_string_append (string, text + prev); + g_free (ret); + text = ret = g_string_free (string, FALSE); + } + + return ret; +} + +static void +escape_and_append_len (GString *string, const gchar *str, gint len) +{ + while (*str != '\0' && len != 0) { + switch (*str) { + case '\\': + /* \ becomes \\ */ + g_string_append (string, "\\\\"); + break; + case '\"': + /* " becomes \" */ + g_string_append (string, "\\\""); + break; + case '\n': + /* Remove end of lines */ + break; + default: + g_string_append_c (string, *str); + } + + str++; + len--; + } +} + +static gboolean +theme_adium_match (const gchar **str, const gchar *match) +{ + gint len; + + len = strlen (match); + if (strncmp (*str, match, len) == 0) { + *str += len - 1; + return TRUE; + } + + return FALSE; +} + +static void +theme_adium_append_html (EmpathyThemeAdium *theme, + const gchar *func, + const gchar *html, gsize len, + const gchar *message, + const gchar *avatar_filename, + const gchar *name, + time_t timestamp) +{ + GString *string; + const gchar *cur = NULL; + gchar *script; + + /* Make some search-and-replace in the html code */ + string = g_string_sized_new (len + strlen (message)); + g_string_append_printf (string, "%s(\"", func); + for (cur = html; *cur != '\0'; cur++) { + const gchar *replace = NULL; + gchar *dup_replace = NULL; + + if (theme_adium_match (&cur, "%message%")) { + replace = message; + } else if (theme_adium_match (&cur, "%userIconPath%")) { + replace = avatar_filename; + } else if (theme_adium_match (&cur, "%sender%")) { + replace = name; + } else if (theme_adium_match (&cur, "%time")) { + gchar *format = NULL; + gchar *end; + + /* Time can be in 2 formats: + * %time% or %time{strftime format}% + * Extract the time format if provided. */ + if (cur[1] == '{') { + cur += 2; + end = strstr (cur, "}%"); + if (!end) { + /* Invalid string */ + continue; + } + format = g_strndup (cur, end - cur); + cur = end + 1; + } else { + cur++; + } + + dup_replace = empathy_time_to_string_local (timestamp, + format ? format : EMPATHY_TIME_FORMAT_DISPLAY_SHORT); + replace = dup_replace; + g_free (format); + } else { + escape_and_append_len (string, cur, 1); + continue; + } + + /* Here we have a replacement to make */ + escape_and_append_len (string, replace, -1); + g_free (dup_replace); + } + g_string_append (string, "\")"); + + script = g_string_free (string, FALSE); + webkit_web_view_execute_script (WEBKIT_WEB_VIEW (theme), script); + g_free (script); +} + +static void +theme_adium_append_message (EmpathyChatView *view, + EmpathyMessage *msg) +{ + EmpathyThemeAdium *theme = EMPATHY_THEME_ADIUM (view); + EmpathyThemeAdiumPriv *priv = GET_PRIV (theme); + EmpathyContact *sender; + gchar *dup_body = NULL; + const gchar *body; + const gchar *name; + EmpathyAvatar *avatar; + const gchar *avatar_filename = NULL; + time_t timestamp; + gchar *html = NULL; + gsize len = 0; + const gchar *func; + + if (!priv->page_loaded) { + priv->message_queue = g_list_prepend (priv->message_queue, + g_object_ref (msg)); + return; + } + + /* Get information */ + sender = empathy_message_get_sender (msg); + timestamp = empathy_message_get_timestamp (msg); + body = empathy_message_get_body (msg); + dup_body = theme_adium_parse_body (theme, body); + if (dup_body) { + body = dup_body; + } + name = empathy_contact_get_name (sender); + + /* If this is a /me, append an event */ + if (empathy_message_get_tptype(msg) == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION) { + gchar *str; + + str = g_strdup_printf ("%s %s", name, body); + empathy_chat_view_append_event (view, str); + g_free (str); + g_free (dup_body); + return; + } + + /* Get the avatar filename, or a fallback */ + avatar = empathy_contact_get_avatar (sender); + if (avatar) { + avatar_filename = avatar->filename; + } + if (!avatar_filename) { + if (!priv->default_avatar_filename) { + priv->default_avatar_filename = + empathy_filename_from_icon_name ("stock_person", + GTK_ICON_SIZE_DIALOG); + } + avatar_filename = priv->default_avatar_filename; + } + + /* Get the right html/func to add the message */ + if (priv->last_contact == sender) { + func = "appendNextMessage"; + if (empathy_contact_is_user (sender)) { + html = priv->out_nextcontent_html; + len = priv->out_nextcontent_len; + } + if (!html) { + html = priv->in_nextcontent_html; + len = priv->in_nextcontent_len; + } + } + if (!html) { + func = "appendMessage"; + if (empathy_contact_is_user (sender)) { + html = priv->out_content_html; + len = priv->out_content_len; + } + if (!html) { + html = priv->in_content_html; + len = priv->in_content_len; + } + } + + theme_adium_append_html (theme, func, html, len, body, avatar_filename, + name, timestamp); + + /* Keep the sender of the last displayed message */ + if (priv->last_contact) { + g_object_unref (priv->last_contact); + } + priv->last_contact = g_object_ref (sender); + + g_free (dup_body); +} + +static void +theme_adium_append_event (EmpathyChatView *view, + const gchar *str) +{ + EmpathyThemeAdium *theme = EMPATHY_THEME_ADIUM (view); + EmpathyThemeAdiumPriv *priv = GET_PRIV (theme); + + if (priv->status_html) { + theme_adium_append_html (theme, "appendMessage", + priv->status_html, priv->status_len, + str, NULL, NULL, + empathy_time_get_current ()); + } + + /* There is no last contact */ + if (priv->last_contact) { + g_object_unref (priv->last_contact); + priv->last_contact = NULL; + } +} + +static void +theme_adium_scroll (EmpathyChatView *view, + gboolean allow_scrolling) +{ + /* FIXME: Is it possible? I guess we need a js function, but I don't + * see any... */ +} + +static void +theme_adium_scroll_down (EmpathyChatView *view) +{ + webkit_web_view_execute_script (WEBKIT_WEB_VIEW (view), "scrollToBottom()"); +} + +static gboolean +theme_adium_get_has_selection (EmpathyChatView *view) +{ + return webkit_web_view_has_selection (WEBKIT_WEB_VIEW (view)); +} + +static void +theme_adium_clear (EmpathyChatView *view) +{ + EmpathyThemeAdiumPriv *priv = GET_PRIV (view); + gchar *basedir_uri; + + priv->page_loaded = FALSE; + basedir_uri = g_strconcat ("file://", priv->basedir, NULL); + webkit_web_view_load_html_string (WEBKIT_WEB_VIEW (view), + priv->template_html, basedir_uri); + g_free (basedir_uri); +} + +static gboolean +theme_adium_find_previous (EmpathyChatView *view, + const gchar *search_criteria, + gboolean new_search) +{ + return webkit_web_view_search_text (WEBKIT_WEB_VIEW (view), + search_criteria, FALSE, + FALSE, TRUE); +} + +static gboolean +theme_adium_find_next (EmpathyChatView *view, + const gchar *search_criteria, + gboolean new_search) +{ + return webkit_web_view_search_text (WEBKIT_WEB_VIEW (view), + search_criteria, FALSE, + TRUE, TRUE); +} + +static void +theme_adium_find_abilities (EmpathyChatView *view, + const gchar *search_criteria, + gboolean *can_do_previous, + gboolean *can_do_next) +{ + /* FIXME: Does webkit provide an API for that? We have wrap=true in + * find_next and find_previous to work around this problem. */ + if (can_do_previous) + *can_do_previous = TRUE; + if (can_do_next) + *can_do_next = TRUE; +} + +static void +theme_adium_highlight (EmpathyChatView *view, + const gchar *text) +{ + webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (view)); + webkit_web_view_mark_text_matches (WEBKIT_WEB_VIEW (view), + text, FALSE, 0); + webkit_web_view_set_highlight_text_matches (WEBKIT_WEB_VIEW (view), + TRUE); +} + +static void +theme_adium_copy_clipboard (EmpathyChatView *view) +{ + webkit_web_view_copy_clipboard (WEBKIT_WEB_VIEW (view)); +} + +static void +theme_adium_iface_init (EmpathyChatViewIface *iface) +{ + iface->append_message = theme_adium_append_message; + iface->append_event = theme_adium_append_event; + iface->scroll = theme_adium_scroll; + iface->scroll_down = theme_adium_scroll_down; + iface->get_has_selection = theme_adium_get_has_selection; + iface->clear = theme_adium_clear; + iface->find_previous = theme_adium_find_previous; + iface->find_next = theme_adium_find_next; + iface->find_abilities = theme_adium_find_abilities; + iface->highlight = theme_adium_highlight; + iface->copy_clipboard = theme_adium_copy_clipboard; +} + +static void +theme_adium_load_finished_cb (WebKitWebView *view, + WebKitWebFrame *frame, + gpointer user_data) +{ + EmpathyThemeAdiumPriv *priv = GET_PRIV (view); + EmpathyChatView *chat_view = EMPATHY_CHAT_VIEW (view); + + DEBUG ("Page loaded"); + priv->page_loaded = TRUE; + + /* Display queued messages */ + priv->message_queue = g_list_reverse (priv->message_queue); + while (priv->message_queue) { + EmpathyMessage *message = priv->message_queue->data; + + theme_adium_append_message (chat_view, message); + priv->message_queue = g_list_remove (priv->message_queue, message); + g_object_unref (message); + } +} + +static void +theme_adium_finalize (GObject *object) +{ + EmpathyThemeAdiumPriv *priv = GET_PRIV (object); + + g_free (priv->basedir); + g_free (priv->template_html); + g_free (priv->in_content_html); + g_free (priv->in_nextcontent_html); + g_free (priv->out_content_html); + g_free (priv->out_nextcontent_html); + g_free (priv->default_avatar_filename); + g_free (priv->path); + + G_OBJECT_CLASS (empathy_theme_adium_parent_class)->finalize (object); +} + +static void +theme_adium_dispose (GObject *object) +{ + EmpathyThemeAdiumPriv *priv = GET_PRIV (object); + + if (priv->smiley_manager) { + g_object_unref (priv->smiley_manager); + priv->smiley_manager = NULL; + } + + if (priv->last_contact) { + g_object_unref (priv->last_contact); + priv->last_contact = NULL; + } + + G_OBJECT_CLASS (empathy_theme_adium_parent_class)->dispose (object); +} + +static void +theme_adium_constructed (GObject *object) +{ + theme_adium_load (EMPATHY_THEME_ADIUM (object)); +} + +static void +theme_adium_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + EmpathyThemeAdiumPriv *priv = GET_PRIV (object); + + switch (param_id) { + case PROP_PATH: + g_value_set_string (value, priv->path); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + +static void +theme_adium_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + EmpathyThemeAdiumPriv *priv = GET_PRIV (object); + + switch (param_id) { + case PROP_PATH: + g_free (priv->path); + priv->path = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + }; +} + +static void +empathy_theme_adium_class_init (EmpathyThemeAdiumClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = theme_adium_finalize; + object_class->dispose = theme_adium_dispose; + object_class->constructed = theme_adium_constructed; + object_class->get_property = theme_adium_get_property; + object_class->set_property = theme_adium_set_property; + + g_object_class_install_property (object_class, + PROP_PATH, + g_param_spec_string ("path", + "The theme path", + "Path to the adium theme", + g_get_home_dir (), + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READWRITE)); + + + g_type_class_add_private (object_class, sizeof (EmpathyThemeAdiumPriv)); +} + +static void +empathy_theme_adium_init (EmpathyThemeAdium *theme) +{ + EmpathyThemeAdiumPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (theme, + EMPATHY_TYPE_THEME_ADIUM, EmpathyThemeAdiumPriv); + + theme->priv = priv; + + priv->smiley_manager = empathy_smiley_manager_dup_singleton (); + + g_signal_connect (theme, "load-finished", + G_CALLBACK (theme_adium_load_finished_cb), + NULL); + g_signal_connect (theme, "navigation-requested", + G_CALLBACK (theme_adium_navigation_requested_cb), + NULL); + g_signal_connect (theme, "populate-popup", + G_CALLBACK (theme_adium_populate_popup_cb), + NULL); +} + +EmpathyThemeAdium * +empathy_theme_adium_new (const gchar *path) +{ + g_return_val_if_fail (empathy_theme_adium_is_valid (path), NULL); + + return g_object_new (EMPATHY_TYPE_THEME_ADIUM, + "path", path, + NULL); +} + +gboolean +empathy_theme_adium_is_valid (const gchar *path) +{ + gboolean ret; + gchar *file; + + /* We ship a default Template.html as fallback if there is any problem + * with the one inside the theme. The only other required file is + * Content.html for incoming messages (outgoing fallback to use + * incoming). */ + file = g_build_filename (path, "Contents", "Resources", "Incoming", + "Content.html", NULL); + ret = g_file_test (file, G_FILE_TEST_EXISTS); + g_free (file); + + return ret; +} + diff --git a/libempathy-gtk/empathy-theme-adium.h b/libempathy-gtk/empathy-theme-adium.h new file mode 100644 index 000000000..b62675680 --- /dev/null +++ b/libempathy-gtk/empathy-theme-adium.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008-2009 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * 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: Xavier Claessens <xclaesse@gmail.com> + */ + +#ifndef __EMPATHY_THEME_ADIUM_H__ +#define __EMPATHY_THEME_ADIUM_H__ + +#include <webkit/webkitwebview.h> + +#include "empathy-chat-view.h" + +G_BEGIN_DECLS + +#define EMPATHY_TYPE_THEME_ADIUM (empathy_theme_adium_get_type ()) +#define EMPATHY_THEME_ADIUM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EMPATHY_TYPE_THEME_ADIUM, EmpathyThemeAdium)) +#define EMPATHY_THEME_ADIUM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EMPATHY_TYPE_THEME_ADIUM, EmpathyThemeAdiumClass)) +#define EMPATHY_IS_THEME_ADIUM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EMPATHY_TYPE_THEME_ADIUM)) +#define EMPATHY_IS_THEME_ADIUM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EMPATHY_TYPE_THEME_ADIUM)) +#define EMPATHY_THEME_ADIUM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EMPATHY_TYPE_THEME_ADIUM, EmpathyThemeAdiumClass)) + +typedef struct _EmpathyThemeAdium EmpathyThemeAdium; +typedef struct _EmpathyThemeAdiumClass EmpathyThemeAdiumClass; + +struct _EmpathyThemeAdium { + WebKitWebView parent; + gpointer priv; +}; + +struct _EmpathyThemeAdiumClass { + WebKitWebViewClass parent_class; +}; + +GType empathy_theme_adium_get_type (void) G_GNUC_CONST; +EmpathyThemeAdium *empathy_theme_adium_new (const gchar *path); +gboolean empathy_theme_adium_is_valid (const gchar *path); + +G_END_DECLS + +#endif /* __EMPATHY_THEME_ADIUM_H__ */ diff --git a/libempathy-gtk/empathy-theme-manager.c b/libempathy-gtk/empathy-theme-manager.c index 0186b5eb7..c6da8ea68 100644 --- a/libempathy-gtk/empathy-theme-manager.c +++ b/libempathy-gtk/empathy-theme-manager.c @@ -38,6 +38,10 @@ #include "empathy-theme-boxes.h" #include "empathy-theme-irc.h" +#ifdef HAVE_WEBKIT +#include "empathy-theme-adium.h" +#endif + #define DEBUG_FLAG EMPATHY_DEBUG_OTHER #include <libempathy/empathy-debug.h> @@ -45,6 +49,8 @@ typedef struct { gchar *name; guint name_notify_id; + gchar *adium_path; + guint adium_path_notify_id; GtkSettings *settings; GList *boxes_views; } EmpathyThemeManagerPriv; @@ -61,6 +67,9 @@ static const gchar *themes[] = { "simple", N_("Simple"), "clean", N_("Clean"), "blue", N_("Blue"), +#ifdef HAVE_WEBKIT + "adium", N_("Adium"), +#endif NULL }; @@ -198,7 +207,7 @@ theme_manager_update_boxes_tags (EmpathyThemeBoxes *theme, "pixels-above-lines", 4, NULL); TAG_SET ("paragraph-background", "paragraph-background-set", text_background); - TAG_SET ("foreground", "foreground-set",highlight_foreground); + TAG_SET ("foreground", "foreground-set", highlight_foreground); empathy_chat_text_view_tag_set (view, EMPATHY_CHAT_TEXT_VIEW_TAG_SPACING, "size", 3000, @@ -318,6 +327,17 @@ empathy_theme_manager_create_view (EmpathyThemeManager *manager) DEBUG ("Using theme %s", priv->name); +#ifdef HAVE_WEBKIT + if (strcmp (priv->name, "adium") == 0) { + if (empathy_theme_adium_is_valid (priv->adium_path)) { + return EMPATHY_CHAT_VIEW (empathy_theme_adium_new (priv->adium_path)); + } else { + /* The adium path is not valid, fallback to classic theme */ + return EMPATHY_CHAT_VIEW (theme_manager_create_irc_view (manager)); + } + } +#endif + if (strcmp (priv->name, "classic") == 0) { return EMPATHY_CHAT_VIEW (theme_manager_create_irc_view (manager)); } @@ -404,6 +424,27 @@ theme_manager_notify_name_cb (EmpathyConf *conf, } static void +theme_manager_notify_adium_path_cb (EmpathyConf *conf, + const gchar *key, + gpointer user_data) +{ + EmpathyThemeManager *manager = EMPATHY_THEME_MANAGER (user_data); + EmpathyThemeManagerPriv *priv = GET_PRIV (manager); + gchar *adium_path = NULL; + + if (!empathy_conf_get_string (conf, key, &adium_path) || + !tp_strdiff (priv->adium_path, adium_path)) { + g_free (adium_path); + return; + } + + g_free (priv->adium_path); + priv->adium_path = adium_path; + + g_signal_emit (manager, signals[THEME_CHANGED], 0, NULL); +} + +static void theme_manager_finalize (GObject *object) { EmpathyThemeManagerPriv *priv = GET_PRIV (object); @@ -411,6 +452,8 @@ theme_manager_finalize (GObject *object) empathy_conf_notify_remove (empathy_conf_get (), priv->name_notify_id); g_free (priv->name); + empathy_conf_notify_remove (empathy_conf_get (), priv->adium_path_notify_id); + g_free (priv->adium_path); for (l = priv->boxes_views; l; l = l->next) { g_object_weak_unref (G_OBJECT (l->data), @@ -460,6 +503,16 @@ empathy_theme_manager_init (EmpathyThemeManager *manager) EMPATHY_PREFS_CHAT_THEME, manager); + /* Take the adium path and track changes */ + priv->adium_path_notify_id = + empathy_conf_notify_add (empathy_conf_get (), + EMPATHY_PREFS_CHAT_ADIUM_PATH, + theme_manager_notify_adium_path_cb, + manager); + theme_manager_notify_adium_path_cb (empathy_conf_get (), + EMPATHY_PREFS_CHAT_ADIUM_PATH, + manager); + /* Track GTK color changes */ priv->settings = gtk_settings_get_default (); g_signal_connect_swapped (priv->settings, "notify::color-hash", diff --git a/libempathy-gtk/empathy-theme.c b/libempathy-gtk/empathy-theme.c deleted file mode 100644 index ca4f66663..000000000 --- a/libempathy-gtk/empathy-theme.c +++ /dev/null @@ -1,408 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2007 Imendio AB - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * 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 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 <config.h> - -#include <string.h> -#include <glib/gi18n-lib.h> -#include <gtk/gtk.h> - -#include <libempathy/empathy-utils.h> - -#include "empathy-chat.h" -#include "empathy-conf.h" -#include "empathy-theme.h" -#include "empathy-smiley-manager.h" - -/* Number of seconds between timestamps when using normal mode, 5 minutes. */ -#define TIMESTAMP_INTERVAL 300 - -#define SCHEMES "(https?|ftps?|nntp|news|javascript|about|ghelp|apt|telnet|"\ - "file|webcal|mailto)" -#define BODY "([^\\ ]+)" -#define END_BODY "([^\\ ]*[^,;\?><()\\ \"\\.])" -#define URI_REGEX "("SCHEMES"://"END_BODY")" \ - "|((mailto:)?"BODY"@"BODY"\\."END_BODY")"\ - "|((www|ftp)\\."END_BODY")" -static GRegex *uri_regex = NULL; - -#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyTheme) - -typedef struct { - EmpathySmileyManager *smiley_manager; - gboolean show_avatars; -} EmpathyThemePriv; - -static void theme_finalize (GObject *object); -static void theme_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec); -static void theme_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec); - - -G_DEFINE_TYPE (EmpathyTheme, empathy_theme, G_TYPE_OBJECT); - -enum { - PROP_0, - PROP_SHOW_AVATARS -}; - -static void -empathy_theme_class_init (EmpathyThemeClass *class) -{ - GObjectClass *object_class; - - object_class = G_OBJECT_CLASS (class); - - object_class->finalize = theme_finalize; - object_class->get_property = theme_get_property; - object_class->set_property = theme_set_property; - - class->update_view = NULL; - class->append_message = NULL; - class->append_event = NULL; - class->append_timestamp = NULL; - class->append_spacing = NULL; - - g_object_class_install_property (object_class, - PROP_SHOW_AVATARS, - g_param_spec_boolean ("show-avatars", - "", "", - TRUE, - G_PARAM_READWRITE)); - - g_type_class_add_private (object_class, sizeof (EmpathyThemePriv)); -} - -static void -empathy_theme_init (EmpathyTheme *theme) -{ - EmpathyThemePriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (theme, - EMPATHY_TYPE_THEME, EmpathyThemePriv); - - theme->priv = priv; - priv->smiley_manager = empathy_smiley_manager_dup_singleton (); -} - -static void -theme_finalize (GObject *object) -{ - EmpathyThemePriv *priv; - - priv = GET_PRIV (object); - - if (priv->smiley_manager) { - g_object_unref (priv->smiley_manager); - } - - (G_OBJECT_CLASS (empathy_theme_parent_class)->finalize) (object); -} - -static void -theme_get_property (GObject *object, - guint param_id, - GValue *value, - GParamSpec *pspec) -{ - EmpathyThemePriv *priv; - - priv = GET_PRIV (object); - - switch (param_id) { - case PROP_SHOW_AVATARS: - g_value_set_boolean (value, priv->show_avatars); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -static void -theme_set_property (GObject *object, - guint param_id, - const GValue *value, - GParamSpec *pspec) -{ - EmpathyThemePriv *priv; - - priv = GET_PRIV (object); - - switch (param_id) { - case PROP_SHOW_AVATARS: - empathy_theme_set_show_avatars (EMPATHY_THEME (object), - g_value_get_boolean (value)); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); - break; - } -} - -void -empathy_theme_maybe_append_date_and_time (EmpathyTheme *theme, - EmpathyChatView *view, - EmpathyMessage *message) -{ - time_t timestamp; - GDate *date, *last_date; - gboolean append_date, append_time; - - date = empathy_message_get_date_and_time (message, ×tamp); - - last_date = g_date_new (); - g_date_set_time_t (last_date, empathy_chat_view_get_last_timestamp (view)); - - append_date = FALSE; - append_time = FALSE; - - if (g_date_compare (date, last_date) > 0) { - append_date = TRUE; - append_time = TRUE; - } - - g_date_free (last_date); - g_date_free (date); - - if (empathy_chat_view_get_last_timestamp (view) + TIMESTAMP_INTERVAL < timestamp) { - append_time = TRUE; - } - - if (append_time || append_date) { - empathy_theme_append_timestamp (theme, view, message, - append_date, append_time); - } -} - -void -empathy_theme_update_view (EmpathyTheme *theme, - EmpathyChatView *view) -{ - if (!EMPATHY_THEME_GET_CLASS(theme)->update_view) { - g_error ("Theme must override update_view"); - } - - EMPATHY_THEME_GET_CLASS(theme)->update_view (theme, view); -} - -void -empathy_theme_append_message (EmpathyTheme *theme, - EmpathyChatView *view, - EmpathyMessage *message) -{ - if (!EMPATHY_THEME_GET_CLASS(theme)->append_message) { - g_warning ("Theme should override append_message"); - return; - } - - EMPATHY_THEME_GET_CLASS(theme)->append_message (theme, view, message); -} - -static void -theme_insert_text_with_emoticons (GtkTextBuffer *buf, - GtkTextIter *iter, - const gchar *str, - EmpathySmileyManager *smiley_manager) -{ - gboolean use_smileys = FALSE; - GSList *smileys, *l; - - empathy_conf_get_bool (empathy_conf_get (), - EMPATHY_PREFS_CHAT_SHOW_SMILEYS, - &use_smileys); - - if (!use_smileys) { - gtk_text_buffer_insert (buf, iter, str, -1); - return; - } - - smileys = empathy_smiley_manager_parse (smiley_manager, str); - for (l = smileys; l; l = l->next) { - EmpathySmiley *smiley; - - smiley = l->data; - if (smiley->pixbuf) { - gtk_text_buffer_insert_pixbuf (buf, iter, smiley->pixbuf); - } else { - gtk_text_buffer_insert (buf, iter, smiley->str, -1); - } - empathy_smiley_free (smiley); - } - g_slist_free (smileys); -} - -void -empathy_theme_append_text (EmpathyTheme *theme, - EmpathyChatView *view, - const gchar *body, - const gchar *tag, - const gchar *link_tag) -{ - EmpathyThemePriv *priv; - GtkTextBuffer *buffer; - GtkTextIter start_iter, end_iter; - GtkTextMark *mark; - GtkTextIter iter; - GMatchInfo *match_info; - gboolean match; - gint last = 0; - gint s = 0, e = 0; - gchar *tmp; - - priv = GET_PRIV (theme); - buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); - - gtk_text_buffer_get_end_iter (buffer, &start_iter); - mark = gtk_text_buffer_create_mark (buffer, NULL, &start_iter, TRUE); - - if (!uri_regex) { - uri_regex = g_regex_new (URI_REGEX, 0, 0, NULL); - } - - for (match = g_regex_match (uri_regex, body, 0, &match_info); match; - match = g_match_info_next (match_info, NULL)) { - if (!g_match_info_fetch_pos (match_info, 0, &s, &e)) - continue; - - if (s > last) { - tmp = empathy_substring (body, last, s); - - gtk_text_buffer_get_end_iter (buffer, &iter); - theme_insert_text_with_emoticons (buffer, - &iter, - tmp, - priv->smiley_manager); - g_free (tmp); - } - - tmp = empathy_substring (body, s, e); - - gtk_text_buffer_get_end_iter (buffer, &iter); - if (!link_tag) { - gtk_text_buffer_insert (buffer, &iter, - tmp, -1); - } else { - gtk_text_buffer_insert_with_tags_by_name (buffer, - &iter, - tmp, - -1, - link_tag, - "link", - NULL); - } - - g_free (tmp); - last = e; - } - g_match_info_free (match_info); - - if (last < strlen (body)) { - gtk_text_buffer_get_end_iter (buffer, &iter); - theme_insert_text_with_emoticons (buffer, - &iter, - body + last, - priv->smiley_manager); - } - - gtk_text_buffer_get_end_iter (buffer, &iter); - gtk_text_buffer_insert (buffer, &iter, "\n", 1); - - /* Apply the style to the inserted text. */ - gtk_text_buffer_get_iter_at_mark (buffer, &start_iter, mark); - gtk_text_buffer_get_end_iter (buffer, &end_iter); - - gtk_text_buffer_apply_tag_by_name (buffer, - tag, - &start_iter, - &end_iter); - - gtk_text_buffer_delete_mark (buffer, mark); -} - -void -empathy_theme_append_event (EmpathyTheme *theme, - EmpathyChatView *view, - const gchar *str) -{ - if (!EMPATHY_THEME_GET_CLASS(theme)->append_event) { - return; - } - - EMPATHY_THEME_GET_CLASS(theme)->append_event (theme, view, str); -} - -void -empathy_theme_append_spacing (EmpathyTheme *theme, - EmpathyChatView *view) -{ - if (!EMPATHY_THEME_GET_CLASS(theme)->append_spacing) { - return; - } - - EMPATHY_THEME_GET_CLASS(theme)->append_spacing (theme, view); -} - - -void -empathy_theme_append_timestamp (EmpathyTheme *theme, - EmpathyChatView *view, - EmpathyMessage *message, - gboolean show_date, - gboolean show_time) -{ - if (!EMPATHY_THEME_GET_CLASS(theme)->append_timestamp) { - return; - } - - EMPATHY_THEME_GET_CLASS(theme)->append_timestamp (theme, view, - message, show_date, - show_time); -} - -gboolean -empathy_theme_get_show_avatars (EmpathyTheme *theme) -{ - EmpathyThemePriv *priv; - - g_return_val_if_fail (EMPATHY_IS_THEME (theme), FALSE); - - priv = GET_PRIV (theme); - - return priv->show_avatars; -} - -void -empathy_theme_set_show_avatars (EmpathyTheme *theme, gboolean show) -{ - EmpathyThemePriv *priv; - - g_return_if_fail (EMPATHY_IS_THEME (theme)); - - priv = GET_PRIV (theme); - - priv->show_avatars = show; - - g_object_notify (G_OBJECT (theme), "show-avatars"); -} - diff --git a/libempathy-gtk/empathy-ui-utils.c b/libempathy-gtk/empathy-ui-utils.c index f30c58aad..cf423de41 100644 --- a/libempathy-gtk/empathy-ui-utils.c +++ b/libempathy-gtk/empathy-ui-utils.c @@ -558,6 +558,29 @@ empathy_pixbuf_from_icon_name (const gchar *icon_name, return empathy_pixbuf_from_icon_name_sized (icon_name, size); } +gchar * +empathy_filename_from_icon_name (const gchar *icon_name, + GtkIconSize icon_size) +{ + GtkIconTheme *icon_theme; + GtkIconInfo *icon_info; + gint w, h; + gint size = 48; + gchar *ret; + + icon_theme = gtk_icon_theme_get_default (); + + if (gtk_icon_size_lookup (icon_size, &w, &h)) { + size = (w + h) / 2; + } + + icon_info = gtk_icon_theme_lookup_icon (icon_theme, icon_name, size, 0); + ret = g_strdup (gtk_icon_info_get_filename (icon_info)); + gtk_icon_info_free (icon_info); + + return ret; +} + /* Stolen from GtkSourceView, hence the weird intendation. Please keep it like * that to make it easier to apply changes from the original code. */ diff --git a/libempathy-gtk/empathy-ui-utils.h b/libempathy-gtk/empathy-ui-utils.h index 4d58fedcf..60d48f3c4 100644 --- a/libempathy-gtk/empathy-ui-utils.h +++ b/libempathy-gtk/empathy-ui-utils.h @@ -90,12 +90,15 @@ GdkPixbuf * empathy_pixbuf_from_avatar_scaled (EmpathyAvatar *avata GdkPixbuf * empathy_pixbuf_avatar_from_contact_scaled (EmpathyContact *contact, gint width, gint height); -GdkPixbuf * empathy_pixbuf_scale_down_if_necessary (GdkPixbuf *pixbuf, +GdkPixbuf * empathy_pixbuf_scale_down_if_necessary (GdkPixbuf *pixbuf, gint max_size); -GdkPixbuf * empathy_pixbuf_from_icon_name (const gchar *icon_name, +GdkPixbuf * empathy_pixbuf_from_icon_name (const gchar *icon_name, GtkIconSize icon_size); -GdkPixbuf * empathy_pixbuf_from_icon_name_sized (const gchar *icon_name, +GdkPixbuf * empathy_pixbuf_from_icon_name_sized (const gchar *icon_name, gint size); +gchar * empathy_filename_from_icon_name (const gchar *icon_name, + GtkIconSize icon_size); + /* Text view */ gboolean empathy_text_iter_forward_search (const GtkTextIter*iter, const gchar *str, |