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
26 #define _PURPLE_STATUS_C_
32 #include "dbus-maybe.h"
41 struct _PurpleStatusType
43 PurpleStatusPrimitive primitive
;
47 char *primary_attr_id
;
50 gboolean user_settable
;
59 struct _PurpleStatusAttr
63 PurpleValue
*value_type
;
69 struct _PurplePresence
71 PurplePresenceContext context
;
78 GHashTable
*status_table
;
80 PurpleStatus
*active_status
;
84 PurpleAccount
*account
;
88 PurpleConversation
*conv
;
95 PurpleAccount
*account
;
109 PurpleStatusType
*type
;
110 PurplePresence
*presence
;
115 * The current values of the attributes for this status. The
116 * key is a string containing the name of the attribute. It is
117 * a borrowed reference from the list of attrs in the
118 * PurpleStatusType. The value is a PurpleValue.
120 GHashTable
*attr_values
;
125 PurpleAccount
*account
;
127 } PurpleStatusBuddyKey
;
129 static int primitive_scores
[] =
134 -75, /* unavailable */
137 -200, /* extended away */
140 -10, /* idle, special case. */
141 -5, /* idle time, special case. */
142 10 /* Offline messageable */
146 #define SCORE_IDLE_TIME 10
147 #define SCORE_OFFLINE_MESSAGE 11
149 /**************************************************************************
150 * PurpleStatusPrimitive API
151 **************************************************************************/
152 static struct PurpleStatusPrimitiveMap
154 PurpleStatusPrimitive type
;
158 } const status_primitive_map
[] =
160 { PURPLE_STATUS_UNSET
, "unset", N_("Unset") },
161 { PURPLE_STATUS_OFFLINE
, "offline", N_("Offline") },
162 { PURPLE_STATUS_AVAILABLE
, "available", N_("Available") },
163 { PURPLE_STATUS_UNAVAILABLE
, "unavailable", N_("Do not disturb") },
164 { PURPLE_STATUS_INVISIBLE
, "invisible", N_("Invisible") },
165 { PURPLE_STATUS_AWAY
, "away", N_("Away") },
166 { PURPLE_STATUS_EXTENDED_AWAY
, "extended_away", N_("Extended away") },
167 { PURPLE_STATUS_MOBILE
, "mobile", N_("Mobile") },
168 { PURPLE_STATUS_TUNE
, "tune", N_("Listening to music") }
172 purple_primitive_get_id_from_type(PurpleStatusPrimitive type
)
176 for (i
= 0; i
< PURPLE_STATUS_NUM_PRIMITIVES
; i
++)
178 if (type
== status_primitive_map
[i
].type
)
179 return status_primitive_map
[i
].id
;
182 return status_primitive_map
[0].id
;
186 purple_primitive_get_name_from_type(PurpleStatusPrimitive type
)
190 for (i
= 0; i
< PURPLE_STATUS_NUM_PRIMITIVES
; i
++)
192 if (type
== status_primitive_map
[i
].type
)
193 return _(status_primitive_map
[i
].name
);
196 return _(status_primitive_map
[0].name
);
199 PurpleStatusPrimitive
200 purple_primitive_get_type_from_id(const char *id
)
204 g_return_val_if_fail(id
!= NULL
, PURPLE_STATUS_UNSET
);
206 for (i
= 0; i
< PURPLE_STATUS_NUM_PRIMITIVES
; i
++)
208 if (purple_strequal(id
, status_primitive_map
[i
].id
))
209 return status_primitive_map
[i
].type
;
212 return status_primitive_map
[0].type
;
216 /**************************************************************************
217 * PurpleStatusType API
218 **************************************************************************/
220 purple_status_type_new_full(PurpleStatusPrimitive primitive
, const char *id
,
221 const char *name
, gboolean saveable
,
222 gboolean user_settable
, gboolean independent
)
224 PurpleStatusType
*status_type
;
226 g_return_val_if_fail(primitive
!= PURPLE_STATUS_UNSET
, NULL
);
228 status_type
= g_new0(PurpleStatusType
, 1);
229 PURPLE_DBUS_REGISTER_POINTER(status_type
, PurpleStatusType
);
231 status_type
->primitive
= primitive
;
232 status_type
->saveable
= saveable
;
233 status_type
->user_settable
= user_settable
;
234 status_type
->independent
= independent
;
237 status_type
->id
= g_strdup(id
);
239 status_type
->id
= g_strdup(purple_primitive_get_id_from_type(primitive
));
242 status_type
->name
= g_strdup(name
);
244 status_type
->name
= g_strdup(purple_primitive_get_name_from_type(primitive
));
250 purple_status_type_new(PurpleStatusPrimitive primitive
, const char *id
,
251 const char *name
, gboolean user_settable
)
253 g_return_val_if_fail(primitive
!= PURPLE_STATUS_UNSET
, NULL
);
255 return purple_status_type_new_full(primitive
, id
, name
, TRUE
,
256 user_settable
, FALSE
);
260 purple_status_type_new_with_attrs(PurpleStatusPrimitive primitive
,
261 const char *id
, const char *name
,
262 gboolean saveable
, gboolean user_settable
,
263 gboolean independent
, const char *attr_id
,
264 const char *attr_name
, PurpleValue
*attr_value
,
267 PurpleStatusType
*status_type
;
270 g_return_val_if_fail(primitive
!= PURPLE_STATUS_UNSET
, NULL
);
271 g_return_val_if_fail(attr_id
!= NULL
, NULL
);
272 g_return_val_if_fail(attr_name
!= NULL
, NULL
);
273 g_return_val_if_fail(attr_value
!= NULL
, NULL
);
275 status_type
= purple_status_type_new_full(primitive
, id
, name
, saveable
,
276 user_settable
, independent
);
278 /* Add the first attribute */
279 purple_status_type_add_attr(status_type
, attr_id
, attr_name
, attr_value
);
281 va_start(args
, attr_value
);
282 purple_status_type_add_attrs_vargs(status_type
, args
);
289 purple_status_type_destroy(PurpleStatusType
*status_type
)
291 g_return_if_fail(status_type
!= NULL
);
293 g_free(status_type
->id
);
294 g_free(status_type
->name
);
295 g_free(status_type
->primary_attr_id
);
297 g_list_foreach(status_type
->attrs
, (GFunc
)purple_status_attr_destroy
, NULL
);
298 g_list_free(status_type
->attrs
);
300 PURPLE_DBUS_UNREGISTER_POINTER(status_type
);
305 purple_status_type_set_primary_attr(PurpleStatusType
*status_type
, const char *id
)
307 g_return_if_fail(status_type
!= NULL
);
309 g_free(status_type
->primary_attr_id
);
310 status_type
->primary_attr_id
= g_strdup(id
);
314 purple_status_type_add_attr(PurpleStatusType
*status_type
, const char *id
,
315 const char *name
, PurpleValue
*value
)
317 PurpleStatusAttr
*attr
;
319 g_return_if_fail(status_type
!= NULL
);
320 g_return_if_fail(id
!= NULL
);
321 g_return_if_fail(name
!= NULL
);
322 g_return_if_fail(value
!= NULL
);
324 attr
= purple_status_attr_new(id
, name
, value
);
326 status_type
->attrs
= g_list_append(status_type
->attrs
, attr
);
330 purple_status_type_add_attrs_vargs(PurpleStatusType
*status_type
, va_list args
)
332 const char *id
, *name
;
335 g_return_if_fail(status_type
!= NULL
);
337 while ((id
= va_arg(args
, const char *)) != NULL
)
339 name
= va_arg(args
, const char *);
340 g_return_if_fail(name
!= NULL
);
342 value
= va_arg(args
, PurpleValue
*);
343 g_return_if_fail(value
!= NULL
);
345 purple_status_type_add_attr(status_type
, id
, name
, value
);
350 purple_status_type_add_attrs(PurpleStatusType
*status_type
, const char *id
,
351 const char *name
, PurpleValue
*value
, ...)
355 g_return_if_fail(status_type
!= NULL
);
356 g_return_if_fail(id
!= NULL
);
357 g_return_if_fail(name
!= NULL
);
358 g_return_if_fail(value
!= NULL
);
360 /* Add the first attribute */
361 purple_status_type_add_attr(status_type
, id
, name
, value
);
363 va_start(args
, value
);
364 purple_status_type_add_attrs_vargs(status_type
, args
);
368 PurpleStatusPrimitive
369 purple_status_type_get_primitive(const PurpleStatusType
*status_type
)
371 g_return_val_if_fail(status_type
!= NULL
, PURPLE_STATUS_UNSET
);
373 return status_type
->primitive
;
377 purple_status_type_get_id(const PurpleStatusType
*status_type
)
379 g_return_val_if_fail(status_type
!= NULL
, NULL
);
381 return status_type
->id
;
385 purple_status_type_get_name(const PurpleStatusType
*status_type
)
387 g_return_val_if_fail(status_type
!= NULL
, NULL
);
389 return status_type
->name
;
393 purple_status_type_is_saveable(const PurpleStatusType
*status_type
)
395 g_return_val_if_fail(status_type
!= NULL
, FALSE
);
397 return status_type
->saveable
;
401 purple_status_type_is_user_settable(const PurpleStatusType
*status_type
)
403 g_return_val_if_fail(status_type
!= NULL
, FALSE
);
405 return status_type
->user_settable
;
409 purple_status_type_is_independent(const PurpleStatusType
*status_type
)
411 g_return_val_if_fail(status_type
!= NULL
, FALSE
);
413 return status_type
->independent
;
417 purple_status_type_is_exclusive(const PurpleStatusType
*status_type
)
419 g_return_val_if_fail(status_type
!= NULL
, FALSE
);
421 return !status_type
->independent
;
425 purple_status_type_is_available(const PurpleStatusType
*status_type
)
427 PurpleStatusPrimitive primitive
;
429 g_return_val_if_fail(status_type
!= NULL
, FALSE
);
431 primitive
= purple_status_type_get_primitive(status_type
);
433 return (primitive
== PURPLE_STATUS_AVAILABLE
);
437 purple_status_type_get_primary_attr(const PurpleStatusType
*status_type
)
439 g_return_val_if_fail(status_type
!= NULL
, NULL
);
441 return status_type
->primary_attr_id
;
445 purple_status_type_get_attr(const PurpleStatusType
*status_type
, const char *id
)
449 g_return_val_if_fail(status_type
!= NULL
, NULL
);
450 g_return_val_if_fail(id
!= NULL
, NULL
);
452 for (l
= status_type
->attrs
; l
!= NULL
; l
= l
->next
)
454 PurpleStatusAttr
*attr
= (PurpleStatusAttr
*)l
->data
;
456 if (purple_strequal(purple_status_attr_get_id(attr
), id
))
464 purple_status_type_get_attrs(const PurpleStatusType
*status_type
)
466 g_return_val_if_fail(status_type
!= NULL
, NULL
);
468 return status_type
->attrs
;
471 const PurpleStatusType
*
472 purple_status_type_find_with_id(GList
*status_types
, const char *id
)
474 PurpleStatusType
*status_type
;
476 g_return_val_if_fail(id
!= NULL
, NULL
);
478 while (status_types
!= NULL
)
480 status_type
= status_types
->data
;
482 if (purple_strequal(id
, status_type
->id
))
485 status_types
= status_types
->next
;
492 /**************************************************************************
493 * PurpleStatusAttr API
494 **************************************************************************/
496 purple_status_attr_new(const char *id
, const char *name
, PurpleValue
*value_type
)
498 PurpleStatusAttr
*attr
;
500 g_return_val_if_fail(id
!= NULL
, NULL
);
501 g_return_val_if_fail(name
!= NULL
, NULL
);
502 g_return_val_if_fail(value_type
!= NULL
, NULL
);
504 attr
= g_new0(PurpleStatusAttr
, 1);
505 PURPLE_DBUS_REGISTER_POINTER(attr
, PurpleStatusAttr
);
507 attr
->id
= g_strdup(id
);
508 attr
->name
= g_strdup(name
);
509 attr
->value_type
= value_type
;
515 purple_status_attr_destroy(PurpleStatusAttr
*attr
)
517 g_return_if_fail(attr
!= NULL
);
522 purple_value_destroy(attr
->value_type
);
524 PURPLE_DBUS_UNREGISTER_POINTER(attr
);
529 purple_status_attr_get_id(const PurpleStatusAttr
*attr
)
531 g_return_val_if_fail(attr
!= NULL
, NULL
);
537 purple_status_attr_get_name(const PurpleStatusAttr
*attr
)
539 g_return_val_if_fail(attr
!= NULL
, NULL
);
545 purple_status_attr_get_value(const PurpleStatusAttr
*attr
)
547 g_return_val_if_fail(attr
!= NULL
, NULL
);
549 return attr
->value_type
;
553 /**************************************************************************
555 **************************************************************************/
557 purple_status_new(PurpleStatusType
*status_type
, PurplePresence
*presence
)
559 PurpleStatus
*status
;
562 g_return_val_if_fail(status_type
!= NULL
, NULL
);
563 g_return_val_if_fail(presence
!= NULL
, NULL
);
565 status
= g_new0(PurpleStatus
, 1);
566 PURPLE_DBUS_REGISTER_POINTER(status
, PurpleStatus
);
568 status
->type
= status_type
;
569 status
->presence
= presence
;
571 status
->attr_values
=
572 g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
,
573 (GDestroyNotify
)purple_value_destroy
);
575 for (l
= purple_status_type_get_attrs(status_type
); l
!= NULL
; l
= l
->next
)
577 PurpleStatusAttr
*attr
= (PurpleStatusAttr
*)l
->data
;
578 PurpleValue
*value
= purple_status_attr_get_value(attr
);
579 PurpleValue
*new_value
= purple_value_dup(value
);
581 g_hash_table_insert(status
->attr_values
,
582 (char *)purple_status_attr_get_id(attr
),
590 * TODO: If the PurpleStatus is in a PurplePresence, then
591 * remove it from the PurplePresence?
594 purple_status_destroy(PurpleStatus
*status
)
596 g_return_if_fail(status
!= NULL
);
598 g_hash_table_destroy(status
->attr_values
);
600 PURPLE_DBUS_UNREGISTER_POINTER(status
);
605 notify_buddy_status_update(PurpleBuddy
*buddy
, PurplePresence
*presence
,
606 PurpleStatus
*old_status
, PurpleStatus
*new_status
)
608 if (purple_prefs_get_bool("/purple/logging/log_system"))
610 time_t current_time
= time(NULL
);
611 const char *buddy_alias
= purple_buddy_get_alias(buddy
);
615 if (old_status
!= NULL
)
617 tmp
= g_strdup_printf(_("%s (%s) changed status from %s to %s"), buddy_alias
,
618 purple_buddy_get_name(buddy
),
619 purple_status_get_name(old_status
),
620 purple_status_get_name(new_status
));
621 logtmp
= g_markup_escape_text(tmp
, -1);
625 /* old_status == NULL when an independent status is toggled. */
627 if (purple_status_is_active(new_status
))
629 tmp
= g_strdup_printf(_("%s (%s) is now %s"), buddy_alias
,
630 purple_buddy_get_name(buddy
),
631 purple_status_get_name(new_status
));
632 logtmp
= g_markup_escape_text(tmp
, -1);
636 tmp
= g_strdup_printf(_("%s (%s) is no longer %s"), buddy_alias
,
637 purple_buddy_get_name(buddy
),
638 purple_status_get_name(new_status
));
639 logtmp
= g_markup_escape_text(tmp
, -1);
643 log
= purple_account_get_log(purple_buddy_get_account(buddy
), FALSE
);
646 purple_log_write(log
, PURPLE_MESSAGE_SYSTEM
, buddy_alias
,
647 current_time
, logtmp
);
656 notify_status_update(PurplePresence
*presence
, PurpleStatus
*old_status
,
657 PurpleStatus
*new_status
)
659 PurplePresenceContext context
= purple_presence_get_context(presence
);
661 if (context
== PURPLE_PRESENCE_CONTEXT_ACCOUNT
)
663 PurpleAccount
*account
= purple_presence_get_account(presence
);
664 PurpleAccountUiOps
*ops
= purple_accounts_get_ui_ops();
666 if (purple_account_get_enabled(account
, purple_core_get_ui()))
667 purple_prpl_change_account_status(account
, old_status
, new_status
);
669 if (ops
!= NULL
&& ops
->status_changed
!= NULL
)
671 ops
->status_changed(account
, new_status
);
674 else if (context
== PURPLE_PRESENCE_CONTEXT_BUDDY
)
676 notify_buddy_status_update(purple_presence_get_buddy(presence
), presence
,
677 old_status
, new_status
);
682 status_has_changed(PurpleStatus
*status
)
684 PurplePresence
*presence
;
685 PurpleStatus
*old_status
;
687 presence
= purple_status_get_presence(status
);
690 * If this status is exclusive, then we must be setting it to "active."
691 * Since we are setting it to active, we want to set the currently
692 * active status to "inactive."
694 if (purple_status_is_exclusive(status
))
696 old_status
= purple_presence_get_active_status(presence
);
697 if (old_status
!= NULL
&& (old_status
!= status
))
698 old_status
->active
= FALSE
;
699 presence
->active_status
= status
;
704 notify_status_update(presence
, old_status
, status
);
708 purple_status_set_active(PurpleStatus
*status
, gboolean active
)
710 purple_status_set_active_with_attrs_list(status
, active
, NULL
);
714 * This used to parse the va_list directly, but now it creates a GList
715 * and passes it to purple_status_set_active_with_attrs_list(). That
716 * function was created because accounts.c needs to pass a GList of
717 * attributes to the status API.
720 purple_status_set_active_with_attrs(PurpleStatus
*status
, gboolean active
, va_list args
)
726 while ((id
= va_arg(args
, const char *)) != NULL
)
728 attrs
= g_list_append(attrs
, (char *)id
);
729 data
= va_arg(args
, void *);
730 attrs
= g_list_append(attrs
, data
);
732 purple_status_set_active_with_attrs_list(status
, active
, attrs
);
737 purple_status_set_active_with_attrs_list(PurpleStatus
*status
, gboolean active
,
740 gboolean changed
= FALSE
;
742 GList
*specified_attr_ids
= NULL
;
743 PurpleStatusType
*status_type
;
745 g_return_if_fail(status
!= NULL
);
747 if (!active
&& purple_status_is_exclusive(status
))
749 purple_debug_error("status",
750 "Cannot deactivate an exclusive status (%s).\n",
751 purple_status_get_id(status
));
755 if (status
->active
!= active
)
760 status
->active
= active
;
762 /* Set any attributes */
771 value
= purple_status_get_attr_value(status
, id
);
774 purple_debug_warning("status", "The attribute \"%s\" on the status \"%s\" is "
775 "not supported.\n", id
, status
->type
->name
);
776 /* Skip over the data and move on to the next attribute */
781 specified_attr_ids
= g_list_prepend(specified_attr_ids
, (gpointer
)id
);
783 if (value
->type
== PURPLE_TYPE_STRING
)
785 const gchar
*string_data
= l
->data
;
787 if (purple_strequal(string_data
, value
->data
.string_data
))
789 purple_status_set_attr_string(status
, id
, string_data
);
792 else if (value
->type
== PURPLE_TYPE_INT
)
794 int int_data
= GPOINTER_TO_INT(l
->data
);
796 if (int_data
== value
->data
.int_data
)
798 purple_status_set_attr_int(status
, id
, int_data
);
801 else if (value
->type
== PURPLE_TYPE_BOOLEAN
)
803 gboolean boolean_data
= GPOINTER_TO_INT(l
->data
);
805 if (boolean_data
== value
->data
.boolean_data
)
807 purple_status_set_attr_boolean(status
, id
, boolean_data
);
812 /* We don't know what the data is--skip over it */
817 /* Reset any unspecified attributes to their default value */
818 status_type
= purple_status_get_type(status
);
819 l
= purple_status_type_get_attrs(status_type
);
821 PurpleStatusAttr
*attr
;
826 if (!g_list_find_custom(specified_attr_ids
, attr
->id
, (GCompareFunc
)strcmp
)) {
827 PurpleValue
*default_value
;
828 default_value
= purple_status_attr_get_value(attr
);
829 if (default_value
->type
== PURPLE_TYPE_STRING
) {
830 const char *cur
= purple_status_get_attr_string(status
, attr
->id
);
831 const char *def
= purple_value_get_string(default_value
);
832 if ((cur
== NULL
&& def
== NULL
)
833 || (cur
!= NULL
&& def
!= NULL
834 && !strcmp(cur
, def
))) {
838 purple_status_set_attr_string(status
, attr
->id
, def
);
839 } else if (default_value
->type
== PURPLE_TYPE_INT
) {
840 int cur
= purple_status_get_attr_int(status
, attr
->id
);
841 int def
= purple_value_get_int(default_value
);
845 purple_status_set_attr_int(status
, attr
->id
, def
);
846 } else if (default_value
->type
== PURPLE_TYPE_BOOLEAN
) {
847 gboolean cur
= purple_status_get_attr_boolean(status
, attr
->id
);
848 gboolean def
= purple_value_get_boolean(default_value
);
852 purple_status_set_attr_boolean(status
, attr
->id
, def
);
857 g_list_free(specified_attr_ids
);
861 status_has_changed(status
);
865 purple_status_set_attr_boolean(PurpleStatus
*status
, const char *id
,
868 PurpleValue
*attr_value
;
870 g_return_if_fail(status
!= NULL
);
871 g_return_if_fail(id
!= NULL
);
873 /* Make sure this attribute exists and is the correct type. */
874 attr_value
= purple_status_get_attr_value(status
, id
);
875 g_return_if_fail(attr_value
!= NULL
);
876 g_return_if_fail(purple_value_get_type(attr_value
) == PURPLE_TYPE_BOOLEAN
);
878 purple_value_set_boolean(attr_value
, value
);
882 purple_status_set_attr_int(PurpleStatus
*status
, const char *id
, int value
)
884 PurpleValue
*attr_value
;
886 g_return_if_fail(status
!= NULL
);
887 g_return_if_fail(id
!= NULL
);
889 /* Make sure this attribute exists and is the correct type. */
890 attr_value
= purple_status_get_attr_value(status
, id
);
891 g_return_if_fail(attr_value
!= NULL
);
892 g_return_if_fail(purple_value_get_type(attr_value
) == PURPLE_TYPE_INT
);
894 purple_value_set_int(attr_value
, value
);
898 purple_status_set_attr_string(PurpleStatus
*status
, const char *id
,
901 PurpleValue
*attr_value
;
903 g_return_if_fail(status
!= NULL
);
904 g_return_if_fail(id
!= NULL
);
906 /* Make sure this attribute exists and is the correct type. */
907 attr_value
= purple_status_get_attr_value(status
, id
);
908 /* This used to be g_return_if_fail, but it's failing a LOT, so
909 * let's generate a log error for now. */
910 /* g_return_if_fail(attr_value != NULL); */
911 if (attr_value
== NULL
) {
912 purple_debug_error("status",
913 "Attempted to set status attribute '%s' for "
914 "status '%s', which is not legal. Fix "
916 purple_status_type_get_name(purple_status_get_type(status
)));
919 g_return_if_fail(purple_value_get_type(attr_value
) == PURPLE_TYPE_STRING
);
921 /* XXX: Check if the value has actually changed. If it has, and the status
922 * is active, should this trigger 'status_has_changed'? */
923 purple_value_set_string(attr_value
, value
);
927 purple_status_get_type(const PurpleStatus
*status
)
929 g_return_val_if_fail(status
!= NULL
, NULL
);
935 purple_status_get_presence(const PurpleStatus
*status
)
937 g_return_val_if_fail(status
!= NULL
, NULL
);
939 return status
->presence
;
943 purple_status_get_id(const PurpleStatus
*status
)
945 g_return_val_if_fail(status
!= NULL
, NULL
);
947 return purple_status_type_get_id(purple_status_get_type(status
));
951 purple_status_get_name(const PurpleStatus
*status
)
953 g_return_val_if_fail(status
!= NULL
, NULL
);
955 return purple_status_type_get_name(purple_status_get_type(status
));
959 purple_status_is_independent(const PurpleStatus
*status
)
961 g_return_val_if_fail(status
!= NULL
, FALSE
);
963 return purple_status_type_is_independent(purple_status_get_type(status
));
967 purple_status_is_exclusive(const PurpleStatus
*status
)
969 g_return_val_if_fail(status
!= NULL
, FALSE
);
971 return purple_status_type_is_exclusive(purple_status_get_type(status
));
975 purple_status_is_available(const PurpleStatus
*status
)
977 g_return_val_if_fail(status
!= NULL
, FALSE
);
979 return purple_status_type_is_available(purple_status_get_type(status
));
983 purple_status_is_active(const PurpleStatus
*status
)
985 g_return_val_if_fail(status
!= NULL
, FALSE
);
987 return status
->active
;
991 purple_status_is_online(const PurpleStatus
*status
)
993 PurpleStatusPrimitive primitive
;
995 g_return_val_if_fail( status
!= NULL
, FALSE
);
997 primitive
= purple_status_type_get_primitive(purple_status_get_type(status
));
999 return (primitive
!= PURPLE_STATUS_UNSET
&&
1000 primitive
!= PURPLE_STATUS_OFFLINE
);
1004 purple_status_get_attr_value(const PurpleStatus
*status
, const char *id
)
1006 g_return_val_if_fail(status
!= NULL
, NULL
);
1007 g_return_val_if_fail(id
!= NULL
, NULL
);
1009 return (PurpleValue
*)g_hash_table_lookup(status
->attr_values
, id
);
1013 purple_status_get_attr_boolean(const PurpleStatus
*status
, const char *id
)
1015 const PurpleValue
*value
;
1017 g_return_val_if_fail(status
!= NULL
, FALSE
);
1018 g_return_val_if_fail(id
!= NULL
, FALSE
);
1020 if ((value
= purple_status_get_attr_value(status
, id
)) == NULL
)
1023 g_return_val_if_fail(purple_value_get_type(value
) == PURPLE_TYPE_BOOLEAN
, FALSE
);
1025 return purple_value_get_boolean(value
);
1029 purple_status_get_attr_int(const PurpleStatus
*status
, const char *id
)
1031 const PurpleValue
*value
;
1033 g_return_val_if_fail(status
!= NULL
, 0);
1034 g_return_val_if_fail(id
!= NULL
, 0);
1036 if ((value
= purple_status_get_attr_value(status
, id
)) == NULL
)
1039 g_return_val_if_fail(purple_value_get_type(value
) == PURPLE_TYPE_INT
, 0);
1041 return purple_value_get_int(value
);
1045 purple_status_get_attr_string(const PurpleStatus
*status
, const char *id
)
1047 const PurpleValue
*value
;
1049 g_return_val_if_fail(status
!= NULL
, NULL
);
1050 g_return_val_if_fail(id
!= NULL
, NULL
);
1052 if ((value
= purple_status_get_attr_value(status
, id
)) == NULL
)
1055 g_return_val_if_fail(purple_value_get_type(value
) == PURPLE_TYPE_STRING
, NULL
);
1057 return purple_value_get_string(value
);
1061 purple_status_compare(const PurpleStatus
*status1
, const PurpleStatus
*status2
)
1063 PurpleStatusType
*type1
, *type2
;
1064 int score1
= 0, score2
= 0;
1066 if ((status1
== NULL
&& status2
== NULL
) ||
1067 (status1
== status2
))
1071 else if (status1
== NULL
)
1073 else if (status2
== NULL
)
1076 type1
= purple_status_get_type(status1
);
1077 type2
= purple_status_get_type(status2
);
1079 if (purple_status_is_active(status1
))
1080 score1
= primitive_scores
[purple_status_type_get_primitive(type1
)];
1082 if (purple_status_is_active(status2
))
1083 score2
= primitive_scores
[purple_status_type_get_primitive(type2
)];
1085 if (score1
> score2
)
1087 else if (score1
< score2
)
1094 /**************************************************************************
1095 * PurplePresence API
1096 **************************************************************************/
1098 purple_presence_new(PurplePresenceContext context
)
1100 PurplePresence
*presence
;
1102 g_return_val_if_fail(context
!= PURPLE_PRESENCE_CONTEXT_UNSET
, NULL
);
1104 presence
= g_new0(PurplePresence
, 1);
1105 PURPLE_DBUS_REGISTER_POINTER(presence
, PurplePresence
);
1107 presence
->context
= context
;
1109 presence
->status_table
=
1110 g_hash_table_new_full(g_str_hash
, g_str_equal
,
1117 purple_presence_new_for_account(PurpleAccount
*account
)
1119 PurplePresence
*presence
= NULL
;
1120 g_return_val_if_fail(account
!= NULL
, NULL
);
1122 presence
= purple_presence_new(PURPLE_PRESENCE_CONTEXT_ACCOUNT
);
1123 presence
->u
.account
= account
;
1124 presence
->statuses
= purple_prpl_get_statuses(account
, presence
);
1130 purple_presence_new_for_conv(PurpleConversation
*conv
)
1132 PurplePresence
*presence
;
1134 g_return_val_if_fail(conv
!= NULL
, NULL
);
1136 presence
= purple_presence_new(PURPLE_PRESENCE_CONTEXT_CONV
);
1137 presence
->u
.chat
.conv
= conv
;
1138 /* presence->statuses = purple_prpl_get_statuses(conv->account, presence); ? */
1144 purple_presence_new_for_buddy(PurpleBuddy
*buddy
)
1146 PurplePresence
*presence
;
1147 PurpleAccount
*account
;
1149 g_return_val_if_fail(buddy
!= NULL
, NULL
);
1150 account
= purple_buddy_get_account(buddy
);
1152 presence
= purple_presence_new(PURPLE_PRESENCE_CONTEXT_BUDDY
);
1154 presence
->u
.buddy
.name
= g_strdup(purple_buddy_get_name(buddy
));
1155 presence
->u
.buddy
.account
= account
;
1156 presence
->statuses
= purple_prpl_get_statuses(account
, presence
);
1158 presence
->u
.buddy
.buddy
= buddy
;
1164 purple_presence_destroy(PurplePresence
*presence
)
1166 g_return_if_fail(presence
!= NULL
);
1168 if (purple_presence_get_context(presence
) == PURPLE_PRESENCE_CONTEXT_BUDDY
)
1170 g_free(presence
->u
.buddy
.name
);
1172 else if (purple_presence_get_context(presence
) == PURPLE_PRESENCE_CONTEXT_CONV
)
1174 g_free(presence
->u
.chat
.user
);
1177 g_list_foreach(presence
->statuses
, (GFunc
)purple_status_destroy
, NULL
);
1178 g_list_free(presence
->statuses
);
1180 g_hash_table_destroy(presence
->status_table
);
1182 PURPLE_DBUS_UNREGISTER_POINTER(presence
);
1187 purple_presence_add_status(PurplePresence
*presence
, PurpleStatus
*status
)
1189 g_return_if_fail(presence
!= NULL
);
1190 g_return_if_fail(status
!= NULL
);
1192 presence
->statuses
= g_list_append(presence
->statuses
, status
);
1194 g_hash_table_insert(presence
->status_table
,
1195 g_strdup(purple_status_get_id(status
)), status
);
1199 purple_presence_add_list(PurplePresence
*presence
, GList
*source_list
)
1203 g_return_if_fail(presence
!= NULL
);
1204 g_return_if_fail(source_list
!= NULL
);
1206 for (l
= source_list
; l
!= NULL
; l
= l
->next
)
1207 purple_presence_add_status(presence
, (PurpleStatus
*)l
->data
);
1211 purple_presence_set_status_active(PurplePresence
*presence
, const char *status_id
,
1214 PurpleStatus
*status
;
1216 g_return_if_fail(presence
!= NULL
);
1217 g_return_if_fail(status_id
!= NULL
);
1219 status
= purple_presence_get_status(presence
, status_id
);
1221 g_return_if_fail(status
!= NULL
);
1222 /* TODO: Should we do the following? */
1223 /* g_return_if_fail(active == status->active); */
1225 if (purple_status_is_exclusive(status
))
1229 purple_debug_warning("status",
1230 "Attempted to set a non-independent status "
1231 "(%s) inactive. Only independent statuses "
1232 "can be specifically marked inactive.",
1238 purple_status_set_active(status
, active
);
1242 purple_presence_switch_status(PurplePresence
*presence
, const char *status_id
)
1244 purple_presence_set_status_active(presence
, status_id
, TRUE
);
1248 update_buddy_idle(PurpleBuddy
*buddy
, PurplePresence
*presence
,
1249 time_t current_time
, gboolean old_idle
, gboolean idle
)
1251 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
1252 PurpleAccount
*account
= purple_buddy_get_account(buddy
);
1254 if (!old_idle
&& idle
)
1256 if (purple_prefs_get_bool("/purple/logging/log_system"))
1258 PurpleLog
*log
= purple_account_get_log(account
, FALSE
);
1263 tmp
= g_strdup_printf(_("%s became idle"),
1264 purple_buddy_get_alias(buddy
));
1265 tmp2
= g_markup_escape_text(tmp
, -1);
1268 purple_log_write(log
, PURPLE_MESSAGE_SYSTEM
,
1269 purple_buddy_get_alias(buddy
), current_time
, tmp2
);
1274 else if (old_idle
&& !idle
)
1276 if (purple_prefs_get_bool("/purple/logging/log_system"))
1278 PurpleLog
*log
= purple_account_get_log(account
, FALSE
);
1283 tmp
= g_strdup_printf(_("%s became unidle"),
1284 purple_buddy_get_alias(buddy
));
1285 tmp2
= g_markup_escape_text(tmp
, -1);
1288 purple_log_write(log
, PURPLE_MESSAGE_SYSTEM
,
1289 purple_buddy_get_alias(buddy
), current_time
, tmp2
);
1295 if (old_idle
!= idle
)
1296 purple_signal_emit(purple_blist_get_handle(), "buddy-idle-changed", buddy
,
1299 purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy
));
1301 /* Should this be done here? It'd perhaps make more sense to
1302 * connect to buddy-[un]idle signals and update from there
1305 if (ops
!= NULL
&& ops
->update
!= NULL
)
1306 ops
->update(purple_get_blist(), (PurpleBlistNode
*)buddy
);
1310 purple_presence_set_idle(PurplePresence
*presence
, gboolean idle
, time_t idle_time
)
1313 time_t current_time
;
1315 g_return_if_fail(presence
!= NULL
);
1317 if (presence
->idle
== idle
&& presence
->idle_time
== idle_time
)
1320 old_idle
= presence
->idle
;
1321 presence
->idle
= idle
;
1322 presence
->idle_time
= (idle
? idle_time
: 0);
1324 current_time
= time(NULL
);
1326 if (purple_presence_get_context(presence
) == PURPLE_PRESENCE_CONTEXT_BUDDY
)
1328 update_buddy_idle(purple_presence_get_buddy(presence
), presence
, current_time
,
1331 else if (purple_presence_get_context(presence
) == PURPLE_PRESENCE_CONTEXT_ACCOUNT
)
1333 PurpleAccount
*account
;
1334 PurpleConnection
*gc
= NULL
;
1335 PurplePlugin
*prpl
= NULL
;
1336 PurplePluginProtocolInfo
*prpl_info
= NULL
;
1338 account
= purple_presence_get_account(presence
);
1340 if (purple_prefs_get_bool("/purple/logging/log_system"))
1342 PurpleLog
*log
= purple_account_get_log(account
, FALSE
);
1349 tmp
= g_strdup_printf(_("+++ %s became idle"), purple_account_get_username(account
));
1351 tmp
= g_strdup_printf(_("+++ %s became unidle"), purple_account_get_username(account
));
1353 msg
= g_markup_escape_text(tmp
, -1);
1355 purple_log_write(log
, PURPLE_MESSAGE_SYSTEM
,
1356 purple_account_get_username(account
),
1357 (idle
? idle_time
: current_time
), msg
);
1362 gc
= purple_account_get_connection(account
);
1365 prpl
= purple_connection_get_prpl(gc
);
1367 if(PURPLE_CONNECTION_IS_CONNECTED(gc
) && prpl
!= NULL
)
1368 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(prpl
);
1370 if (prpl_info
&& prpl_info
->set_idle
)
1371 prpl_info
->set_idle(gc
, (idle
? (current_time
- idle_time
) : 0));
1376 purple_presence_set_login_time(PurplePresence
*presence
, time_t login_time
)
1378 g_return_if_fail(presence
!= NULL
);
1380 if (presence
->login_time
== login_time
)
1383 presence
->login_time
= login_time
;
1386 PurplePresenceContext
1387 purple_presence_get_context(const PurplePresence
*presence
)
1389 g_return_val_if_fail(presence
!= NULL
, PURPLE_PRESENCE_CONTEXT_UNSET
);
1391 return presence
->context
;
1395 purple_presence_get_account(const PurplePresence
*presence
)
1397 PurplePresenceContext context
;
1399 g_return_val_if_fail(presence
!= NULL
, NULL
);
1401 context
= purple_presence_get_context(presence
);
1403 g_return_val_if_fail(context
== PURPLE_PRESENCE_CONTEXT_ACCOUNT
||
1404 context
== PURPLE_PRESENCE_CONTEXT_BUDDY
, NULL
);
1406 return presence
->u
.account
;
1409 PurpleConversation
*
1410 purple_presence_get_conversation(const PurplePresence
*presence
)
1412 g_return_val_if_fail(presence
!= NULL
, NULL
);
1413 g_return_val_if_fail(purple_presence_get_context(presence
) ==
1414 PURPLE_PRESENCE_CONTEXT_CONV
, NULL
);
1416 return presence
->u
.chat
.conv
;
1420 purple_presence_get_chat_user(const PurplePresence
*presence
)
1422 g_return_val_if_fail(presence
!= NULL
, NULL
);
1423 g_return_val_if_fail(purple_presence_get_context(presence
) ==
1424 PURPLE_PRESENCE_CONTEXT_CONV
, NULL
);
1426 return presence
->u
.chat
.user
;
1430 purple_presence_get_buddy(const PurplePresence
*presence
)
1432 g_return_val_if_fail(presence
!= NULL
, NULL
);
1433 g_return_val_if_fail(purple_presence_get_context(presence
) ==
1434 PURPLE_PRESENCE_CONTEXT_BUDDY
, NULL
);
1436 return presence
->u
.buddy
.buddy
;
1440 purple_presence_get_statuses(const PurplePresence
*presence
)
1442 g_return_val_if_fail(presence
!= NULL
, NULL
);
1444 return presence
->statuses
;
1448 purple_presence_get_status(const PurplePresence
*presence
, const char *status_id
)
1450 PurpleStatus
*status
;
1453 g_return_val_if_fail(presence
!= NULL
, NULL
);
1454 g_return_val_if_fail(status_id
!= NULL
, NULL
);
1456 /* What's the purpose of this hash table? */
1457 status
= (PurpleStatus
*)g_hash_table_lookup(presence
->status_table
,
1460 if (status
== NULL
) {
1461 for (l
= purple_presence_get_statuses(presence
);
1462 l
!= NULL
&& status
== NULL
; l
= l
->next
)
1464 PurpleStatus
*temp_status
= l
->data
;
1466 if (purple_strequal(status_id
, purple_status_get_id(temp_status
)))
1467 status
= temp_status
;
1471 g_hash_table_insert(presence
->status_table
,
1472 g_strdup(purple_status_get_id(status
)), status
);
1479 purple_presence_get_active_status(const PurplePresence
*presence
)
1481 g_return_val_if_fail(presence
!= NULL
, NULL
);
1483 return presence
->active_status
;
1487 purple_presence_is_available(const PurplePresence
*presence
)
1489 PurpleStatus
*status
;
1491 g_return_val_if_fail(presence
!= NULL
, FALSE
);
1493 status
= purple_presence_get_active_status(presence
);
1495 return ((status
!= NULL
&& purple_status_is_available(status
)) &&
1496 !purple_presence_is_idle(presence
));
1500 purple_presence_is_online(const PurplePresence
*presence
)
1502 PurpleStatus
*status
;
1504 g_return_val_if_fail(presence
!= NULL
, FALSE
);
1506 if ((status
= purple_presence_get_active_status(presence
)) == NULL
)
1509 return purple_status_is_online(status
);
1513 purple_presence_is_status_active(const PurplePresence
*presence
,
1514 const char *status_id
)
1516 PurpleStatus
*status
;
1518 g_return_val_if_fail(presence
!= NULL
, FALSE
);
1519 g_return_val_if_fail(status_id
!= NULL
, FALSE
);
1521 status
= purple_presence_get_status(presence
, status_id
);
1523 return (status
!= NULL
&& purple_status_is_active(status
));
1527 purple_presence_is_status_primitive_active(const PurplePresence
*presence
,
1528 PurpleStatusPrimitive primitive
)
1532 g_return_val_if_fail(presence
!= NULL
, FALSE
);
1533 g_return_val_if_fail(primitive
!= PURPLE_STATUS_UNSET
, FALSE
);
1535 for (l
= purple_presence_get_statuses(presence
);
1536 l
!= NULL
; l
= l
->next
) {
1537 PurpleStatus
*temp_status
= l
->data
;
1538 PurpleStatusType
*type
= purple_status_get_type(temp_status
);
1540 if (purple_status_type_get_primitive(type
) == primitive
&&
1541 purple_status_is_active(temp_status
))
1548 purple_presence_is_idle(const PurplePresence
*presence
)
1550 g_return_val_if_fail(presence
!= NULL
, FALSE
);
1552 return purple_presence_is_online(presence
) && presence
->idle
;
1556 purple_presence_get_idle_time(const PurplePresence
*presence
)
1558 g_return_val_if_fail(presence
!= NULL
, 0);
1560 return presence
->idle_time
;
1564 purple_presence_get_login_time(const PurplePresence
*presence
)
1566 g_return_val_if_fail(presence
!= NULL
, 0);
1568 return purple_presence_is_online(presence
) ? presence
->login_time
: 0;
1572 purple_presence_compute_score(const PurplePresence
*presence
)
1577 for (l
= purple_presence_get_statuses(presence
); l
!= NULL
; l
= l
->next
) {
1578 PurpleStatus
*status
= (PurpleStatus
*)l
->data
;
1579 PurpleStatusType
*type
= purple_status_get_type(status
);
1581 if (purple_status_is_active(status
)) {
1582 score
+= primitive_scores
[purple_status_type_get_primitive(type
)];
1583 if (!purple_status_is_online(status
)) {
1584 PurpleBuddy
*b
= purple_presence_get_buddy(presence
);
1585 if (b
&& purple_account_supports_offline_message(purple_buddy_get_account(b
), b
))
1586 score
+= primitive_scores
[SCORE_OFFLINE_MESSAGE
];
1590 score
+= purple_account_get_int(purple_presence_get_account(presence
), "score", 0);
1591 if (purple_presence_is_idle(presence
))
1592 score
+= primitive_scores
[SCORE_IDLE
];
1597 purple_presence_compare(const PurplePresence
*presence1
,
1598 const PurplePresence
*presence2
)
1600 time_t idle_time_1
, idle_time_2
;
1601 int score1
= 0, score2
= 0;
1603 if (presence1
== presence2
)
1605 else if (presence1
== NULL
)
1607 else if (presence2
== NULL
)
1610 if (purple_presence_is_online(presence1
) &&
1611 !purple_presence_is_online(presence2
))
1613 else if (purple_presence_is_online(presence2
) &&
1614 !purple_presence_is_online(presence1
))
1617 /* Compute the score of the first set of statuses. */
1618 score1
= purple_presence_compute_score(presence1
);
1620 /* Compute the score of the second set of statuses. */
1621 score2
= purple_presence_compute_score(presence2
);
1623 idle_time_1
= time(NULL
) - purple_presence_get_idle_time(presence1
);
1624 idle_time_2
= time(NULL
) - purple_presence_get_idle_time(presence2
);
1626 if (idle_time_1
> idle_time_2
)
1627 score1
+= primitive_scores
[SCORE_IDLE_TIME
];
1628 else if (idle_time_1
< idle_time_2
)
1629 score2
+= primitive_scores
[SCORE_IDLE_TIME
];
1631 if (score1
< score2
)
1633 else if (score1
> score2
)
1640 /**************************************************************************
1642 **************************************************************************/
1644 score_pref_changed_cb(const char *name
, PurplePrefType type
,
1645 gconstpointer value
, gpointer data
)
1647 int index
= GPOINTER_TO_INT(data
);
1649 primitive_scores
[index
] = GPOINTER_TO_INT(value
);
1653 purple_status_get_handle(void) {
1660 purple_status_init(void)
1662 void *handle
= purple_status_get_handle
;
1664 purple_prefs_add_none("/purple/status");
1665 purple_prefs_add_none("/purple/status/scores");
1667 purple_prefs_add_int("/purple/status/scores/offline",
1668 primitive_scores
[PURPLE_STATUS_OFFLINE
]);
1669 purple_prefs_add_int("/purple/status/scores/available",
1670 primitive_scores
[PURPLE_STATUS_AVAILABLE
]);
1671 purple_prefs_add_int("/purple/status/scores/invisible",
1672 primitive_scores
[PURPLE_STATUS_INVISIBLE
]);
1673 purple_prefs_add_int("/purple/status/scores/away",
1674 primitive_scores
[PURPLE_STATUS_AWAY
]);
1675 purple_prefs_add_int("/purple/status/scores/extended_away",
1676 primitive_scores
[PURPLE_STATUS_EXTENDED_AWAY
]);
1677 purple_prefs_add_int("/purple/status/scores/idle",
1678 primitive_scores
[SCORE_IDLE
]);
1679 purple_prefs_add_int("/purple/status/scores/offline_msg",
1680 primitive_scores
[SCORE_OFFLINE_MESSAGE
]);
1682 purple_prefs_connect_callback(handle
, "/purple/status/scores/offline",
1683 score_pref_changed_cb
,
1684 GINT_TO_POINTER(PURPLE_STATUS_OFFLINE
));
1685 purple_prefs_connect_callback(handle
, "/purple/status/scores/available",
1686 score_pref_changed_cb
,
1687 GINT_TO_POINTER(PURPLE_STATUS_AVAILABLE
));
1688 purple_prefs_connect_callback(handle
, "/purple/status/scores/invisible",
1689 score_pref_changed_cb
,
1690 GINT_TO_POINTER(PURPLE_STATUS_INVISIBLE
));
1691 purple_prefs_connect_callback(handle
, "/purple/status/scores/away",
1692 score_pref_changed_cb
,
1693 GINT_TO_POINTER(PURPLE_STATUS_AWAY
));
1694 purple_prefs_connect_callback(handle
, "/purple/status/scores/extended_away",
1695 score_pref_changed_cb
,
1696 GINT_TO_POINTER(PURPLE_STATUS_EXTENDED_AWAY
));
1697 purple_prefs_connect_callback(handle
, "/purple/status/scores/idle",
1698 score_pref_changed_cb
,
1699 GINT_TO_POINTER(SCORE_IDLE
));
1700 purple_prefs_connect_callback(handle
, "/purple/status/scores/offline_msg",
1701 score_pref_changed_cb
,
1702 GINT_TO_POINTER(SCORE_OFFLINE_MESSAGE
));
1704 purple_prefs_trigger_callback("/purple/status/scores/offline");
1705 purple_prefs_trigger_callback("/purple/status/scores/available");
1706 purple_prefs_trigger_callback("/purple/status/scores/invisible");
1707 purple_prefs_trigger_callback("/purple/status/scores/away");
1708 purple_prefs_trigger_callback("/purple/status/scores/extended_away");
1709 purple_prefs_trigger_callback("/purple/status/scores/idle");
1710 purple_prefs_trigger_callback("/purple/status/scores/offline_msg");
1714 purple_status_uninit(void)