2 * @file status.c Status API
8 * Purple is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
30 #include "dbus-maybe.h"
39 struct _PurpleStatusType
41 PurpleStatusPrimitive primitive
;
45 char *primary_attr_id
;
48 gboolean user_settable
;
57 struct _PurpleStatusAttr
61 PurpleValue
*value_type
;
67 struct _PurplePresence
69 PurplePresenceContext context
;
76 GHashTable
*status_table
;
78 PurpleStatus
*active_status
;
82 PurpleAccount
*account
;
86 PurpleConversation
*conv
;
93 PurpleAccount
*account
;
107 PurpleStatusType
*type
;
108 PurplePresence
*presence
;
113 * The current values of the attributes for this status. The
114 * key is a string containing the name of the attribute. It is
115 * a borrowed reference from the list of attrs in the
116 * PurpleStatusType. The value is a PurpleValue.
118 GHashTable
*attr_values
;
123 PurpleAccount
*account
;
125 } PurpleStatusBuddyKey
;
127 static int primitive_scores
[] =
132 -75, /* unavailable */
135 -200, /* extended away */
138 -10, /* idle, special case. */
139 -5, /* idle time, special case. */
140 10 /* Offline messageable */
144 #define SCORE_IDLE_TIME 10
145 #define SCORE_OFFLINE_MESSAGE 11
147 /**************************************************************************
148 * PurpleStatusPrimitive API
149 **************************************************************************/
150 static struct PurpleStatusPrimitiveMap
152 PurpleStatusPrimitive type
;
156 } const status_primitive_map
[] =
158 { PURPLE_STATUS_UNSET
, "unset", N_("Unset") },
159 { PURPLE_STATUS_OFFLINE
, "offline", N_("Offline") },
160 { PURPLE_STATUS_AVAILABLE
, "available", N_("Available") },
161 { PURPLE_STATUS_UNAVAILABLE
, "unavailable", N_("Do not disturb") },
162 { PURPLE_STATUS_INVISIBLE
, "invisible", N_("Invisible") },
163 { PURPLE_STATUS_AWAY
, "away", N_("Away") },
164 { PURPLE_STATUS_EXTENDED_AWAY
, "extended_away", N_("Extended away") },
165 { PURPLE_STATUS_MOBILE
, "mobile", N_("Mobile") },
166 { PURPLE_STATUS_TUNE
, "tune", N_("Listening to music") }
170 purple_primitive_get_id_from_type(PurpleStatusPrimitive type
)
174 for (i
= 0; i
< PURPLE_STATUS_NUM_PRIMITIVES
; i
++)
176 if (type
== status_primitive_map
[i
].type
)
177 return status_primitive_map
[i
].id
;
180 return status_primitive_map
[0].id
;
184 purple_primitive_get_name_from_type(PurpleStatusPrimitive type
)
188 for (i
= 0; i
< PURPLE_STATUS_NUM_PRIMITIVES
; i
++)
190 if (type
== status_primitive_map
[i
].type
)
191 return _(status_primitive_map
[i
].name
);
194 return _(status_primitive_map
[0].name
);
197 PurpleStatusPrimitive
198 purple_primitive_get_type_from_id(const char *id
)
202 g_return_val_if_fail(id
!= NULL
, PURPLE_STATUS_UNSET
);
204 for (i
= 0; i
< PURPLE_STATUS_NUM_PRIMITIVES
; i
++)
206 if (!strcmp(id
, status_primitive_map
[i
].id
))
207 return status_primitive_map
[i
].type
;
210 return status_primitive_map
[0].type
;
214 /**************************************************************************
215 * PurpleStatusType API
216 **************************************************************************/
218 purple_status_type_new_full(PurpleStatusPrimitive primitive
, const char *id
,
219 const char *name
, gboolean saveable
,
220 gboolean user_settable
, gboolean independent
)
222 PurpleStatusType
*status_type
;
224 g_return_val_if_fail(primitive
!= PURPLE_STATUS_UNSET
, NULL
);
226 status_type
= g_new0(PurpleStatusType
, 1);
227 PURPLE_DBUS_REGISTER_POINTER(status_type
, PurpleStatusType
);
229 status_type
->primitive
= primitive
;
230 status_type
->saveable
= saveable
;
231 status_type
->user_settable
= user_settable
;
232 status_type
->independent
= independent
;
235 status_type
->id
= g_strdup(id
);
237 status_type
->id
= g_strdup(purple_primitive_get_id_from_type(primitive
));
240 status_type
->name
= g_strdup(name
);
242 status_type
->name
= g_strdup(purple_primitive_get_name_from_type(primitive
));
248 purple_status_type_new(PurpleStatusPrimitive primitive
, const char *id
,
249 const char *name
, gboolean user_settable
)
251 g_return_val_if_fail(primitive
!= PURPLE_STATUS_UNSET
, NULL
);
253 return purple_status_type_new_full(primitive
, id
, name
, TRUE
,
254 user_settable
, FALSE
);
258 purple_status_type_new_with_attrs(PurpleStatusPrimitive primitive
,
259 const char *id
, const char *name
,
260 gboolean saveable
, gboolean user_settable
,
261 gboolean independent
, const char *attr_id
,
262 const char *attr_name
, PurpleValue
*attr_value
,
265 PurpleStatusType
*status_type
;
268 g_return_val_if_fail(primitive
!= PURPLE_STATUS_UNSET
, NULL
);
269 g_return_val_if_fail(attr_id
!= NULL
, NULL
);
270 g_return_val_if_fail(attr_name
!= NULL
, NULL
);
271 g_return_val_if_fail(attr_value
!= NULL
, NULL
);
273 status_type
= purple_status_type_new_full(primitive
, id
, name
, saveable
,
274 user_settable
, independent
);
276 /* Add the first attribute */
277 purple_status_type_add_attr(status_type
, attr_id
, attr_name
, attr_value
);
279 va_start(args
, attr_value
);
280 purple_status_type_add_attrs_vargs(status_type
, args
);
287 purple_status_type_destroy(PurpleStatusType
*status_type
)
289 g_return_if_fail(status_type
!= NULL
);
291 g_free(status_type
->id
);
292 g_free(status_type
->name
);
293 g_free(status_type
->primary_attr_id
);
295 g_list_foreach(status_type
->attrs
, (GFunc
)purple_status_attr_destroy
, NULL
);
296 g_list_free(status_type
->attrs
);
298 PURPLE_DBUS_UNREGISTER_POINTER(status_type
);
303 purple_status_type_set_primary_attr(PurpleStatusType
*status_type
, const char *id
)
305 g_return_if_fail(status_type
!= NULL
);
307 g_free(status_type
->primary_attr_id
);
308 status_type
->primary_attr_id
= g_strdup(id
);
312 purple_status_type_add_attr(PurpleStatusType
*status_type
, const char *id
,
313 const char *name
, PurpleValue
*value
)
315 PurpleStatusAttr
*attr
;
317 g_return_if_fail(status_type
!= NULL
);
318 g_return_if_fail(id
!= NULL
);
319 g_return_if_fail(name
!= NULL
);
320 g_return_if_fail(value
!= NULL
);
322 attr
= purple_status_attr_new(id
, name
, value
);
324 status_type
->attrs
= g_list_append(status_type
->attrs
, attr
);
328 purple_status_type_add_attrs_vargs(PurpleStatusType
*status_type
, va_list args
)
330 const char *id
, *name
;
333 g_return_if_fail(status_type
!= NULL
);
335 while ((id
= va_arg(args
, const char *)) != NULL
)
337 name
= va_arg(args
, const char *);
338 g_return_if_fail(name
!= NULL
);
340 value
= va_arg(args
, PurpleValue
*);
341 g_return_if_fail(value
!= NULL
);
343 purple_status_type_add_attr(status_type
, id
, name
, value
);
348 purple_status_type_add_attrs(PurpleStatusType
*status_type
, const char *id
,
349 const char *name
, PurpleValue
*value
, ...)
353 g_return_if_fail(status_type
!= NULL
);
354 g_return_if_fail(id
!= NULL
);
355 g_return_if_fail(name
!= NULL
);
356 g_return_if_fail(value
!= NULL
);
358 /* Add the first attribute */
359 purple_status_type_add_attr(status_type
, id
, name
, value
);
361 va_start(args
, value
);
362 purple_status_type_add_attrs_vargs(status_type
, args
);
366 PurpleStatusPrimitive
367 purple_status_type_get_primitive(const PurpleStatusType
*status_type
)
369 g_return_val_if_fail(status_type
!= NULL
, PURPLE_STATUS_UNSET
);
371 return status_type
->primitive
;
375 purple_status_type_get_id(const PurpleStatusType
*status_type
)
377 g_return_val_if_fail(status_type
!= NULL
, NULL
);
379 return status_type
->id
;
383 purple_status_type_get_name(const PurpleStatusType
*status_type
)
385 g_return_val_if_fail(status_type
!= NULL
, NULL
);
387 return status_type
->name
;
391 purple_status_type_is_saveable(const PurpleStatusType
*status_type
)
393 g_return_val_if_fail(status_type
!= NULL
, FALSE
);
395 return status_type
->saveable
;
399 purple_status_type_is_user_settable(const PurpleStatusType
*status_type
)
401 g_return_val_if_fail(status_type
!= NULL
, FALSE
);
403 return status_type
->user_settable
;
407 purple_status_type_is_independent(const PurpleStatusType
*status_type
)
409 g_return_val_if_fail(status_type
!= NULL
, FALSE
);
411 return status_type
->independent
;
415 purple_status_type_is_exclusive(const PurpleStatusType
*status_type
)
417 g_return_val_if_fail(status_type
!= NULL
, FALSE
);
419 return !status_type
->independent
;
423 purple_status_type_is_available(const PurpleStatusType
*status_type
)
425 PurpleStatusPrimitive primitive
;
427 g_return_val_if_fail(status_type
!= NULL
, FALSE
);
429 primitive
= purple_status_type_get_primitive(status_type
);
431 return (primitive
== PURPLE_STATUS_AVAILABLE
);
435 purple_status_type_get_primary_attr(const PurpleStatusType
*status_type
)
437 g_return_val_if_fail(status_type
!= NULL
, NULL
);
439 return status_type
->primary_attr_id
;
443 purple_status_type_get_attr(const PurpleStatusType
*status_type
, const char *id
)
447 g_return_val_if_fail(status_type
!= NULL
, NULL
);
448 g_return_val_if_fail(id
!= NULL
, NULL
);
450 for (l
= status_type
->attrs
; l
!= NULL
; l
= l
->next
)
452 PurpleStatusAttr
*attr
= (PurpleStatusAttr
*)l
->data
;
454 if (!strcmp(purple_status_attr_get_id(attr
), id
))
462 purple_status_type_get_attrs(const PurpleStatusType
*status_type
)
464 g_return_val_if_fail(status_type
!= NULL
, NULL
);
466 return status_type
->attrs
;
469 const PurpleStatusType
*
470 purple_status_type_find_with_id(GList
*status_types
, const char *id
)
472 PurpleStatusType
*status_type
;
474 g_return_val_if_fail(id
!= NULL
, NULL
);
476 while (status_types
!= NULL
)
478 status_type
= status_types
->data
;
480 if (!strcmp(id
, status_type
->id
))
483 status_types
= status_types
->next
;
490 /**************************************************************************
491 * PurpleStatusAttr API
492 **************************************************************************/
494 purple_status_attr_new(const char *id
, const char *name
, PurpleValue
*value_type
)
496 PurpleStatusAttr
*attr
;
498 g_return_val_if_fail(id
!= NULL
, NULL
);
499 g_return_val_if_fail(name
!= NULL
, NULL
);
500 g_return_val_if_fail(value_type
!= NULL
, NULL
);
502 attr
= g_new0(PurpleStatusAttr
, 1);
503 PURPLE_DBUS_REGISTER_POINTER(attr
, PurpleStatusAttr
);
505 attr
->id
= g_strdup(id
);
506 attr
->name
= g_strdup(name
);
507 attr
->value_type
= value_type
;
513 purple_status_attr_destroy(PurpleStatusAttr
*attr
)
515 g_return_if_fail(attr
!= NULL
);
520 purple_value_destroy(attr
->value_type
);
522 PURPLE_DBUS_UNREGISTER_POINTER(attr
);
527 purple_status_attr_get_id(const PurpleStatusAttr
*attr
)
529 g_return_val_if_fail(attr
!= NULL
, NULL
);
535 purple_status_attr_get_name(const PurpleStatusAttr
*attr
)
537 g_return_val_if_fail(attr
!= NULL
, NULL
);
543 purple_status_attr_get_value(const PurpleStatusAttr
*attr
)
545 g_return_val_if_fail(attr
!= NULL
, NULL
);
547 return attr
->value_type
;
551 /**************************************************************************
553 **************************************************************************/
555 purple_status_new(PurpleStatusType
*status_type
, PurplePresence
*presence
)
557 PurpleStatus
*status
;
560 g_return_val_if_fail(status_type
!= NULL
, NULL
);
561 g_return_val_if_fail(presence
!= NULL
, NULL
);
563 status
= g_new0(PurpleStatus
, 1);
564 PURPLE_DBUS_REGISTER_POINTER(status
, PurpleStatus
);
566 status
->type
= status_type
;
567 status
->presence
= presence
;
569 status
->attr_values
=
570 g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
,
571 (GDestroyNotify
)purple_value_destroy
);
573 for (l
= purple_status_type_get_attrs(status_type
); l
!= NULL
; l
= l
->next
)
575 PurpleStatusAttr
*attr
= (PurpleStatusAttr
*)l
->data
;
576 PurpleValue
*value
= purple_status_attr_get_value(attr
);
577 PurpleValue
*new_value
= purple_value_dup(value
);
579 g_hash_table_insert(status
->attr_values
,
580 (char *)purple_status_attr_get_id(attr
),
588 * TODO: If the PurpleStatus is in a PurplePresence, then
589 * remove it from the PurplePresence?
592 purple_status_destroy(PurpleStatus
*status
)
594 g_return_if_fail(status
!= NULL
);
596 g_hash_table_destroy(status
->attr_values
);
598 PURPLE_DBUS_UNREGISTER_POINTER(status
);
603 notify_buddy_status_update(PurpleBuddy
*buddy
, PurplePresence
*presence
,
604 PurpleStatus
*old_status
, PurpleStatus
*new_status
)
606 if (purple_prefs_get_bool("/purple/logging/log_system"))
608 time_t current_time
= time(NULL
);
609 const char *buddy_alias
= purple_buddy_get_alias(buddy
);
613 if (old_status
!= NULL
)
615 tmp
= g_strdup_printf(_("%s (%s) changed status from %s to %s"), buddy_alias
, buddy
->name
,
616 purple_status_get_name(old_status
),
617 purple_status_get_name(new_status
));
618 logtmp
= g_markup_escape_text(tmp
, -1);
622 /* old_status == NULL when an independent status is toggled. */
624 if (purple_status_is_active(new_status
))
626 tmp
= g_strdup_printf(_("%s (%s) is now %s"), buddy_alias
, buddy
->name
,
627 purple_status_get_name(new_status
));
628 logtmp
= g_markup_escape_text(tmp
, -1);
632 tmp
= g_strdup_printf(_("%s (%s) is no longer %s"), buddy_alias
, buddy
->name
,
633 purple_status_get_name(new_status
));
634 logtmp
= g_markup_escape_text(tmp
, -1);
638 log
= purple_account_get_log(buddy
->account
, FALSE
);
641 purple_log_write(log
, PURPLE_MESSAGE_SYSTEM
, buddy_alias
,
642 current_time
, logtmp
);
651 notify_status_update(PurplePresence
*presence
, PurpleStatus
*old_status
,
652 PurpleStatus
*new_status
)
654 PurplePresenceContext context
= purple_presence_get_context(presence
);
656 if (context
== PURPLE_PRESENCE_CONTEXT_ACCOUNT
)
658 PurpleAccount
*account
= purple_presence_get_account(presence
);
659 PurpleAccountUiOps
*ops
= purple_accounts_get_ui_ops();
661 if (purple_account_get_enabled(account
, purple_core_get_ui()))
662 purple_prpl_change_account_status(account
, old_status
, new_status
);
664 if (ops
!= NULL
&& ops
->status_changed
!= NULL
)
666 ops
->status_changed(account
, new_status
);
669 else if (context
== PURPLE_PRESENCE_CONTEXT_BUDDY
)
671 notify_buddy_status_update(purple_presence_get_buddy(presence
), presence
,
672 old_status
, new_status
);
677 status_has_changed(PurpleStatus
*status
)
679 PurplePresence
*presence
;
680 PurpleStatus
*old_status
;
682 presence
= purple_status_get_presence(status
);
685 * If this status is exclusive, then we must be setting it to "active."
686 * Since we are setting it to active, we want to set the currently
687 * active status to "inactive."
689 if (purple_status_is_exclusive(status
))
691 old_status
= purple_presence_get_active_status(presence
);
692 if (old_status
!= NULL
&& (old_status
!= status
))
693 old_status
->active
= FALSE
;
694 presence
->active_status
= status
;
699 notify_status_update(presence
, old_status
, status
);
703 purple_status_set_active(PurpleStatus
*status
, gboolean active
)
705 purple_status_set_active_with_attrs_list(status
, active
, NULL
);
709 * This used to parse the va_list directly, but now it creates a GList
710 * and passes it to purple_status_set_active_with_attrs_list(). That
711 * function was created because accounts.c needs to pass a GList of
712 * attributes to the status API.
715 purple_status_set_active_with_attrs(PurpleStatus
*status
, gboolean active
, va_list args
)
721 while ((id
= va_arg(args
, const char *)) != NULL
)
723 attrs
= g_list_append(attrs
, (char *)id
);
724 data
= va_arg(args
, void *);
725 attrs
= g_list_append(attrs
, data
);
727 purple_status_set_active_with_attrs_list(status
, active
, attrs
);
732 purple_status_set_active_with_attrs_list(PurpleStatus
*status
, gboolean active
,
735 gboolean changed
= FALSE
;
737 GList
*specified_attr_ids
= NULL
;
738 PurpleStatusType
*status_type
;
740 g_return_if_fail(status
!= NULL
);
742 if (!active
&& purple_status_is_exclusive(status
))
744 purple_debug_error("status",
745 "Cannot deactivate an exclusive status (%s).\n",
746 purple_status_get_id(status
));
750 if (status
->active
!= active
)
755 status
->active
= active
;
757 /* Set any attributes */
766 value
= purple_status_get_attr_value(status
, id
);
769 purple_debug_warning("status", "The attribute \"%s\" on the status \"%s\" is "
770 "not supported.\n", id
, status
->type
->name
);
771 /* Skip over the data and move on to the next attribute */
776 specified_attr_ids
= g_list_prepend(specified_attr_ids
, (gpointer
)id
);
778 if (value
->type
== PURPLE_TYPE_STRING
)
780 const gchar
*string_data
= l
->data
;
782 if (((string_data
== NULL
) && (value
->data
.string_data
== NULL
)) ||
783 ((string_data
!= NULL
) && (value
->data
.string_data
!= NULL
) &&
784 !strcmp(string_data
, value
->data
.string_data
)))
788 purple_status_set_attr_string(status
, id
, string_data
);
791 else if (value
->type
== PURPLE_TYPE_INT
)
793 int int_data
= GPOINTER_TO_INT(l
->data
);
795 if (int_data
== value
->data
.int_data
)
797 purple_status_set_attr_int(status
, id
, int_data
);
800 else if (value
->type
== PURPLE_TYPE_BOOLEAN
)
802 gboolean boolean_data
= GPOINTER_TO_INT(l
->data
);
804 if (boolean_data
== value
->data
.boolean_data
)
806 purple_status_set_attr_boolean(status
, id
, boolean_data
);
811 /* We don't know what the data is--skip over it */
816 /* Reset any unspecified attributes to their default value */
817 status_type
= purple_status_get_type(status
);
818 l
= purple_status_type_get_attrs(status_type
);
820 PurpleStatusAttr
*attr
;
825 if (!g_list_find_custom(specified_attr_ids
, attr
->id
, (GCompareFunc
)strcmp
)) {
826 PurpleValue
*default_value
;
827 default_value
= purple_status_attr_get_value(attr
);
828 if (default_value
->type
== PURPLE_TYPE_STRING
) {
829 const char *cur
= purple_status_get_attr_string(status
, attr
->id
);
830 const char *def
= purple_value_get_string(default_value
);
831 if ((cur
== NULL
&& def
== NULL
)
832 || (cur
!= NULL
&& def
!= NULL
833 && !strcmp(cur
, def
))) {
837 purple_status_set_attr_string(status
, attr
->id
, def
);
838 } else if (default_value
->type
== PURPLE_TYPE_INT
) {
839 int cur
= purple_status_get_attr_int(status
, attr
->id
);
840 int def
= purple_value_get_int(default_value
);
844 purple_status_set_attr_int(status
, attr
->id
, def
);
845 } else if (default_value
->type
== PURPLE_TYPE_BOOLEAN
) {
846 gboolean cur
= purple_status_get_attr_boolean(status
, attr
->id
);
847 gboolean def
= purple_value_get_boolean(default_value
);
851 purple_status_set_attr_boolean(status
, attr
->id
, def
);
856 g_list_free(specified_attr_ids
);
860 status_has_changed(status
);
864 purple_status_set_attr_boolean(PurpleStatus
*status
, const char *id
,
867 PurpleValue
*attr_value
;
869 g_return_if_fail(status
!= NULL
);
870 g_return_if_fail(id
!= NULL
);
872 /* Make sure this attribute exists and is the correct type. */
873 attr_value
= purple_status_get_attr_value(status
, id
);
874 g_return_if_fail(attr_value
!= NULL
);
875 g_return_if_fail(purple_value_get_type(attr_value
) == PURPLE_TYPE_BOOLEAN
);
877 purple_value_set_boolean(attr_value
, value
);
881 purple_status_set_attr_int(PurpleStatus
*status
, const char *id
, int value
)
883 PurpleValue
*attr_value
;
885 g_return_if_fail(status
!= NULL
);
886 g_return_if_fail(id
!= NULL
);
888 /* Make sure this attribute exists and is the correct type. */
889 attr_value
= purple_status_get_attr_value(status
, id
);
890 g_return_if_fail(attr_value
!= NULL
);
891 g_return_if_fail(purple_value_get_type(attr_value
) == PURPLE_TYPE_INT
);
893 purple_value_set_int(attr_value
, value
);
897 purple_status_set_attr_string(PurpleStatus
*status
, const char *id
,
900 PurpleValue
*attr_value
;
902 g_return_if_fail(status
!= NULL
);
903 g_return_if_fail(id
!= NULL
);
905 /* Make sure this attribute exists and is the correct type. */
906 attr_value
= purple_status_get_attr_value(status
, id
);
907 /* This used to be g_return_if_fail, but it's failing a LOT, so
908 * let's generate a log error for now. */
909 /* g_return_if_fail(attr_value != NULL); */
910 if (attr_value
== NULL
) {
911 purple_debug_error("status",
912 "Attempted to set status attribute '%s' for "
913 "status '%s', which is not legal. Fix "
915 purple_status_type_get_name(purple_status_get_type(status
)));
918 g_return_if_fail(purple_value_get_type(attr_value
) == PURPLE_TYPE_STRING
);
920 /* XXX: Check if the value has actually changed. If it has, and the status
921 * is active, should this trigger 'status_has_changed'? */
922 purple_value_set_string(attr_value
, value
);
926 purple_status_get_type(const PurpleStatus
*status
)
928 g_return_val_if_fail(status
!= NULL
, NULL
);
934 purple_status_get_presence(const PurpleStatus
*status
)
936 g_return_val_if_fail(status
!= NULL
, NULL
);
938 return status
->presence
;
942 purple_status_get_id(const PurpleStatus
*status
)
944 g_return_val_if_fail(status
!= NULL
, NULL
);
946 return purple_status_type_get_id(purple_status_get_type(status
));
950 purple_status_get_name(const PurpleStatus
*status
)
952 g_return_val_if_fail(status
!= NULL
, NULL
);
954 return purple_status_type_get_name(purple_status_get_type(status
));
958 purple_status_is_independent(const PurpleStatus
*status
)
960 g_return_val_if_fail(status
!= NULL
, FALSE
);
962 return purple_status_type_is_independent(purple_status_get_type(status
));
966 purple_status_is_exclusive(const PurpleStatus
*status
)
968 g_return_val_if_fail(status
!= NULL
, FALSE
);
970 return purple_status_type_is_exclusive(purple_status_get_type(status
));
974 purple_status_is_available(const PurpleStatus
*status
)
976 g_return_val_if_fail(status
!= NULL
, FALSE
);
978 return purple_status_type_is_available(purple_status_get_type(status
));
982 purple_status_is_active(const PurpleStatus
*status
)
984 g_return_val_if_fail(status
!= NULL
, FALSE
);
986 return status
->active
;
990 purple_status_is_online(const PurpleStatus
*status
)
992 PurpleStatusPrimitive primitive
;
994 g_return_val_if_fail( status
!= NULL
, FALSE
);
996 primitive
= purple_status_type_get_primitive(purple_status_get_type(status
));
998 return (primitive
!= PURPLE_STATUS_UNSET
&&
999 primitive
!= PURPLE_STATUS_OFFLINE
);
1003 purple_status_get_attr_value(const PurpleStatus
*status
, const char *id
)
1005 g_return_val_if_fail(status
!= NULL
, NULL
);
1006 g_return_val_if_fail(id
!= NULL
, NULL
);
1008 return (PurpleValue
*)g_hash_table_lookup(status
->attr_values
, id
);
1012 purple_status_get_attr_boolean(const PurpleStatus
*status
, const char *id
)
1014 const PurpleValue
*value
;
1016 g_return_val_if_fail(status
!= NULL
, FALSE
);
1017 g_return_val_if_fail(id
!= NULL
, FALSE
);
1019 if ((value
= purple_status_get_attr_value(status
, id
)) == NULL
)
1022 g_return_val_if_fail(purple_value_get_type(value
) == PURPLE_TYPE_BOOLEAN
, FALSE
);
1024 return purple_value_get_boolean(value
);
1028 purple_status_get_attr_int(const PurpleStatus
*status
, const char *id
)
1030 const PurpleValue
*value
;
1032 g_return_val_if_fail(status
!= NULL
, 0);
1033 g_return_val_if_fail(id
!= NULL
, 0);
1035 if ((value
= purple_status_get_attr_value(status
, id
)) == NULL
)
1038 g_return_val_if_fail(purple_value_get_type(value
) == PURPLE_TYPE_INT
, 0);
1040 return purple_value_get_int(value
);
1044 purple_status_get_attr_string(const PurpleStatus
*status
, const char *id
)
1046 const PurpleValue
*value
;
1048 g_return_val_if_fail(status
!= NULL
, NULL
);
1049 g_return_val_if_fail(id
!= NULL
, NULL
);
1051 if ((value
= purple_status_get_attr_value(status
, id
)) == NULL
)
1054 g_return_val_if_fail(purple_value_get_type(value
) == PURPLE_TYPE_STRING
, NULL
);
1056 return purple_value_get_string(value
);
1060 purple_status_compare(const PurpleStatus
*status1
, const PurpleStatus
*status2
)
1062 PurpleStatusType
*type1
, *type2
;
1063 int score1
= 0, score2
= 0;
1065 if ((status1
== NULL
&& status2
== NULL
) ||
1066 (status1
== status2
))
1070 else if (status1
== NULL
)
1072 else if (status2
== NULL
)
1075 type1
= purple_status_get_type(status1
);
1076 type2
= purple_status_get_type(status2
);
1078 if (purple_status_is_active(status1
))
1079 score1
= primitive_scores
[purple_status_type_get_primitive(type1
)];
1081 if (purple_status_is_active(status2
))
1082 score2
= primitive_scores
[purple_status_type_get_primitive(type2
)];
1084 if (score1
> score2
)
1086 else if (score1
< score2
)
1093 /**************************************************************************
1094 * PurplePresence API
1095 **************************************************************************/
1097 purple_presence_new(PurplePresenceContext context
)
1099 PurplePresence
*presence
;
1101 g_return_val_if_fail(context
!= PURPLE_PRESENCE_CONTEXT_UNSET
, NULL
);
1103 presence
= g_new0(PurplePresence
, 1);
1104 PURPLE_DBUS_REGISTER_POINTER(presence
, PurplePresence
);
1106 presence
->context
= context
;
1108 presence
->status_table
=
1109 g_hash_table_new_full(g_str_hash
, g_str_equal
,
1116 purple_presence_new_for_account(PurpleAccount
*account
)
1118 PurplePresence
*presence
= NULL
;
1119 g_return_val_if_fail(account
!= NULL
, NULL
);
1121 presence
= purple_presence_new(PURPLE_PRESENCE_CONTEXT_ACCOUNT
);
1122 presence
->u
.account
= account
;
1123 presence
->statuses
= purple_prpl_get_statuses(account
, presence
);
1129 purple_presence_new_for_conv(PurpleConversation
*conv
)
1131 PurplePresence
*presence
;
1133 g_return_val_if_fail(conv
!= NULL
, NULL
);
1135 presence
= purple_presence_new(PURPLE_PRESENCE_CONTEXT_CONV
);
1136 presence
->u
.chat
.conv
= conv
;
1137 /* presence->statuses = purple_prpl_get_statuses(conv->account, presence); ? */
1143 purple_presence_new_for_buddy(PurpleBuddy
*buddy
)
1145 PurplePresence
*presence
;
1146 PurpleAccount
*account
;
1148 g_return_val_if_fail(buddy
!= NULL
, NULL
);
1149 account
= buddy
->account
;
1151 presence
= purple_presence_new(PURPLE_PRESENCE_CONTEXT_BUDDY
);
1153 presence
->u
.buddy
.name
= g_strdup(buddy
->name
);
1154 presence
->u
.buddy
.account
= buddy
->account
;
1155 presence
->statuses
= purple_prpl_get_statuses(buddy
->account
, presence
);
1157 presence
->u
.buddy
.buddy
= buddy
;
1163 purple_presence_destroy(PurplePresence
*presence
)
1165 g_return_if_fail(presence
!= NULL
);
1167 if (purple_presence_get_context(presence
) == PURPLE_PRESENCE_CONTEXT_BUDDY
)
1169 g_free(presence
->u
.buddy
.name
);
1171 else if (purple_presence_get_context(presence
) == PURPLE_PRESENCE_CONTEXT_CONV
)
1173 g_free(presence
->u
.chat
.user
);
1176 g_list_foreach(presence
->statuses
, (GFunc
)purple_status_destroy
, NULL
);
1177 g_list_free(presence
->statuses
);
1179 g_hash_table_destroy(presence
->status_table
);
1181 PURPLE_DBUS_UNREGISTER_POINTER(presence
);
1186 purple_presence_add_status(PurplePresence
*presence
, PurpleStatus
*status
)
1188 g_return_if_fail(presence
!= NULL
);
1189 g_return_if_fail(status
!= NULL
);
1191 presence
->statuses
= g_list_append(presence
->statuses
, status
);
1193 g_hash_table_insert(presence
->status_table
,
1194 g_strdup(purple_status_get_id(status
)), status
);
1198 purple_presence_add_list(PurplePresence
*presence
, GList
*source_list
)
1202 g_return_if_fail(presence
!= NULL
);
1203 g_return_if_fail(source_list
!= NULL
);
1205 for (l
= source_list
; l
!= NULL
; l
= l
->next
)
1206 purple_presence_add_status(presence
, (PurpleStatus
*)l
->data
);
1210 purple_presence_set_status_active(PurplePresence
*presence
, const char *status_id
,
1213 PurpleStatus
*status
;
1215 g_return_if_fail(presence
!= NULL
);
1216 g_return_if_fail(status_id
!= NULL
);
1218 status
= purple_presence_get_status(presence
, status_id
);
1220 g_return_if_fail(status
!= NULL
);
1221 /* TODO: Should we do the following? */
1222 /* g_return_if_fail(active == status->active); */
1224 if (purple_status_is_exclusive(status
))
1228 purple_debug_warning("status",
1229 "Attempted to set a non-independent status "
1230 "(%s) inactive. Only independent statuses "
1231 "can be specifically marked inactive.",
1237 purple_status_set_active(status
, active
);
1241 purple_presence_switch_status(PurplePresence
*presence
, const char *status_id
)
1243 purple_presence_set_status_active(presence
, status_id
, TRUE
);
1247 update_buddy_idle(PurpleBuddy
*buddy
, PurplePresence
*presence
,
1248 time_t current_time
, gboolean old_idle
, gboolean idle
)
1250 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
1252 if (!old_idle
&& idle
)
1254 if (purple_prefs_get_bool("/purple/logging/log_system"))
1256 PurpleLog
*log
= purple_account_get_log(buddy
->account
, FALSE
);
1261 tmp
= g_strdup_printf(_("%s became idle"),
1262 purple_buddy_get_alias(buddy
));
1263 tmp2
= g_markup_escape_text(tmp
, -1);
1266 purple_log_write(log
, PURPLE_MESSAGE_SYSTEM
,
1267 purple_buddy_get_alias(buddy
), current_time
, tmp2
);
1272 else if (old_idle
&& !idle
)
1274 if (purple_prefs_get_bool("/purple/logging/log_system"))
1276 PurpleLog
*log
= purple_account_get_log(buddy
->account
, FALSE
);
1281 tmp
= g_strdup_printf(_("%s became unidle"),
1282 purple_buddy_get_alias(buddy
));
1283 tmp2
= g_markup_escape_text(tmp
, -1);
1286 purple_log_write(log
, PURPLE_MESSAGE_SYSTEM
,
1287 purple_buddy_get_alias(buddy
), current_time
, tmp2
);
1293 if (old_idle
!= idle
)
1294 purple_signal_emit(purple_blist_get_handle(), "buddy-idle-changed", buddy
,
1297 purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy
));
1299 /* Should this be done here? It'd perhaps make more sense to
1300 * connect to buddy-[un]idle signals and update from there
1303 if (ops
!= NULL
&& ops
->update
!= NULL
)
1304 ops
->update(purple_get_blist(), (PurpleBlistNode
*)buddy
);
1308 purple_presence_set_idle(PurplePresence
*presence
, gboolean idle
, time_t idle_time
)
1311 time_t current_time
;
1313 g_return_if_fail(presence
!= NULL
);
1315 if (presence
->idle
== idle
&& presence
->idle_time
== idle_time
)
1318 old_idle
= presence
->idle
;
1319 presence
->idle
= idle
;
1320 presence
->idle_time
= (idle
? idle_time
: 0);
1322 current_time
= time(NULL
);
1324 if (purple_presence_get_context(presence
) == PURPLE_PRESENCE_CONTEXT_BUDDY
)
1326 update_buddy_idle(purple_presence_get_buddy(presence
), presence
, current_time
,
1329 else if (purple_presence_get_context(presence
) == PURPLE_PRESENCE_CONTEXT_ACCOUNT
)
1331 PurpleAccount
*account
;
1332 PurpleConnection
*gc
= NULL
;
1333 PurplePlugin
*prpl
= NULL
;
1334 PurplePluginProtocolInfo
*prpl_info
= NULL
;
1336 account
= purple_presence_get_account(presence
);
1338 if (purple_prefs_get_bool("/purple/logging/log_system"))
1340 PurpleLog
*log
= purple_account_get_log(account
, FALSE
);
1347 tmp
= g_strdup_printf(_("+++ %s became idle"), purple_account_get_username(account
));
1349 tmp
= g_strdup_printf(_("+++ %s became unidle"), purple_account_get_username(account
));
1351 msg
= g_markup_escape_text(tmp
, -1);
1353 purple_log_write(log
, PURPLE_MESSAGE_SYSTEM
,
1354 purple_account_get_username(account
),
1355 (idle
? idle_time
: current_time
), msg
);
1360 gc
= purple_account_get_connection(account
);
1363 prpl
= purple_connection_get_prpl(gc
);
1365 if(PURPLE_CONNECTION_IS_CONNECTED(gc
) && prpl
!= NULL
)
1366 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(prpl
);
1368 if (prpl_info
&& prpl_info
->set_idle
)
1369 prpl_info
->set_idle(gc
, (idle
? (current_time
- idle_time
) : 0));
1374 purple_presence_set_login_time(PurplePresence
*presence
, time_t login_time
)
1376 g_return_if_fail(presence
!= NULL
);
1378 if (presence
->login_time
== login_time
)
1381 presence
->login_time
= login_time
;
1384 PurplePresenceContext
1385 purple_presence_get_context(const PurplePresence
*presence
)
1387 g_return_val_if_fail(presence
!= NULL
, PURPLE_PRESENCE_CONTEXT_UNSET
);
1389 return presence
->context
;
1393 purple_presence_get_account(const PurplePresence
*presence
)
1395 PurplePresenceContext context
;
1397 g_return_val_if_fail(presence
!= NULL
, NULL
);
1399 context
= purple_presence_get_context(presence
);
1401 g_return_val_if_fail(context
== PURPLE_PRESENCE_CONTEXT_ACCOUNT
||
1402 context
== PURPLE_PRESENCE_CONTEXT_BUDDY
, NULL
);
1404 return presence
->u
.account
;
1407 PurpleConversation
*
1408 purple_presence_get_conversation(const PurplePresence
*presence
)
1410 g_return_val_if_fail(presence
!= NULL
, NULL
);
1411 g_return_val_if_fail(purple_presence_get_context(presence
) ==
1412 PURPLE_PRESENCE_CONTEXT_CONV
, NULL
);
1414 return presence
->u
.chat
.conv
;
1418 purple_presence_get_chat_user(const PurplePresence
*presence
)
1420 g_return_val_if_fail(presence
!= NULL
, NULL
);
1421 g_return_val_if_fail(purple_presence_get_context(presence
) ==
1422 PURPLE_PRESENCE_CONTEXT_CONV
, NULL
);
1424 return presence
->u
.chat
.user
;
1428 purple_presence_get_buddy(const PurplePresence
*presence
)
1430 g_return_val_if_fail(presence
!= NULL
, NULL
);
1431 g_return_val_if_fail(purple_presence_get_context(presence
) ==
1432 PURPLE_PRESENCE_CONTEXT_BUDDY
, NULL
);
1434 return presence
->u
.buddy
.buddy
;
1438 purple_presence_get_statuses(const PurplePresence
*presence
)
1440 g_return_val_if_fail(presence
!= NULL
, NULL
);
1442 return presence
->statuses
;
1446 purple_presence_get_status(const PurplePresence
*presence
, const char *status_id
)
1448 PurpleStatus
*status
;
1451 g_return_val_if_fail(presence
!= NULL
, NULL
);
1452 g_return_val_if_fail(status_id
!= NULL
, NULL
);
1454 /* What's the purpose of this hash table? */
1455 status
= (PurpleStatus
*)g_hash_table_lookup(presence
->status_table
,
1458 if (status
== NULL
) {
1459 for (l
= purple_presence_get_statuses(presence
);
1460 l
!= NULL
&& status
== NULL
; l
= l
->next
)
1462 PurpleStatus
*temp_status
= l
->data
;
1464 if (!strcmp(status_id
, purple_status_get_id(temp_status
)))
1465 status
= temp_status
;
1469 g_hash_table_insert(presence
->status_table
,
1470 g_strdup(purple_status_get_id(status
)), status
);
1477 purple_presence_get_active_status(const PurplePresence
*presence
)
1479 g_return_val_if_fail(presence
!= NULL
, NULL
);
1481 return presence
->active_status
;
1485 purple_presence_is_available(const PurplePresence
*presence
)
1487 PurpleStatus
*status
;
1489 g_return_val_if_fail(presence
!= NULL
, FALSE
);
1491 status
= purple_presence_get_active_status(presence
);
1493 return ((status
!= NULL
&& purple_status_is_available(status
)) &&
1494 !purple_presence_is_idle(presence
));
1498 purple_presence_is_online(const PurplePresence
*presence
)
1500 PurpleStatus
*status
;
1502 g_return_val_if_fail(presence
!= NULL
, FALSE
);
1504 if ((status
= purple_presence_get_active_status(presence
)) == NULL
)
1507 return purple_status_is_online(status
);
1511 purple_presence_is_status_active(const PurplePresence
*presence
,
1512 const char *status_id
)
1514 PurpleStatus
*status
;
1516 g_return_val_if_fail(presence
!= NULL
, FALSE
);
1517 g_return_val_if_fail(status_id
!= NULL
, FALSE
);
1519 status
= purple_presence_get_status(presence
, status_id
);
1521 return (status
!= NULL
&& purple_status_is_active(status
));
1525 purple_presence_is_status_primitive_active(const PurplePresence
*presence
,
1526 PurpleStatusPrimitive primitive
)
1530 g_return_val_if_fail(presence
!= NULL
, FALSE
);
1531 g_return_val_if_fail(primitive
!= PURPLE_STATUS_UNSET
, FALSE
);
1533 for (l
= purple_presence_get_statuses(presence
);
1534 l
!= NULL
; l
= l
->next
) {
1535 PurpleStatus
*temp_status
= l
->data
;
1536 PurpleStatusType
*type
= purple_status_get_type(temp_status
);
1538 if (purple_status_type_get_primitive(type
) == primitive
&&
1539 purple_status_is_active(temp_status
))
1546 purple_presence_is_idle(const PurplePresence
*presence
)
1548 g_return_val_if_fail(presence
!= NULL
, FALSE
);
1550 return purple_presence_is_online(presence
) && presence
->idle
;
1554 purple_presence_get_idle_time(const PurplePresence
*presence
)
1556 g_return_val_if_fail(presence
!= NULL
, 0);
1558 return presence
->idle_time
;
1562 purple_presence_get_login_time(const PurplePresence
*presence
)
1564 g_return_val_if_fail(presence
!= NULL
, 0);
1566 return purple_presence_is_online(presence
) ? presence
->login_time
: 0;
1570 purple_presence_compute_score(const PurplePresence
*presence
)
1575 for (l
= purple_presence_get_statuses(presence
); l
!= NULL
; l
= l
->next
) {
1576 PurpleStatus
*status
= (PurpleStatus
*)l
->data
;
1577 PurpleStatusType
*type
= purple_status_get_type(status
);
1579 if (purple_status_is_active(status
)) {
1580 score
+= primitive_scores
[purple_status_type_get_primitive(type
)];
1581 if (!purple_status_is_online(status
)) {
1582 PurpleBuddy
*b
= purple_presence_get_buddy(presence
);
1583 if (b
&& purple_account_supports_offline_message(purple_buddy_get_account(b
), b
))
1584 score
+= primitive_scores
[SCORE_OFFLINE_MESSAGE
];
1588 score
+= purple_account_get_int(purple_presence_get_account(presence
), "score", 0);
1589 if (purple_presence_is_idle(presence
))
1590 score
+= primitive_scores
[SCORE_IDLE
];
1595 purple_presence_compare(const PurplePresence
*presence1
,
1596 const PurplePresence
*presence2
)
1598 time_t idle_time_1
, idle_time_2
;
1599 int score1
= 0, score2
= 0;
1601 if (presence1
== presence2
)
1603 else if (presence1
== NULL
)
1605 else if (presence2
== NULL
)
1608 if (purple_presence_is_online(presence1
) &&
1609 !purple_presence_is_online(presence2
))
1611 else if (purple_presence_is_online(presence2
) &&
1612 !purple_presence_is_online(presence1
))
1615 /* Compute the score of the first set of statuses. */
1616 score1
= purple_presence_compute_score(presence1
);
1618 /* Compute the score of the second set of statuses. */
1619 score2
= purple_presence_compute_score(presence2
);
1621 idle_time_1
= time(NULL
) - purple_presence_get_idle_time(presence1
);
1622 idle_time_2
= time(NULL
) - purple_presence_get_idle_time(presence2
);
1624 if (idle_time_1
> idle_time_2
)
1625 score1
+= primitive_scores
[SCORE_IDLE_TIME
];
1626 else if (idle_time_1
< idle_time_2
)
1627 score2
+= primitive_scores
[SCORE_IDLE_TIME
];
1629 if (score1
< score2
)
1631 else if (score1
> score2
)
1638 /**************************************************************************
1640 **************************************************************************/
1642 score_pref_changed_cb(const char *name
, PurplePrefType type
,
1643 gconstpointer value
, gpointer data
)
1645 int index
= GPOINTER_TO_INT(data
);
1647 primitive_scores
[index
] = GPOINTER_TO_INT(value
);
1651 purple_status_get_handle(void) {
1658 purple_status_init(void)
1660 void *handle
= purple_status_get_handle
;
1662 purple_prefs_add_none("/purple/status");
1663 purple_prefs_add_none("/purple/status/scores");
1665 purple_prefs_add_int("/purple/status/scores/offline",
1666 primitive_scores
[PURPLE_STATUS_OFFLINE
]);
1667 purple_prefs_add_int("/purple/status/scores/available",
1668 primitive_scores
[PURPLE_STATUS_AVAILABLE
]);
1669 purple_prefs_add_int("/purple/status/scores/invisible",
1670 primitive_scores
[PURPLE_STATUS_INVISIBLE
]);
1671 purple_prefs_add_int("/purple/status/scores/away",
1672 primitive_scores
[PURPLE_STATUS_AWAY
]);
1673 purple_prefs_add_int("/purple/status/scores/extended_away",
1674 primitive_scores
[PURPLE_STATUS_EXTENDED_AWAY
]);
1675 purple_prefs_add_int("/purple/status/scores/idle",
1676 primitive_scores
[SCORE_IDLE
]);
1677 purple_prefs_add_int("/purple/status/scores/offline_msg",
1678 primitive_scores
[SCORE_OFFLINE_MESSAGE
]);
1680 purple_prefs_connect_callback(handle
, "/purple/status/scores/offline",
1681 score_pref_changed_cb
,
1682 GINT_TO_POINTER(PURPLE_STATUS_OFFLINE
));
1683 purple_prefs_connect_callback(handle
, "/purple/status/scores/available",
1684 score_pref_changed_cb
,
1685 GINT_TO_POINTER(PURPLE_STATUS_AVAILABLE
));
1686 purple_prefs_connect_callback(handle
, "/purple/status/scores/invisible",
1687 score_pref_changed_cb
,
1688 GINT_TO_POINTER(PURPLE_STATUS_INVISIBLE
));
1689 purple_prefs_connect_callback(handle
, "/purple/status/scores/away",
1690 score_pref_changed_cb
,
1691 GINT_TO_POINTER(PURPLE_STATUS_AWAY
));
1692 purple_prefs_connect_callback(handle
, "/purple/status/scores/extended_away",
1693 score_pref_changed_cb
,
1694 GINT_TO_POINTER(PURPLE_STATUS_EXTENDED_AWAY
));
1695 purple_prefs_connect_callback(handle
, "/purple/status/scores/idle",
1696 score_pref_changed_cb
,
1697 GINT_TO_POINTER(SCORE_IDLE
));
1698 purple_prefs_connect_callback(handle
, "/purple/status/scores/offline_msg",
1699 score_pref_changed_cb
,
1700 GINT_TO_POINTER(SCORE_OFFLINE_MESSAGE
));
1702 purple_prefs_trigger_callback("/purple/status/scores/offline");
1703 purple_prefs_trigger_callback("/purple/status/scores/available");
1704 purple_prefs_trigger_callback("/purple/status/scores/invisible");
1705 purple_prefs_trigger_callback("/purple/status/scores/away");
1706 purple_prefs_trigger_callback("/purple/status/scores/extended_away");
1707 purple_prefs_trigger_callback("/purple/status/scores/idle");
1708 purple_prefs_trigger_callback("/purple/status/scores/offline_msg");
1712 purple_status_uninit(void)