~ubuntu-branches/ubuntu/precise/empathy/precise-proposed-201205180810

« back to all changes in this revision

Viewing changes to libempathy-gtk/empathy-theme-adium.c

  • Committer: Bazaar Package Importer
  • Author(s): Brian Curtis, Brian Curtis, Ken VanDine
  • Date: 2011-06-01 10:35:24 UTC
  • mfrom: (1.1.70 upstream) (6.3.44 experimental)
  • Revision ID: james.westby@ubuntu.com-20110601103524-wx3wgp71394730jt
Tags: 3.1.1-1ubuntu1
[ Brian Curtis ]
* Merge with Debian experimental, remaining Ubuntu changes:
* debian/control:
  - Drop geoclue/mapping build-depends (they are in Universe)
  - Add Vcz-Bzr link
  - Add Suggests on telepathy-idle
  - Bump telepathy-butterfly, telepathy-haze to recommends
  - Don't recommend the freedesktop sound theme we have an ubuntu one
  - Add build depend for libunity-dev
* debian/rules:
  - Use autoreconf.mk
  - Disable map and location
* debian/empathy.install:
  - Install message indicator configuration
* debian/indicators/empathy:
  - Message indicator configuration
* debian/patches/01_lpi.patch:
  - Add Launchpad integration
* debian/patches/10_use_notify_osd_icons.patch:
  - Use the notify-osd image for new messages
* debian/patches/34_start_raised_execpt_in_session.patch
  - If not started with the session, we should always raise
* debian/patches/36_chat_window_default_size.patch:
  - Make the default chat window size larger
* debian/patches/37_facebook_default.patch:
  - Make facebook the default chat account type
* debian/patches/38_lp_569289.patch
  - Set freenode as default IRC network for new IRC accounts 
* debian/patches/41_unity_launcher_progress.patch
  - Display file transfer progress in the unity launcher

[ Ken VanDine ]
* debian/control
  - build depend on libgcr-3-dev instead of libgcr-dev
  - dropped build depends for libindicate, we will use telepathy-indicator
  - Depend on dconf-gsettings-backend | gsettings-backend
  - Added a Recommends for telepathy-indicator
* +debian/empathy.gsettings-override
  - Added an override for notifications-focus
* debian/patches/series
  - commented out 23_idomessagedialog_for_voip_and_ft.patch, until ido has 
    been ported to gtk3

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
#include "config.h"
23
23
 
24
24
#include <string.h>
25
 
#include <glib/gi18n.h>
 
25
#include <glib/gi18n-lib.h>
26
26
 
27
27
#include <webkit/webkit.h>
28
28
#include <telepathy-glib/dbus.h>
29
29
#include <telepathy-glib/util.h>
30
30
 
31
 
#include <gconf/gconf-client.h>
32
31
#include <pango/pango.h>
33
32
#include <gdk/gdk.h>
34
33
 
48
47
 
49
48
#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyThemeAdium)
50
49
 
51
 
/* GConf key containing current value of font */
52
 
#define EMPATHY_GCONF_FONT_KEY_NAME "/desktop/gnome/interface/document_font_name"
53
50
#define BORING_DPI_DEFAULT 96
54
51
 
55
52
/* "Join" consecutive messages with timestamps within five minutes */
59
56
        EmpathyAdiumData     *data;
60
57
        EmpathySmileyManager *smiley_manager;
61
58
        EmpathyContact       *last_contact;
62
 
        time_t                last_timestamp;
 
59
        gint64                last_timestamp;
63
60
        gboolean              last_is_backlog;
64
61
        guint                 pages_loading;
65
 
        GList                *message_queue;
 
62
        /* Queue of GValue* containing an EmpathyMessage or string */
 
63
        GQueue                message_queue;
66
64
        GtkWidget            *inspector_window;
67
65
        GSettings            *gsettings_chat;
 
66
        gboolean              has_focus;
 
67
        gboolean              has_unread_message;
 
68
        gboolean              allow_scrolling;
68
69
} EmpathyThemeAdiumPriv;
69
70
 
70
71
struct _EmpathyAdiumData {
74
75
        gchar *default_avatar_filename;
75
76
        gchar *default_incoming_avatar_filename;
76
77
        gchar *default_outgoing_avatar_filename;
77
 
        gchar *template_html;
78
 
        gchar *in_content_html;
79
 
        gsize  in_content_len;
80
 
        gchar *in_context_html;
81
 
        gsize  in_context_len;
82
 
        gchar *in_nextcontent_html;
83
 
        gsize  in_nextcontent_len;
84
 
        gchar *in_nextcontext_html;
85
 
        gsize  in_nextcontext_len;
86
 
        gchar *out_content_html;
87
 
        gsize  out_content_len;
88
 
        gchar *out_context_html;
89
 
        gsize  out_context_len;
90
 
        gchar *out_nextcontent_html;
91
 
        gsize  out_nextcontent_len;
92
 
        gchar *out_nextcontext_html;
93
 
        gsize  out_nextcontext_len;
94
 
        gchar *status_html;
95
 
        gsize  status_len;
96
78
        GHashTable *info;
 
79
        guint version;
 
80
        gboolean custom_template;
 
81
 
 
82
        /* HTML bits */
 
83
        const gchar *template_html;
 
84
        const gchar *content_html;
 
85
        const gchar *in_content_html;
 
86
        const gchar *in_context_html;
 
87
        const gchar *in_nextcontent_html;
 
88
        const gchar *in_nextcontext_html;
 
89
        const gchar *out_content_html;
 
90
        const gchar *out_context_html;
 
91
        const gchar *out_nextcontent_html;
 
92
        const gchar *out_nextcontext_html;
 
93
        const gchar *status_html;
 
94
 
 
95
        /* Above html strings are pointers to strings stored in this array.
 
96
         * We do this because of fallbacks, some htmls could be pointing the
 
97
         * same string. */
 
98
        GPtrArray *strings_to_free;
97
99
};
98
100
 
99
101
static void theme_adium_iface_init (EmpathyChatViewIface *iface);
192
194
        g_free (uri);
193
195
}
194
196
 
 
197
/* Replace each %@ in format with string passed in args */
 
198
static gchar *
 
199
string_with_format (const gchar *format,
 
200
                    const gchar *first_string,
 
201
                    ...)
 
202
{
 
203
        va_list args;
 
204
        const gchar *str;
 
205
        GString *result;
 
206
 
 
207
        va_start (args, first_string);
 
208
        result = g_string_sized_new (strlen (format));
 
209
        for (str = first_string; str != NULL; str = va_arg (args, const gchar *)) {
 
210
                const gchar *next;
 
211
 
 
212
                next = strstr (format, "%@");
 
213
                if (next == NULL) {
 
214
                        break;
 
215
                }
 
216
 
 
217
                g_string_append_len (result, format, next - format);
 
218
                g_string_append (result, str);
 
219
                format = next + 2;
 
220
        }
 
221
        g_string_append (result, format);
 
222
        va_end (args);
 
223
 
 
224
        return g_string_free (result, FALSE);
 
225
}
 
226
 
195
227
static void
196
228
theme_adium_match_newline (const gchar *text,
197
229
                           gssize len,
252
284
};
253
285
 
254
286
static gchar *
255
 
theme_adium_parse_body (const gchar *text)
 
287
theme_adium_parse_body (EmpathyThemeAdium *self,
 
288
        const gchar *text)
