]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-theme-adium.c
Do not use empathy_contact_equal(), we can now compare pointers directly.
[empathy.git] / libempathy-gtk / empathy-theme-adium.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2008 Collabora Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  * Authors: Xavier Claessens <xclaesse@gmail.com>
20  */
21
22 #include "config.h"
23
24 #include <string.h>
25 #include <glib/gi18n.h>
26
27 #include <webkit/webkitnetworkrequest.h>
28
29 #include <libempathy/empathy-time.h>
30 #include <libempathy/empathy-utils.h>
31
32 #include "empathy-theme-adium.h"
33 #include "empathy-smiley-manager.h"
34 #include "empathy-conf.h"
35 #include "empathy-ui-utils.h"
36
37 #define DEBUG_FLAG EMPATHY_DEBUG_CHAT
38 #include <libempathy/empathy-debug.h>
39
40 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyThemeAdium)
41
42 typedef struct {
43         EmpathySmileyManager *smiley_manager;
44         EmpathyContact       *last_contact;
45         gboolean              page_loaded;
46         GList                *message_queue;
47         gchar                *path;
48         gchar                *default_avatar_filename;
49         gchar                *template_html;
50         gchar                *basedir;
51         gchar                *in_content_html;
52         gsize                 in_content_len;
53         gchar                *in_nextcontent_html;
54         gsize                 in_nextcontent_len;
55         gchar                *out_content_html;
56         gsize                 out_content_len;
57         gchar                *out_nextcontent_html;
58         gsize                 out_nextcontent_len;
59         gchar                *status_html;
60         gsize                 status_len;
61 } EmpathyThemeAdiumPriv;
62
63 static void theme_adium_iface_init (EmpathyChatViewIface *iface);
64
65 enum {
66         PROP_0,
67         PROP_PATH,
68 };
69
70 G_DEFINE_TYPE_WITH_CODE (EmpathyThemeAdium, empathy_theme_adium,
71                          WEBKIT_TYPE_WEB_VIEW,
72                          G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CHAT_VIEW,
73                                                 theme_adium_iface_init));
74
75 static void
76 theme_adium_load (EmpathyThemeAdium *theme)
77 {
78         EmpathyThemeAdiumPriv *priv = GET_PRIV (theme);
79         gchar                 *file;
80         gchar                 *template_html = NULL;
81         gsize                  template_len;
82         GString               *string;
83         gchar                **strv = NULL;
84         gchar                 *css_path;
85         guint                  len = 0;
86         guint                  i = 0;
87
88         priv->basedir = g_strconcat (priv->path, G_DIR_SEPARATOR_S "Contents" G_DIR_SEPARATOR_S "Resources" G_DIR_SEPARATOR_S, NULL);
89
90         /* Load html files */
91         file = g_build_filename (priv->basedir, "Incoming", "Content.html", NULL);
92         g_file_get_contents (file, &priv->in_content_html, &priv->in_content_len, NULL);
93         g_free (file);
94
95         file = g_build_filename (priv->basedir, "Incoming", "NextContent.html", NULL);
96         g_file_get_contents (file, &priv->in_nextcontent_html, &priv->in_nextcontent_len, NULL);
97         g_free (file);
98
99         file = g_build_filename (priv->basedir, "Outgoing", "Content.html", NULL);
100         g_file_get_contents (file, &priv->out_content_html, &priv->out_content_len, NULL);
101         g_free (file);
102
103         file = g_build_filename (priv->basedir, "Outgoing", "NextContent.html", NULL);
104         g_file_get_contents (file, &priv->out_nextcontent_html, &priv->out_nextcontent_len, NULL);
105         g_free (file);
106
107         file = g_build_filename (priv->basedir, "Status.html", NULL);
108         g_file_get_contents (file, &priv->status_html, &priv->status_len, NULL);
109         g_free (file);
110
111         css_path = g_build_filename (priv->basedir, "main.css", NULL);
112
113         /* There is 2 formats for Template.html: The old one has 4 parameters,
114          * the new one has 5 parameters. */
115         file = g_build_filename (priv->basedir, "Template.html", NULL);
116         if (g_file_get_contents (file, &template_html, &template_len, NULL)) {
117                 strv = g_strsplit (template_html, "%@", -1);
118                 len = g_strv_length (strv);
119         }
120         g_free (file);
121
122         if (len != 5 && len != 6) {
123                 /* Either the theme has no template or it don't have the good
124                  * number of parameters. Fallback to use our own template. */
125                 g_free (template_html);
126                 g_strfreev (strv);
127
128                 file = empathy_file_lookup ("Template.html", "data");
129                 g_file_get_contents (file, &template_html, &template_len, NULL);
130                 g_free (file);
131                 strv = g_strsplit (template_html, "%@", -1);
132                 len = g_strv_length (strv);
133         }
134
135         /* Replace %@ with the needed information in the template html. */
136         string = g_string_sized_new (template_len);
137         g_string_append (string, strv[i++]);
138         g_string_append (string, priv->basedir);
139         g_string_append (string, strv[i++]);
140         if (len == 6) {
141                 /* We include main.css by default */
142                 g_string_append_printf (string, "@import url(\"%s\");", css_path);
143                 g_string_append (string, strv[i++]);
144                 /* FIXME: We should set the variant css here */
145                 g_string_append (string, "");
146         } else {
147                 /* FIXME: We should set main.css OR the variant css */
148                 g_string_append (string, css_path);
149         }
150         g_string_append (string, strv[i++]);
151         g_string_append (string, ""); /* We don't want header */
152         g_string_append (string, strv[i++]);
153         g_string_append (string, ""); /* FIXME: We don't support footer yet */
154         g_string_append (string, strv[i++]);
155         priv->template_html = g_string_free (string, FALSE);
156
157         /* Load the template */
158         webkit_web_view_load_html_string (WEBKIT_WEB_VIEW (theme),
159                                           priv->template_html, priv->basedir);
160
161         g_free (template_html);
162         g_free (css_path);
163         g_strfreev (strv);
164 }
165
166 static WebKitNavigationResponse
167 theme_adium_navigation_requested_cb (WebKitWebView        *view,
168                                      WebKitWebFrame       *frame,
169                                      WebKitNetworkRequest *request,
170                                      gpointer              user_data)
171 {
172         const gchar *uri;
173
174         uri = webkit_network_request_get_uri (request);
175         empathy_url_show (GTK_WIDGET (view), uri);
176
177         return WEBKIT_NAVIGATION_RESPONSE_IGNORE;
178 }
179
180 static void
181 theme_adium_populate_popup_cb (WebKitWebView *view,
182                                GtkMenu       *menu,
183                                gpointer       user_data)
184 {
185         GtkWidget *item;
186
187         /* Remove default menu items */
188         gtk_container_foreach (GTK_CONTAINER (menu),
189                 (GtkCallback) gtk_widget_destroy, NULL);
190         
191         /* Select all item */
192         item = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL);
193         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
194         gtk_widget_show (item);
195                 
196         g_signal_connect_swapped (item, "activate",
197                                   G_CALLBACK (webkit_web_view_select_all),
198                                   view);
199
200         /* Copy menu item */
201         if (webkit_web_view_can_copy_clipboard (view)) {
202                 item = gtk_image_menu_item_new_from_stock (GTK_STOCK_COPY, NULL);
203                 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
204                 gtk_widget_show (item);
205                 
206                 g_signal_connect_swapped (item, "activate",
207                                           G_CALLBACK (webkit_web_view_copy_clipboard),
208                                           view);
209         }
210
211         /* Clear menu item */
212         item = gtk_separator_menu_item_new ();
213         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
214         gtk_widget_show (item);
215                 
216         item = gtk_image_menu_item_new_from_stock (GTK_STOCK_CLEAR, NULL);
217         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
218         gtk_widget_show (item);
219                 
220         g_signal_connect_swapped (item, "activate",
221                                   G_CALLBACK (empathy_chat_view_clear),
222                                   view);
223
224         /* FIXME: Add open_link and copy_link when those bugs are fixed:
225          * https://bugs.webkit.org/show_bug.cgi?id=16092
226          * https://bugs.webkit.org/show_bug.cgi?id=16562
227          */
228 }
229
230 static gchar *
231 theme_adium_parse_body (EmpathyThemeAdium *theme,
232                         const gchar       *text)
233 {
234         EmpathyThemeAdiumPriv *priv = GET_PRIV (theme);
235         gboolean               use_smileys = FALSE;
236         GSList                *smileys, *l;
237         GString               *string;
238         gint                   i;
239         GRegex                *uri_regex;
240         GMatchInfo            *match_info;
241         gboolean               match;
242         gchar                 *ret = NULL;
243         gint                   prev;
244
245         empathy_conf_get_bool (empathy_conf_get (),
246                                EMPATHY_PREFS_CHAT_SHOW_SMILEYS,
247                                &use_smileys);
248
249         if (use_smileys) {
250                 /* Replace smileys by a <img/> tag */
251                 string = g_string_sized_new (strlen (text));
252                 smileys = empathy_smiley_manager_parse (priv->smiley_manager, text);
253                 for (l = smileys; l; l = l->next) {
254                         EmpathySmiley *smiley;
255
256                         smiley = l->data;
257                         if (smiley->path) {
258                                 g_string_append_printf (string,
259                                                         "<abbr title='%s'><img src=\"%s\"/ alt=\"%s\"/></abbr>",
260                                                         smiley->str, smiley->path, smiley->str);
261                         } else {
262                                 gchar *str;
263
264                                 str = g_markup_escape_text (smiley->str, -1);
265                                 g_string_append (string, str);
266                                 g_free (str);
267                         }
268                         empathy_smiley_free (smiley);
269                 }
270                 g_slist_free (smileys);
271
272                 g_free (ret);
273                 text = ret = g_string_free (string, FALSE);
274         }
275
276         /* Add <a href></a> arround links */
277         uri_regex = empathy_uri_regex_dup_singleton ();
278         match = g_regex_match (uri_regex, text, 0, &match_info);
279         if (match) {
280                 gint last = 0;
281                 gint s = 0, e = 0;
282
283                 string = g_string_sized_new (strlen (text));
284                 do {
285                         g_match_info_fetch_pos (match_info, 0, &s, &e);
286
287                         if (s > last) {
288                                 /* Append the text between last link (or the
289                                  * start of the message) and this link */
290                                 g_string_append_len (string, text + last, s - last);
291                         }
292
293                         /* Append the link inside <a href=""></a> tag */
294                         g_string_append (string, "<a href=\"");
295                         g_string_append_len (string, text + s, e - s);
296                         g_string_append (string, "\">");
297                         g_string_append_len (string, text + s, e - s);
298                         g_string_append (string, "</a>");
299
300                         last = e;
301                 } while (g_match_info_next (match_info, NULL));
302
303                 if (e < strlen (text)) {
304                         /* Append the text after the last link */
305                         g_string_append_len (string, text + e, strlen (text) - e);
306                 }
307
308                 g_free (ret);
309                 text = ret = g_string_free (string, FALSE);
310         }
311         g_match_info_free (match_info);
312         g_regex_unref (uri_regex);
313
314         /* Replace \n by <br/> */
315         string = NULL;
316         prev = 0;
317         for (i = 0; text[i] != '\0'; i++) {
318                 if (text[i] == '\n') {
319                         if (!string ) {
320                                 string = g_string_sized_new (strlen (text));
321                         }
322                         g_string_append_len (string, text + prev, i - prev);
323                         g_string_append (string, "<br/>");
324                         prev = i + 1;
325                 }
326         }
327         if (string) {
328                 g_string_append (string, text + prev);
329                 g_free (ret);
330                 text = ret = g_string_free (string, FALSE);
331         }
332
333         return ret;
334 }
335
336 static void
337 escape_and_append_len (GString *string, const gchar *str, gint len)
338 {
339         while (*str != '\0' && len != 0) {
340                 switch (*str) {
341                 case '\\':
342                         /* \ becomes \\ */
343                         g_string_append (string, "\\\\");       
344                         break;
345                 case '\"':
346                         /* " becomes \" */
347                         g_string_append (string, "\\\"");
348                         break;
349                 case '\n':
350                         /* Remove end of lines */
351                         break;
352                 default:
353                         g_string_append_c (string, *str);
354                 }
355
356                 str++;
357                 len--;
358         }
359 }
360
361 static gboolean
362 theme_adium_match (const gchar **str, const gchar *match)
363 {
364         gint len;
365
366         len = strlen (match);
367         if (strncmp (*str, match, len) == 0) {
368                 *str += len - 1;
369                 return TRUE;
370         }
371
372         return FALSE;
373 }
374
375 static void
376 theme_adium_append_html (EmpathyThemeAdium *theme,
377                          const gchar       *func,
378                          const gchar       *html, gsize len,
379                          const gchar       *message,
380                          const gchar       *avatar_filename,
381                          const gchar       *name,
382                          time_t             timestamp)
383 {
384         GString     *string;
385         const gchar *cur = NULL;
386         gchar       *script;
387
388         /* Make some search-and-replace in the html code */
389         string = g_string_sized_new (len + strlen (message));
390         g_string_append_printf (string, "%s(\"", func);
391         for (cur = html; *cur != '\0'; cur++) {
392                 const gchar *replace = NULL;
393                 gchar       *dup_replace = NULL;
394
395                 if (theme_adium_match (&cur, "%message%")) {
396                         replace = message;
397                 } else if (theme_adium_match (&cur, "%userIconPath%")) {
398                         replace = avatar_filename;
399                 } else if (theme_adium_match (&cur, "%sender%")) {
400                         replace = name;
401                 } else if (theme_adium_match (&cur, "%time")) {
402                         gchar *format = NULL;
403                         gchar *end;
404
405                         /* Extract the time format if provided. */
406                         if (*cur == '{') {
407                                 end = strstr (cur + 1, "}%");
408                                 if (!end) {
409                                         /* Invalid string */
410                                         continue;
411                                 }
412                                 cur++;
413                                 format = g_strndup (cur, end - cur);
414                                 cur = end;
415                         } else {
416                                 cur++;
417                         }
418
419                         dup_replace = empathy_time_to_string_local (timestamp,
420                                 format ? format : EMPATHY_TIME_FORMAT_DISPLAY_SHORT);
421                         replace = dup_replace;
422                         g_free (format);
423                 } else {
424                         escape_and_append_len (string, cur, 1);
425                         continue;
426                 }
427
428                 /* Here we have a replacement to make */
429                 escape_and_append_len (string, replace, -1);
430                 g_free (dup_replace);
431         }
432         g_string_append (string, "\")");
433
434         script = g_string_free (string, FALSE);
435         webkit_web_view_execute_script (WEBKIT_WEB_VIEW (theme), script);
436         g_free (script);
437 }
438
439 static void
440 theme_adium_append_message (EmpathyChatView *view,
441                             EmpathyMessage  *msg)
442 {
443         EmpathyThemeAdium     *theme = EMPATHY_THEME_ADIUM (view);
444         EmpathyThemeAdiumPriv *priv = GET_PRIV (theme);
445         EmpathyContact        *sender;
446         gchar                 *dup_body = NULL;
447         const gchar           *body;
448         const gchar           *name;
449         EmpathyAvatar         *avatar;
450         const gchar           *avatar_filename = NULL;
451         time_t                 timestamp;
452         gchar                 *html = NULL;
453         gsize                  len = 0;
454         const gchar           *func;
455
456         if (!priv->page_loaded) {
457                 priv->message_queue = g_list_prepend (priv->message_queue,
458                                                       g_object_ref (msg));
459                 return;
460         }
461
462         /* Get information */
463         sender = empathy_message_get_sender (msg);
464         timestamp = empathy_message_get_timestamp (msg);
465         body = empathy_message_get_body (msg);
466         dup_body = theme_adium_parse_body (theme, body);
467         if (dup_body) {
468                 body = dup_body;
469         }
470         name = empathy_contact_get_name (sender);
471
472         /* If this is a /me, append an event */
473         if (empathy_message_get_tptype(msg) == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION) {
474                 gchar *str;
475
476                 str = g_strdup_printf ("%s %s", name, body);
477                 empathy_chat_view_append_event (view, str);
478                 g_free (str);
479                 g_free (dup_body);
480                 return;
481         }
482
483         /* Get the avatar filename, or a fallback */
484         avatar = empathy_contact_get_avatar (sender);
485         if (avatar) {
486                 avatar_filename = avatar->filename;
487         }
488         if (!avatar_filename) {
489                 if (!priv->default_avatar_filename) {
490                         priv->default_avatar_filename =
491                                 empathy_filename_from_icon_name ("stock_person",
492                                                                  GTK_ICON_SIZE_DIALOG);
493                 }
494                 avatar_filename = priv->default_avatar_filename;
495         }
496
497         /* Get the right html/func to add the message */
498         if (priv->last_contact == sender) {
499                 func = "appendNextMessage";
500                 if (empathy_contact_is_user (sender)) {
501                         html = priv->out_nextcontent_html;
502                         len = priv->out_nextcontent_len;
503                 }
504                 if (!html) {
505                         html = priv->in_nextcontent_html;
506                         len = priv->in_nextcontent_len;
507                 }
508         }
509         if (!html) {
510                 func = "appendMessage";
511                 if (empathy_contact_is_user (sender)) {
512                         html = priv->out_content_html;
513                         len = priv->out_content_len;
514                 }
515                 if (!html) {
516                         html = priv->in_content_html;
517                         len = priv->in_content_len;
518                 }
519         }
520
521         theme_adium_append_html (theme, func, html, len, body, avatar_filename,
522                                  name, timestamp);
523
524         /* Keep the sender of the last displayed message */
525         if (priv->last_contact) {
526                 g_object_unref (priv->last_contact);
527         }
528         priv->last_contact = g_object_ref (sender);
529
530         g_free (dup_body);
531 }
532
533 static void
534 theme_adium_append_event (EmpathyChatView *view,
535                           const gchar     *str)
536 {
537         EmpathyThemeAdium     *theme = EMPATHY_THEME_ADIUM (view);
538         EmpathyThemeAdiumPriv *priv = GET_PRIV (theme);
539
540         if (priv->status_html) {
541                 theme_adium_append_html (theme, "appendMessage",
542                                          priv->status_html, priv->status_len,
543                                          str, NULL, NULL,
544                                          empathy_time_get_current ());
545         }
546
547         /* There is no last contact */
548         if (priv->last_contact) {
549                 g_object_unref (priv->last_contact);
550                 priv->last_contact = NULL;
551         }
552 }
553
554 static void
555 theme_adium_scroll (EmpathyChatView *view,
556                     gboolean         allow_scrolling)
557 {
558         /* FIXME: Is it possible? I guess we need a js function, but I don't 
559          * see any... */
560 }
561
562 static void
563 theme_adium_scroll_down (EmpathyChatView *view)
564 {
565         webkit_web_view_execute_script (WEBKIT_WEB_VIEW (view), "scrollToBottom()");
566 }
567
568 static gboolean
569 theme_adium_get_has_selection (EmpathyChatView *view)
570 {
571         return webkit_web_view_has_selection (WEBKIT_WEB_VIEW (view));
572 }
573
574 static void
575 theme_adium_clear (EmpathyChatView *view)
576 {
577         EmpathyThemeAdiumPriv *priv = GET_PRIV (view);
578
579         priv->page_loaded = FALSE;
580         webkit_web_view_load_html_string (WEBKIT_WEB_VIEW (view),
581                                           priv->template_html, priv->basedir);
582 }
583
584 static gboolean
585 theme_adium_find_previous (EmpathyChatView *view,
586                            const gchar     *search_criteria,
587                            gboolean         new_search)
588 {
589         return webkit_web_view_search_text (WEBKIT_WEB_VIEW (view),
590                                             search_criteria, FALSE,
591                                             FALSE, TRUE);
592 }
593
594 static gboolean
595 theme_adium_find_next (EmpathyChatView *view,
596                        const gchar     *search_criteria,
597                        gboolean         new_search)
598 {
599         return webkit_web_view_search_text (WEBKIT_WEB_VIEW (view),
600                                             search_criteria, FALSE,
601                                             TRUE, TRUE);
602 }
603
604 static void
605 theme_adium_find_abilities (EmpathyChatView *view,
606                             const gchar    *search_criteria,
607                             gboolean       *can_do_previous,
608                             gboolean       *can_do_next)
609 {
610         /* FIXME: Does webkit provide an API for that? We have wrap=true in
611          * find_next and find_previous to work around this problem. */
612         if (can_do_previous)
613                 *can_do_previous = TRUE;
614         if (can_do_next)
615                 *can_do_next = TRUE;
616 }
617
618 static void
619 theme_adium_highlight (EmpathyChatView *view,
620                        const gchar     *text)
621 {
622         webkit_web_view_unmark_text_matches (WEBKIT_WEB_VIEW (view));
623         webkit_web_view_mark_text_matches (WEBKIT_WEB_VIEW (view),
624                                            text, FALSE, 0);
625         webkit_web_view_set_highlight_text_matches (WEBKIT_WEB_VIEW (view),
626                                                     TRUE);
627 }
628
629 static void
630 theme_adium_copy_clipboard (EmpathyChatView *view)
631 {
632         webkit_web_view_copy_clipboard (WEBKIT_WEB_VIEW (view));
633 }
634
635 static void
636 theme_adium_iface_init (EmpathyChatViewIface *iface)
637 {
638         iface->append_message = theme_adium_append_message;
639         iface->append_event = theme_adium_append_event;
640         iface->scroll = theme_adium_scroll;
641         iface->scroll_down = theme_adium_scroll_down;
642         iface->get_has_selection = theme_adium_get_has_selection;
643         iface->clear = theme_adium_clear;
644         iface->find_previous = theme_adium_find_previous;
645         iface->find_next = theme_adium_find_next;
646         iface->find_abilities = theme_adium_find_abilities;
647         iface->highlight = theme_adium_highlight;
648         iface->copy_clipboard = theme_adium_copy_clipboard;
649 }
650
651 static void
652 theme_adium_load_finished_cb (WebKitWebView  *view,
653                               WebKitWebFrame *frame,
654                               gpointer        user_data)
655 {
656         EmpathyThemeAdiumPriv *priv = GET_PRIV (view);
657         EmpathyChatView       *chat_view = EMPATHY_CHAT_VIEW (view);
658
659         DEBUG ("Page loaded");
660         priv->page_loaded = TRUE;
661
662         /* Display queued messages */
663         priv->message_queue = g_list_reverse (priv->message_queue);
664         while (priv->message_queue) {
665                 EmpathyMessage *message = priv->message_queue->data;
666
667                 theme_adium_append_message (chat_view, message);
668                 priv->message_queue = g_list_remove (priv->message_queue, message);
669                 g_object_unref (message);
670         }
671 }
672
673 static void
674 theme_adium_finalize (GObject *object)
675 {
676         EmpathyThemeAdiumPriv *priv = GET_PRIV (object);
677
678         g_free (priv->basedir);
679         g_free (priv->template_html);
680         g_free (priv->in_content_html);
681         g_free (priv->in_nextcontent_html);
682         g_free (priv->out_content_html);
683         g_free (priv->out_nextcontent_html);
684         g_free (priv->default_avatar_filename);
685         g_free (priv->path);
686         g_object_unref (priv->smiley_manager);
687
688         if (priv->last_contact) {
689                 g_object_unref (priv->last_contact);
690         }
691
692         G_OBJECT_CLASS (empathy_theme_adium_parent_class)->finalize (object);
693 }
694
695 static void
696 theme_adium_constructed (GObject *object)
697 {
698         theme_adium_load (EMPATHY_THEME_ADIUM (object));
699 }
700
701 static void
702 theme_adium_get_property (GObject    *object,
703                           guint       param_id,
704                           GValue     *value,
705                           GParamSpec *pspec)
706 {
707         EmpathyThemeAdiumPriv *priv = GET_PRIV (object);
708
709         switch (param_id) {
710         case PROP_PATH:
711                 g_value_set_string (value, priv->path);
712                 break;
713         default:
714                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
715                 break;
716         };
717 }
718
719 static void
720 theme_adium_set_property (GObject      *object,
721                           guint         param_id,
722                           const GValue *value,
723                           GParamSpec   *pspec)
724 {
725         EmpathyThemeAdiumPriv *priv = GET_PRIV (object);
726
727         switch (param_id) {
728         case PROP_PATH:
729                 g_free (priv->path);
730                 priv->path = g_value_dup_string (value);
731                 break;
732         default:
733                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
734                 break;
735         };
736 }
737
738 static void
739 empathy_theme_adium_class_init (EmpathyThemeAdiumClass *klass)
740 {
741         GObjectClass *object_class = G_OBJECT_CLASS (klass);
742         
743         object_class->finalize = theme_adium_finalize;
744         object_class->constructed = theme_adium_constructed;
745         object_class->get_property = theme_adium_get_property;
746         object_class->set_property = theme_adium_set_property;
747
748         g_object_class_install_property (object_class,
749                                          PROP_PATH,
750                                          g_param_spec_string ("path",
751                                                               "The theme path",
752                                                               "Path to the adium theme",
753                                                               g_get_home_dir (),
754                                                               G_PARAM_CONSTRUCT_ONLY |
755                                                               G_PARAM_READWRITE));
756
757
758         g_type_class_add_private (object_class, sizeof (EmpathyThemeAdiumPriv));
759 }
760
761 static void
762 empathy_theme_adium_init (EmpathyThemeAdium *theme)
763 {
764         EmpathyThemeAdiumPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (theme,
765                 EMPATHY_TYPE_THEME_ADIUM, EmpathyThemeAdiumPriv);
766
767         theme->priv = priv;     
768
769         priv->smiley_manager = empathy_smiley_manager_dup_singleton ();
770
771         g_signal_connect (theme, "load-finished",
772                           G_CALLBACK (theme_adium_load_finished_cb),
773                           NULL);
774         g_signal_connect (theme, "navigation-requested",
775                           G_CALLBACK (theme_adium_navigation_requested_cb),
776                           NULL);
777         g_signal_connect (theme, "populate-popup",
778                           G_CALLBACK (theme_adium_populate_popup_cb),
779                           NULL);
780 }
781
782 EmpathyThemeAdium *
783 empathy_theme_adium_new (const gchar *path)
784 {
785         g_return_val_if_fail (empathy_theme_adium_is_valid (path), NULL);
786
787         return g_object_new (EMPATHY_TYPE_THEME_ADIUM,
788                              "path", path,
789                              NULL);
790 }
791
792 gboolean
793 empathy_theme_adium_is_valid (const gchar *path)
794 {
795         gboolean ret;
796         gchar   *file;
797
798         /* We ship a default Template.html as fallback if there is any problem
799          * with the one inside the theme. The only other required file is
800          * Content.html for incoming messages (outgoing fallback to use
801          * incoming). */
802         file = g_build_filename (path, "Contents", "Resources", "Incoming",
803                                  "Content.html", NULL);
804         ret = g_file_test (file, G_FILE_TEST_EXISTS);
805         g_free (file);
806
807         return ret;
808 }
809