src/status.c

branch
gaim
changeset 20470
77693555855f
parent 13071
b98e72d4089a
parent 20469
b2836a24d81e
child 20471
1966704b3e42
equal deleted inserted replaced
13071:b98e72d4089a 20470:77693555855f
1 /**
2 * @file status.c Status API
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 "blist.h"
28 #include "core.h"
29 #include "dbus-maybe.h"
30 #include "debug.h"
31 #include "notify.h"
32 #include "prefs.h"
33 #include "status.h"
34
35 /**
36 * A type of status.
37 */
38 struct _GaimStatusType
39 {
40 GaimStatusPrimitive primitive;
41
42 char *id;
43 char *name;
44 char *primary_attr_id;
45
46 gboolean saveable;
47 gboolean user_settable;
48 gboolean independent;
49
50 GList *attrs;
51 };
52
53 /**
54 * A status attribute.
55 */
56 struct _GaimStatusAttr
57 {
58 char *id;
59 char *name;
60 GaimValue *value_type;
61 };
62
63 /**
64 * A list of statuses.
65 */
66 struct _GaimPresence
67 {
68 GaimPresenceContext context;
69
70 gboolean idle;
71 time_t idle_time;
72 time_t login_time;
73
74 GList *statuses;
75 GHashTable *status_table;
76
77 GaimStatus *active_status;
78
79 union
80 {
81 GaimAccount *account;
82
83 struct
84 {
85 GaimConversation *conv;
86 char *user;
87
88 } chat;
89
90 struct
91 {
92 GaimAccount *account;
93 char *name;
94 size_t ref_count;
95 GList *buddies;
96
97 } buddy;
98
99 } u;
100 };
101
102 /**
103 * An active status.
104 */
105 struct _GaimStatus
106 {
107 GaimStatusType *type;
108 GaimPresence *presence;
109
110 const char *title;
111
112 gboolean active;
113
114 GHashTable *attr_values;
115 };
116
117 typedef struct
118 {
119 GaimAccount *account;
120 char *name;
121 } GaimStatusBuddyKey;
122
123 static int primitive_scores[] =
124 {
125 0, /* unset */
126 -500, /* offline */
127 100, /* available */
128 -75, /* unavailable */
129 -50, /* invisible */
130 -100, /* away */
131 -200, /* extended away */
132 -400, /* mobile */
133 -10, /* idle, special case. */
134 -5 /* idle time, special case. */
135 };
136
137 static GHashTable *buddy_presences = NULL;
138
139 #define SCORE_IDLE 8
140 #define SCORE_IDLE_TIME 9
141
142 /**************************************************************************
143 * GaimStatusPrimitive API
144 **************************************************************************/
145 static struct GaimStatusPrimitiveMap
146 {
147 GaimStatusPrimitive type;
148 const char *id;
149 const char *name;
150
151 } const status_primitive_map[] =
152 {
153 { GAIM_STATUS_UNSET, "unset", N_("Unset") },
154 { GAIM_STATUS_OFFLINE, "offline", N_("Offline") },
155 { GAIM_STATUS_AVAILABLE, "available", N_("Available") },
156 { GAIM_STATUS_UNAVAILABLE, "unavailable", N_("Unavailable") },
157 { GAIM_STATUS_INVISIBLE, "invisible", N_("Invisible") },
158 { GAIM_STATUS_AWAY, "away", N_("Away") },
159 { GAIM_STATUS_EXTENDED_AWAY, "extended_away", N_("Extended Away") },
160 { GAIM_STATUS_MOBILE, "mobile", N_("Mobile") }
161 };
162
163 const char *
164 gaim_primitive_get_id_from_type(GaimStatusPrimitive type)
165 {
166 int i;
167
168 for (i = 0; i < GAIM_STATUS_NUM_PRIMITIVES; i++)
169 {
170 if (type == status_primitive_map[i].type)
171 return status_primitive_map[i].id;
172 }
173
174 return status_primitive_map[0].id;
175 }
176
177 const char *
178 gaim_primitive_get_name_from_type(GaimStatusPrimitive type)
179 {
180 int i;
181
182 for (i = 0; i < GAIM_STATUS_NUM_PRIMITIVES; i++)
183 {
184 if (type == status_primitive_map[i].type)
185 return status_primitive_map[i].name;
186 }
187
188 return status_primitive_map[0].name;
189 }
190
191 GaimStatusPrimitive
192 gaim_primitive_get_type_from_id(const char *id)
193 {
194 int i;
195
196 g_return_val_if_fail(id != NULL, GAIM_STATUS_UNSET);
197
198 for (i = 0; i < GAIM_STATUS_NUM_PRIMITIVES; i++)
199 {
200 if (!strcmp(id, status_primitive_map[i].id))
201 return status_primitive_map[i].type;
202 }
203
204 return status_primitive_map[0].type;
205 }
206
207
208 /**************************************************************************
209 * GaimStatusType API
210 **************************************************************************/
211 GaimStatusType *
212 gaim_status_type_new_full(GaimStatusPrimitive primitive, const char *id,
213 const char *name, gboolean saveable,
214 gboolean user_settable, gboolean independent)
215 {
216 GaimStatusType *status_type;
217
218 g_return_val_if_fail(primitive != GAIM_STATUS_UNSET, NULL);
219
220 status_type = g_new0(GaimStatusType, 1);
221 GAIM_DBUS_REGISTER_POINTER(status_type, GaimStatusType);
222
223 status_type->primitive = primitive;
224 status_type->saveable = saveable;
225 status_type->user_settable = user_settable;
226 status_type->independent = independent;
227
228 if (id != NULL)
229 status_type->id = g_strdup(id);
230 else
231 status_type->id = g_strdup(gaim_primitive_get_id_from_type(primitive));
232
233 if (name != NULL)
234 status_type->name = g_strdup(name);
235 else
236 status_type->name = g_strdup(_(gaim_primitive_get_name_from_type(primitive)));
237
238 return status_type;
239 }
240
241 GaimStatusType *
242 gaim_status_type_new(GaimStatusPrimitive primitive, const char *id,
243 const char *name, gboolean user_settable)
244 {
245 g_return_val_if_fail(primitive != GAIM_STATUS_UNSET, NULL);
246
247 return gaim_status_type_new_full(primitive, id, name, FALSE,
248 user_settable, FALSE);
249 }
250
251 GaimStatusType *
252 gaim_status_type_new_with_attrs(GaimStatusPrimitive primitive,
253 const char *id, const char *name,
254 gboolean saveable, gboolean user_settable,
255 gboolean independent, const char *attr_id,
256 const char *attr_name, GaimValue *attr_value,
257 ...)
258 {
259 GaimStatusType *status_type;
260 va_list args;
261
262 g_return_val_if_fail(primitive != GAIM_STATUS_UNSET, NULL);
263 g_return_val_if_fail(attr_id != NULL, NULL);
264 g_return_val_if_fail(attr_name != NULL, NULL);
265 g_return_val_if_fail(attr_value != NULL, NULL);
266
267 status_type = gaim_status_type_new_full(primitive, id, name, saveable,
268 user_settable, independent);
269
270 /* Add the first attribute */
271 gaim_status_type_add_attr(status_type, attr_id, attr_name, attr_value);
272
273 va_start(args, attr_value);
274 gaim_status_type_add_attrs_vargs(status_type, args);
275 va_end(args);
276
277 return status_type;
278 }
279
280 void
281 gaim_status_type_destroy(GaimStatusType *status_type)
282 {
283 GList *l;
284
285 g_return_if_fail(status_type != NULL);
286
287 g_free(status_type->id);
288 g_free(status_type->name);
289
290 if (status_type->primary_attr_id != NULL)
291 g_free(status_type->primary_attr_id);
292
293 if (status_type->attrs != NULL)
294 {
295 for (l = status_type->attrs; l != NULL; l = l->next)
296 gaim_status_attr_destroy((GaimStatusAttr *)l->data);
297
298 g_list_free(status_type->attrs);
299 }
300
301 GAIM_DBUS_UNREGISTER_POINTER(status_type);
302 g_free(status_type);
303 }
304
305 void
306 gaim_status_type_set_primary_attr(GaimStatusType *status_type, const char *id)
307 {
308 g_return_if_fail(status_type != NULL);
309
310 if (status_type->primary_attr_id != NULL)
311 g_free(status_type->primary_attr_id);
312
313 status_type->primary_attr_id = (id == NULL ? NULL : g_strdup(id));
314 }
315
316 void
317 gaim_status_type_add_attr(GaimStatusType *status_type, const char *id,
318 const char *name, GaimValue *value)
319 {
320 GaimStatusAttr *attr;
321
322 g_return_if_fail(status_type != NULL);
323 g_return_if_fail(id != NULL);
324 g_return_if_fail(name != NULL);
325 g_return_if_fail(value != NULL);
326
327 attr = gaim_status_attr_new(id, name, value);
328
329 status_type->attrs = g_list_append(status_type->attrs, attr);
330 }
331
332 void
333 gaim_status_type_add_attrs_vargs(GaimStatusType *status_type, va_list args)
334 {
335 const char *id, *name;
336 GaimValue *value;
337
338 g_return_if_fail(status_type != NULL);
339
340 while ((id = va_arg(args, const char *)) != NULL)
341 {
342 name = va_arg(args, const char *);
343 g_return_if_fail(name != NULL);
344
345 value = va_arg(args, GaimValue *);
346 g_return_if_fail(value != NULL);
347
348 gaim_status_type_add_attr(status_type, id, name, value);
349 }
350 }
351
352 void
353 gaim_status_type_add_attrs(GaimStatusType *status_type, const char *id,
354 const char *name, GaimValue *value, ...)
355 {
356 va_list args;
357
358 g_return_if_fail(status_type != NULL);
359 g_return_if_fail(id != NULL);
360 g_return_if_fail(name != NULL);
361 g_return_if_fail(value != NULL);
362
363 /* Add the first attribute */
364 gaim_status_type_add_attr(status_type, id, name, value);
365
366 va_start(args, value);
367 gaim_status_type_add_attrs_vargs(status_type, args);
368 va_end(args);
369 }
370
371 GaimStatusPrimitive
372 gaim_status_type_get_primitive(const GaimStatusType *status_type)
373 {
374 g_return_val_if_fail(status_type != NULL, GAIM_STATUS_UNSET);
375
376 return status_type->primitive;
377 }
378
379 const char *
380 gaim_status_type_get_id(const GaimStatusType *status_type)
381 {
382 g_return_val_if_fail(status_type != NULL, NULL);
383
384 return status_type->id;
385 }
386
387 const char *
388 gaim_status_type_get_name(const GaimStatusType *status_type)
389 {
390 g_return_val_if_fail(status_type != NULL, NULL);
391
392 return status_type->name;
393 }
394
395 gboolean
396 gaim_status_type_is_saveable(const GaimStatusType *status_type)
397 {
398 g_return_val_if_fail(status_type != NULL, FALSE);
399
400 return status_type->saveable;
401 }
402
403 gboolean
404 gaim_status_type_is_user_settable(const GaimStatusType *status_type)
405 {
406 g_return_val_if_fail(status_type != NULL, FALSE);
407
408 return status_type->user_settable;
409 }
410
411 gboolean
412 gaim_status_type_is_independent(const GaimStatusType *status_type)
413 {
414 g_return_val_if_fail(status_type != NULL, FALSE);
415
416 return status_type->independent;
417 }
418
419 gboolean
420 gaim_status_type_is_exclusive(const GaimStatusType *status_type)
421 {
422 g_return_val_if_fail(status_type != NULL, FALSE);
423
424 return !status_type->independent;
425 }
426
427 gboolean
428 gaim_status_type_is_available(const GaimStatusType *status_type)
429 {
430 GaimStatusPrimitive primitive;
431
432 g_return_val_if_fail(status_type != NULL, FALSE);
433
434 primitive = gaim_status_type_get_primitive(status_type);
435
436 return (primitive == GAIM_STATUS_AVAILABLE);
437 }
438
439 const char *
440 gaim_status_type_get_primary_attr(const GaimStatusType *status_type)
441 {
442 g_return_val_if_fail(status_type != NULL, NULL);
443
444 return status_type->primary_attr_id;
445 }
446
447 GaimStatusAttr *
448 gaim_status_type_get_attr(const GaimStatusType *status_type, const char *id)
449 {
450 GList *l;
451
452 g_return_val_if_fail(status_type != NULL, NULL);
453 g_return_val_if_fail(id != NULL, NULL);
454
455 for (l = status_type->attrs; l != NULL; l = l->next)
456 {
457 GaimStatusAttr *attr = (GaimStatusAttr *)l->data;
458
459 if (!strcmp(gaim_status_attr_get_id(attr), id))
460 return attr;
461 }
462
463 return NULL;
464 }
465
466 const GList *
467 gaim_status_type_get_attrs(const GaimStatusType *status_type)
468 {
469 g_return_val_if_fail(status_type != NULL, NULL);
470
471 return status_type->attrs;
472 }
473
474 const GaimStatusType *
475 gaim_status_type_find_with_id(GList *status_types, const char *id)
476 {
477 GaimStatusType *status_type;
478
479 g_return_val_if_fail(id != NULL, NULL);
480
481 while (status_types != NULL)
482 {
483 status_type = status_types->data;
484
485 if (!strcmp(id, status_type->id))
486 return status_type;
487
488 status_types = status_types->next;
489 }
490
491 return NULL;
492 }
493
494
495 /**************************************************************************
496 * GaimStatusAttr API
497 **************************************************************************/
498 GaimStatusAttr *
499 gaim_status_attr_new(const char *id, const char *name, GaimValue *value_type)
500 {
501 GaimStatusAttr *attr;
502
503 g_return_val_if_fail(id != NULL, NULL);
504 g_return_val_if_fail(name != NULL, NULL);
505 g_return_val_if_fail(value_type != NULL, NULL);
506
507 attr = g_new0(GaimStatusAttr, 1);
508 GAIM_DBUS_REGISTER_POINTER(attr, GaimStatusAttr);
509
510 attr->id = g_strdup(id);
511 attr->name = g_strdup(name);
512 attr->value_type = value_type;
513
514 return attr;
515 }
516
517 void
518 gaim_status_attr_destroy(GaimStatusAttr *attr)
519 {
520 g_return_if_fail(attr != NULL);
521
522 g_free(attr->id);
523 g_free(attr->name);
524
525 gaim_value_destroy(attr->value_type);
526
527 GAIM_DBUS_UNREGISTER_POINTER(attr);
528 g_free(attr);
529 }
530
531 const char *
532 gaim_status_attr_get_id(const GaimStatusAttr *attr)
533 {
534 g_return_val_if_fail(attr != NULL, NULL);
535
536 return attr->id;
537 }
538
539 const char *
540 gaim_status_attr_get_name(const GaimStatusAttr *attr)
541 {
542 g_return_val_if_fail(attr != NULL, NULL);
543
544 return attr->name;
545 }
546
547 GaimValue *
548 gaim_status_attr_get_value(const GaimStatusAttr *attr)
549 {
550 g_return_val_if_fail(attr != NULL, NULL);
551
552 return attr->value_type;
553 }
554
555
556 /**************************************************************************
557 * GaimStatus API
558 **************************************************************************/
559 GaimStatus *
560 gaim_status_new(GaimStatusType *status_type, GaimPresence *presence)
561 {
562 GaimStatus *status;
563 const GList *l;
564
565 g_return_val_if_fail(status_type != NULL, NULL);
566 g_return_val_if_fail(presence != NULL, NULL);
567
568 status = g_new0(GaimStatus, 1);
569 GAIM_DBUS_REGISTER_POINTER(status, GaimStatus);
570
571 status->type = status_type;
572 status->presence = presence;
573
574 status->attr_values =
575 g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
576 (GDestroyNotify)gaim_value_destroy);
577
578 for (l = gaim_status_type_get_attrs(status_type); l != NULL; l = l->next)
579 {
580 GaimStatusAttr *attr = (GaimStatusAttr *)l->data;
581 GaimValue *value = gaim_status_attr_get_value(attr);
582 GaimValue *new_value = gaim_value_dup(value);
583
584 g_hash_table_insert(status->attr_values,
585 g_strdup(gaim_status_attr_get_id(attr)),
586 new_value);
587 }
588
589 return status;
590 }
591
592 /*
593 * TODO: If the GaimStatus is in a GaimPresence, then
594 * remove it from the GaimPresence?
595 */
596 void
597 gaim_status_destroy(GaimStatus *status)
598 {
599 g_return_if_fail(status != NULL);
600
601 g_hash_table_destroy(status->attr_values);
602
603 GAIM_DBUS_UNREGISTER_POINTER(status);
604 g_free(status);
605 }
606
607 static void
608 notify_buddy_status_update(GaimBuddy *buddy, GaimPresence *presence,
609 GaimStatus *old_status, GaimStatus *new_status)
610 {
611 GaimBlistUiOps *ops = gaim_blist_get_ui_ops();
612
613 if (gaim_prefs_get_bool("/core/logging/log_system"))
614 {
615 time_t current_time = time(NULL);
616 const char *buddy_alias = gaim_buddy_get_alias(buddy);
617 char *tmp = NULL;
618
619 if (gaim_status_is_available(new_status))
620 {
621 if (((old_status == NULL) || !gaim_status_is_online(old_status)))
622 {
623 tmp = g_strdup_printf(_("%s signed on"), buddy_alias);
624 }
625 else if (!gaim_status_is_available(old_status))
626 {
627 tmp = g_strdup_printf(_("%s came back"), buddy_alias);
628 }
629 }
630 else if ((old_status != NULL) && gaim_status_is_available(old_status))
631 {
632 if (!gaim_status_is_online(new_status))
633 {
634 tmp = g_strdup_printf(_("%s signed off"), buddy_alias);
635 }
636 else if (!gaim_status_is_available(new_status))
637 {
638 tmp = g_strdup_printf(_("%s went away"), buddy_alias);
639 }
640 }
641
642 if (tmp != NULL)
643 {
644 GaimLog *log = gaim_account_get_log(buddy->account);
645
646 gaim_log_write(log, GAIM_MESSAGE_SYSTEM, buddy_alias,
647 current_time, tmp);
648 g_free(tmp);
649 }
650 }
651
652 if (ops != NULL && ops->update != NULL)
653 ops->update(gaim_get_blist(), (GaimBlistNode*)buddy);
654 }
655
656 static void
657 notify_status_update(GaimPresence *presence, GaimStatus *old_status,
658 GaimStatus *new_status)
659 {
660 GaimPresenceContext context = gaim_presence_get_context(presence);
661
662 if (context == GAIM_PRESENCE_CONTEXT_ACCOUNT)
663 {
664 GaimAccount *account = gaim_presence_get_account(presence);
665 GaimAccountUiOps *ops = gaim_accounts_get_ui_ops();
666
667 if (gaim_account_get_enabled(account, gaim_core_get_ui()))
668 gaim_prpl_change_account_status(account, old_status, new_status);
669
670 if (ops != NULL && ops->status_changed != NULL)
671 {
672 ops->status_changed(account, new_status);
673 }
674 }
675 else if (context == GAIM_PRESENCE_CONTEXT_BUDDY)
676 {
677 const GList *l;
678
679 for (l = gaim_presence_get_buddies(presence); l != NULL; l = l->next)
680 {
681 notify_buddy_status_update((GaimBuddy *)l->data, presence,
682 old_status, new_status);
683 }
684 }
685 }
686
687 static void
688 status_has_changed(GaimStatus *status)
689 {
690 GaimPresence *presence;
691 GaimStatus *old_status;
692
693 presence = gaim_status_get_presence(status);
694
695 /*
696 * If this status is exclusive, then we must be setting it to "active."
697 * Since we are setting it to active, we want to set the currently
698 * active status to "inactive."
699 */
700 if (gaim_status_is_exclusive(status))
701 {
702 old_status = gaim_presence_get_active_status(presence);
703 if (old_status != NULL && (old_status != status))
704 old_status->active = FALSE;
705 presence->active_status = status;
706 }
707 else
708 old_status = NULL;
709
710 notify_status_update(presence, old_status, status);
711 }
712
713 void
714 gaim_status_set_active(GaimStatus *status, gboolean active)
715 {
716 gaim_status_set_active_with_attrs(status, active, NULL);
717 }
718
719 /*
720 * This used to parse the va_list directly, but now it creates a GList
721 * and passes it to gaim_status_set_active_with_attrs_list(). That
722 * function was created because accounts.c needs to pass a GList of
723 * attributes to the status API.
724 */
725 void
726 gaim_status_set_active_with_attrs(GaimStatus *status, gboolean active, va_list args)
727 {
728 GList *attrs = NULL;
729 const gchar *id;
730 gpointer data;
731
732 if (args != NULL)
733 {
734 while ((id = va_arg(args, const char *)) != NULL)
735 {
736 attrs = g_list_append(attrs, (char *)id);
737 data = va_arg(args, void *);
738 attrs = g_list_append(attrs, data);
739 }
740 }
741 gaim_status_set_active_with_attrs_list(status, active, attrs);
742 g_list_free(attrs);
743 }
744
745 void
746 gaim_status_set_active_with_attrs_list(GaimStatus *status, gboolean active,
747 const GList *attrs)
748 {
749 gboolean changed = FALSE;
750 const gchar *id;
751
752 g_return_if_fail(status != NULL);
753
754 if (!active && gaim_status_is_exclusive(status))
755 {
756 gaim_debug_error("status",
757 "Cannot deactivate an exclusive status (%s).\n",
758 gaim_status_get_id(status));
759 return;
760 }
761
762 if (status->active != active)
763 {
764 changed = TRUE;
765 }
766
767 status->active = active;
768
769 /* Set any attributes */
770 while (attrs)
771 {
772 GaimValue *value;
773
774 id = attrs->data;
775 attrs = attrs->next;
776 value = gaim_status_get_attr_value(status, id);
777 if (value == NULL)
778 {
779 gaim_debug_warning("status", "The attribute \"%s\" on the status \"%s\" is "
780 "not supported.\n", id, status->type->name);
781 /* Skip over the data and move on to the next attribute */
782 attrs = attrs->next;
783 continue;
784 }
785
786 if (value->type == GAIM_TYPE_STRING)
787 {
788 const gchar *string_data = attrs->data;
789 attrs = attrs->next;
790 if (((string_data == NULL) && (value->data.string_data == NULL)) ||
791 ((string_data != NULL) && (value->data.string_data != NULL) &&
792 !strcmp(string_data, value->data.string_data)))
793 {
794 continue;
795 }
796 gaim_status_set_attr_string(status, id, string_data);
797 changed = TRUE;
798 }
799 else if (value->type == GAIM_TYPE_INT)
800 {
801 int int_data = GPOINTER_TO_INT(attrs->data);
802 attrs = attrs->next;
803 if (int_data == value->data.int_data)
804 continue;
805 gaim_status_set_attr_int(status, id, int_data);
806 changed = TRUE;
807 }
808 else if (value->type == GAIM_TYPE_BOOLEAN)
809 {
810 gboolean boolean_data = GPOINTER_TO_INT(attrs->data);
811 attrs = attrs->next;
812 if (boolean_data == value->data.boolean_data)
813 continue;
814 gaim_status_set_attr_int(status, id, boolean_data);
815 changed = TRUE;
816 }
817 else
818 {
819 /* We don't know what the data is--skip over it */
820 attrs = attrs->next;
821 }
822 }
823
824 if (!changed)
825 return;
826 status_has_changed(status);
827 }
828
829 void
830 gaim_status_set_attr_boolean(GaimStatus *status, const char *id,
831 gboolean value)
832 {
833 GaimStatusType *status_type;
834 GaimValue *attr_value;
835
836 g_return_if_fail(status != NULL);
837 g_return_if_fail(id != NULL);
838
839 status_type = gaim_status_get_type(status);
840
841 /* Make sure this attribute exists and is the correct type. */
842 attr_value = gaim_status_get_attr_value(status, id);
843 g_return_if_fail(attr_value != NULL);
844 g_return_if_fail(gaim_value_get_type(attr_value) == GAIM_TYPE_BOOLEAN);
845
846 gaim_value_set_boolean(attr_value, value);
847 }
848
849 void
850 gaim_status_set_attr_int(GaimStatus *status, const char *id, int value)
851 {
852 GaimStatusType *status_type;
853 GaimValue *attr_value;
854
855 g_return_if_fail(status != NULL);
856 g_return_if_fail(id != NULL);
857
858 status_type = gaim_status_get_type(status);
859
860 /* Make sure this attribute exists and is the correct type. */
861 attr_value = gaim_status_get_attr_value(status, id);
862 g_return_if_fail(attr_value != NULL);
863 g_return_if_fail(gaim_value_get_type(attr_value) == GAIM_TYPE_INT);
864
865 gaim_value_set_int(attr_value, value);
866 }
867
868 void
869 gaim_status_set_attr_string(GaimStatus *status, const char *id,
870 const char *value)
871 {
872 GaimStatusType *status_type;
873 GaimValue *attr_value;
874
875 g_return_if_fail(status != NULL);
876 g_return_if_fail(id != NULL);
877
878 status_type = gaim_status_get_type(status);
879
880 /* Make sure this attribute exists and is the correct type. */
881 attr_value = gaim_status_get_attr_value(status, id);
882 /* This used to be g_return_if_fail, but it's failing a LOT, so
883 * let's generate a log error for now. */
884 /* g_return_if_fail(attr_value != NULL); */
885 if (attr_value == NULL) {
886 gaim_debug_error("status",
887 "Attempted to set status attribute '%s' for "
888 "status '%s', which is not legal. Fix "
889 "this!\n", id,
890 gaim_status_type_get_name(gaim_status_get_type(status)));
891 return;
892
893 }
894 g_return_if_fail(gaim_value_get_type(attr_value) == GAIM_TYPE_STRING);
895
896 gaim_value_set_string(attr_value, value);
897 }
898
899 GaimStatusType *
900 gaim_status_get_type(const GaimStatus *status)
901 {
902 g_return_val_if_fail(status != NULL, NULL);
903
904 return status->type;
905 }
906
907 GaimPresence *
908 gaim_status_get_presence(const GaimStatus *status)
909 {
910 g_return_val_if_fail(status != NULL, NULL);
911
912 return status->presence;
913 }
914
915 const char *
916 gaim_status_get_id(const GaimStatus *status)
917 {
918 g_return_val_if_fail(status != NULL, NULL);
919
920 return gaim_status_type_get_id(gaim_status_get_type(status));
921 }
922
923 const char *
924 gaim_status_get_name(const GaimStatus *status)
925 {
926 g_return_val_if_fail(status != NULL, NULL);
927
928 return gaim_status_type_get_name(gaim_status_get_type(status));
929 }
930
931 gboolean
932 gaim_status_is_independent(const GaimStatus *status)
933 {
934 g_return_val_if_fail(status != NULL, FALSE);
935
936 return gaim_status_type_is_independent(gaim_status_get_type(status));
937 }
938
939 gboolean
940 gaim_status_is_exclusive(const GaimStatus *status)
941 {
942 g_return_val_if_fail(status != NULL, FALSE);
943
944 return gaim_status_type_is_exclusive(gaim_status_get_type(status));
945 }
946
947 gboolean
948 gaim_status_is_available(const GaimStatus *status)
949 {
950 g_return_val_if_fail(status != NULL, FALSE);
951
952 return gaim_status_type_is_available(gaim_status_get_type(status));
953 }
954
955 gboolean
956 gaim_status_is_active(const GaimStatus *status)
957 {
958 g_return_val_if_fail(status != NULL, FALSE);
959
960 return status->active;
961 }
962
963 gboolean
964 gaim_status_is_online(const GaimStatus *status)
965 {
966 GaimStatusPrimitive primitive;
967
968 g_return_val_if_fail( status != NULL, FALSE);
969
970 primitive = gaim_status_type_get_primitive(gaim_status_get_type(status));
971
972 return (primitive != GAIM_STATUS_UNSET &&
973 primitive != GAIM_STATUS_OFFLINE);
974 }
975
976 GaimValue *
977 gaim_status_get_attr_value(const GaimStatus *status, const char *id)
978 {
979 g_return_val_if_fail(status != NULL, NULL);
980 g_return_val_if_fail(id != NULL, NULL);
981
982 return (GaimValue *)g_hash_table_lookup(status->attr_values, id);
983 }
984
985 gboolean
986 gaim_status_get_attr_boolean(const GaimStatus *status, const char *id)
987 {
988 const GaimValue *value;
989
990 g_return_val_if_fail(status != NULL, FALSE);
991 g_return_val_if_fail(id != NULL, FALSE);
992
993 if ((value = gaim_status_get_attr_value(status, id)) == NULL)
994 return FALSE;
995
996 g_return_val_if_fail(gaim_value_get_type(value) == GAIM_TYPE_BOOLEAN, FALSE);
997
998 return gaim_value_get_boolean(value);
999 }
1000
1001 int
1002 gaim_status_get_attr_int(const GaimStatus *status, const char *id)
1003 {
1004 const GaimValue *value;
1005
1006 g_return_val_if_fail(status != NULL, 0);
1007 g_return_val_if_fail(id != NULL, 0);
1008
1009 if ((value = gaim_status_get_attr_value(status, id)) == NULL)
1010 return 0;
1011
1012 g_return_val_if_fail(gaim_value_get_type(value) == GAIM_TYPE_INT, 0);
1013
1014 return gaim_value_get_int(value);
1015 }
1016
1017 const char *
1018 gaim_status_get_attr_string(const GaimStatus *status, const char *id)
1019 {
1020 const GaimValue *value;
1021
1022 g_return_val_if_fail(status != NULL, NULL);
1023 g_return_val_if_fail(id != NULL, NULL);
1024
1025 if ((value = gaim_status_get_attr_value(status, id)) == NULL)
1026 return NULL;
1027
1028 g_return_val_if_fail(gaim_value_get_type(value) == GAIM_TYPE_STRING, NULL);
1029
1030 return gaim_value_get_string(value);
1031 }
1032
1033 gint
1034 gaim_status_compare(const GaimStatus *status1, const GaimStatus *status2)
1035 {
1036 GaimStatusType *type1, *type2;
1037 int score1 = 0, score2 = 0;
1038
1039 if ((status1 == NULL && status2 == NULL) ||
1040 (status1 == status2))
1041 {
1042 return 0;
1043 }
1044 else if (status1 == NULL)
1045 return 1;
1046 else if (status2 == NULL)
1047 return -1;
1048
1049 type1 = gaim_status_get_type(status1);
1050 type2 = gaim_status_get_type(status2);
1051
1052 if (gaim_status_is_active(status1))
1053 score1 = primitive_scores[gaim_status_type_get_primitive(type1)];
1054
1055 if (gaim_status_is_active(status2))
1056 score2 = primitive_scores[gaim_status_type_get_primitive(type2)];
1057
1058 if (score1 > score2)
1059 return -1;
1060 else if (score1 < score2)
1061 return 1;
1062
1063 return 0;
1064 }
1065
1066
1067 /**************************************************************************
1068 * GaimPresence API
1069 **************************************************************************/
1070 GaimPresence *
1071 gaim_presence_new(GaimPresenceContext context)
1072 {
1073 GaimPresence *presence;
1074
1075 g_return_val_if_fail(context != GAIM_PRESENCE_CONTEXT_UNSET, NULL);
1076
1077 presence = g_new0(GaimPresence, 1);
1078 GAIM_DBUS_REGISTER_POINTER(presence, GaimPresence);
1079
1080 presence->context = context;
1081
1082 presence->status_table =
1083 g_hash_table_new_full(g_str_hash, g_str_equal,
1084 g_free, NULL);
1085
1086 return presence;
1087 }
1088
1089 GaimPresence *
1090 gaim_presence_new_for_account(GaimAccount *account)
1091 {
1092 GaimPresence *presence = NULL;
1093 g_return_val_if_fail(account != NULL, NULL);
1094
1095 presence = gaim_presence_new(GAIM_PRESENCE_CONTEXT_ACCOUNT);
1096 presence->u.account = account;
1097 presence->statuses = gaim_prpl_get_statuses(account, presence);
1098
1099 return presence;
1100 }
1101
1102 GaimPresence *
1103 gaim_presence_new_for_conv(GaimConversation *conv)
1104 {
1105 GaimPresence *presence;
1106
1107 g_return_val_if_fail(conv != NULL, NULL);
1108
1109 presence = gaim_presence_new(GAIM_PRESENCE_CONTEXT_CONV);
1110 presence->u.chat.conv = conv;
1111 /* presence->statuses = gaim_prpl_get_statuses(conv->account, presence); ? */
1112
1113 return presence;
1114 }
1115
1116 GaimPresence *
1117 gaim_presence_new_for_buddy(GaimBuddy *buddy)
1118 {
1119 GaimPresence *presence;
1120 GaimStatusBuddyKey *key;
1121 GaimAccount *account;
1122
1123 g_return_val_if_fail(buddy != NULL, NULL);
1124 account = buddy->account;
1125
1126 key = g_new0(GaimStatusBuddyKey, 1);
1127 key->account = buddy->account;
1128 key->name = g_strdup(buddy->name);
1129
1130 presence = g_hash_table_lookup(buddy_presences, key);
1131 if (presence == NULL)
1132 {
1133 presence = gaim_presence_new(GAIM_PRESENCE_CONTEXT_BUDDY);
1134
1135 presence->u.buddy.name = g_strdup(buddy->name);
1136 presence->u.buddy.account = buddy->account;
1137 presence->statuses = gaim_prpl_get_statuses(buddy->account, presence);
1138
1139 g_hash_table_insert(buddy_presences, key, presence);
1140 }
1141 else
1142 {
1143 g_free(key->name);
1144 g_free(key);
1145 }
1146
1147 presence->u.buddy.ref_count++;
1148 presence->u.buddy.buddies = g_list_append(presence->u.buddy.buddies,
1149 buddy);
1150
1151 return presence;
1152 }
1153
1154 void
1155 gaim_presence_destroy(GaimPresence *presence)
1156 {
1157 g_return_if_fail(presence != NULL);
1158
1159 if (gaim_presence_get_context(presence) == GAIM_PRESENCE_CONTEXT_BUDDY)
1160 {
1161 GaimStatusBuddyKey key;
1162
1163 if(presence->u.buddy.ref_count != 0)
1164 return;
1165
1166 key.account = presence->u.buddy.account;
1167 key.name = presence->u.buddy.name;
1168
1169 g_hash_table_remove(buddy_presences, &key);
1170
1171 if (presence->u.buddy.name != NULL)
1172 g_free(presence->u.buddy.name);
1173 }
1174 else if (gaim_presence_get_context(presence) == GAIM_PRESENCE_CONTEXT_CONV)
1175 {
1176 if (presence->u.chat.user != NULL)
1177 g_free(presence->u.chat.user);
1178 }
1179
1180 if (presence->statuses != NULL) {
1181 g_list_foreach(presence->statuses, (GFunc)gaim_status_destroy, NULL);
1182 g_list_free(presence->statuses);
1183 }
1184
1185 g_hash_table_destroy(presence->status_table);
1186
1187 GAIM_DBUS_UNREGISTER_POINTER(presence);
1188 g_free(presence);
1189 }
1190
1191 /*
1192 * TODO: Maybe we should cal gaim_presence_destroy() after we
1193 * decrement the ref count? I don't see why we should
1194 * make other places do it manually when we can do it here.
1195 */
1196 void
1197 gaim_presence_remove_buddy(GaimPresence *presence, GaimBuddy *buddy)
1198 {
1199 g_return_if_fail(presence != NULL);
1200 g_return_if_fail(buddy != NULL);
1201 g_return_if_fail(gaim_presence_get_context(presence) ==
1202 GAIM_PRESENCE_CONTEXT_BUDDY);
1203
1204 if (g_list_find(presence->u.buddy.buddies, buddy) != NULL)
1205 {
1206 presence->u.buddy.buddies = g_list_remove(presence->u.buddy.buddies,
1207 buddy);
1208 presence->u.buddy.ref_count--;
1209 }
1210 }
1211
1212 void
1213 gaim_presence_add_status(GaimPresence *presence, GaimStatus *status)
1214 {
1215 g_return_if_fail(presence != NULL);
1216 g_return_if_fail(status != NULL);
1217
1218 presence->statuses = g_list_append(presence->statuses, status);
1219
1220 g_hash_table_insert(presence->status_table,
1221 g_strdup(gaim_status_get_id(status)), status);
1222 }
1223
1224 void
1225 gaim_presence_add_list(GaimPresence *presence, const GList *source_list)
1226 {
1227 const GList *l;
1228
1229 g_return_if_fail(presence != NULL);
1230 g_return_if_fail(source_list != NULL);
1231
1232 for (l = source_list; l != NULL; l = l->next)
1233 gaim_presence_add_status(presence, (GaimStatus *)l->data);
1234 }
1235
1236 void
1237 gaim_presence_set_status_active(GaimPresence *presence, const char *status_id,
1238 gboolean active)
1239 {
1240 GaimStatus *status;
1241
1242 g_return_if_fail(presence != NULL);
1243 g_return_if_fail(status_id != NULL);
1244
1245 status = gaim_presence_get_status(presence, status_id);
1246
1247 g_return_if_fail(status != NULL);
1248 /* TODO: Should we do the following? */
1249 /* g_return_if_fail(active == status->active); */
1250
1251 if (gaim_status_is_exclusive(status))
1252 {
1253 if (!active)
1254 {
1255 gaim_debug_warning("status",
1256 "Attempted to set a non-independent status "
1257 "(%s) inactive. Only independent statuses "
1258 "can be specifically marked inactive.",
1259 status_id);
1260 return;
1261 }
1262 }
1263
1264 gaim_status_set_active(status, active);
1265 }
1266
1267 void
1268 gaim_presence_switch_status(GaimPresence *presence, const char *status_id)
1269 {
1270 gaim_presence_set_status_active(presence, status_id, TRUE);
1271 }
1272
1273 static void
1274 update_buddy_idle(GaimBuddy *buddy, GaimPresence *presence,
1275 time_t current_time, gboolean old_idle, gboolean idle)
1276 {
1277 GaimBlistUiOps *ops = gaim_get_blist()->ui_ops;
1278
1279 if (!old_idle && idle)
1280 {
1281 if (gaim_prefs_get_bool("/core/logging/log_system"))
1282 {
1283 GaimLog *log = gaim_account_get_log(buddy->account);
1284 char *tmp = g_strdup_printf(_("%s became idle"),
1285 gaim_buddy_get_alias(buddy));
1286
1287 gaim_log_write(log, GAIM_MESSAGE_SYSTEM,
1288 gaim_buddy_get_alias(buddy), current_time, tmp);
1289 g_free(tmp);
1290 }
1291 }
1292 else if (old_idle && !idle)
1293 {
1294 if (gaim_prefs_get_bool("/core/logging/log_system"))
1295 {
1296 GaimLog *log = gaim_account_get_log(buddy->account);
1297 char *tmp = g_strdup_printf(_("%s became unidle"),
1298 gaim_buddy_get_alias(buddy));
1299
1300 gaim_log_write(log, GAIM_MESSAGE_SYSTEM,
1301 gaim_buddy_get_alias(buddy), current_time, tmp);
1302 g_free(tmp);
1303 }
1304 }
1305
1306 if (old_idle != idle)
1307 gaim_signal_emit(gaim_blist_get_handle(), "buddy-idle-changed", buddy,
1308 old_idle, idle);
1309
1310 gaim_contact_invalidate_priority_buddy(gaim_buddy_get_contact(buddy));
1311
1312 /* Should this be done here? It'd perhaps make more sense to
1313 * connect to buddy-[un]idle signals and update from there
1314 */
1315
1316 if (ops != NULL && ops->update != NULL)
1317 ops->update(gaim_get_blist(), (GaimBlistNode *)buddy);
1318 }
1319
1320 void
1321 gaim_presence_set_idle(GaimPresence *presence, gboolean idle, time_t idle_time)
1322 {
1323 gboolean old_idle;
1324
1325 g_return_if_fail(presence != NULL);
1326
1327 if (presence->idle == idle && presence->idle_time == idle_time)
1328 return;
1329
1330 old_idle = presence->idle;
1331 presence->idle = idle;
1332 presence->idle_time = (idle ? idle_time : 0);
1333
1334 if (gaim_presence_get_context(presence) == GAIM_PRESENCE_CONTEXT_BUDDY)
1335 {
1336 const GList *l;
1337 time_t current_time = time(NULL);
1338
1339 for (l = gaim_presence_get_buddies(presence); l != NULL; l = l->next)
1340 {
1341 update_buddy_idle((GaimBuddy *)l->data, presence, current_time,
1342 old_idle, idle);
1343 }
1344 }
1345 else if(gaim_presence_get_context(presence) == GAIM_PRESENCE_CONTEXT_ACCOUNT)
1346 {
1347 GaimAccount *account;
1348 GaimConnection *gc;
1349 GaimPluginProtocolInfo *prpl_info = NULL;
1350
1351 account = gaim_presence_get_account(presence);
1352
1353 if (gaim_prefs_get_bool("/core/logging/log_system"))
1354 {
1355 GaimLog *log = gaim_account_get_log(account);
1356 char *msg;
1357
1358 if (idle)
1359 msg = g_strdup_printf(_("+++ %s became idle"), gaim_account_get_username(account));
1360 else
1361 msg = g_strdup_printf(_("+++ %s became unidle"), gaim_account_get_username(account));
1362 gaim_log_write(log, GAIM_MESSAGE_SYSTEM,
1363 gaim_account_get_username(account),
1364 idle_time, msg);
1365 g_free(msg);
1366 }
1367
1368 gc = gaim_account_get_connection(account);
1369
1370 if (gc != NULL && gc->prpl != NULL)
1371 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl);
1372
1373 if (prpl_info && g_list_find(gaim_connections_get_all(), gc) &&
1374 prpl_info->set_idle)
1375 prpl_info->set_idle(gc, time(NULL) - idle_time);
1376 }
1377 }
1378
1379 void
1380 gaim_presence_set_login_time(GaimPresence *presence, time_t login_time)
1381 {
1382 g_return_if_fail(presence != NULL);
1383
1384 if (presence->login_time == login_time)
1385 return;
1386
1387 presence->login_time = login_time;
1388 }
1389
1390 GaimPresenceContext
1391 gaim_presence_get_context(const GaimPresence *presence)
1392 {
1393 g_return_val_if_fail(presence != NULL, GAIM_PRESENCE_CONTEXT_UNSET);
1394
1395 return presence->context;
1396 }
1397
1398 GaimAccount *
1399 gaim_presence_get_account(const GaimPresence *presence)
1400 {
1401 GaimPresenceContext context;
1402
1403 g_return_val_if_fail(presence != NULL, NULL);
1404
1405 context = gaim_presence_get_context(presence);
1406
1407 g_return_val_if_fail(context == GAIM_PRESENCE_CONTEXT_ACCOUNT ||
1408 context == GAIM_PRESENCE_CONTEXT_BUDDY, NULL);
1409
1410 return presence->u.account;
1411 }
1412
1413 GaimConversation *
1414 gaim_presence_get_conversation(const GaimPresence *presence)
1415 {
1416 g_return_val_if_fail(presence != NULL, NULL);
1417 g_return_val_if_fail(gaim_presence_get_context(presence) ==
1418 GAIM_PRESENCE_CONTEXT_CONV, NULL);
1419
1420 return presence->u.chat.conv;
1421 }
1422
1423 const char *
1424 gaim_presence_get_chat_user(const GaimPresence *presence)
1425 {
1426 g_return_val_if_fail(presence != NULL, NULL);
1427 g_return_val_if_fail(gaim_presence_get_context(presence) ==
1428 GAIM_PRESENCE_CONTEXT_CONV, NULL);
1429
1430 return presence->u.chat.user;
1431 }
1432
1433 const GList *
1434 gaim_presence_get_buddies(const GaimPresence *presence)
1435 {
1436 g_return_val_if_fail(presence != NULL, NULL);
1437 g_return_val_if_fail(gaim_presence_get_context(presence) ==
1438 GAIM_PRESENCE_CONTEXT_BUDDY, NULL);
1439
1440 return presence->u.buddy.buddies;
1441 }
1442
1443 const GList *
1444 gaim_presence_get_statuses(const GaimPresence *presence)
1445 {
1446 g_return_val_if_fail(presence != NULL, NULL);
1447
1448 return presence->statuses;
1449 }
1450
1451 GaimStatus *
1452 gaim_presence_get_status(const GaimPresence *presence, const char *status_id)
1453 {
1454 GaimStatus *status;
1455 const GList *l = NULL;
1456
1457 g_return_val_if_fail(presence != NULL, NULL);
1458 g_return_val_if_fail(status_id != NULL, NULL);
1459
1460 /* What's the purpose of this hash table? */
1461 status = (GaimStatus *)g_hash_table_lookup(presence->status_table,
1462 status_id);
1463
1464 if (status == NULL) {
1465 for (l = gaim_presence_get_statuses(presence);
1466 l != NULL && status == NULL; l = l->next)
1467 {
1468 GaimStatus *temp_status = l->data;
1469
1470 if (!strcmp(status_id, gaim_status_get_id(temp_status)))
1471 status = temp_status;
1472 }
1473
1474 if (status != NULL)
1475 g_hash_table_insert(presence->status_table,
1476 g_strdup(gaim_status_get_id(status)), status);
1477 }
1478
1479 return status;
1480 }
1481
1482 GaimStatus *
1483 gaim_presence_get_active_status(const GaimPresence *presence)
1484 {
1485 g_return_val_if_fail(presence != NULL, NULL);
1486
1487 return presence->active_status;
1488 }
1489
1490 gboolean
1491 gaim_presence_is_available(const GaimPresence *presence)
1492 {
1493 GaimStatus *status;
1494
1495 g_return_val_if_fail(presence != NULL, FALSE);
1496
1497 status = gaim_presence_get_active_status(presence);
1498
1499 return ((status != NULL && gaim_status_is_available(status)) &&
1500 !gaim_presence_is_idle(presence));
1501 }
1502
1503 gboolean
1504 gaim_presence_is_online(const GaimPresence *presence)
1505 {
1506 GaimStatus *status;
1507
1508 g_return_val_if_fail(presence != NULL, FALSE);
1509
1510 if ((status = gaim_presence_get_active_status(presence)) == NULL)
1511 return FALSE;
1512
1513 return gaim_status_is_online(status);
1514 }
1515
1516 gboolean
1517 gaim_presence_is_status_active(const GaimPresence *presence,
1518 const char *status_id)
1519 {
1520 GaimStatus *status;
1521
1522 g_return_val_if_fail(presence != NULL, FALSE);
1523 g_return_val_if_fail(status_id != NULL, FALSE);
1524
1525 status = gaim_presence_get_status(presence, status_id);
1526
1527 return (status != NULL && gaim_status_is_active(status));
1528 }
1529
1530 gboolean
1531 gaim_presence_is_status_primitive_active(const GaimPresence *presence,
1532 GaimStatusPrimitive primitive)
1533 {
1534 GaimStatus *status;
1535 GaimStatusType *status_type;
1536
1537 g_return_val_if_fail(presence != NULL, FALSE);
1538 g_return_val_if_fail(primitive != GAIM_STATUS_UNSET, FALSE);
1539
1540 status = gaim_presence_get_active_status(presence);
1541 status_type = gaim_status_get_type(status);
1542
1543 if (gaim_status_type_get_primitive(status_type) == primitive)
1544 return TRUE;
1545
1546 return FALSE;
1547 }
1548
1549 gboolean
1550 gaim_presence_is_idle(const GaimPresence *presence)
1551 {
1552 g_return_val_if_fail(presence != NULL, FALSE);
1553
1554 return gaim_presence_is_online(presence) && presence->idle;
1555 }
1556
1557 time_t
1558 gaim_presence_get_idle_time(const GaimPresence *presence)
1559 {
1560 g_return_val_if_fail(presence != NULL, 0);
1561
1562 return presence->idle_time;
1563 }
1564
1565 time_t
1566 gaim_presence_get_login_time(const GaimPresence *presence)
1567 {
1568 g_return_val_if_fail(presence != NULL, 0);
1569
1570 return gaim_presence_is_online(presence) ? presence->login_time : 0;
1571 }
1572
1573 gint
1574 gaim_presence_compare(const GaimPresence *presence1,
1575 const GaimPresence *presence2)
1576 {
1577 gboolean idle1, idle2;
1578 time_t idle_time_1, idle_time_2;
1579 int score1 = 0, score2 = 0;
1580 const GList *l;
1581
1582 if (presence1 == presence2)
1583 return 0;
1584 else if (presence1 == NULL)
1585 return 1;
1586 else if (presence2 == NULL)
1587 return -1;
1588
1589 /* Compute the score of the first set of statuses. */
1590 for (l = gaim_presence_get_statuses(presence1); l != NULL; l = l->next)
1591 {
1592 GaimStatus *status = (GaimStatus *)l->data;
1593 GaimStatusType *type = gaim_status_get_type(status);
1594
1595 if (gaim_status_is_active(status))
1596 score1 += primitive_scores[gaim_status_type_get_primitive(type)];
1597 }
1598 score1 += gaim_account_get_int(gaim_presence_get_account(presence1), "score", 0);
1599
1600 /* Compute the score of the second set of statuses. */
1601 for (l = gaim_presence_get_statuses(presence2); l != NULL; l = l->next)
1602 {
1603 GaimStatus *status = (GaimStatus *)l->data;
1604 GaimStatusType *type = gaim_status_get_type(status);
1605
1606 if (gaim_status_is_active(status))
1607 score2 += primitive_scores[gaim_status_type_get_primitive(type)];
1608 }
1609 score2 += gaim_account_get_int(gaim_presence_get_account(presence2), "score", 0);
1610
1611 idle1 = gaim_presence_is_idle(presence1);
1612 idle2 = gaim_presence_is_idle(presence2);
1613
1614 if (idle1)
1615 score1 += primitive_scores[SCORE_IDLE];
1616
1617 if (idle2)
1618 score2 += primitive_scores[SCORE_IDLE];
1619
1620 idle_time_1 = time(NULL) - gaim_presence_get_idle_time(presence1);
1621 idle_time_2 = time(NULL) - gaim_presence_get_idle_time(presence2);
1622
1623 if (idle_time_1 > idle_time_2)
1624 score1 += primitive_scores[SCORE_IDLE_TIME];
1625 else if (idle_time_1 < idle_time_2)
1626 score2 += primitive_scores[SCORE_IDLE_TIME];
1627
1628 if (score1 < score2)
1629 return 1;
1630 else if (score1 > score2)
1631 return -1;
1632
1633 return 0;
1634 }
1635
1636
1637 /**************************************************************************
1638 * Status subsystem
1639 **************************************************************************/
1640 static void
1641 score_pref_changed_cb(const char *name, GaimPrefType type,
1642 gconstpointer value, gpointer data)
1643 {
1644 int index = GPOINTER_TO_INT(data);
1645
1646 primitive_scores[index] = GPOINTER_TO_INT(value);
1647 }
1648
1649 static guint
1650 gaim_buddy_presences_hash(gconstpointer key)
1651 {
1652 const GaimStatusBuddyKey *me = key;
1653 guint ret;
1654 char *str;
1655
1656 str = g_strdup_printf("%p%s", me->account, me->name);
1657 ret = g_str_hash(str);
1658 g_free(str);
1659
1660 return ret;
1661 }
1662
1663 static gboolean
1664 gaim_buddy_presences_equal(gconstpointer a, gconstpointer b)
1665 {
1666 GaimStatusBuddyKey *key_a = (GaimStatusBuddyKey *)a;
1667 GaimStatusBuddyKey *key_b = (GaimStatusBuddyKey *)b;
1668
1669 if(key_a->account == key_b->account &&
1670 !strcmp(key_a->name, key_b->name))
1671 return TRUE;
1672 else
1673 return FALSE;
1674 }
1675
1676 static void
1677 gaim_buddy_presences_key_free(gpointer a)
1678 {
1679 GaimStatusBuddyKey *key = (GaimStatusBuddyKey *)a;
1680 g_free(key->name);
1681 g_free(key);
1682 }
1683
1684 void *
1685 gaim_status_get_handle(void) {
1686 static int handle;
1687
1688 return &handle;
1689 }
1690
1691 void
1692 gaim_status_init(void)
1693 {
1694 void *handle = gaim_status_get_handle;
1695
1696 gaim_prefs_add_none("/core/status");
1697 gaim_prefs_add_none("/core/status/scores");
1698
1699 gaim_prefs_add_int("/core/status/scores/offline",
1700 primitive_scores[GAIM_STATUS_OFFLINE]);
1701 gaim_prefs_add_int("/core/status/scores/available",
1702 primitive_scores[GAIM_STATUS_AVAILABLE]);
1703 gaim_prefs_add_int("/core/status/scores/invisible",
1704 primitive_scores[GAIM_STATUS_INVISIBLE]);
1705 gaim_prefs_add_int("/core/status/scores/away",
1706 primitive_scores[GAIM_STATUS_AWAY]);
1707 gaim_prefs_add_int("/core/status/scores/extended_away",
1708 primitive_scores[GAIM_STATUS_EXTENDED_AWAY]);
1709 gaim_prefs_add_int("/core/status/scores/idle",
1710 primitive_scores[SCORE_IDLE]);
1711
1712 gaim_prefs_connect_callback(handle, "/core/status/scores/offline",
1713 score_pref_changed_cb,
1714 GINT_TO_POINTER(GAIM_STATUS_OFFLINE));
1715 gaim_prefs_connect_callback(handle, "/core/status/scores/available",
1716 score_pref_changed_cb,
1717 GINT_TO_POINTER(GAIM_STATUS_AVAILABLE));
1718 gaim_prefs_connect_callback(handle, "/core/status/scores/invisible",
1719 score_pref_changed_cb,
1720 GINT_TO_POINTER(GAIM_STATUS_INVISIBLE));
1721 gaim_prefs_connect_callback(handle, "/core/status/scores/away",
1722 score_pref_changed_cb,
1723 GINT_TO_POINTER(GAIM_STATUS_AWAY));
1724 gaim_prefs_connect_callback(handle, "/core/status/scores/extended_away",
1725 score_pref_changed_cb,
1726 GINT_TO_POINTER(GAIM_STATUS_EXTENDED_AWAY));
1727 gaim_prefs_connect_callback(handle, "/core/status/scores/idle",
1728 score_pref_changed_cb,
1729 GINT_TO_POINTER(SCORE_IDLE));
1730
1731 buddy_presences = g_hash_table_new_full(gaim_buddy_presences_hash,
1732 gaim_buddy_presences_equal,
1733 gaim_buddy_presences_key_free, NULL);
1734 }
1735
1736 void
1737 gaim_status_uninit(void)
1738 {
1739 if (buddy_presences != NULL)
1740 {
1741 g_hash_table_destroy(buddy_presences);
1742
1743 buddy_presences = NULL;
1744 }
1745 }

mercurial