256
289
{
 
290
        EmpathyThemeAdiumPriv *priv = GET_PRIV (self);
257
291
        EmpathyStringParser *parsers;
258
292
        GString *string;
259
 
        GSettings *gsettings = g_settings_new (EMPATHY_PREFS_CHAT_SCHEMA);
260
293
 
261
294
        /* Check if we have to parse smileys */
262
 
        if (g_settings_get_boolean (gsettings, EMPATHY_PREFS_CHAT_SHOW_SMILEYS))
 
295
        if (g_settings_get_boolean (priv->gsettings_chat,
 
296
          EMPATHY_PREFS_CHAT_SHOW_SMILEYS))
263
297
                parsers = string_parsers_with_smiley;
264
298
        else
265
299
                parsers = string_parsers;
270
304
        string = g_string_sized_new (strlen (text));
271
305
        empathy_string_parser_substr (text, -1, parsers, string);
272
306
 
273
 
        g_object_unref (gsettings);
 
307
        /* Wrap body in order to make tabs and multiple spaces displayed
 
308
         * properly. See bug #625745. */
 
309
        g_string_prepend (string, "<div style=\"display: inline; "
 
310
                                               "white-space: pre-wrap\"'>");
 
311
        g_string_append (string, "</div>");
274
312
 
275
313
        return g_string_free (string, FALSE);
276
314
}
278
316
static void
279
317
escape_and_append_len (GString *string, const gchar *str, gint len)
280
318
{
281
 
        while (*str != '\0' && len != 0) {
 
319
        while (str != NULL && *str != '\0' && len != 0) {
282
320
                switch (*str) {
283
321
                case '\\':
284
322
                        /* \ becomes \\ */
300
338
        }
301
339
}
302
340
 
 
341
/* If *str starts with match, returns TRUE and move pointer to the end */
303
342
static gboolean
304
 
theme_adium_match (const gchar **str, const gchar *match)
 
343
theme_adium_match (const gchar **str,
 
344
                   const gchar *match)
305
345
{
306
346
        gint len;
307
347
 
314
354
        return FALSE;
315
355
}
316
356
 
 
357
/* Like theme_adium_match() but also return the X part if match is like %foo{X}% */
 
358
static gboolean
 
359
theme_adium_match_with_format (const gchar **str,
 
360
                               const gchar *match,
 
361
                               gchar **format)
 
362
{
 
363
        const gchar *cur = *str;
 
364
        const gchar *end;
 
365
 
 
366
        if (!theme_adium_match (&cur, match)) {
 
367
                return FALSE;
 
368
        }
 
369
        cur++;
 
370
 
 
371
        end = strstr (cur, "}%");
 
372
        if (!end) {
 
373
                return FALSE;
 
374
        }
 
375
 
 
376
        *format = g_strndup (cur , end - cur);
 
377
        *str = end + 1;
 
378
        return TRUE;
 
379
}
 
380
 
 
381
/* List of colors used by %senderColor%. Copied from
 
382
 * adium/Frameworks/AIUtilities\ Framework/Source/AIColorAdditions.m
 
383
 */
 
384
static gchar *colors[] = {
 
385
        "aqua", "aquamarine", "blue", "blueviolet", "brown", "burlywood", "cadetblue",
 
386
        "chartreuse", "chocolate", "coral", "cornflowerblue", "crimson", "cyan",
 
387
        "darkblue", "darkcyan", "darkgoldenrod", "darkgreen", "darkgrey", "darkkhaki",
 
388
        "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", "darkred",
 
389
        "darksalmon", "darkseagreen", "darkslateblue", "darkslategrey",
 
390
        "darkturquoise", "darkviolet", "deeppink", "deepskyblue", "dimgrey",
 
391
        "dodgerblue", "firebrick", "forestgreen", "fuchsia", "gold", "goldenrod",
 
392
        "green", "greenyellow", "grey", "hotpink", "indianred", "indigo", "lawngreen",
 
393
        "lightblue", "lightcoral",
 
394
        "lightgreen", "lightgrey", "lightpink", "lightsalmon", "lightseagreen",
 
395
        "lightskyblue", "lightslategrey", "lightsteelblue", "lime", "limegreen",
 
396
        "magenta", "maroon", "mediumaquamarine", "mediumblue", "mediumorchid",
 
397
        "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen",
 
398
        "mediumturquoise", "mediumvioletred", "midnightblue", "navy", "olive",
 
399
        "olivedrab", "orange", "orangered", "orchid", "palegreen", "paleturquoise",
 
400
        "palevioletred", "peru", "pink", "plum", "powderblue", "purple", "red",
 
401
        "rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen",
 
402
        "sienna", "silver", "skyblue", "slateblue", "slategrey", "springgreen",
 
403
        "steelblue", "tan", "teal", "thistle", "tomato", "turquoise", "violet",
 
404
        "yellowgreen",
 
405
};
 
406
 
317
407
static void
318
408
theme_adium_append_html (EmpathyThemeAdium *theme,
319
409
                         const gchar       *func,
320
 
                         const gchar       *html, gsize len,
 
410
                         const gchar       *html,
321
411
                         const gchar       *message,
322
412
                         const gchar       *avatar_filename,
323
413
                         const gchar       *name,
324
414
                         const gchar       *contact_id,
325
415
                         const gchar       *service_name,
326
416
                         const gchar       *message_classes,
327
 
                         time_t             timestamp,
 
417
                         gint64             timestamp,
328
418
                         gboolean           is_backlog)
329
419
{
330
420
        GString     *string;
332
422
        gchar       *script;
333
423
 
334
424
        /* Make some search-and-replace in the html code */
335
 
        string = g_string_sized_new (len + strlen (message));
 
425
        string = g_string_sized_new (strlen (html) + strlen (message));
336
426
        g_string_append_printf (string, "%s(\"", func);
337
427
        for (cur = html; *cur != '\0'; cur++) {
338
428
                const gchar *replace = NULL;
339
429
                gchar       *dup_replace = NULL;
 
430
                gchar       *format = NULL;
340
431
 
341
 
                if (theme_adium_match (&cur, "%message%")) {
342
 
                        replace = message;
343
 
                } else if (theme_adium_match (&cur, "%messageClasses%")) {
344
 
                        replace = message_classes;
345
 
                } else if (theme_adium_match (&cur, "%userIconPath%")) {
 
432
                /* Those are all well known keywords that needs replacement in
 
433
                 * html files. Please keep them in the same order than the adium
 
434
                 * spec. See http://trac.adium.im/wiki/CreatingMessageStyles */
 
435
                if (theme_adium_match (&cur, "%userIconPath%")) {
346
436
                        replace = avatar_filename;
347
 
                } else if (theme_adium_match (&cur, "%sender%")) {
348
 
                        replace = name;
349
437
                } else if (theme_adium_match (&cur, "%senderScreenName%")) {
350
438
                        replace = contact_id;
 
439
                } else if (theme_adium_match (&cur, "%sender%")) {
 
440
                        replace = name;
 
441
                } else if (theme_adium_match (&cur, "%senderColor%")) {
 
442
                        /* A color derived from the user's name.
 
443
                         * FIXME: If a colon separated list of HTML colors is at
 
444
                         * Incoming/SenderColors.txt it will be used instead of
 
445
                         * the default colors.
 
446
                         */
 
447
                        if (contact_id != NULL) {
 
448
                                guint hash = g_str_hash (contact_id);
 
449
                                replace = colors[hash % G_N_ELEMENTS (colors)];
 
450
                        }
 
451
                } else if (theme_adium_match (&cur, "%senderStatusIcon%")) {
 
452
                        /* FIXME: The path to the status icon of the sender
 
453
                         * (available, away, etc...)
 
454
                         */
 
455
                } else if (theme_adium_match (&cur, "%messageDirection%")) {
 
456
                        /* FIXME: The text direction of the message
 
457
                         * (either rtl or ltr)
 
458
                         */
351
459
                } else if (theme_adium_match (&cur, "%senderDisplayName%")) {
352
 
                        /* %senderDisplayName% -
353
 
                         * "The serverside (remotely set) name of the sender,
354
 
                         *  such as an MSN display name."
 
460
                        /* FIXME: The serverside (remotely set) name of the
 
461
                         * sender, such as an MSN display name.
355
462
                         *
356
 
                         * We don't have access to that yet so we use local
357
 
                         * alias instead.*/
 
463
                         *  We don't have access to that yet so we use
 
464
                         * local alias instead.
 
465
                         */
358
466
                        replace = name;
359
 
                } else if (theme_adium_match (&cur, "%service%")) {
360
 
                        replace = service_name;
 
467
                } else if (theme_adium_match_with_format (&cur, "%textbackgroundcolor{", &format)) {
 
468
                        /* FIXME: This keyword is used to represent the
 
469
                         * highlight background color. "X" is the opacity of the
 
470
                         * background, ranges from 0 to 1 and can be any decimal
 
471
                         * between.
 
472
                         */
 
473
                } else if (theme_adium_match (&cur, "%message%")) {
 
474
                        replace = message;
 
475
                } else if (theme_adium_match (&cur, "%time%") ||
 
476
                           theme_adium_match_with_format (&cur, "%time{", &format)) {
 
477
                        /* FIXME: format is not exactly strftime.
 
478
                         * See NSDateFormatter spec:
 
479
                         * http://www.stepcase.com/blog/2008/12/02/format-string-for-the-iphone-nsdateformatter/
 
480
                         */
 
481
                        if (is_backlog) {
 
482
                                dup_replace = empathy_time_to_string_local (timestamp,
 
483
                                        format ? format : EMPATHY_TIME_DATE_FORMAT_DISPLAY_SHORT);
 
484
                        } else {
 
485
                                dup_replace = empathy_time_to_string_local (timestamp,
 
486
                                        format ? format : EMPATHY_TIME_FORMAT_DISPLAY_SHORT);
 
487
                        }
 
488
                        replace = dup_replace;
361
489
                } else if (theme_adium_match (&cur, "%shortTime%")) {
362
490
                        dup_replace = empathy_time_to_string_local (timestamp,
363
491
                                EMPATHY_TIME_FORMAT_DISPLAY_SHORT);
364
492
                        replace = dup_replace;
365
 
                } else if (theme_adium_match (&cur, "%time")) {
366
 
                        gchar *format = NULL;
367
 
                        gchar *end;
368
 
                        /* Time can be in 2 formats:
369
 
                         * %time% or %time{strftime format}%
370
 
                         * Extract the time format if provided. */
371
 
                        if (cur[1] == '{') {
372
 
                                cur += 2;
373
 
                                end = strstr (cur, "}%");
374
 
                                if (!end) {
375
 
                                        /* Invalid string */
376
 
                                        continue;
377
 
                                }
378
 
                                format = g_strndup (cur, end - cur);
379
 
                                cur = end + 1;
380
 
                        } else {
381
 
                                cur++;
382
 
                        }
383
 
 
384
 
                        if (is_backlog) {
385
 
                                dup_replace = empathy_time_to_string_local (timestamp,
386
 
                                        format ? format : EMPATHY_TIME_DATE_FORMAT_DISPLAY_SHORT);
387
 
                        } else {
388
 
                                dup_replace = empathy_time_to_string_local (timestamp,
389
 
                                        format ? format : EMPATHY_TIME_FORMAT_DISPLAY_SHORT);
390
 
                        }
391
 
                        replace = dup_replace;
392
 
                        g_free (format);
 
493
                } else if (theme_adium_match (&cur, "%service%")) {
 
494
                        replace = service_name;
 
495
                } else if (theme_adium_match (&cur, "%variant%")) {
 
496
                        /* FIXME: The name of the active message style variant,
 
497
                         * with all spaces replaced with an underscore.
 
498
                         * A variant named "Alternating Messages - Blue Red"
 
499
                         * will become "Alternating_Messages_-_Blue_Red".
 
500
                         */
 
501
                } else if (theme_adium_match (&cur, "%userIcons%")) {
 
502
                        /* FIXME: mus t be "hideIcons" if use preference is set
 
503
                         * to hide avatars */
 
504
                        replace = "showIcons";
 
505
                } else if (theme_adium_match (&cur, "%messageClasses%")) {
 
506
                        replace = message_classes;
 
507
                } else if (theme_adium_match (&cur, "%status%")) {
 
508
                        /* FIXME: A description of the status event. This is
 
509
                         * neither in the user's local language nor expected to
 
510
                         * be displayed; it may be useful to use a different div
 
511
                         * class to present different types of status messages.
 
512
                         * The following is a list of some of the more important
 
513
                         * status messages; your message style should be able to
 
514
                         * handle being shown a status message not in this list,
 
515
                         * as even at present the list is incomplete and is
 
516
                         * certain to become out of date in the future:
 
517
                         *      online
 
518
                         *      offline
 
519
                         *      away
 
520
                         *      away_message
 
521
                         *      return_away
 
522
                         *      idle
 
523
                         *      return_idle
 
524
                         *      date_separator
 
525
                         *      contact_joined (group chats)
 
526
                         *      contact_left
 
527
                         *      error
 
528
                         *      timed_out
 
529
                         *      encryption (all OTR messages use this status)
 
530
                         *      purple (all IRC topic and join/part messages use this status)
 
531
                         *      fileTransferStarted
 
532
                         *      fileTransferCompleted
 
533
                         */
393
534
                } else {
394
535
                        escape_and_append_len (string, cur, 1);
395
536
                        continue;
397
538
 
398
539
                /* Here we have a replacement to make */
399
540
                escape_and_append_len (string, replace, -1);
 
541
 
400
542
                g_free (dup_replace);
 
543
                g_free (format);
401
544
        }
402
545
        g_string_append (string, "\")");
403
546
 
413
556
        EmpathyThemeAdium     *theme = EMPATHY_THEME_ADIUM (view);
414
557
        EmpathyThemeAdiumPriv *priv = GET_PRIV (theme);
415
558
 
416
 
        if (priv->data->status_html) {
417
 
                theme_adium_append_html (theme, "appendMessage",
418
 
                                         priv->data->status_html,
419
 
                                         priv->data->status_len,
420
 
                                         escaped, NULL, NULL, NULL, NULL,
421
 
                                         "event", empathy_time_get_current (), FALSE);
422
 
        }
 
559
        theme_adium_append_html (theme, "appendMessage",
 
560
                                 priv->data->status_html, escaped, NULL, NULL, NULL,
 
561
                                 NULL, "event",
 
562
                                 empathy_time_get_current (), FALSE);
423
563
 
424
564
        /* There is no last contact */
425
565
        if (priv->last_contact) {
429
569
}
430
570
 
431
571
static void
 
572
theme_adium_remove_focus_marks (EmpathyThemeAdium *theme)
 
573
{
 
574
        WebKitDOMDocument *dom;
 
575
        WebKitDOMNodeList *nodes;
 
576
        guint i;
 
577
        GError *error = NULL;
 
578
 
 
579
        dom = webkit_web_view_get_dom_document (WEBKIT_WEB_VIEW (theme));
 
580
        if (dom == NULL) {
 
581
                return;
 
582
        }
 
583
 
 
584
        /* Get all nodes with focus class */
 
585
        nodes = webkit_dom_document_query_selector_all (dom, ".focus", &error);
 
586
        if (nodes == NULL) {
 
587
                DEBUG ("Error getting focus nodes: %s",
 
588
                        error ? error->message : "No error");
 
589
                g_clear_error (&error);
 
590
                return;
 
591
        }
 
592
 
 
593
        /* Remove focus and firstFocus class */
 
594
        for (i = 0; i < webkit_dom_node_list_get_length (nodes); i++) {
 
595
                WebKitDOMNode *node = webkit_dom_node_list_item (nodes, i);
 
596
                WebKitDOMHTMLElement *element = WEBKIT_DOM_HTML_ELEMENT (node);
 
597
                gchar *class_name;
 
598
                gchar **classes, **iter;
 
599
                GString *new_class_name;
 
600
                gboolean first = TRUE;
 
601
 
 
602
                if (element == NULL) {
 
603
                        continue;
 
604
                }
 
605
 
 
606
                class_name = webkit_dom_html_element_get_class_name (element);
 
607
                classes = g_strsplit (class_name, " ", -1);
 
608
                new_class_name = g_string_sized_new (strlen (class_name));
 
609
                for (iter = classes; *iter != NULL; iter++) {
 
610
                        if (tp_strdiff (*iter, "focus") &&
 
611
                            tp_strdiff (*iter, "firstFocus")) {
 
612
                                if (!first) {
 
613
                                        g_string_append_c (new_class_name, ' ');
 
614
                                }
 
615
                                g_string_append (new_class_name, *iter);
 
616
                                first = FALSE;
 
617
                        }
 
618
                }
 
619
 
 
620
                webkit_dom_html_element_set_class_name (element, new_class_name->str);
 
621
 
 
622
                g_free (class_name);
 
623
                g_strfreev (classes);
 
624
                g_string_free (new_class_name, TRUE);
 
625
        }
 
626
}
 
627
 
 
628
static void
432
629
theme_adium_append_message (EmpathyChatView *view,
433
630
                            EmpathyMessage  *msg)
434
631
{
442
639
        const gchar           *contact_id;
443
640
        EmpathyAvatar         *avatar;
444
641
        const gchar           *avatar_filename = NULL;
445
 
        time_t                 timestamp;
446
 
        gchar                 *html = NULL;
447
 
        gsize                  len = 0;
 
642
        gint64                 timestamp;
 
643
        const gchar           *html = NULL;
448
644
        const gchar           *func;
449
645
        const gchar           *service_name;
450
646
        GString               *message_classes = NULL;
451
647
        gboolean               is_backlog;
452
648
        gboolean               consecutive;
 
649
        gboolean               action;
453
650
 
454
651
        if (priv->pages_loading != 0) {
455
 
                priv->message_queue = g_list_prepend (priv->message_queue,
456
 
                                                      g_object_ref (msg));
 
652
                GValue *value = tp_g_value_slice_new (EMPATHY_TYPE_MESSAGE);
 
653
                g_value_set_object (value, msg);
 
654
                g_queue_push_tail (&priv->message_queue, value);
457
655
                return;
458
656
        }
459
657
 
466
664
                service_name = tp_account_get_protocol (account);
467
665
        timestamp = empathy_message_get_timestamp (msg);
468
666
        body = empathy_message_get_body (msg);
469
 
        body_escaped = theme_adium_parse_body (body);
 
667
        body_escaped = theme_adium_parse_body (theme, body);
470
668
        name = empathy_contact_get_alias (sender);
471
669
        contact_id = empathy_contact_get_id (sender);
 
670
        action = (empathy_message_get_tptype (msg) == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION);
472
671
 
473
 
        /* If this is a /me, append an event */
474
 
        if (empathy_message_get_tptype (msg) == TP_CHANNEL_TEXT_MESSAGE_TYPE_ACTION) {
 
672
        /* If this is a /me probably */
 
673
        if (action) {
475
674
                gchar *str;
476
675
 
477
 
                str = g_strdup_printf ("%s %s", name, body_escaped);
478
 
                theme_adium_append_event_escaped (view, str);
479
 
 
480
 
                g_free (str);
 
676
                if (priv->data->version >= 4 || !priv->data->custom_template) {
 
677
                        str = g_strdup_printf ("<span class='actionMessageUserName'>%s</span>"
 
678
                                               "<span class='actionMessageBody'>%s</span>",
 
679
                                               name, body_escaped);
 
680
                } else {
 
681
                        str = g_strdup_printf ("*%s*", body_escaped);
 
682
                }
481
683
                g_free (body_escaped);
482
 
                return;
 
684
                body_escaped = str;
483
685
        }
484
686
 
485
687
        /* Get the avatar filename, or a fallback */
517
719
 
518
720
        /* Define message classes */
519
721
        message_classes = g_string_new ("message");
 
722
        if (!priv->has_focus && !is_backlog) {
 
723
                if (!priv->has_unread_message) {
 
724
                        /* This is the first message we receive since we lost
 
725
                         * focus; remove previous unread marks. */
 
726
                        theme_adium_remove_focus_marks (theme);
 
727
 
 
728
                        g_string_append (message_classes, " firstFocus");
 
729
                        priv->has_unread_message = TRUE;
 
730
                }
 
731
                g_string_append (message_classes, " focus");
 
732
        }
520
733
        if (is_backlog) {
521
734
                g_string_append (message_classes, " history");
522
735
        }
528
741
        } else {
529
742
                g_string_append (message_classes, " incoming");
530
743
        }
 
744
        if (empathy_message_should_highlight (msg)) {
 
745
                g_string_append (message_classes, " mention");
 
746
        }
 
747
        if (empathy_message_get_tptype (msg) == TP_CHANNEL_TEXT_MESSAGE_TYPE_AUTO_REPLY) {
 
748
                g_string_append (message_classes, " autoreply");
 
749
        }
 
750
        if (action) {
 
751
                g_string_append (message_classes, " action");
 
752
        }
 
753
        /* FIXME: other classes:
 
754
         * status - the message is a status change
 
755
         * event - the message is a notification of something happening
 
756
         *         (for example, encryption being turned on)
 
757
         * %status% - See %status% in theme_adium_append_html ()
 
758
         */
531
759
 
532
760
        /* Define javascript function to use */
533
761
        if (consecutive) {
534
 
                func = "appendNextMessage";
 
762
                func = priv->allow_scrolling ? "appendNextMessage" : "appendNextMessageNoScroll";
535
763
        } else {
536
 
                func = "appendMessage";
 
764
                func = priv->allow_scrolling ? "appendMessage" : "appendMessageNoScroll";
537
765
        }
538
766
 
539
 
        /* Outgoing */
540
767
        if (empathy_contact_is_user (sender)) {
541
 
                if (consecutive) {
542
 
                        if (is_backlog) {
543
 
                                html = priv->data->out_nextcontext_html;
544
 
                                len = priv->data->out_nextcontext_len;
545
 
                        }
546
 
 
547
 
                        /* Not backlog, or fallback if NextContext.html
548
 
                         * is missing */
549
 
                        if (html == NULL) {
550
 
                                html = priv->data->out_nextcontent_html;
551
 
                                len = priv->data->out_nextcontent_len;
552
 
                        }
553
 
                }
554
 
 
555
 
                /* Not consecutive, or fallback if NextContext.html and/or
556
 
                 * NextContent.html are missing */
557
 
                if (html == NULL) {
558
 
                        if (is_backlog) {
559
 
                                html = priv->data->out_context_html;
560
 
                                len = priv->data->out_context_len;
561
 
                        }
562
 
 
563
 
                        if (html == NULL) {
564
 
                                html = priv->data->out_content_html;
565
 
                                len = priv->data->out_content_len;
566
 
                        }
567
 
                }
568
 
        }
569
 
 
570
 
        /* Incoming, or fallback if outgoing files are missing */
571
 
        if (html == NULL) {
572
 
                if (consecutive) {
573
 
                        if (is_backlog) {
574
 
                                html = priv->data->in_nextcontext_html;
575
 
                                len = priv->data->in_nextcontext_len;
576
 
                        }
577
 
 
578
 
                        /* Note backlog, or fallback if NextContext.html
579
 
                         * is missing */
580
 
                        if (html == NULL) {
581
 
                                html = priv->data->in_nextcontent_html;
582
 
                                len = priv->data->in_nextcontent_len;
583
 
                        }
584
 
                }
585
 
 
586
 
                /* Not consecutive, or fallback if NextContext.html and/or
587
 
                 * NextContent.html are missing */
588
 
                if (html == NULL) {
589
 
                        if (is_backlog) {
590
 
                                html = priv->data->in_context_html;
591
 
                                len = priv->data->in_context_len;
592
 
                        }
593
 
 
594
 
                        if (html == NULL) {
595
 
                                html = priv->data->in_content_html;
596
 
                                len = priv->data->in_content_len;
597
 
                        }
598
 
                }
599
 
        }
600
 
 
601
 
        if (html != NULL) {
602
 
                theme_adium_append_html (theme, func, html, len, body_escaped,
603
 
                                         avatar_filename, name, contact_id,
604
 
                                         service_name, message_classes->str,
605
 
                                         timestamp, is_backlog);
 
768
                /* out */
 
769
                if (is_backlog) {
 
770
                        /* context */
 
771
                        html = consecutive ? priv->data->out_nextcontext_html : priv->data->out_context_html;
 
772
                } else {
 
773
                        /* content */
 
774
                        html = consecutive ? priv->data->out_nextcontent_html : priv->data->out_content_html;
 
775
                }
606
776
        } else {
607
 
                DEBUG ("Couldn't find HTML file for this message");
 
777
                /* in */
 
778
                if (is_backlog) {
 
779
                        /* context */
 
780
                        html = consecutive ? priv->data->in_nextcontext_html : priv->data->in_context_html;
 
781
                } else {
 
782
                        /* content */
 
783
                        html = consecutive ? priv->data->in_nextcontent_html : priv->data->in_content_html;
 
784
                }
608
785
        }
609
786
 
 
787
        theme_adium_append_html (theme, func, html, body_escaped,
 
788
                                 avatar_filename, name, contact_id,
 
789
                                 service_name, message_classes->str,
 
790
                                 timestamp, is_backlog);
 
791
 
610
792
        /* Keep the sender of the last displayed message */
611
793
        if (priv->last_contact) {
612
794
                g_object_unref (priv->last_contact);
623
805
theme_adium_append_event (EmpathyChatView *view,
624
806
                          const gchar     *str)
625
807
{
 
808
        EmpathyThemeAdiumPriv *priv = GET_PRIV (view);
626
809
        gchar *str_escaped;
627
810
 
 
811
        if (priv->pages_loading != 0) {
 
812
                g_queue_push_tail (&priv->message_queue,
 
813
                        tp_g_value_slice_new_string (str));
 
814
                return;
 
815
        }
 
816
 
628
817
        str_escaped = g_markup_escape_text (str, -1);
629
818
        theme_adium_append_event_escaped (view, str_escaped);
630
819
        g_free (str_escaped);
634
823
theme_adium_scroll (EmpathyChatView *view,
635
824
                    gboolean         allow_scrolling)
636
825
{
637
 
        /* FIXME: Is it possible? I guess we need a js function, but I don't
638
 
         * see any... */
 
826
        EmpathyThemeAdiumPriv *priv = GET_PRIV (view);
 
827
 
 
828
        priv->allow_scrolling = allow_scrolling;
 
829
        if (allow_scrolling) {
 
830
                empathy_chat_view_scroll_down (view);
 
831
        }
639
832
}
640
833
 
641
834
static void
642
835
theme_adium_scroll_down (EmpathyChatView *view)
643
836
{
644
 
        webkit_web_view_execute_script (WEBKIT_WEB_VIEW (view), "scrollToBottom()");
 
837
        webkit_web_view_execute_script (WEBKIT_WEB_VIEW (view), "alignChat(true);");
645
838
}
646
839
 
647
840
static gboolean
729
922
}
730
923
 
731
924
static void
 
925
theme_adium_focus_toggled (EmpathyChatView *view,
 
926
                           gboolean         has_focus)
 
927
{
 
928
        EmpathyThemeAdiumPriv *priv = GET_PRIV (view);
 
929
 
 
930
        priv->has_focus = has_focus;
 
931
        if (priv->has_focus) {
 
932
                priv->has_unread_message = FALSE;
 
933
        }
 
934
}
 
935
 
 
936
static void
732
937
theme_adium_context_menu_selection_done_cb (GtkMenuShell *menu, gpointer user_data)
733
938
{
734
939
        WebKitHitTestResult *hit_test_result = WEBKIT_HIT_TEST_RESULT (user_data);
749
954
        g_object_get (G_OBJECT (hit_test_result), "context", &context, NULL);
750
955
 
751
956
        /* The menu */
752
 
        menu = gtk_menu_new ();
 
957
        menu = empathy_context_menu_new (GTK_WIDGET (view));
753
958
 
754
959
        /* Select all item */
755
960
        item = gtk_image_menu_item_new_from_stock (GTK_STOCK_SELECT_ALL, NULL);
810
1015
        gtk_widget_show_all (menu);
811
1016
        gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
812
1017
                        event->button, event->time);
813
 
        g_object_ref_sink (menu);
814
 
        g_object_unref (menu);
815
1018
}
816
1019
 
817
1020
static gboolean
849
1052
        iface->find_abilities = theme_adium_find_abilities;
850
1053
        iface->highlight = theme_adium_highlight;
851
1054
        iface->copy_clipboard = theme_adium_copy_clipboard;
 
1055
        iface->focus_toggled = theme_adium_focus_toggled;
852
1056
}
853
1057
 
854
1058
static void
858
1062
{
859
1063
        EmpathyThemeAdiumPriv *priv = GET_PRIV (view);
860
1064
        EmpathyChatView       *chat_view = EMPATHY_CHAT_VIEW (view);
 
1065
        GList                 *l;
861
1066
 
862
1067
        DEBUG ("Page loaded");
863
1068
        priv->pages_loading--;
866
1071
                return;
867
1072
 
868
1073
        /* Display queued messages */
869
 
        priv->message_queue = g_list_reverse (priv->message_queue);
870
 
        while (priv->message_queue) {
871
 
                EmpathyMessage *message = priv->message_queue->data;
872
 
 
873
 
                theme_adium_append_message (chat_view, message);
874
 
                priv->message_queue = g_list_remove (priv->message_queue, message);
875
 
                g_object_unref (message);
 
1074
        for (l = priv->message_queue.head; l != NULL; l = l->next) {
 
1075
                GValue *value = l->data;
 
1076
 
 
1077
                if (G_VALUE_HOLDS_OBJECT (value)) {
 
1078
                        theme_adium_append_message (chat_view,
 
1079
                                g_value_get_object (value));
 
1080
                } else {
 
1081
                        theme_adium_append_event (chat_view,
 
1082
                                g_value_get_string (value));
 
1083
                }
 
1084
 
 
1085
                tp_g_value_slice_free (value);
876
1086
        }
 
1087
        g_queue_clear (&priv->message_queue);
877
1088
}
878
1089
 
879
1090
static void
978
1189
static PangoFontDescription *
979
1190
theme_adium_get_default_font (void)
980
1191
{
981
 
        GConfClient *gconf_client;
 
1192
        GSettings *gsettings;
982
1193
        PangoFontDescription *pango_fd;
983
 
        gchar *gconf_font_family;
984
 
 
985
 
        gconf_client = gconf_client_get_default ();
986
 
        if (gconf_client == NULL) {
987
 
                return NULL;
988
 
        }
989
 
        gconf_font_family = gconf_client_get_string (gconf_client,
990
 
                     EMPATHY_GCONF_FONT_KEY_NAME,
991
 
                     NULL);
992
 
        if (gconf_font_family == NULL) {
993
 
                g_object_unref (gconf_client);
994
 
                return NULL;
995
 
        }
996
 
        pango_fd = pango_font_description_from_string (gconf_font_family);
997
 
        g_free (gconf_font_family);
998
 
        g_object_unref (gconf_client);
 
1194
        gchar *font_family;
 
1195
 
 
1196
        gsettings = g_settings_new (EMPATHY_PREFS_DESKTOP_INTERFACE_SCHEMA);
 
1197
 
 
1198
        font_family = g_settings_get_string (gsettings,
 
1199
                     EMPATHY_PREFS_DESKTOP_INTERFACE_DOCUMENT_FONT_NAME);
 
1200
 
 
1201
        if (font_family == NULL)
 
1202
                return NULL;
 
1203
 
 
1204
        pango_fd = pango_font_description_from_string (font_family);
 
1205
        g_free (font_family);
 
1206
        g_object_unref (gsettings);
999
1207
        return pango_fd;
1000
1208
}
1001
1209
 
1152
1360
 
1153
1361
        theme->priv = priv;
1154
1362
 
 
1363
        g_queue_init (&priv->message_queue);
 
1364
        priv->allow_scrolling = TRUE;
1155
1365
        priv->smiley_manager = empathy_smiley_manager_dup_singleton ();
1156
1366
 
1157
1367
        g_signal_connect (theme, "load-finished",
1192
1402
        ret = g_file_test (file, G_FILE_TEST_EXISTS);
1193
1403
        g_free (file);
1194
1404
 
1195
 
        if (ret == FALSE)
1196
 
                return ret;
 
1405
        if (!ret)
 
1406
                return FALSE;
1197
1407
 
1198
1408
        /* We ship a default Template.html as fallback if there is any problem
1199
1409
         * with the one inside the theme. The only other required file is
1200
 
         * Content.html for incoming messages (outgoing fallback to use
1201
 
         * incoming). */
1202
 
        file = g_build_filename (path, "Contents", "Resources", "Incoming",
1203
 
                                 "Content.html", NULL);
 
1410
         * Content.html OR Incoming/Content.html*/
 
1411
        file = g_build_filename (path, "Contents", "Resources", "Content.html",
 
1412
                                 NULL);
1204
1413
        ret = g_file_test (file, G_FILE_TEST_EXISTS);
1205
1414
        g_free (file);
1206
1415
 
 
1416
        if (!ret) {
 
1417
                file = g_build_filename (path, "Contents", "Resources", "Incoming",
 
1418
                                         "Content.html", NULL);
 
1419
                ret = g_file_test (file, G_FILE_TEST_EXISTS);
 
1420
                g_free (file);
 
1421
        }
 
1422
 
1207
1423
        return ret;
1208
1424
}
1209
1425
 
1233
1449
        return info;
1234
1450
}
1235
1451
 
 
1452
static guint
 
1453
adium_info_get_version (GHashTable *info)
 
1454
{
 
1455
        return tp_asv_get_int32 (info, "MessageViewVersion", NULL);
 
1456
}
 
1457
 
 
1458
static const gchar *
 
1459
adium_info_get_no_variant_name (GHashTable *info)
 
1460
{
 
1461
        const gchar *name = tp_asv_get_string (info, "DisplayNameForNoVariant");
 
1462
        return name ? name : _("Normal");
 
1463
}
 
1464
 
 
1465
static const gchar *
 
1466
adium_info_get_default_or_first_variant (GHashTable *info)
 
1467
{
 
1468
        const gchar *name;
 
1469
        GPtrArray *variants;
 
1470
 
 
1471
        name = empathy_adium_info_get_default_variant (info);
 
1472
        if (name != NULL) {
 
1473
                return name;
 
1474
        }
 
1475
 
 
1476
        variants = empathy_adium_info_get_available_variants (info);
 
1477
        g_assert (variants->len > 0);
 
1478
        return g_ptr_array_index (variants, 0);
 
1479
}
 
1480
 
 
1481
static gchar *
 
1482
adium_info_dup_path_for_variant (GHashTable *info,
 
1483
                                 const gchar *variant)
 
1484
{
 
1485
        guint version = adium_info_get_version (info);
 
1486
        const gchar *no_variant = adium_info_get_no_variant_name (info);
 
1487
 
 
1488
        if (version <= 2 && !tp_strdiff (variant, no_variant)) {
 
1489
                return g_strdup ("main.css");
 
1490
        }
 
1491
 
 
1492
        return g_strdup_printf ("Variants/%s.css", variant);
 
1493
 
 
1494
}
 
1495
 
 
1496
const gchar *
 
1497
empathy_adium_info_get_default_variant (GHashTable *info)
 
1498
{
 
1499
        if (adium_info_get_version (info) <= 2) {
 
1500
                return adium_info_get_no_variant_name (info);
 
1501
        }
 
1502
 
 
1503
        return tp_asv_get_string (info, "DefaultVariant");
 
1504
}
 
1505
 
 
1506
GPtrArray *
 
1507
empathy_adium_info_get_available_variants (GHashTable *info)
 
1508
{
 
1509
        GPtrArray *variants;
 
1510
        const gchar *path;
 
1511
        gchar *dirpath;
 
1512
        GDir *dir;
 
1513
 
 
1514
        variants = tp_asv_get_boxed (info, "AvailableVariants", G_TYPE_PTR_ARRAY);
 
1515
        if (variants != NULL) {
 
1516
                return variants;
 
1517
        }
 
1518
 
 
1519
        variants = g_ptr_array_new_with_free_func (g_free);
 
1520
        tp_asv_take_boxed (info, g_strdup ("AvailableVariants"),
 
1521
                G_TYPE_PTR_ARRAY, variants);
 
1522
 
 
1523
        path = tp_asv_get_string (info, "path");
 
1524
        dirpath = g_build_filename (path, "Contents", "Resources", "Variants", NULL);
 
1525
        dir = g_dir_open (dirpath, 0, NULL);
 
1526
        if (dir != NULL) {
 
1527
                const gchar *name;
 
1528
 
 
1529
                for (name = g_dir_read_name (dir);
 
1530
                     name != NULL;
 
1531
                     name = g_dir_read_name (dir)) {
 
1532
                        gchar *display_name;
 
1533
 
 
1534
                        if (!g_str_has_suffix (name, ".css")) {
 
1535
                                continue;
 
1536
                        }
 
1537
 
 
1538
                        display_name = g_strdup (name);
 
1539
                        strstr (display_name, ".css")[0] = '\0';
 
1540
                        g_ptr_array_add (variants, display_name);
 
1541
                }
 
1542
                g_dir_close (dir);
 
1543
        }
 
1544
        g_free (dirpath);
 
1545
 
 
1546
        if (adium_info_get_version (info) <= 2) {
 
1547
                g_ptr_array_add (variants,
 
1548
                        g_strdup (adium_info_get_no_variant_name (info)));
 
1549
        }
 
1550
 
 
1551
        return variants;
 
1552
}
 
1553
 
1236
1554
GType
1237
1555
empathy_adium_data_get_type (void)
1238
1556
{
1252
1570
empathy_adium_data_new_with_info (const gchar *path, GHashTable *info)
1253
1571
{
1254
1572
        EmpathyAdiumData *data;
1255
 
        gchar            *file;
1256
1573
        gchar            *template_html = NULL;
1257
 
        gsize             template_len;
1258
1574
        gchar            *footer_html = NULL;
1259
 
        gsize             footer_len;
1260
 
        GString          *string;
1261
 
        gchar           **strv = NULL;
1262
 
        gchar            *css_path;
1263
 
        guint             len = 0;
1264
 
        guint             i = 0;
 
1575
        gchar            *variant_path;
 
1576
        gchar            *tmp;
1265
1577
 
1266
1578
        g_return_val_if_fail (empathy_adium_path_is_valid (path), NULL);
1267
1579
 
1271
1583
        data->basedir = g_strconcat (path, G_DIR_SEPARATOR_S "Contents"
1272
1584
                G_DIR_SEPARATOR_S "Resources" G_DIR_SEPARATOR_S, NULL);
1273
1585
        data->info = g_hash_table_ref (info);
 
1586
        data->version = adium_info_get_version (info);
 
1587
        data->strings_to_free = g_ptr_array_new_with_free_func (g_free);
 
1588
 
 
1589
        DEBUG ("Loading theme at %s", path);
 
1590
 
 
1591
#define LOAD(path, var) \
 
1592
                tmp = g_build_filename (data->basedir, path, NULL); \
 
1593
                g_file_get_contents (tmp, &var, NULL, NULL); \
 
1594
                g_free (tmp); \
 
1595
 
 
1596
#define LOAD_CONST(path, var) \
 
1597
        { \
 
1598
                gchar *content; \
 
1599
                LOAD (path, content); \
 
1600
                if (content != NULL) { \
 
1601
                        g_ptr_array_add (data->strings_to_free, content); \
 
1602
                } \
 
1603
                var = content; \
 
1604
        }
1274
1605
 
1275
1606
        /* Load html files */
1276
 
        file = g_build_filename (data->basedir, "Incoming", "Content.html", NULL);
1277
 
        g_file_get_contents (file, &data->in_content_html, &data->in_content_len, NULL);
1278
 
        g_free (file);
1279
 
 
1280
 
        file = g_build_filename (data->basedir, "Incoming", "NextContent.html", NULL);
1281
 
        g_file_get_contents (file, &data->in_nextcontent_html, &data->in_nextcontent_len, NULL);
1282
 
        g_free (file);
1283
 
 
1284
 
        file = g_build_filename (data->basedir, "Incoming", "Context.html", NULL);
1285
 
        g_file_get_contents (file, &data->in_context_html, &data->in_context_len, NULL);
1286
 
        g_free (file);
1287
 
 
1288
 
        file = g_build_filename (data->basedir, "Incoming", "NextContext.html", NULL);
1289
 
        g_file_get_contents (file, &data->in_nextcontext_html, &data->in_nextcontext_len, NULL);
1290
 
        g_free (file);
1291
 
 
1292
 
        file = g_build_filename (data->basedir, "Outgoing", "Content.html", NULL);
1293
 
        g_file_get_contents (file, &data->out_content_html, &data->out_content_len, NULL);
1294
 
        g_free (file);
1295
 
 
1296
 
        file = g_build_filename (data->basedir, "Outgoing", "NextContent.html", NULL);
1297
 
        g_file_get_contents (file, &data->out_nextcontent_html, &data->out_nextcontent_len, NULL);
1298
 
        g_free (file);
1299
 
 
1300
 
        file = g_build_filename (data->basedir, "Outgoing", "Context.html", NULL);
1301
 
        g_file_get_contents (file, &data->out_context_html, &data->out_context_len, NULL);
1302
 
        g_free (file);
1303
 
 
1304
 
        file = g_build_filename (data->basedir, "Outgoing", "NextContext.html", NULL);
1305
 
        g_file_get_contents (file, &data->out_nextcontext_html, &data->out_nextcontext_len, NULL);
1306
 
        g_free (file);
1307
 
 
1308
 
        file = g_build_filename (data->basedir, "Status.html", NULL);
1309
 
        g_file_get_contents (file, &data->status_html, &data->status_len, NULL);
1310
 
        g_free (file);
1311
 
 
1312
 
        file = g_build_filename (data->basedir, "Footer.html", NULL);
1313
 
        g_file_get_contents (file, &footer_html, &footer_len, NULL);
1314
 
        g_free (file);
1315
 
 
1316
 
        file = g_build_filename (data->basedir, "Incoming", "buddy_icon.png", NULL);
1317
 
        if (g_file_test (file, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
1318
 
                data->default_incoming_avatar_filename = file;
1319
 
        } else {
1320
 
                g_free (file);
1321
 
        }
1322
 
 
1323
 
        file = g_build_filename (data->basedir, "Outgoing", "buddy_icon.png", NULL);
1324
 
        if (g_file_test (file, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
1325
 
                data->default_outgoing_avatar_filename = file;
1326
 
        } else {
1327
 
                g_free (file);
1328
 
        }
1329
 
 
1330
 
        css_path = g_build_filename (data->basedir, "main.css", NULL);
1331
 
 
1332
 
        /* There is 2 formats for Template.html: The old one has 4 parameters,
1333
 
         * the new one has 5 parameters. */
1334
 
        file = g_build_filename (data->basedir, "Template.html", NULL);
1335
 
        if (g_file_get_contents (file, &template_html, &template_len, NULL)) {
1336
 
                strv = g_strsplit (template_html, "%@", -1);
1337
 
                len = g_strv_length (strv);
1338
 
        }
1339
 
        g_free (file);
1340
 
 
1341
 
        if (len != 5 && len != 6) {
1342
 
                /* Either the theme has no template or it don't have the good
1343
 
                 * number of parameters. Fallback to use our own template. */
1344
 
                g_free (template_html);
1345
 
                g_strfreev (strv);
1346
 
 
1347
 
                file = empathy_file_lookup ("Template.html", "data");
1348
 
                g_file_get_contents (file, &template_html, &template_len, NULL);
1349
 
                g_free (file);
1350
 
                strv = g_strsplit (template_html, "%@", -1);
1351
 
                len = g_strv_length (strv);
1352
 
        }
1353
 
 
1354
 
        /* Replace %@ with the needed information in the template html. */
1355
 
        string = g_string_sized_new (template_len);
1356
 
        g_string_append (string, strv[i++]);
1357
 
        g_string_append (string, data->basedir);
1358
 
        g_string_append (string, strv[i++]);
1359
 
        if (len == 6) {
1360
 
                const gchar *variant;
1361
 
 
1362
 
                /* We include main.css by default */
1363
 
                g_string_append_printf (string, "@import url(\"%s\");", css_path);
1364
 
                g_string_append (string, strv[i++]);
1365
 
                variant = tp_asv_get_string (data->info, "DefaultVariant");
1366
 
                if (variant) {
1367
 
                        g_string_append (string, "Variants/");
1368
 
                        g_string_append (string, variant);
1369
 
                        g_string_append (string, ".css");
1370
 
                }
1371
 
        } else {
1372
 
                /* FIXME: We should set main.css OR the variant css */
1373
 
                g_string_append (string, css_path);
1374
 
        }
1375
 
        g_string_append (string, strv[i++]);
1376
 
        g_string_append (string, ""); /* We don't want header */
1377
 
        g_string_append (string, strv[i++]);
1378
 
        /* FIXME: We should replace adium %macros% in footer */
1379
 
        if (footer_html) {
1380
 
                g_string_append (string, footer_html);
1381
 
        }
1382
 
        g_string_append (string, strv[i++]);
1383
 
        data->template_html = g_string_free (string, FALSE);
1384
 
 
 
1607
        LOAD_CONST ("Content.html", data->content_html);
 
1608
        LOAD_CONST ("Incoming/Content.html", data->in_content_html);
 
1609
        LOAD_CONST ("Incoming/NextContent.html", data->in_nextcontent_html);
 
1610
        LOAD_CONST ("Incoming/Context.html", data->in_context_html);
 
1611
        LOAD_CONST ("Incoming/NextContext.html", data->in_nextcontext_html);
 
1612
        LOAD_CONST ("Outgoing/Content.html", data->out_content_html);
 
1613
        LOAD_CONST ("Outgoing/NextContent.html", data->out_nextcontent_html);
 
1614
        LOAD_CONST ("Outgoing/Context.html", data->out_context_html);
 
1615
        LOAD_CONST ("Outgoing/NextContext.html", data->out_nextcontext_html);
 
1616
        LOAD_CONST ("Status.html", data->status_html);
 
1617
        LOAD ("Template.html", template_html);
 
1618
        LOAD ("Footer.html", footer_html);
 
1619
 
 
1620
#undef LOAD_CONST
 
1621
#undef LOAD
 
1622
 
 
1623
        /* HTML fallbacks: If we have at least content OR in_content, then
 
1624
         * everything else gets a fallback */
 
1625
 
 
1626
#define FALLBACK(html, fallback) \
 
1627
        if (html == NULL) { \
 
1628
                html = fallback; \
 
1629
        }
 
1630
 
 
1631
        /* in_nextcontent -> in_content -> content */
 
1632
        FALLBACK (data->in_content_html,      data->content_html);
 
1633
        FALLBACK (data->in_nextcontent_html,  data->in_content_html);
 
1634
 
 
1635
        /* context -> content */
 
1636
        FALLBACK (data->in_context_html,      data->in_content_html);
 
1637
        FALLBACK (data->in_nextcontext_html,  data->in_nextcontent_html);
 
1638
        FALLBACK (data->out_context_html,     data->out_content_html);
 
1639
        FALLBACK (data->out_nextcontext_html, data->out_nextcontent_html);
 
1640
 
 
1641
        /* out -> in */
 
1642
        FALLBACK (data->out_content_html,     data->in_content_html);
 
1643
        FALLBACK (data->out_nextcontent_html, data->in_nextcontent_html);
 
1644
        FALLBACK (data->out_context_html,     data->in_context_html);
 
1645
        FALLBACK (data->out_nextcontext_html, data->in_nextcontext_html);
 
1646
 
 
1647
        /* status -> in_content */
 
1648
        FALLBACK (data->status_html,          data->in_content_html);
 
1649
 
 
1650
#undef FALLBACK
 
1651
 
 
1652
        /* template -> empathy's template */
 
1653
        data->custom_template = (template_html != NULL);
 
1654
        if (template_html == NULL) {
 
1655
                tmp = empathy_file_lookup ("Template.html", "data");
 
1656
                g_file_get_contents (tmp, &template_html, NULL, NULL);
 
1657
                g_free (tmp);
 
1658
        }
 
1659
 
 
1660
        /* Default avatar */
 
1661
        tmp = g_build_filename (data->basedir, "Incoming", "buddy_icon.png", NULL);
 
1662
        if (g_file_test (tmp, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
 
1663
                data->default_incoming_avatar_filename = tmp;
 
1664
        } else {
 
1665
                g_free (tmp);
 
1666
        }
 
1667
        tmp = g_build_filename (data->basedir, "Outgoing", "buddy_icon.png", NULL);
 
1668
        if (g_file_test (tmp, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
 
1669
                data->default_outgoing_avatar_filename = tmp;
 
1670
        } else {
 
1671
                g_free (tmp);
 
1672
        }
 
1673
 
 
1674
        variant_path = adium_info_dup_path_for_variant (info,
 
1675
                adium_info_get_default_or_first_variant (info));
 
1676
 
 
1677
        /* Old custom templates had only 4 parameters.
 
1678
         * New templates have 5 parameters */
 
1679
        if (data->version <= 2 && data->custom_template) {
 
1680
                tmp = string_with_format (template_html,
 
1681
                        data->basedir,
 
1682
                        variant_path,
 
1683
                        "", /* The header */
 
1684
                        footer_html ? footer_html : "",
 
1685
                        NULL);
 
1686
        } else {
 
1687
                tmp = string_with_format (template_html,
 
1688
                        data->basedir,
 
1689
                        data->version <= 2 ? "" : "@import url( \"main.css\" );",
 
1690
                        variant_path,
 
1691
                        "", /* The header */
 
1692
                        footer_html ? footer_html : "",
 
1693
                        NULL);
 
1694
        }
 
1695
        g_ptr_array_add (data->strings_to_free, tmp);
 
1696
        data->template_html = tmp;
 
1697
 
 
1698
        g_free (template_html);
1385
1699
        g_free (footer_html);
1386
 
        g_free (template_html);
1387
 
        g_free (css_path);
1388
 
        g_strfreev (strv);
 
1700
        g_free (variant_path);
1389
1701
 
1390
1702
        return data;
1391
1703
}
1421
1733
        if (g_atomic_int_dec_and_test (&data->ref_count)) {
1422
1734
                g_free (data->path);
1423
1735
                g_free (data->basedir);
1424
 
                g_free (data->template_html);
1425
 
                g_free (data->in_content_html);
1426
 
                g_free (data->in_nextcontent_html);
1427
 
                g_free (data->in_context_html);
1428
 
                g_free (data->in_nextcontext_html);
1429
 
                g_free (data->out_content_html);
1430
 
                g_free (data->out_nextcontent_html);
1431
 
                g_free (data->out_context_html);
1432
 
                g_free (data->out_nextcontext_html);
1433
1736
                g_free (data->default_avatar_filename);
1434
1737
                g_free (data->default_incoming_avatar_filename);
1435
1738
                g_free (data->default_outgoing_avatar_filename);
1436
 
                g_free (data->status_html);
1437
1739
                g_hash_table_unref (data->info);
 
1740
                g_ptr_array_unref (data->strings_to_free);
 
1741
 
1438
1742
                g_slice_free (EmpathyAdiumData, data);
1439
1743
        }
1440
1744
}