Mercurial > pidgin
annotate src/savedstatuses.c @ 13968:6fc412e59214
[gaim-migrate @ 16525]
A bunch of little things
* Use GAIM_CONNECTION_IS_VALID(gc) in a lot of places where
we were doing g_list_find(gaim_connections_get_all(), gc)
* Get rid of a lot of places where we were doing
g_list_find(gaim_connections_get_all(), gc). The handle used
by the request API ensures that the ok and cancel callback
functions won't be called if the gc is destroyed. However,
GAIM_CONNECTION_IS_VALID(gc) is still very important for
callback functions where we can't cancel the request.
For example, gaim_proxy_connect() callback functions.
* "Added" a function to Yahoo! that should help us notice
when our buddies change their buddy icon/display picture
* Some comments in a few places
* Changed GAIM_CONNECTION_IS_VALID(gc) to only look through
the list of "all" connections and not the list of
"connecting" connections. Some time ago we changed how
this was done so that the list of "all" connections now
includes the "connection" connections.
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Thu, 20 Jul 2006 08:11:54 +0000 |
parents | ec858948d292 |
children | a8a033a89ee0 |
rev | line source |
---|---|
10418 | 1 /** |
13761
ec858948d292
[gaim-migrate @ 16173]
Richard Laager <rlaager@wiktel.com>
parents:
13681
diff
changeset
|
2 * @file savedstatuses.c Saved Status API |
10418 | 3 * @ingroup core |
4 * | |
5 * gaim | |
6 * | |
7 * Gaim is the legal property of its developers, whose names are too numerous | |
8 * to list here. Please refer to the COPYRIGHT file distributed with this | |
9 * source distribution. | |
10 * | |
11 * This program is free software; you can redistribute it and/or modify | |
12 * it under the terms of the GNU General Public License as published by | |
13 * the Free Software Foundation; either version 2 of the License, or | |
14 * (at your option) any later version. | |
15 * | |
16 * This program is distributed in the hope that it will be useful, | |
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 * GNU General Public License for more details. | |
20 * | |
21 * You should have received a copy of the GNU General Public License | |
22 * along with this program; if not, write to the Free Software | |
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
24 */ | |
25 #include "internal.h" | |
26 | |
27 #include "debug.h" | |
28 #include "notify.h" | |
29 #include "savedstatuses.h" | |
13761
ec858948d292
[gaim-migrate @ 16173]
Richard Laager <rlaager@wiktel.com>
parents:
13681
diff
changeset
|
30 #include "dbus-maybe.h" |
10418 | 31 #include "status.h" |
32 #include "util.h" | |
33 #include "xmlnode.h" | |
34 | |
35 /** | |
12327 | 36 * The maximum number of transient statuses to save. This |
37 * is used during the shutdown process to clean out old | |
38 * transient statuses. | |
39 */ | |
40 #define MAX_TRANSIENTS 5 | |
41 | |
42 /** | |
13681 | 43 * The default message to use when the user becomes auto-away. |
44 */ | |
45 #define DEFAULT_AUTOAWAY_MESSAGE _("I'm not here right now") | |
46 | |
47 /** | |
12125 | 48 * The information stores a snap-shot of the statuses of all |
10418 | 49 * your accounts. Basically these are your saved away messages. |
50 * There is an overall status and message that applies to | |
51 * all your accounts, and then each individual account can | |
52 * optionally have a different custom status and message. | |
53 * | |
54 * The changes to status.xml caused by the new status API | |
55 * are fully backward compatible. The new status API just | |
56 * adds the optional sub-statuses to the XML file. | |
57 */ | |
10419 | 58 struct _GaimSavedStatus |
10418 | 59 { |
60 char *title; | |
61 GaimStatusPrimitive type; | |
62 char *message; | |
63 | |
12125 | 64 /** The timestamp when this saved status was created. This must be unique. */ |
65 time_t creation_time; | |
66 | |
67 time_t lastused; | |
68 | |
12688 | 69 unsigned int usage_count; |
70 | |
10419 | 71 GList *substatuses; /**< A list of GaimSavedStatusSub's. */ |
10418 | 72 }; |
73 | |
74 /* | |
75 * TODO: If a GaimStatusType is deleted, need to also delete any | |
10419 | 76 * associated GaimSavedStatusSub's? |
10418 | 77 */ |
10419 | 78 struct _GaimSavedStatusSub |
10418 | 79 { |
80 GaimAccount *account; | |
81 const GaimStatusType *type; | |
82 char *message; | |
83 }; | |
84 | |
12125 | 85 static GList *saved_statuses = NULL; |
86 static guint save_timer = 0; | |
87 static gboolean statuses_loaded = FALSE; | |
88 | |
89 /* | |
90 * This hash table keeps track of which timestamps we've | |
91 * used so that we don't have two saved statuses with the | |
92 * same 'creation_time' timestamp. The 'created' timestamp | |
93 * is used as a unique identifier. | |
94 * | |
95 * So the key in this hash table is the creation_time and | |
96 * the value is a pointer to the GaimSavedStatus. | |
97 */ | |
98 static GHashTable *creation_times; | |
10418 | 99 |
12327 | 100 static void schedule_save(void); |
10427 | 101 |
10428 | 102 /********************************************************************* |
103 * Private utility functions * | |
104 *********************************************************************/ | |
10418 | 105 |
106 static void | |
12724 | 107 free_saved_status_sub(GaimSavedStatusSub *substatus) |
10418 | 108 { |
109 g_return_if_fail(substatus != NULL); | |
110 | |
111 g_free(substatus->message); | |
13761
ec858948d292
[gaim-migrate @ 16173]
Richard Laager <rlaager@wiktel.com>
parents:
13681
diff
changeset
|
112 GAIM_DBUS_UNREGISTER_POINTER(substatus); |
10418 | 113 g_free(substatus); |
114 } | |
115 | |
116 static void | |
12724 | 117 free_saved_status(GaimSavedStatus *status) |
10418 | 118 { |
119 g_return_if_fail(status != NULL); | |
120 | |
121 g_free(status->title); | |
122 g_free(status->message); | |
123 | |
124 while (status->substatuses != NULL) | |
125 { | |
10419 | 126 GaimSavedStatusSub *substatus = status->substatuses->data; |
10418 | 127 status->substatuses = g_list_remove(status->substatuses, substatus); |
12724 | 128 free_saved_status_sub(substatus); |
10418 | 129 } |
130 | |
13761
ec858948d292
[gaim-migrate @ 16173]
Richard Laager <rlaager@wiktel.com>
parents:
13681
diff
changeset
|
131 GAIM_DBUS_UNREGISTER_POINTER(status); |
10418 | 132 g_free(status); |
133 } | |
134 | |
12125 | 135 /* |
136 * Set the timestamp for when this saved status was created, and | |
137 * make sure it is unique. | |
138 */ | |
139 static void | |
140 set_creation_time(GaimSavedStatus *status, time_t creation_time) | |
141 { | |
142 g_return_if_fail(status != NULL); | |
143 | |
144 /* Avoid using 0 because it's an invalid hash key */ | |
145 status->creation_time = creation_time != 0 ? creation_time : 1; | |
146 | |
147 while (g_hash_table_lookup(creation_times, &status->creation_time) != NULL) | |
148 status->creation_time++; | |
149 | |
150 g_hash_table_insert(creation_times, | |
151 &status->creation_time, | |
152 status); | |
153 } | |
154 | |
12724 | 155 /** |
156 * A magic number is calcuated for each status, and then the | |
157 * statuses are ordered by the magic number. The magic number | |
158 * is the date the status was last used offset by one day for | |
159 * each time the status has been used (but only by 10 days at | |
160 * the most). | |
161 * | |
162 * The goal is to have recently used statuses at the top of | |
163 * the list, but to also keep frequently used statuses near | |
164 * the top. | |
165 */ | |
12327 | 166 static gint |
12724 | 167 saved_statuses_sort_func(gconstpointer a, gconstpointer b) |
12327 | 168 { |
12724 | 169 const GaimSavedStatus *saved_status_a = a; |
170 const GaimSavedStatus *saved_status_b = b; | |
171 time_t time_a = saved_status_a->lastused + | |
172 (MIN(saved_status_a->usage_count, 10) * 86400); | |
173 time_t time_b = saved_status_b->lastused + | |
174 (MIN(saved_status_b->usage_count, 10) * 86400); | |
175 if (time_a > time_b) | |
12327 | 176 return -1; |
12724 | 177 if (time_a < time_b) |
12327 | 178 return 1; |
179 return 0; | |
180 } | |
181 | |
182 /** | |
183 * Transient statuses are added and removed automatically by | |
184 * Gaim. If they're not used for a certain length of time then | |
185 * they'll expire and be automatically removed. This function | |
186 * does the expiration. | |
187 */ | |
188 static void | |
189 remove_old_transient_statuses() | |
190 { | |
191 GList *l, *next; | |
13197 | 192 GaimSavedStatus *saved_status, *current_status; |
12724 | 193 int count; |
194 time_t creation_time; | |
12327 | 195 |
13197 | 196 current_status = gaim_savedstatus_get_current(); |
197 | |
12724 | 198 /* |
199 * Iterate through the list of saved statuses. Delete all | |
200 * transient statuses except for the first MAX_TRANSIENTS | |
201 * (remember, the saved statuses are already sorted by popularity). | |
202 */ | |
203 count = 0; | |
12327 | 204 for (l = saved_statuses; l != NULL; l = next) |
205 { | |
206 next = l->next; | |
207 saved_status = l->data; | |
12724 | 208 if (gaim_savedstatus_is_transient(saved_status)) |
12327 | 209 { |
12724 | 210 if (count == MAX_TRANSIENTS) |
211 { | |
13197 | 212 if (saved_status != current_status) |
213 { | |
214 saved_statuses = g_list_remove(saved_statuses, saved_status); | |
215 creation_time = gaim_savedstatus_get_creation_time(saved_status); | |
216 g_hash_table_remove(creation_times, &creation_time); | |
217 free_saved_status(saved_status); | |
218 } | |
12724 | 219 } |
220 else | |
221 count++; | |
12327 | 222 } |
223 } | |
12724 | 224 |
225 if (count == MAX_TRANSIENTS) | |
226 schedule_save(); | |
12327 | 227 } |
228 | |
10428 | 229 /********************************************************************* |
10429 | 230 * Writing to disk * |
10428 | 231 *********************************************************************/ |
10418 | 232 |
233 static xmlnode * | |
10419 | 234 substatus_to_xmlnode(GaimSavedStatusSub *substatus) |
10418 | 235 { |
236 xmlnode *node, *child; | |
237 | |
238 node = xmlnode_new("substatus"); | |
239 | |
10424 | 240 child = xmlnode_new_child(node, "account"); |
241 xmlnode_set_attrib(child, "protocol", gaim_account_get_protocol_id(substatus->account)); | |
242 xmlnode_insert_data(child, gaim_account_get_username(substatus->account), -1); | |
10418 | 243 |
10424 | 244 child = xmlnode_new_child(node, "state"); |
10418 | 245 xmlnode_insert_data(child, gaim_status_type_get_id(substatus->type), -1); |
246 | |
247 if (substatus->message != NULL) | |
248 { | |
10424 | 249 child = xmlnode_new_child(node, "message"); |
10418 | 250 xmlnode_insert_data(child, substatus->message, -1); |
251 } | |
252 | |
253 return node; | |
254 } | |
255 | |
256 static xmlnode * | |
10419 | 257 status_to_xmlnode(GaimSavedStatus *status) |
10418 | 258 { |
259 xmlnode *node, *child; | |
12125 | 260 char buf[21]; |
10418 | 261 GList *cur; |
262 | |
12125 | 263 node = xmlnode_new("status"); |
264 if (status->title != NULL) | |
12283 | 265 { |
12125 | 266 xmlnode_set_attrib(node, "name", status->title); |
12283 | 267 } |
268 else | |
269 { | |
270 /* | |
271 * Gaim 1.5.0 and earlier require a name to be set, so we | |
272 * do this little hack to maintain backward compatability | |
12309 | 273 * in the status.xml file. Eventually this should be removed |
12283 | 274 * and we should determine if a status is transient by |
275 * whether the "name" attribute is set to something or if | |
276 * it does not exist at all. | |
277 */ | |
278 xmlnode_set_attrib(node, "name", "Auto-Cached"); | |
279 xmlnode_set_attrib(node, "transient", "true"); | |
280 } | |
11651 | 281 |
12125 | 282 snprintf(buf, sizeof(buf), "%lu", status->creation_time); |
283 xmlnode_set_attrib(node, "created", buf); | |
284 | |
285 snprintf(buf, sizeof(buf), "%lu", status->lastused); | |
286 xmlnode_set_attrib(node, "lastused", buf); | |
10418 | 287 |
12688 | 288 snprintf(buf, sizeof(buf), "%u", status->usage_count); |
289 xmlnode_set_attrib(node, "usage_count", buf); | |
290 | |
10424 | 291 child = xmlnode_new_child(node, "state"); |
292 xmlnode_insert_data(child, gaim_primitive_get_id_from_type(status->type), -1); | |
10418 | 293 |
11651 | 294 if (status->message != NULL) |
295 { | |
296 child = xmlnode_new_child(node, "message"); | |
297 xmlnode_insert_data(child, status->message, -1); | |
298 } | |
10418 | 299 |
300 for (cur = status->substatuses; cur != NULL; cur = cur->next) | |
301 { | |
302 child = substatus_to_xmlnode(cur->data); | |
303 xmlnode_insert_child(node, child); | |
304 } | |
305 | |
306 return node; | |
307 } | |
308 | |
309 static xmlnode * | |
310 statuses_to_xmlnode(void) | |
311 { | |
312 xmlnode *node, *child; | |
313 GList *cur; | |
314 | |
315 node = xmlnode_new("statuses"); | |
10423 | 316 xmlnode_set_attrib(node, "version", "1.0"); |
10418 | 317 |
318 for (cur = saved_statuses; cur != NULL; cur = cur->next) | |
319 { | |
320 child = status_to_xmlnode(cur->data); | |
321 xmlnode_insert_child(node, child); | |
322 } | |
323 | |
324 return node; | |
325 } | |
326 | |
327 static void | |
328 sync_statuses(void) | |
329 { | |
10423 | 330 xmlnode *node; |
10418 | 331 char *data; |
332 | |
10428 | 333 if (!statuses_loaded) |
334 { | |
10418 | 335 gaim_debug_error("status", "Attempted to save statuses before they " |
336 "were read!\n"); | |
337 return; | |
338 } | |
339 | |
10423 | 340 node = statuses_to_xmlnode(); |
341 data = xmlnode_to_formatted_str(node, NULL); | |
10418 | 342 gaim_util_write_data_to_file("status.xml", data, -1); |
343 g_free(data); | |
10423 | 344 xmlnode_free(node); |
10418 | 345 } |
346 | |
347 static gboolean | |
10428 | 348 save_cb(gpointer data) |
10418 | 349 { |
350 sync_statuses(); | |
10428 | 351 save_timer = 0; |
10418 | 352 return FALSE; |
353 } | |
354 | |
355 static void | |
356 schedule_save(void) | |
357 { | |
10428 | 358 if (save_timer == 0) |
359 save_timer = gaim_timeout_add(5000, save_cb, NULL); | |
10418 | 360 } |
361 | |
362 | |
10428 | 363 /********************************************************************* |
364 * Reading from disk * | |
365 *********************************************************************/ | |
366 | |
10419 | 367 static GaimSavedStatusSub * |
10418 | 368 parse_substatus(xmlnode *substatus) |
369 { | |
10419 | 370 GaimSavedStatusSub *ret; |
10418 | 371 xmlnode *node; |
10425 | 372 char *data; |
10418 | 373 |
10419 | 374 ret = g_new0(GaimSavedStatusSub, 1); |
13761
ec858948d292
[gaim-migrate @ 16173]
Richard Laager <rlaager@wiktel.com>
parents:
13681
diff
changeset
|
375 GAIM_DBUS_REGISTER_POINTER(ret, GaimSavedStatusSub); |
10418 | 376 |
377 /* Read the account */ | |
378 node = xmlnode_get_child(substatus, "account"); | |
379 if (node != NULL) | |
380 { | |
381 char *acct_name; | |
382 const char *protocol; | |
383 acct_name = xmlnode_get_data(node); | |
384 protocol = xmlnode_get_attrib(node, "protocol"); | |
385 if ((acct_name != NULL) && (protocol != NULL)) | |
386 ret->account = gaim_accounts_find(acct_name, protocol); | |
387 g_free(acct_name); | |
388 } | |
389 | |
390 if (ret->account == NULL) | |
391 { | |
392 g_free(ret); | |
393 return NULL; | |
394 } | |
395 | |
396 /* Read the state */ | |
397 node = xmlnode_get_child(substatus, "state"); | |
10426 | 398 if ((node != NULL) && ((data = xmlnode_get_data(node)) != NULL)) |
10425 | 399 { |
10418 | 400 ret->type = gaim_status_type_find_with_id( |
10425 | 401 ret->account->status_types, data); |
10418 | 402 g_free(data); |
403 } | |
404 | |
405 /* Read the message */ | |
406 node = xmlnode_get_child(substatus, "message"); | |
10426 | 407 if ((node != NULL) && ((data = xmlnode_get_data(node)) != NULL)) |
10425 | 408 { |
10418 | 409 ret->message = data; |
10425 | 410 } |
10418 | 411 |
412 return ret; | |
413 } | |
414 | |
415 /** | |
416 * Parse a saved status and add it to the saved_statuses linked list. | |
417 * | |
418 * Here's an example of the XML for a saved status: | |
419 * <status name="Girls"> | |
420 * <state>away</state> | |
421 * <message>I like the way that they walk | |
422 * And it's chill to hear them talk | |
423 * And I can always make them smile | |
424 * From White Castle to the Nile</message> | |
425 * <substatus> | |
426 * <account protocol='prpl-oscar'>markdoliner</account> | |
427 * <state>available</state> | |
428 * <message>The ladies man is here to answer your queries.</message> | |
429 * </substatus> | |
430 * <substatus> | |
431 * <account protocol='prpl-oscar'>giantgraypanda</account> | |
432 * <state>away</state> | |
433 * <message>A.C. ain't in charge no more.</message> | |
434 * </substatus> | |
435 * </status> | |
436 * | |
437 * I know. Moving, huh? | |
438 */ | |
10419 | 439 static GaimSavedStatus * |
10418 | 440 parse_status(xmlnode *status) |
441 { | |
10419 | 442 GaimSavedStatus *ret; |
10418 | 443 xmlnode *node; |
444 const char *attrib; | |
10425 | 445 char *data; |
10418 | 446 int i; |
447 | |
10419 | 448 ret = g_new0(GaimSavedStatus, 1); |
13761
ec858948d292
[gaim-migrate @ 16173]
Richard Laager <rlaager@wiktel.com>
parents:
13681
diff
changeset
|
449 GAIM_DBUS_REGISTER_POINTER(ret, GaimSavedStatus); |
10418 | 450 |
12283 | 451 attrib = xmlnode_get_attrib(status, "transient"); |
452 if ((attrib == NULL) || (strcmp(attrib, "true"))) | |
453 { | |
454 /* Read the title */ | |
455 attrib = xmlnode_get_attrib(status, "name"); | |
456 ret->title = g_strdup(attrib); | |
457 } | |
12125 | 458 |
459 if (ret->title != NULL) | |
10418 | 460 { |
12125 | 461 /* Ensure the title is unique */ |
462 i = 2; | |
463 while (gaim_savedstatus_find(ret->title) != NULL) | |
464 { | |
465 g_free(ret->title); | |
466 ret->title = g_strdup_printf("%s %d", attrib, i); | |
467 i++; | |
468 } | |
10418 | 469 } |
470 | |
12125 | 471 /* Read the creation time */ |
472 attrib = xmlnode_get_attrib(status, "created"); | |
473 set_creation_time(ret, (attrib != NULL ? atol(attrib) : 0)); | |
474 | |
475 /* Read the last used time */ | |
476 attrib = xmlnode_get_attrib(status, "lastused"); | |
477 ret->lastused = (attrib != NULL ? atol(attrib) : 0); | |
478 | |
12688 | 479 /* Read the usage count */ |
480 attrib = xmlnode_get_attrib(status, "usage_count"); | |
481 ret->usage_count = (attrib != NULL ? atol(attrib) : 0); | |
482 | |
10418 | 483 /* Read the primitive status type */ |
484 node = xmlnode_get_child(status, "state"); | |
10426 | 485 if ((node != NULL) && ((data = xmlnode_get_data(node)) != NULL)) |
10425 | 486 { |
10419 | 487 ret->type = gaim_primitive_get_type_from_id(data); |
10418 | 488 g_free(data); |
489 } | |
490 | |
491 /* Read the message */ | |
492 node = xmlnode_get_child(status, "message"); | |
10426 | 493 if ((node != NULL) && ((data = xmlnode_get_data(node)) != NULL)) |
10425 | 494 { |
10418 | 495 ret->message = data; |
10425 | 496 } |
10418 | 497 |
498 /* Read substatuses */ | |
12056 | 499 for (node = xmlnode_get_child(status, "substatus"); node != NULL; |
10418 | 500 node = xmlnode_get_next_twin(node)) |
501 { | |
10419 | 502 GaimSavedStatusSub *new; |
10418 | 503 new = parse_substatus(node); |
504 if (new != NULL) | |
12056 | 505 ret->substatuses = g_list_prepend(ret->substatuses, new); |
10418 | 506 } |
507 | |
508 return ret; | |
509 } | |
510 | |
511 /** | |
512 * Read the saved statuses from a file in the Gaim user dir. | |
513 * | |
514 * @return TRUE on success, FALSE on failure (if the file can not | |
515 * be opened, or if it contains invalid XML). | |
516 */ | |
10425 | 517 static void |
518 load_statuses(void) | |
10418 | 519 { |
520 xmlnode *statuses, *status; | |
521 | |
10426 | 522 statuses_loaded = TRUE; |
523 | |
10425 | 524 statuses = gaim_util_read_xml_from_file("status.xml", _("saved statuses")); |
10418 | 525 |
526 if (statuses == NULL) | |
10425 | 527 return; |
10418 | 528 |
529 for (status = xmlnode_get_child(statuses, "status"); status != NULL; | |
530 status = xmlnode_get_next_twin(status)) | |
531 { | |
10419 | 532 GaimSavedStatus *new; |
10418 | 533 new = parse_status(status); |
12056 | 534 saved_statuses = g_list_prepend(saved_statuses, new); |
10418 | 535 } |
12724 | 536 saved_statuses = g_list_sort(saved_statuses, saved_statuses_sort_func); |
10418 | 537 |
538 xmlnode_free(statuses); | |
539 } | |
540 | |
541 | |
542 /************************************************************************** | |
543 * Saved status API | |
544 **************************************************************************/ | |
10419 | 545 GaimSavedStatus * |
546 gaim_savedstatus_new(const char *title, GaimStatusPrimitive type) | |
10418 | 547 { |
10419 | 548 GaimSavedStatus *status; |
10418 | 549 |
12056 | 550 /* Make sure we don't already have a saved status with this title. */ |
12125 | 551 if (title != NULL) |
552 g_return_val_if_fail(gaim_savedstatus_find(title) == NULL, NULL); | |
10420 | 553 |
10419 | 554 status = g_new0(GaimSavedStatus, 1); |
13761
ec858948d292
[gaim-migrate @ 16173]
Richard Laager <rlaager@wiktel.com>
parents:
13681
diff
changeset
|
555 GAIM_DBUS_REGISTER_POINTER(status, GaimSavedStatus); |
10418 | 556 status->title = g_strdup(title); |
557 status->type = type; | |
12125 | 558 set_creation_time(status, time(NULL)); |
10418 | 559 |
12724 | 560 saved_statuses = g_list_insert_sorted(saved_statuses, status, saved_statuses_sort_func); |
10418 | 561 |
562 schedule_save(); | |
563 | |
564 return status; | |
565 } | |
566 | |
10420 | 567 void |
12056 | 568 gaim_savedstatus_set_title(GaimSavedStatus *status, const char *title) |
569 { | |
570 g_return_if_fail(status != NULL); | |
571 | |
572 /* Make sure we don't already have a saved status with this title. */ | |
573 g_return_if_fail(gaim_savedstatus_find(title) == NULL); | |
574 | |
575 g_free(status->title); | |
576 status->title = g_strdup(title); | |
577 | |
578 schedule_save(); | |
579 } | |
580 | |
581 void | |
11651 | 582 gaim_savedstatus_set_type(GaimSavedStatus *status, GaimStatusPrimitive type) |
583 { | |
584 g_return_if_fail(status != NULL); | |
585 | |
586 status->type = type; | |
587 | |
588 schedule_save(); | |
589 } | |
590 | |
591 void | |
10420 | 592 gaim_savedstatus_set_message(GaimSavedStatus *status, const char *message) |
593 { | |
594 g_return_if_fail(status != NULL); | |
595 | |
596 g_free(status->message); | |
13246 | 597 if ((message != NULL) && (*message == '\0')) |
598 status->message = NULL; | |
599 else | |
600 status->message = g_strdup(message); | |
10420 | 601 |
602 schedule_save(); | |
603 } | |
604 | |
12056 | 605 void |
12080 | 606 gaim_savedstatus_set_substatus(GaimSavedStatus *saved_status, |
607 const GaimAccount *account, | |
608 const GaimStatusType *type, | |
609 const char *message) | |
12056 | 610 { |
611 GaimSavedStatusSub *substatus; | |
612 | |
613 g_return_if_fail(saved_status != NULL); | |
614 g_return_if_fail(account != NULL); | |
615 g_return_if_fail(type != NULL); | |
616 | |
617 /* Find an existing substatus or create a new one */ | |
12080 | 618 substatus = gaim_savedstatus_get_substatus(saved_status, account); |
12056 | 619 if (substatus == NULL) |
620 { | |
621 substatus = g_new0(GaimSavedStatusSub, 1); | |
13761
ec858948d292
[gaim-migrate @ 16173]
Richard Laager <rlaager@wiktel.com>
parents:
13681
diff
changeset
|
622 GAIM_DBUS_REGISTER_POINTER(substatus, GaimSavedStatusSub); |
12056 | 623 substatus->account = (GaimAccount *)account; |
624 saved_status->substatuses = g_list_prepend(saved_status->substatuses, substatus); | |
625 } | |
626 | |
627 substatus->type = type; | |
628 g_free(substatus->message); | |
629 substatus->message = g_strdup(message); | |
630 | |
631 schedule_save(); | |
632 } | |
633 | |
634 void | |
12080 | 635 gaim_savedstatus_unset_substatus(GaimSavedStatus *saved_status, |
636 const GaimAccount *account) | |
12056 | 637 { |
638 GList *iter; | |
639 GaimSavedStatusSub *substatus; | |
640 | |
641 g_return_if_fail(saved_status != NULL); | |
642 g_return_if_fail(account != NULL); | |
643 | |
644 for (iter = saved_status->substatuses; iter != NULL; iter = iter->next) | |
645 { | |
646 substatus = iter->data; | |
647 if (substatus->account == account) | |
648 { | |
649 saved_status->substatuses = g_list_delete_link(saved_status->substatuses, iter); | |
650 g_free(substatus->message); | |
651 g_free(substatus); | |
652 return; | |
653 } | |
654 } | |
655 } | |
656 | |
10418 | 657 gboolean |
10419 | 658 gaim_savedstatus_delete(const char *title) |
10418 | 659 { |
10419 | 660 GaimSavedStatus *status; |
12125 | 661 time_t creation_time, current, idleaway; |
10418 | 662 |
10419 | 663 status = gaim_savedstatus_find(title); |
10418 | 664 |
665 if (status == NULL) | |
666 return FALSE; | |
667 | |
668 saved_statuses = g_list_remove(saved_statuses, status); | |
12125 | 669 creation_time = gaim_savedstatus_get_creation_time(status); |
670 g_hash_table_remove(creation_times, &creation_time); | |
12724 | 671 free_saved_status(status); |
10418 | 672 |
673 schedule_save(); | |
674 | |
12125 | 675 /* |
676 * If we just deleted our current status or our idleaway status, | |
677 * then set the appropriate pref back to 0. | |
678 */ | |
679 current = gaim_prefs_get_int("/core/savedstatus/current"); | |
680 if (current == creation_time) | |
681 gaim_prefs_set_int("/core/savedstatus/current", 0); | |
682 | |
683 idleaway = gaim_prefs_get_int("/core/savedstatus/idleaway"); | |
684 if (idleaway == creation_time) | |
685 gaim_prefs_set_int("/core/savedstatus/idleaway", 0); | |
686 | |
10418 | 687 return TRUE; |
688 } | |
689 | |
690 const GList * | |
691 gaim_savedstatuses_get_all(void) | |
692 { | |
693 return saved_statuses; | |
694 } | |
695 | |
12688 | 696 GList * |
697 gaim_savedstatuses_get_popular(unsigned int how_many) | |
698 { | |
13673 | 699 GList *popular = NULL; |
12688 | 700 GList *cur; |
701 int i; | |
13675 | 702 GaimSavedStatus *current, *next; |
13673 | 703 |
704 /* We don't want the current status to be in the GList */ | |
705 current = gaim_savedstatus_get_current(); | |
12688 | 706 |
707 /* Copy 'how_many' elements to a new list */ | |
13673 | 708 i = 0; |
709 cur = saved_statuses; | |
710 while ((i < how_many) && (cur != NULL)) | |
12688 | 711 { |
13675 | 712 next = cur->data; |
713 if ((next != current) && (!gaim_savedstatus_is_transient(next) | |
714 || gaim_savedstatus_get_message(next) != NULL)) | |
13673 | 715 { |
716 popular = g_list_prepend(popular, cur->data); | |
717 i++; | |
718 } | |
12688 | 719 cur = cur->next; |
720 } | |
721 | |
13673 | 722 popular = g_list_reverse(popular); |
723 | |
724 return popular; | |
12688 | 725 } |
726 | |
10419 | 727 GaimSavedStatus * |
12125 | 728 gaim_savedstatus_get_current() |
729 { | |
730 int creation_time; | |
12197 | 731 GaimSavedStatus *saved_status = NULL; |
12125 | 732 |
733 creation_time = gaim_prefs_get_int("/core/savedstatus/current"); | |
734 | |
12197 | 735 if (creation_time != 0) |
736 saved_status = g_hash_table_lookup(creation_times, &creation_time); | |
737 | |
738 if (saved_status == NULL) | |
12125 | 739 { |
740 /* | |
13681 | 741 * We don't have a current saved status! This is either a new |
12197 | 742 * Gaim user or someone upgrading from Gaim 1.5.0 or older, or |
743 * possibly someone who deleted the status they were currently | |
744 * using? In any case, add a default status. | |
12125 | 745 */ |
746 saved_status = gaim_savedstatus_new(NULL, GAIM_STATUS_AVAILABLE); | |
13246 | 747 gaim_prefs_set_int("/core/savedstatus/current", |
748 gaim_savedstatus_get_creation_time(saved_status)); | |
12125 | 749 } |
750 | |
751 return saved_status; | |
752 } | |
753 | |
754 GaimSavedStatus * | |
755 gaim_savedstatus_get_idleaway() | |
756 { | |
757 int creation_time; | |
13681 | 758 GaimSavedStatus *saved_status = NULL; |
12125 | 759 |
760 creation_time = gaim_prefs_get_int("/core/savedstatus/idleaway"); | |
761 | |
13681 | 762 if (creation_time != 0) |
763 saved_status = g_hash_table_lookup(creation_times, &creation_time); | |
764 | |
765 if (saved_status == NULL) | |
12125 | 766 { |
13681 | 767 /* We don't have a specified "idle" status! Weird. */ |
768 saved_status = gaim_savedstatus_find_transient_by_type_and_message( | |
769 GAIM_STATUS_AWAY, DEFAULT_AUTOAWAY_MESSAGE); | |
770 | |
771 if (saved_status == NULL) | |
772 { | |
773 saved_status = gaim_savedstatus_new(NULL, GAIM_STATUS_AWAY); | |
774 gaim_savedstatus_set_message(saved_status, DEFAULT_AUTOAWAY_MESSAGE); | |
775 gaim_prefs_set_int("/core/savedstatus/idleaway", | |
776 gaim_savedstatus_get_creation_time(saved_status)); | |
777 } | |
12125 | 778 } |
779 | |
780 return saved_status; | |
781 } | |
782 | |
783 GaimSavedStatus * | |
13681 | 784 gaim_savedstatus_get_startup() |
785 { | |
786 int creation_time; | |
787 GaimSavedStatus *saved_status = NULL; | |
788 | |
789 creation_time = gaim_prefs_get_int("/core/savedstatus/startup"); | |
790 | |
791 if (creation_time != 0) | |
792 saved_status = g_hash_table_lookup(creation_times, &creation_time); | |
793 | |
794 if (saved_status == NULL) | |
795 { | |
796 /* | |
797 * We don't have a status to apply. | |
798 * This may be the first login, or the user wants to | |
799 * restore the "current" status. | |
800 */ | |
801 saved_status = gaim_savedstatus_get_current(); | |
802 } | |
803 | |
804 return saved_status; | |
805 } | |
806 | |
807 | |
808 GaimSavedStatus * | |
10419 | 809 gaim_savedstatus_find(const char *title) |
10418 | 810 { |
12056 | 811 GList *iter; |
10419 | 812 GaimSavedStatus *status; |
10418 | 813 |
11977 | 814 g_return_val_if_fail(title != NULL, NULL); |
815 | |
12056 | 816 for (iter = saved_statuses; iter != NULL; iter = iter->next) |
10418 | 817 { |
12056 | 818 status = (GaimSavedStatus *)iter->data; |
12125 | 819 if ((status->title != NULL) && !strcmp(status->title, title)) |
10418 | 820 return status; |
821 } | |
822 | |
823 return NULL; | |
824 } | |
825 | |
12690 | 826 GaimSavedStatus * |
827 gaim_savedstatus_find_by_creation_time(time_t creation_time) | |
828 { | |
829 GList *iter; | |
830 GaimSavedStatus *status; | |
831 | |
832 for (iter = saved_statuses; iter != NULL; iter = iter->next) | |
833 { | |
834 status = (GaimSavedStatus *)iter->data; | |
835 if (status->creation_time == creation_time) | |
836 return status; | |
837 } | |
838 | |
839 return NULL; | |
840 } | |
841 | |
13012 | 842 GaimSavedStatus * |
13244 | 843 gaim_savedstatus_find_transient_by_type_and_message(GaimStatusPrimitive type, |
844 const char *message) | |
13012 | 845 { |
846 GList *iter; | |
847 GaimSavedStatus *status; | |
848 | |
849 for (iter = saved_statuses; iter != NULL; iter = iter->next) | |
850 { | |
851 status = (GaimSavedStatus *)iter->data; | |
13244 | 852 if ((status->type == type) && gaim_savedstatus_is_transient(status) && |
13012 | 853 (((status->message == NULL) && (message == NULL)) || |
854 ((status->message != NULL) && (message != NULL) && !strcmp(status->message, message)))) | |
855 { | |
856 return status; | |
857 } | |
858 } | |
859 | |
860 return NULL; | |
861 } | |
862 | |
11651 | 863 gboolean |
864 gaim_savedstatus_is_transient(const GaimSavedStatus *saved_status) | |
865 { | |
12197 | 866 g_return_val_if_fail(saved_status != NULL, TRUE); |
867 | |
12125 | 868 return (saved_status->title == NULL); |
11651 | 869 } |
870 | |
10418 | 871 const char * |
10419 | 872 gaim_savedstatus_get_title(const GaimSavedStatus *saved_status) |
10418 | 873 { |
12690 | 874 const char *message; |
875 | |
12197 | 876 g_return_val_if_fail(saved_status != NULL, NULL); |
877 | |
12690 | 878 /* If we have a title then return it */ |
879 if (saved_status->title != NULL) | |
880 return saved_status->title; | |
881 | |
882 /* Otherwise, this is a transient status and we make up a title on the fly */ | |
883 message = gaim_savedstatus_get_message(saved_status); | |
12688 | 884 |
12781 | 885 if ((message == NULL) || (*message == '\0')) |
12690 | 886 { |
887 GaimStatusPrimitive primitive; | |
888 primitive = gaim_savedstatus_get_type(saved_status); | |
12814
f88f145884c0
[gaim-migrate @ 15162]
Richard Laager <rlaager@wiktel.com>
parents:
12781
diff
changeset
|
889 return gaim_primitive_get_name_from_type(primitive); |
12690 | 890 } |
891 else | |
892 { | |
893 char *stripped; | |
894 static char buf[64]; | |
895 stripped = gaim_markup_strip_html(message); | |
896 gaim_util_chrreplace(stripped, '\n', ' '); | |
897 strncpy(buf, stripped, sizeof(buf)); | |
898 buf[sizeof(buf) - 1] = '\0'; | |
899 if ((strlen(stripped) + 1) > sizeof(buf)) | |
12688 | 900 { |
12690 | 901 /* Truncate and ellipsize */ |
902 char *tmp = g_utf8_find_prev_char(buf, &buf[sizeof(buf) - 4]); | |
903 strcpy(tmp, "..."); | |
12688 | 904 } |
12690 | 905 g_free(stripped); |
906 return buf; | |
12688 | 907 } |
10418 | 908 } |
909 | |
910 GaimStatusPrimitive | |
10419 | 911 gaim_savedstatus_get_type(const GaimSavedStatus *saved_status) |
10418 | 912 { |
12197 | 913 g_return_val_if_fail(saved_status != NULL, GAIM_STATUS_OFFLINE); |
914 | |
10418 | 915 return saved_status->type; |
916 } | |
917 | |
918 const char * | |
10419 | 919 gaim_savedstatus_get_message(const GaimSavedStatus *saved_status) |
10418 | 920 { |
12197 | 921 g_return_val_if_fail(saved_status != NULL, NULL); |
922 | |
10418 | 923 return saved_status->message; |
924 } | |
925 | |
12125 | 926 time_t |
927 gaim_savedstatus_get_creation_time(const GaimSavedStatus *saved_status) | |
928 { | |
12197 | 929 g_return_val_if_fail(saved_status != NULL, 0); |
930 | |
12125 | 931 return saved_status->creation_time; |
932 } | |
933 | |
11651 | 934 gboolean |
935 gaim_savedstatus_has_substatuses(const GaimSavedStatus *saved_status) | |
936 { | |
12197 | 937 g_return_val_if_fail(saved_status != NULL, FALSE); |
938 | |
11651 | 939 return (saved_status->substatuses != NULL); |
940 } | |
941 | |
12056 | 942 GaimSavedStatusSub * |
12080 | 943 gaim_savedstatus_get_substatus(const GaimSavedStatus *saved_status, |
944 const GaimAccount *account) | |
12056 | 945 { |
946 GList *iter; | |
947 GaimSavedStatusSub *substatus; | |
948 | |
949 g_return_val_if_fail(saved_status != NULL, NULL); | |
950 g_return_val_if_fail(account != NULL, NULL); | |
951 | |
952 for (iter = saved_status->substatuses; iter != NULL; iter = iter->next) | |
953 { | |
954 substatus = iter->data; | |
955 if (substatus->account == account) | |
956 return substatus; | |
957 } | |
958 | |
959 return NULL; | |
960 } | |
961 | |
962 const GaimStatusType * | |
963 gaim_savedstatus_substatus_get_type(const GaimSavedStatusSub *substatus) | |
964 { | |
965 g_return_val_if_fail(substatus != NULL, NULL); | |
966 | |
967 return substatus->type; | |
968 } | |
969 | |
970 const char * | |
971 gaim_savedstatus_substatus_get_message(const GaimSavedStatusSub *substatus) | |
972 { | |
973 g_return_val_if_fail(substatus != NULL, NULL); | |
974 | |
975 return substatus->message; | |
976 } | |
977 | |
11724 | 978 void |
12125 | 979 gaim_savedstatus_activate(GaimSavedStatus *saved_status) |
11724 | 980 { |
11733 | 981 GList *accounts, *node; |
13387
d9dbb874a30c
[gaim-migrate @ 15760]
Richard Laager <rlaager@wiktel.com>
parents:
13246
diff
changeset
|
982 GaimSavedStatus *old = gaim_savedstatus_get_current(); |
11724 | 983 |
11727 | 984 g_return_if_fail(saved_status != NULL); |
985 | |
12729 | 986 /* Make sure our list of saved statuses remains sorted */ |
987 saved_status->lastused = time(NULL); | |
988 saved_status->usage_count++; | |
989 saved_statuses = g_list_remove(saved_statuses, saved_status); | |
990 saved_statuses = g_list_insert_sorted(saved_statuses, saved_status, saved_statuses_sort_func); | |
991 | |
11724 | 992 accounts = gaim_accounts_get_all_active(); |
11733 | 993 for (node = accounts; node != NULL; node = node->next) |
11724 | 994 { |
995 GaimAccount *account; | |
996 | |
11733 | 997 account = node->data; |
12857
e5f780a6137b
[gaim-migrate @ 15208]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
12814
diff
changeset
|
998 |
11724 | 999 gaim_savedstatus_activate_for_account(saved_status, account); |
1000 } | |
11733 | 1001 |
1002 g_list_free(accounts); | |
11954 | 1003 |
12125 | 1004 gaim_prefs_set_int("/core/savedstatus/current", |
1005 gaim_savedstatus_get_creation_time(saved_status)); | |
13387
d9dbb874a30c
[gaim-migrate @ 15760]
Richard Laager <rlaager@wiktel.com>
parents:
13246
diff
changeset
|
1006 |
d9dbb874a30c
[gaim-migrate @ 15760]
Richard Laager <rlaager@wiktel.com>
parents:
13246
diff
changeset
|
1007 gaim_signal_emit(gaim_savedstatuses_get_handle(), "savedstatus-changed", |
d9dbb874a30c
[gaim-migrate @ 15760]
Richard Laager <rlaager@wiktel.com>
parents:
13246
diff
changeset
|
1008 saved_status, old); |
d9dbb874a30c
[gaim-migrate @ 15760]
Richard Laager <rlaager@wiktel.com>
parents:
13246
diff
changeset
|
1009 |
11724 | 1010 } |
1011 | |
1012 void | |
1013 gaim_savedstatus_activate_for_account(const GaimSavedStatus *saved_status, | |
1014 GaimAccount *account) | |
1015 { | |
12056 | 1016 const GaimStatusType *status_type; |
1017 const GaimSavedStatusSub *substatus; | |
1018 const char *message = NULL; | |
11724 | 1019 |
11727 | 1020 g_return_if_fail(saved_status != NULL); |
1021 g_return_if_fail(account != NULL); | |
1022 | |
12080 | 1023 substatus = gaim_savedstatus_get_substatus(saved_status, account); |
12056 | 1024 if (substatus != NULL) |
1025 { | |
1026 status_type = substatus->type; | |
1027 message = substatus->message; | |
1028 } | |
1029 else | |
11724 | 1030 { |
12056 | 1031 status_type = gaim_account_get_status_type_with_primitive(account, saved_status->type); |
1032 if (status_type == NULL) | |
1033 return; | |
1034 message = saved_status->message; | |
1035 } | |
1036 | |
1037 if ((message != NULL) && | |
1038 (gaim_status_type_get_attr(status_type, "message"))) | |
1039 { | |
1040 gaim_account_set_status(account, gaim_status_type_get_id(status_type), | |
1041 TRUE, "message", message, NULL); | |
1042 } | |
1043 else | |
1044 { | |
1045 gaim_account_set_status(account, gaim_status_type_get_id(status_type), | |
1046 TRUE, NULL); | |
11724 | 1047 } |
1048 } | |
1049 | |
11318 | 1050 void * |
1051 gaim_savedstatuses_get_handle(void) | |
1052 { | |
1053 static int handle; | |
1054 | |
1055 return &handle; | |
1056 } | |
1057 | |
10418 | 1058 void |
1059 gaim_savedstatuses_init(void) | |
1060 { | |
13387
d9dbb874a30c
[gaim-migrate @ 15760]
Richard Laager <rlaager@wiktel.com>
parents:
13246
diff
changeset
|
1061 void *handle = gaim_savedstatuses_get_handle(); |
d9dbb874a30c
[gaim-migrate @ 15760]
Richard Laager <rlaager@wiktel.com>
parents:
13246
diff
changeset
|
1062 |
12125 | 1063 creation_times = g_hash_table_new(g_int_hash, g_int_equal); |
11975 | 1064 |
12125 | 1065 /* |
1066 * Using 0 as the creation_time is a special case. | |
1067 * If someone calls gaim_savedstatus_get_current() or | |
1068 * gaim_savedstatus_get_idleaway() and either of those functions | |
1069 * sees a creation_time of 0, then it will create a default | |
1070 * saved status and return that to the user. | |
1071 */ | |
1072 gaim_prefs_add_none("/core/savedstatus"); | |
1073 gaim_prefs_add_int("/core/savedstatus/current", 0); | |
12857
e5f780a6137b
[gaim-migrate @ 15208]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
12814
diff
changeset
|
1074 gaim_prefs_add_int("/core/savedstatus/startup", 0); |
e5f780a6137b
[gaim-migrate @ 15208]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
12814
diff
changeset
|
1075 gaim_prefs_add_bool("/core/savedstatus/startup_current_status", TRUE); |
12125 | 1076 gaim_prefs_add_int("/core/savedstatus/idleaway", 0); |
11975 | 1077 |
12125 | 1078 load_statuses(); |
13387
d9dbb874a30c
[gaim-migrate @ 15760]
Richard Laager <rlaager@wiktel.com>
parents:
13246
diff
changeset
|
1079 |
d9dbb874a30c
[gaim-migrate @ 15760]
Richard Laager <rlaager@wiktel.com>
parents:
13246
diff
changeset
|
1080 gaim_signal_register(handle, "savedstatus-changed", |
d9dbb874a30c
[gaim-migrate @ 15760]
Richard Laager <rlaager@wiktel.com>
parents:
13246
diff
changeset
|
1081 gaim_marshal_VOID__POINTER_POINTER, NULL, 2, |
d9dbb874a30c
[gaim-migrate @ 15760]
Richard Laager <rlaager@wiktel.com>
parents:
13246
diff
changeset
|
1082 gaim_value_new(GAIM_TYPE_SUBTYPE, |
d9dbb874a30c
[gaim-migrate @ 15760]
Richard Laager <rlaager@wiktel.com>
parents:
13246
diff
changeset
|
1083 GAIM_SUBTYPE_SAVEDSTATUS), |
d9dbb874a30c
[gaim-migrate @ 15760]
Richard Laager <rlaager@wiktel.com>
parents:
13246
diff
changeset
|
1084 gaim_value_new(GAIM_TYPE_SUBTYPE, |
d9dbb874a30c
[gaim-migrate @ 15760]
Richard Laager <rlaager@wiktel.com>
parents:
13246
diff
changeset
|
1085 GAIM_SUBTYPE_SAVEDSTATUS)); |
10418 | 1086 } |
1087 | |
1088 void | |
1089 gaim_savedstatuses_uninit(void) | |
1090 { | |
12327 | 1091 remove_old_transient_statuses(); |
1092 | |
10428 | 1093 if (save_timer != 0) |
10418 | 1094 { |
10428 | 1095 gaim_timeout_remove(save_timer); |
1096 save_timer = 0; | |
10418 | 1097 sync_statuses(); |
1098 } | |
1099 | |
1100 while (saved_statuses != NULL) { | |
12056 | 1101 GaimSavedStatus *saved_status = saved_statuses->data; |
1102 saved_statuses = g_list_remove(saved_statuses, saved_status); | |
12724 | 1103 free_saved_status(saved_status); |
10418 | 1104 } |
12125 | 1105 |
1106 g_hash_table_destroy(creation_times); | |
13387
d9dbb874a30c
[gaim-migrate @ 15760]
Richard Laager <rlaager@wiktel.com>
parents:
13246
diff
changeset
|
1107 |
d9dbb874a30c
[gaim-migrate @ 15760]
Richard Laager <rlaager@wiktel.com>
parents:
13246
diff
changeset
|
1108 gaim_signals_unregister_by_instance(gaim_savedstatuses_get_handle()); |
10418 | 1109 } |
12056 | 1110 |