pidgin/gtkconv-theme-loader.c

changeset 32710
e07993a28f20
parent 32588
b0ce9232396b
child 32958
98a8270ec6aa
equal deleted inserted replaced
32056:5ecbc7cf4ee1 32710:e07993a28f20
1 /*
2 * PidginConvThemeLoader for Pidgin
3 *
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.
7 *
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.
12 *
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.
17 *
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
21 */
22
23 #include "pidgin.h"
24 #include "gtkconv-theme-loader.h"
25 #include "gtkconv-theme.h"
26
27 #include "xmlnode.h"
28 #include "debug.h"
29
30 /*****************************************************************************
31 * Conversation Theme Builder
32 *****************************************************************************/
33
34 static GHashTable *
35 read_info_plist(xmlnode *plist)
36 {
37 GHashTable *info;
38 xmlnode *key, *value;
39 gboolean fail = FALSE;
40
41 info = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
42
43 for (key = xmlnode_get_child(plist, "dict/key");
44 key;
45 key = xmlnode_get_next_twin(key)) {
46 char *keyname;
47 GValue *val;
48
49 ;
50 for (value = key->next; value && value->type != XMLNODE_TYPE_TAG; value = value->next)
51 ;
52 if (!value) {
53 fail = TRUE;
54 break;
55 }
56
57 val = g_new0(GValue, 1);
58 if (g_str_equal(value->name, "string")) {
59 g_value_init(val, G_TYPE_STRING);
60 g_value_take_string(val, xmlnode_get_data_unescaped(value));
61
62 } else if (g_str_equal(value->name, "true")) {
63 g_value_init(val, G_TYPE_BOOLEAN);
64 g_value_set_boolean(val, TRUE);
65
66 } else if (g_str_equal(value->name, "false")) {
67 g_value_init(val, G_TYPE_BOOLEAN);
68 g_value_set_boolean(val, FALSE);
69
70 } else if (g_str_equal(value->name, "real")) {
71 char *temp = xmlnode_get_data_unescaped(value);
72 g_value_init(val, G_TYPE_FLOAT);
73 g_value_set_float(val, atof(temp));
74 g_free(temp);
75
76 } else if (g_str_equal(value->name, "integer")) {
77 char *temp = xmlnode_get_data_unescaped(value);
78 g_value_init(val, G_TYPE_INT);
79 g_value_set_int(val, atoi(temp));
80 g_free(temp);
81
82 } else {
83 /* NOTE: We don't support array, data, date, or dict as values,
84 since they don't seem to be needed for styles. */
85 g_free(val);
86 fail = TRUE;
87 break;
88 }
89
90 keyname = xmlnode_get_data_unescaped(key);
91 g_hash_table_insert(info, keyname, val);
92 }
93
94 if (fail) {
95 g_hash_table_destroy(info);
96 info = NULL;
97 }
98
99 return info;
100 }
101
102 static PurpleTheme *
103 pidgin_conv_loader_build(const gchar *dir)
104 {
105 PidginConvTheme *theme = NULL;
106 char *contents;
107 xmlnode *plist;
108 GHashTable *info;
109 GValue *val;
110 int MessageViewVersion;
111 const char *CFBundleName;
112 const char *CFBundleIdentifier;
113 GDir *variants;
114 char *variant_dir;
115
116 g_return_val_if_fail(dir != NULL, NULL);
117
118 /* Load Info.plist for theme information */
119 contents = g_build_filename(dir, "Contents", NULL);
120 plist = xmlnode_from_file(contents, "Info.plist", "Info.plist", "gtkconv-theme-loader");
121 g_free(contents);
122 if (plist == NULL) {
123 purple_debug_error("gtkconv-theme-loader",
124 "Failed to load Contents/Info.plist in %s\n", dir);
125 return NULL;
126 }
127
128 info = read_info_plist(plist);
129 xmlnode_free(plist);
130 if (info == NULL) {
131 purple_debug_error("gtkconv-theme-loader",
132 "Failed to load Contents/Info.plist in %s\n", dir);
133 return NULL;
134 }
135
136 /* Check for required keys: CFBundleName */
137 val = g_hash_table_lookup(info, "CFBundleName");
138 if (!val || !G_VALUE_HOLDS_STRING(val)) {
139 purple_debug_error("gtkconv-theme-loader",
140 "%s/Contents/Info.plist missing required string key CFBundleName.\n",
141 dir);
142 g_hash_table_destroy(info);
143 return NULL;
144 }
145 CFBundleName = g_value_get_string(val);
146
147 /* Check for required keys: CFBundleIdentifier */
148 val = g_hash_table_lookup(info, "CFBundleIdentifier");
149 if (!val || !G_VALUE_HOLDS_STRING(val)) {
150 purple_debug_error("gtkconv-theme-loader",
151 "%s/Contents/Info.plist missing required string key CFBundleIdentifier.\n",
152 dir);
153 g_hash_table_destroy(info);
154 return NULL;
155 }
156 CFBundleIdentifier = g_value_get_string(val);
157
158 /* Check for required keys: MessageViewVersion */
159 val = g_hash_table_lookup(info, "MessageViewVersion");
160 if (!val || !G_VALUE_HOLDS_INT(val)) {
161 purple_debug_error("gtkconv-theme-loader",
162 "%s/Contents/Info.plist missing required integer key MessageViewVersion.\n",
163 dir);
164 g_hash_table_destroy(info);
165 return NULL;
166 }
167
168 MessageViewVersion = g_value_get_int(val);
169 if (MessageViewVersion < 3) {
170 purple_debug_error("gtkconv-theme-loader",
171 "%s is a legacy style (version %d) and will not be loaded.\n",
172 CFBundleName, MessageViewVersion);
173 g_hash_table_destroy(info);
174 return NULL;
175 }
176
177 theme = g_object_new(PIDGIN_TYPE_CONV_THEME,
178 "type", "conversation",
179 "name", CFBundleName,
180 "directory", dir,
181 "info", info, NULL);
182
183 /* Read list of variants */
184 variant_dir = g_build_filename(dir, "Contents", "Resources", "Variants", NULL);
185 variants = g_dir_open(variant_dir, 0, NULL);
186 g_free(variant_dir);
187
188 if (variants) {
189 char *prefname;
190 const char *default_variant = NULL;
191 const char *file;
192
193 /* Make sure prefs exist */
194 prefname = g_strdup_printf(PIDGIN_PREFS_ROOT "/conversations/themes/%s",
195 CFBundleIdentifier);
196 purple_prefs_add_none(prefname);
197 g_free(prefname);
198
199 /* Try user-set variant */
200 prefname = g_strdup_printf(PIDGIN_PREFS_ROOT "/conversations/themes/%s/variant",
201 CFBundleIdentifier);
202 if (purple_prefs_exists(prefname))
203 default_variant = purple_prefs_get_string(prefname);
204 g_free(prefname);
205
206 if (default_variant && *default_variant) {
207 pidgin_conversation_theme_set_variant(theme, default_variant);
208
209 } else {
210 /* Try theme default */
211 val = g_hash_table_lookup(info, "DefaultVariant");
212 if (val && G_VALUE_HOLDS_STRING(val)) {
213 default_variant = g_value_get_string(val);
214 if (default_variant && *default_variant)
215 pidgin_conversation_theme_set_variant(theme, default_variant);
216 else
217 default_variant = NULL;
218 } else
219 default_variant = NULL;
220 }
221
222 while ((file = g_dir_read_name(variants)) != NULL) {
223 const char *end = g_strrstr(file, ".css");
224 char *name;
225
226 if ((end == NULL) || (*(end + 4) != '\0'))
227 continue;
228
229 name = g_strndup(file, end - file);
230 pidgin_conversation_theme_add_variant(theme, name);
231
232 /* Set variant with first found */
233 if (!default_variant) {
234 pidgin_conversation_theme_set_variant(theme, name);
235 default_variant = name;
236 }
237 }
238
239 g_dir_close(variants);
240 }
241
242 return PURPLE_THEME(theme);
243 }
244
245 /******************************************************************************
246 * GObject Stuff
247 *****************************************************************************/
248
249 static void
250 pidgin_conv_theme_loader_class_init(PidginConvThemeLoaderClass *klass)
251 {
252 PurpleThemeLoaderClass *loader_klass = PURPLE_THEME_LOADER_CLASS(klass);
253
254 loader_klass->purple_theme_loader_build = pidgin_conv_loader_build;
255 }
256
257
258 GType
259 pidgin_conversation_theme_loader_get_type(void)
260 {
261 static GType type = 0;
262 if (type == 0) {
263 static const GTypeInfo info = {
264 sizeof(PidginConvThemeLoaderClass),
265 NULL, /* base_init */
266 NULL, /* base_finalize */
267 (GClassInitFunc)pidgin_conv_theme_loader_class_init, /* class_init */
268 NULL, /* class_finalize */
269 NULL, /* class_data */
270 sizeof (PidginConvThemeLoader),
271 0, /* n_preallocs */
272 NULL, /* instance_init */
273 NULL, /* value table */
274 };
275 type = g_type_register_static(PURPLE_TYPE_THEME_LOADER,
276 "PidginConvThemeLoader", &info, 0);
277 }
278 return type;
279 }
280

mercurial