Move migration to core, check for errors
[pidgin-git.git] / pidgin / gtkconv-theme.c
blobed56403476c590cb6b7be610d930db4c1bcdee44
1 /*
2 * Conversation Themes for Pidgin
4 * Pidgin is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 #include "internal.h"
24 #include "glibcompat.h"
26 #include "gtkconv-theme.h"
28 #include "conversation.h"
29 #include "debug.h"
30 #include "prefs.h"
31 #include "xmlnode.h"
33 #include "pidgin.h"
34 #include "gtkconv.h"
35 #include "gtkwebview.h"
37 #include <stdlib.h>
38 #include <string.h>
40 #define PIDGIN_CONV_THEME_GET_PRIVATE(Gobject) \
41 (G_TYPE_INSTANCE_GET_PRIVATE((Gobject), PIDGIN_TYPE_CONV_THEME, PidginConvThemePrivate))
43 /******************************************************************************
44 * Structs
45 *****************************************************************************/
47 typedef struct {
48 /* current config options */
49 char *variant; /* allowed to be NULL if there are no variants */
50 GList *variants;
52 /* Info.plist keys/values */
53 GHashTable *info;
55 /* caches */
56 char *template_html;
57 char *header_html;
58 char *footer_html;
59 char *topic_html;
60 char *status_html;
61 char *content_html;
62 char *incoming_content_html;
63 char *outgoing_content_html;
64 char *incoming_next_content_html;
65 char *outgoing_next_content_html;
66 char *incoming_context_html;
67 char *outgoing_context_html;
68 char *incoming_next_context_html;
69 char *outgoing_next_context_html;
70 char *basestyle_css;
72 GArray *nick_colors;
73 } PidginConvThemePrivate;
75 /******************************************************************************
76 * Enums
77 *****************************************************************************/
79 enum {
80 PROP_ZERO = 0,
81 PROP_INFO,
82 PROP_VARIANT,
83 PROP_LAST
86 /******************************************************************************
87 * Globals
88 *****************************************************************************/
90 static GObjectClass *parent_class = NULL;
91 static GParamSpec *properties[PROP_LAST];
93 /******************************************************************************
94 * Helper Functions
95 *****************************************************************************/
97 static const GValue *
98 get_key(PidginConvThemePrivate *priv, const char *key, gboolean specific)
100 GValue *val = NULL;
102 /* Try variant-specific key */
103 if (specific && priv->variant) {
104 char *name = g_strdup_printf("%s:%s", key, priv->variant);
105 val = g_hash_table_lookup(priv->info, name);
106 g_free(name);
109 /* Try generic key */
110 if (!val) {
111 val = g_hash_table_lookup(priv->info, key);
114 return val;
117 /* The template path can either come from the theme, or can
118 * be stock Template.html that comes with Pidgin */
119 static char *
120 get_template_path(const char *dir)
122 char *file;
124 file = g_build_filename(dir, "Contents", "Resources", "Template.html", NULL);
126 if (!g_file_test(file, G_FILE_TEST_EXISTS)) {
127 g_free(file);
128 #if defined(_WIN32) && !defined(USE_WIN32_FHS)
129 file = g_build_filename(PURPLE_DATADIR,
130 "theme", "Template.html", NULL);
131 #else
132 file = g_build_filename(PURPLE_DATADIR,
133 "pidgin", "theme", "Template.html", NULL);
134 #endif
137 return file;
140 static const char *
141 get_template_html(PidginConvThemePrivate *priv, const char *dir)
143 char *file;
145 if (priv->template_html)
146 return priv->template_html;
148 file = get_template_path(dir);
150 if (!g_file_get_contents(file, &priv->template_html, NULL, NULL)) {
151 purple_debug_error("webkit", "Could not locate a Template.html (%s)\n", file);
152 priv->template_html = g_strdup("");
154 g_free(file);
156 return priv->template_html;
159 static const char *
160 get_basestyle_css(PidginConvThemePrivate *priv, const char *dir)
162 char *file;
164 if (priv->basestyle_css)
165 return priv->basestyle_css;
167 file = g_build_filename(dir, "Contents", "Resources", "main.css", NULL);
168 if (!g_file_get_contents(file, &priv->basestyle_css, NULL, NULL))
169 priv->basestyle_css = g_strdup("");
170 g_free(file);
172 return priv->basestyle_css;
175 static const char *
176 get_header_html(PidginConvThemePrivate *priv, const char *dir)
178 char *file;
180 if (priv->header_html)
181 return priv->header_html;
183 file = g_build_filename(dir, "Contents", "Resources", "Header.html", NULL);
184 if (!g_file_get_contents(file, &priv->header_html, NULL, NULL))
185 priv->header_html = g_strdup("");
186 g_free(file);
188 return priv->header_html;
191 static const char *
192 get_footer_html(PidginConvThemePrivate *priv, const char *dir)
194 char *file;
196 if (priv->footer_html)
197 return priv->footer_html;
199 file = g_build_filename(dir, "Contents", "Resources", "Footer.html", NULL);
200 if (!g_file_get_contents(file, &priv->footer_html, NULL, NULL))
201 priv->footer_html = g_strdup("");
202 g_free(file);
204 return priv->footer_html;
207 static const char *
208 get_topic_html(PidginConvThemePrivate *priv, const char *dir)
210 char *file;
212 if (priv->topic_html)
213 return priv->topic_html;
215 file = g_build_filename(dir, "Contents", "Resources", "Topic.html", NULL);
216 if (!g_file_get_contents(file, &priv->topic_html, NULL, NULL)) {
217 purple_debug_info("webkit", "%s could not find Resources/Topic.html\n", dir);
218 priv->topic_html = g_strdup("");
220 g_free(file);
222 return priv->topic_html;
225 static const char *
226 get_content_html(PidginConvThemePrivate *priv, const char *dir)
228 char *file;
230 if (priv->content_html)
231 return priv->content_html;
233 file = g_build_filename(dir, "Contents", "Resources", "Content.html", NULL);
234 if (!g_file_get_contents(file, &priv->content_html, NULL, NULL)) {
235 purple_debug_info("webkit", "%s did not have a Content.html\n", dir);
236 priv->content_html = g_strdup("");
238 g_free(file);
240 return priv->content_html;
243 static const char *
244 get_status_html(PidginConvThemePrivate *priv, const char *dir)
246 char *file;
248 if (priv->status_html)
249 return priv->status_html;
251 file = g_build_filename(dir, "Contents", "Resources", "Status.html", NULL);
252 if (!g_file_get_contents(file, &priv->status_html, NULL, NULL)) {
253 purple_debug_info("webkit", "%s could not find Resources/Status.html\n", dir);
254 priv->status_html = g_strdup(get_content_html(priv, dir));
256 g_free(file);
258 return priv->status_html;
261 static const char *
262 get_incoming_content_html(PidginConvThemePrivate *priv, const char *dir)
264 char *file;
266 if (priv->incoming_content_html)
267 return priv->incoming_content_html;
269 file = g_build_filename(dir, "Contents", "Resources", "Incoming", "Content.html", NULL);
270 if (!g_file_get_contents(file, &priv->incoming_content_html, NULL, NULL)) {
271 purple_debug_info("webkit", "%s did not have a Incoming/Content.html\n", dir);
272 priv->incoming_content_html = g_strdup(get_content_html(priv, dir));
274 g_free(file);
276 return priv->incoming_content_html;
279 static const char *
280 get_incoming_next_content_html(PidginConvThemePrivate *priv, const char *dir)
282 char *file;
284 if (priv->incoming_next_content_html)
285 return priv->incoming_next_content_html;
287 file = g_build_filename(dir, "Contents", "Resources", "Incoming", "NextContent.html", NULL);
288 if (!g_file_get_contents(file, &priv->incoming_next_content_html, NULL, NULL)) {
289 priv->incoming_next_content_html = g_strdup(get_incoming_content_html(priv, dir));
291 g_free(file);
293 return priv->incoming_next_content_html;
296 static const char *
297 get_incoming_context_html(PidginConvThemePrivate *priv, const char *dir)
299 char *file;
301 if (priv->incoming_context_html)
302 return priv->incoming_context_html;
304 file = g_build_filename(dir, "Contents", "Resources", "Incoming", "Context.html", NULL);
305 if (!g_file_get_contents(file, &priv->incoming_context_html, NULL, NULL)) {
306 purple_debug_info("webkit", "%s did not have a Incoming/Context.html\n", dir);
307 priv->incoming_context_html = g_strdup(get_incoming_content_html(priv, dir));
309 g_free(file);
311 return priv->incoming_context_html;
314 static const char *
315 get_incoming_next_context_html(PidginConvThemePrivate *priv, const char *dir)
317 char *file;
319 if (priv->incoming_next_context_html)
320 return priv->incoming_next_context_html;
322 file = g_build_filename(dir, "Contents", "Resources", "Incoming", "NextContext.html", NULL);
323 if (!g_file_get_contents(file, &priv->incoming_next_context_html, NULL, NULL)) {
324 priv->incoming_next_context_html = g_strdup(get_incoming_context_html(priv, dir));
326 g_free(file);
328 return priv->incoming_next_context_html;
331 static const char *
332 get_outgoing_content_html(PidginConvThemePrivate *priv, const char *dir)
334 char *file;
336 if (priv->outgoing_content_html)
337 return priv->outgoing_content_html;
339 file = g_build_filename(dir, "Contents", "Resources", "Outgoing", "Content.html", NULL);
340 if (!g_file_get_contents(file, &priv->outgoing_content_html, NULL, NULL)) {
341 priv->outgoing_content_html = g_strdup(get_incoming_content_html(priv, dir));
343 g_free(file);
345 return priv->outgoing_content_html;
348 static const char *
349 get_outgoing_next_content_html(PidginConvThemePrivate *priv, const char *dir)
351 char *file;
353 if (priv->outgoing_next_content_html)
354 return priv->outgoing_next_content_html;
356 file = g_build_filename(dir, "Contents", "Resources", "Outgoing", "NextContent.html", NULL);
357 if (!g_file_get_contents(file, &priv->outgoing_next_content_html, NULL, NULL)) {
358 priv->outgoing_next_content_html = g_strdup(get_outgoing_content_html(priv, dir));
361 return priv->outgoing_next_content_html;
364 static const char *
365 get_outgoing_context_html(PidginConvThemePrivate *priv, const char *dir)
367 char *file;
369 if (priv->outgoing_context_html)
370 return priv->outgoing_context_html;
372 file = g_build_filename(dir, "Contents", "Resources", "Outgoing", "Context.html", NULL);
373 if (!g_file_get_contents(file, &priv->outgoing_context_html, NULL, NULL)) {
374 priv->outgoing_context_html = g_strdup(get_incoming_context_html(priv, dir));
376 g_free(file);
378 return priv->outgoing_context_html;
381 static const char *
382 get_outgoing_next_context_html(PidginConvThemePrivate *priv, const char *dir)
384 char *file;
386 if (priv->outgoing_next_context_html)
387 return priv->outgoing_next_context_html;
389 file = g_build_filename(dir, "Contents", "Resources", "Outgoing", "NextContext.html", NULL);
390 if (!g_file_get_contents(file, &priv->outgoing_next_context_html, NULL, NULL)) {
391 priv->outgoing_next_context_html = g_strdup(get_outgoing_context_html(priv, dir));
393 g_free(file);
395 return priv->outgoing_next_context_html;
398 /******************************************************************************
399 * GObject Stuff
400 *****************************************************************************/
402 static void
403 pidgin_conv_theme_get_property(GObject *obj, guint param_id, GValue *value,
404 GParamSpec *psec)
406 PidginConvTheme *theme = PIDGIN_CONV_THEME(obj);
408 switch (param_id) {
409 case PROP_INFO:
410 g_value_set_boxed(value, (gpointer)pidgin_conversation_theme_get_info(theme));
411 break;
413 case PROP_VARIANT:
414 g_value_set_string(value, pidgin_conversation_theme_get_variant(theme));
415 break;
417 default:
418 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec);
419 break;
423 static void
424 pidgin_conv_theme_set_property(GObject *obj, guint param_id, const GValue *value,
425 GParamSpec *psec)
427 PidginConvTheme *theme = PIDGIN_CONV_THEME(obj);
429 switch (param_id) {
430 case PROP_INFO:
431 pidgin_conversation_theme_set_info(theme, g_value_get_boxed(value));
432 break;
434 case PROP_VARIANT:
435 pidgin_conversation_theme_set_variant(theme, g_value_get_string(value));
436 break;
438 default:
439 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec);
440 break;
444 static void
445 pidgin_conv_theme_init(GTypeInstance *instance,
446 gpointer klass)
448 #if 0
449 PidginConvThemePrivate *priv;
451 priv = PIDGIN_CONV_THEME_GET_PRIVATE(instance);
452 #endif
455 static void
456 pidgin_conv_theme_finalize(GObject *obj)
458 PidginConvThemePrivate *priv;
459 GList *list;
461 priv = PIDGIN_CONV_THEME_GET_PRIVATE(obj);
463 g_free(priv->template_html);
464 g_free(priv->header_html);
465 g_free(priv->footer_html);
466 g_free(priv->topic_html);
467 g_free(priv->status_html);
468 g_free(priv->content_html);
469 g_free(priv->incoming_content_html);
470 g_free(priv->outgoing_content_html);
471 g_free(priv->incoming_next_content_html);
472 g_free(priv->outgoing_next_content_html);
473 g_free(priv->incoming_context_html);
474 g_free(priv->outgoing_context_html);
475 g_free(priv->incoming_next_context_html);
476 g_free(priv->outgoing_next_context_html);
477 g_free(priv->basestyle_css);
479 if (priv->info)
480 g_hash_table_destroy(priv->info);
482 list = priv->variants;
483 while (list) {
484 g_free(list->data);
485 list = g_list_delete_link(list, list);
487 g_free(priv->variant);
489 if (priv->nick_colors)
490 g_array_unref(priv->nick_colors);
492 parent_class->finalize(obj);
495 static void
496 pidgin_conv_theme_class_init(PidginConvThemeClass *klass)
498 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
500 parent_class = g_type_class_peek_parent(klass);
502 g_type_class_add_private(klass, sizeof(PidginConvThemePrivate));
504 obj_class->get_property = pidgin_conv_theme_get_property;
505 obj_class->set_property = pidgin_conv_theme_set_property;
506 obj_class->finalize = pidgin_conv_theme_finalize;
508 /* INFO */
509 properties[PROP_INFO] = g_param_spec_boxed("info", "Info",
510 "The information about this theme",
511 G_TYPE_HASH_TABLE,
512 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
514 /* VARIANT */
515 properties[PROP_VARIANT] = g_param_spec_string("variant", "Variant",
516 "The current variant for this theme",
517 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
519 g_object_class_install_properties(obj_class, PROP_LAST, properties);
522 GType
523 pidgin_conversation_theme_get_type(void)
525 static GType type = 0;
526 if (type == 0) {
527 static const GTypeInfo info = {
528 sizeof(PidginConvThemeClass),
529 NULL, /* base_init */
530 NULL, /* base_finalize */
531 (GClassInitFunc)pidgin_conv_theme_class_init, /* class_init */
532 NULL, /* class_finalize */
533 NULL, /* class_data */
534 sizeof(PidginConvTheme),
535 0, /* n_preallocs */
536 pidgin_conv_theme_init, /* instance_init */
537 NULL, /* value table */
539 type = g_type_register_static(PURPLE_TYPE_THEME,
540 "PidginConvTheme", &info, 0);
542 return type;
545 /*****************************************************************************
546 * Public API functions
547 *****************************************************************************/
549 const GHashTable *
550 pidgin_conversation_theme_get_info(const PidginConvTheme *theme)
552 PidginConvThemePrivate *priv;
554 g_return_val_if_fail(theme != NULL, NULL);
556 priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
557 return priv->info;
560 void
561 pidgin_conversation_theme_set_info(PidginConvTheme *theme, GHashTable *info)
563 PidginConvThemePrivate *priv;
565 g_return_if_fail(theme != NULL);
567 priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
569 if (priv->info)
570 g_hash_table_destroy(priv->info);
572 priv->info = info;
574 g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_INFO]);
577 const GValue *
578 pidgin_conversation_theme_lookup(PidginConvTheme *theme, const char *key, gboolean specific)
580 PidginConvThemePrivate *priv;
582 g_return_val_if_fail(theme != NULL, NULL);
584 priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
586 return get_key(priv, key, specific);
589 const char *
590 pidgin_conversation_theme_get_template(PidginConvTheme *theme, PidginConvThemeTemplateType type)
592 PidginConvThemePrivate *priv;
593 const char *dir;
594 const char *html;
596 g_return_val_if_fail(theme != NULL, NULL);
598 priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
599 dir = purple_theme_get_dir(PURPLE_THEME(theme));
601 switch (type) {
602 case PIDGIN_CONVERSATION_THEME_TEMPLATE_MAIN:
603 html = get_template_html(priv, dir);
604 break;
605 case PIDGIN_CONVERSATION_THEME_TEMPLATE_HEADER:
606 html = get_header_html(priv, dir);
607 break;
608 case PIDGIN_CONVERSATION_THEME_TEMPLATE_FOOTER:
609 html = get_footer_html(priv, dir);
610 break;
611 case PIDGIN_CONVERSATION_THEME_TEMPLATE_TOPIC:
612 html = get_topic_html(priv, dir);
613 break;
614 case PIDGIN_CONVERSATION_THEME_TEMPLATE_STATUS:
615 html = get_status_html(priv, dir);
616 break;
617 case PIDGIN_CONVERSATION_THEME_TEMPLATE_CONTENT:
618 html = get_content_html(priv, dir);
619 break;
620 case PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTENT:
621 html = get_incoming_content_html(priv, dir);
622 break;
623 case PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_NEXT_CONTENT:
624 html = get_incoming_next_content_html(priv, dir);
625 break;
626 case PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTEXT:
627 html = get_incoming_context_html(priv, dir);
628 break;
629 case PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_NEXT_CONTEXT:
630 html = get_incoming_next_context_html(priv, dir);
631 break;
632 case PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_CONTENT:
633 html = get_outgoing_content_html(priv, dir);
634 break;
635 case PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_NEXT_CONTENT:
636 html = get_outgoing_next_content_html(priv, dir);
637 break;
638 case PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_CONTEXT:
639 html = get_outgoing_context_html(priv, dir);
640 break;
641 case PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_NEXT_CONTEXT:
642 html = get_outgoing_next_context_html(priv, dir);
643 break;
644 case PIDGIN_CONVERSATION_THEME_TEMPLATE_BASESTYLE_CSS:
645 html = get_basestyle_css(priv, dir);
646 break;
647 default:
648 purple_debug_error("gtkconv-theme",
649 "Requested invalid template type (%d) for theme %s.\n",
650 type, purple_theme_get_name(PURPLE_THEME(theme)));
651 html = NULL;
654 return html;
657 void
658 pidgin_conversation_theme_add_variant(PidginConvTheme *theme, char *variant)
660 PidginConvThemePrivate *priv;
662 g_return_if_fail(theme != NULL);
663 g_return_if_fail(variant != NULL);
665 priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
667 priv->variants = g_list_prepend(priv->variants, variant);
670 const char *
671 pidgin_conversation_theme_get_variant(PidginConvTheme *theme)
673 PidginConvThemePrivate *priv;
675 g_return_val_if_fail(theme != NULL, NULL);
677 priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
679 return priv->variant;
682 void
683 pidgin_conversation_theme_set_variant(PidginConvTheme *theme, const char *variant)
685 PidginConvThemePrivate *priv;
686 const GValue *val;
687 char *prefname;
689 g_return_if_fail(theme != NULL);
690 g_return_if_fail(variant != NULL);
692 priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
694 g_free(priv->variant);
695 priv->variant = g_strdup(variant);
697 val = get_key(priv, "CFBundleIdentifier", FALSE);
698 prefname = g_strdup_printf(PIDGIN_PREFS_ROOT "/conversations/themes/%s/variant",
699 g_value_get_string(val));
700 purple_prefs_set_string(prefname, variant);
701 g_free(prefname);
703 g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_VARIANT]);
706 const GList *
707 pidgin_conversation_theme_get_variants(PidginConvTheme *theme)
709 PidginConvThemePrivate *priv;
711 g_return_val_if_fail(theme != NULL, NULL);
713 priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
715 return priv->variants;
718 char *
719 pidgin_conversation_theme_get_template_path(PidginConvTheme *theme)
721 const char *dir;
723 g_return_val_if_fail(theme != NULL, NULL);
725 dir = purple_theme_get_dir(PURPLE_THEME(theme));
727 return get_template_path(dir);
730 char *
731 pidgin_conversation_theme_get_css_path(PidginConvTheme *theme)
733 PidginConvThemePrivate *priv;
734 const char *dir;
736 g_return_val_if_fail(theme != NULL, NULL);
738 priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
740 dir = purple_theme_get_dir(PURPLE_THEME(theme));
741 if (!priv->variant) {
742 return g_build_filename(dir, "Contents", "Resources", "main.css", NULL);
743 } else {
744 char *file = g_strdup_printf("%s.css", priv->variant);
745 char *ret = g_build_filename(dir, "Contents", "Resources", "Variants", file, NULL);
746 g_free(file);
747 return ret;
751 GArray *
752 pidgin_conversation_theme_get_nick_colors(PidginConvTheme *theme)
754 PidginConvThemePrivate *priv;
755 const char *dir;
757 g_return_val_if_fail(theme != NULL, NULL);
759 priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme);
761 dir = purple_theme_get_dir(PURPLE_THEME(theme));
762 if (NULL == priv->nick_colors)
764 char *file = g_build_filename(dir, "Contents", "Resources", "Incoming", "SenderColors.txt", NULL);
765 char *contents;
766 priv->nick_colors = g_array_new(FALSE, FALSE, sizeof(GdkRGBA));
767 if (g_file_get_contents(file, &contents, NULL, NULL)) {
768 int i;
769 gchar ** color_strings = g_strsplit_set(contents, "\r\n:", -1);
771 for(i=0; color_strings[i]; i++)
773 GdkRGBA color;
774 if (gdk_rgba_parse(&color, color_strings[i])) {
775 g_array_append_val(priv->nick_colors, color);
779 g_strfreev(color_strings);
780 g_free(contents);
782 g_free(file);
785 if(priv->nick_colors->len)
786 return g_array_ref(priv->nick_colors);
787 else
788 return NULL;