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 */
141 -10, /* idle, special case. */
142 -5, /* idle time, special case. */
143 10 /* Offline messageable */
147 #define SCORE_IDLE_TIME 10
148 #define SCORE_OFFLINE_MESSAGE 11
150 /**************************************************************************
151 * PurpleStatusPrimitive API
152 **************************************************************************/
153 static struct PurpleStatusPrimitiveMap
155 PurpleStatusPrimitive type
;
159 } const status_primitive_map
[] =
161 { PURPLE_STATUS_UNSET
, "unset", N_("Unset") },
162 { PURPLE_STATUS_OFFLINE
, "offline", N_("Offline") },
163 { PURPLE_STATUS_AVAILABLE
, "available", N_("Available") },
164 { PURPLE_STATUS_UNAVAILABLE
, "unavailable", N_("Do not disturb") },
165 { PURPLE_STATUS_INVISIBLE
, "invisible", N_("Invisible") },
166 { PURPLE_STATUS_AWAY
, "away", N_("Away") },
167 { PURPLE_STATUS_EXTENDED_AWAY
, "extended_away", N_("Extended away") },
168 { PURPLE_STATUS_MOBILE
, "mobile", N_("Mobile") },
169 { PURPLE_STATUS_TUNE
, "tune", N_("Listening to music"), },
170 { PURPLE_STATUS_MOOD
, "mood", N_("Feeling") },
174 purple_primitive_get_id_from_type(PurpleStatusPrimitive type
)
178 for (i
= 0; i
< PURPLE_STATUS_NUM_PRIMITIVES
; i
++)
180 if (type
== status_primitive_map
[i
].type
)
181 return status_primitive_map
[i
].id
;
184 return status_primitive_map
[0].id
;
188 purple_primitive_get_name_from_type(PurpleStatusPrimitive type
)
192 for (i
= 0; i
< PURPLE_STATUS_NUM_PRIMITIVES
; i
++)
194 if (type
== status_primitive_map
[i
].type
)
195 return _(status_primitive_map
[i
].name
);
198 return _(status_primitive_map
[0].name
);
201 PurpleStatusPrimitive
202 purple_primitive_get_type_from_id(const char *id
)
206 g_return_val_if_fail(id
!= NULL
, PURPLE_STATUS_UNSET
);
208 for (i
= 0; i
< PURPLE_STATUS_NUM_PRIMITIVES
; i
++)
210 if (purple_strequal(id
, status_primitive_map
[i
].id
))
211 return status_primitive_map
[i
].type
;
214 return status_primitive_map
[0].type
;
218 /**************************************************************************
219 * PurpleStatusType API
220 **************************************************************************/
222 purple_status_type_new_full(PurpleStatusPrimitive primitive
, const char *id
,
223 const char *name
, gboolean saveable
,
224 gboolean user_settable
, gboolean independent
)
226 PurpleStatusType
*status_type
;
228 g_return_val_if_fail(primitive
!= PURPLE_STATUS_UNSET
, NULL
);
230 status_type
= g_new0(PurpleStatusType
, 1);
231 PURPLE_DBUS_REGISTER_POINTER(status_type
, PurpleStatusType
);
233 status_type
->primitive
= primitive
;
234 status_type
->saveable
= saveable
;
235 status_type
->user_settable
= user_settable
;
236 status_type
->independent
= independent
;
239 status_type
->id
= g_strdup(id
);
241 status_type
->id
= g_strdup(purple_primitive_get_id_from_type(primitive
));
244 status_type
->name
= g_strdup(name
);
246 status_type
->name
= g_strdup(purple_primitive_get_name_from_type(primitive
));
252 purple_status_type_new(PurpleStatusPrimitive primitive
, const char *id
,
253 const char *name
, gboolean user_settable
)
255 g_return_val_if_fail(primitive
!= PURPLE_STATUS_UNSET
, NULL
);
257 return purple_status_type_new_full(primitive
, id
, name
, TRUE
,
258 user_settable
, FALSE
);
262 purple_status_type_new_with_attrs(PurpleStatusPrimitive primitive
,
263 const char *id
, const char *name
,
264 gboolean saveable
, gboolean user_settable
,
265 gboolean independent
, const char *attr_id
,
266 const char *attr_name
, PurpleValue
*attr_value
,
269 PurpleStatusType
*status_type
;
272 g_return_val_if_fail(primitive
!= PURPLE_STATUS_UNSET
, NULL
);
273 g_return_val_if_fail(attr_id
!= NULL
, NULL
);
274 g_return_val_if_fail(attr_name
!= NULL
, NULL
);
275 g_return_val_if_fail(attr_value
!= NULL
, NULL
);
277 status_type
= purple_status_type_new_full(primitive
, id
, name
, saveable
,
278 user_settable
, independent
);
280 /* Add the first attribute */
281 purple_status_type_add_attr(status_type
, attr_id
, attr_name
, attr_value
);
283 va_start(args
, attr_value
);
284 purple_status_type_add_attrs_vargs(status_type
, args
);
291 purple_status_type_destroy(PurpleStatusType
*status_type
)
293 g_return_if_fail(status_type
!= NULL
);
295 g_free(status_type
->id
);
296 g_free(status_type
->name
);
297 g_free(status_type
->primary_attr_id
);
299 g_list_foreach(status_type
->attrs
, (GFunc
)purple_status_attr_destroy
, NULL
);
300 g_list_free(status_type
->attrs
);
302 PURPLE_DBUS_UNREGISTER_POINTER(status_type
);
307 purple_status_type_set_primary_attr(PurpleStatusType
*status_type
, const char *id
)
309 g_return_if_fail(status_type
!= NULL
);
311 g_free(status_type
->primary_attr_id
);
312 status_type
->primary_attr_id
= g_strdup(id
);
316 purple_status_type_add_attr(PurpleStatusType
*status_type
, const char *id
,
317 const char *name
, PurpleValue
*value
)
319 PurpleStatusAttr
*attr
;
321 g_return_if_fail(status_type
!= NULL
);
322 g_return_if_fail(id
!= NULL
);
323 g_return_if_fail(name
!= NULL
);
324 g_return_if_fail(value
!= NULL
);
326 attr
= purple_status_attr_new(id
, name
, value
);
328 status_type
->attrs
= g_list_append(status_type
->attrs
, attr
);
332 purple_status_type_add_attrs_vargs(PurpleStatusType
*status_type
, va_list args
)
334 const char *id
, *name
;
337 g_return_if_fail(status_type
!= NULL
);
339 while ((id
= va_arg(args
, const char *)) != NULL
)
341 name
= va_arg(args
, const char *);
342 g_return_if_fail(name
!= NULL
);
344 value
= va_arg(args
, PurpleValue
*);
345 g_return_if_fail(value
!= NULL
);
347 purple_status_type_add_attr(status_type
, id
, name
, value
);
352 purple_status_type_add_attrs(PurpleStatusType
*status_type
, const char *id
,
353 const char *name
, PurpleValue
*value
, ...)
357 g_return_if_fail(status_type
!= NULL
);
358 g_return_if_fail(id
!= NULL
);
359 g_return_if_fail(name
!= NULL
);
360 g_return_if_fail(value
!= NULL
);
362 /* Add the first attribute */
363 purple_status_type_add_attr(status_type
, id
, name
, value
);
365 va_start(args
, value
);
366 purple_status_type_add_attrs_vargs(status_type
, args
);
370 PurpleStatusPrimitive
371 purple_status_type_get_primitive(const PurpleStatusType
*status_type
)
373 g_return_val_if_fail(status_type
!= NULL
, PURPLE_STATUS_UNSET
);
375 return status_type
->primitive
;
379 purple_status_type_get_id(const PurpleStatusType
*status_type
)
381 g_return_val_if_fail(status_type
!= NULL
, NULL
);
383 return status_type
->id
;
387 purple_status_type_get_name(const PurpleStatusType
*status_type
)
389 g_return_val_if_fail(status_type
!= NULL
, NULL
);
391 return status_type
->name
;
395 purple_status_type_is_saveable(const PurpleStatusType
*status_type
)
397 g_return_val_if_fail(status_type
!= NULL
, FALSE
);
399 return status_type
->saveable
;
403 purple_status_type_is_user_settable(const PurpleStatusType
*status_type
)
405 g_return_val_if_fail(status_type
!= NULL
, FALSE
);
407 return status_type
->user_settable
;
411 purple_status_type_is_independent(const PurpleStatusType
*status_type
)
413 g_return_val_if_fail(status_type
!= NULL
, FALSE
);
415 return status_type
->independent
;
419 purple_status_type_is_exclusive(const PurpleStatusType
*status_type
)
421 g_return_val_if_fail(status_type
!= NULL
, FALSE
);
423 return !status_type
->independent
;
427 purple_status_type_is_available(const PurpleStatusType
*status_type
)
429 PurpleStatusPrimitive primitive
;
431 g_return_val_if_fail(status_type
!= NULL
, FALSE
);
433 primitive
= purple_status_type_get_primitive(status_type
);
435 return (primitive
== PURPLE_STATUS_AVAILABLE
);
439 purple_status_type_get_primary_attr(const PurpleStatusType
*status_type
)
441 g_return_val_if_fail(status_type
!= NULL
, NULL
);
443 return status_type
->primary_attr_id
;
447 purple_status_type_get_attr(const PurpleStatusType
*status_type
, const char *id
)
451 g_return_val_if_fail(status_type
!= NULL
, NULL
);
452 g_return_val_if_fail(id
!= NULL
, NULL
);
454 for (l
= status_type
->attrs
; l
!= NULL
; l
= l
->next
)
456 PurpleStatusAttr
*attr
= (PurpleStatusAttr
*)l
->data
;
458 if (purple_strequal(purple_status_attr_get_id(attr
), id
))
466 purple_status_type_get_attrs(const PurpleStatusType
*status_type
)
468 g_return_val_if_fail(status_type
!= NULL
, NULL
);
470 return status_type
->attrs
;
473 const PurpleStatusType
*
474 purple_status_type_find_with_id(GList
*status_types
, const char *id
)
476 PurpleStatusType
*status_type
;
478 g_return_val_if_fail(id
!= NULL
, NULL
);
480 while (status_types
!= NULL
)
482 status_type
= status_types
->data
;
484 if (purple_strequal(id
, status_type
->id
))
487 status_types
= status_types
->next
;
494 /**************************************************************************
495 * PurpleStatusAttr API
496 **************************************************************************/
498 purple_status_attr_new(const char *id
, const char *name
, PurpleValue
*value_type
)
500 PurpleStatusAttr
*attr
;
502 g_return_val_if_fail(id
!= NULL
, NULL
);
503 g_return_val_if_fail(name
!= NULL
, NULL
);
504 g_return_val_if_fail(value_type
!= NULL
, NULL
);
506 attr
= g_new0(PurpleStatusAttr
, 1);
507 PURPLE_DBUS_REGISTER_POINTER(attr
, PurpleStatusAttr
);
509 attr
->id
= g_strdup(id
);
510 attr
->name
= g_strdup(name
);
511 attr
->value_type
= value_type
;
517 purple_status_attr_destroy(PurpleStatusAttr
*attr
)
519 g_return_if_fail(attr
!= NULL
);
524 purple_value_destroy(attr
->value_type
);
526 PURPLE_DBUS_UNREGISTER_POINTER(attr
);
531 purple_status_attr_get_id(const PurpleStatusAttr
*attr
)
533 g_return_val_if_fail(attr
!= NULL
, NULL
);
539 purple_status_attr_get_name(const PurpleStatusAttr
*attr
)
541 g_return_val_if_fail(attr
!= NULL
, NULL
);
547 purple_status_attr_get_value(const PurpleStatusAttr
*attr
)
549 g_return_val_if_fail(attr
!= NULL
, NULL
);
551 return attr
->value_type
;
555 /**************************************************************************
557 **************************************************************************/
559 purple_status_new(PurpleStatusType
*status_type
, PurplePresence
*presence
)
561 PurpleStatus
*status
;
564 g_return_val_if_fail(status_type
!= NULL
, NULL
);
565 g_return_val_if_fail(presence
!= NULL
, NULL
);
567 status
= g_new0(PurpleStatus
, 1);
568 PURPLE_DBUS_REGISTER_POINTER(status
, PurpleStatus
);
570 status
->type
= status_type
;
571 status
->presence
= presence
;
573 status
->attr_values
=
574 g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
,
575 (GDestroyNotify
)purple_value_destroy
);
577 for (l
= purple_status_type_get_attrs(status_type
); l
!= NULL
; l
= l
->next
)
579 PurpleStatusAttr
*attr
= (PurpleStatusAttr
*)l
->data
;
580 PurpleValue
*value
= purple_status_attr_get_value(attr
);
581 PurpleValue
*new_value
= purple_value_dup(value
);
583 g_hash_table_insert(status
->attr_values
,
584 (char *)purple_status_attr_get_id(attr
),
592 * TODO: If the PurpleStatus is in a PurplePresence, then
593 * remove it from the PurplePresence?
596 purple_status_destroy(PurpleStatus
*status
)
598 g_return_if_fail(status
!= NULL
);
600 g_hash_table_destroy(status
->attr_values
);
602 PURPLE_DBUS_UNREGISTER_POINTER(status
);
607 notify_buddy_status_update(PurpleBuddy
*buddy
, PurplePresence
*presence
,
608 PurpleStatus
*old_status
, PurpleStatus
*new_status
)
610 if (purple_prefs_get_bool("/purple/logging/log_system"))
612 time_t current_time
= time(NULL
);
613 const char *buddy_alias
= purple_buddy_get_alias(buddy
);
617 if (old_status
!= NULL
)
619 tmp
= g_strdup_printf(_("%s (%s) changed status from %s to %s"), buddy_alias
,
620 purple_buddy_get_name(buddy
),
621 purple_status_get_name(old_status
),
622 purple_status_get_name(new_status
));
623 logtmp
= g_markup_escape_text(tmp
, -1);
627 /* old_status == NULL when an independent status is toggled. */
629 if (purple_status_is_active(new_status
))
631 tmp
= g_strdup_printf(_("%s (%s) is now %s"), buddy_alias
,
632 purple_buddy_get_name(buddy
),
633 purple_status_get_name(new_status
));
634 logtmp
= g_markup_escape_text(tmp
, -1);
638 tmp
= g_strdup_printf(_("%s (%s) is no longer %s"), buddy_alias
,
639 purple_buddy_get_name(buddy
),
640 purple_status_get_name(new_status
));
641 logtmp
= g_markup_escape_text(tmp
, -1);
645 log
= purple_account_get_log(purple_buddy_get_account(buddy
), FALSE
);
648 purple_log_write(log
, PURPLE_MESSAGE_SYSTEM
, buddy_alias
,
649 current_time
, logtmp
);
658 notify_status_update(PurplePresence
*presence
, PurpleStatus
*old_status
,
659 PurpleStatus
*new_status
)
661 PurplePresenceContext context
= purple_presence_get_context(presence
);
663 if (context
== PURPLE_PRESENCE_CONTEXT_ACCOUNT
)
665 PurpleAccount
*account
= purple_presence_get_account(presence
);
666 PurpleAccountUiOps
*ops
= purple_accounts_get_ui_ops();
668 if (purple_account_get_enabled(account
, purple_core_get_ui()))
669 purple_prpl_change_account_status(account
, old_status
, new_status
);
671 if (ops
!= NULL
&& ops
->status_changed
!= NULL
)
673 ops
->status_changed(account
, new_status
);
676 else if (context
== PURPLE_PRESENCE_CONTEXT_BUDDY
)
678 notify_buddy_status_update(purple_presence_get_buddy(presence
), presence
,
679 old_status
, new_status
);
684 status_has_changed(PurpleStatus
*status
)
686 PurplePresence
*presence
;
687 PurpleStatus
*old_status
;
689 presence
= purple_status_get_presence(status
);
692 * If this status is exclusive, then we must be setting it to "active."
693 * Since we are setting it to active, we want to set the currently
694 * active status to "inactive."
696 if (purple_status_is_exclusive(status
))
698 old_status
= purple_presence_get_active_status(presence
);
699 if (old_status
!= NULL
&& (old_status
!= status
))
700 old_status
->active
= FALSE
;
701 presence
->active_status
= status
;
706 notify_status_update(presence
, old_status
, status
);
710 purple_status_set_active(PurpleStatus
*status
, gboolean active
)
712 purple_status_set_active_with_attrs_list(status
, active
, NULL
);
716 * This used to parse the va_list directly, but now it creates a GList
717 * and passes it to purple_status_set_active_with_attrs_list(). That
718 * function was created because accounts.c needs to pass a GList of
719 * attributes to the status API.
722 purple_status_set_active_with_attrs(PurpleStatus
*status
, gboolean active
, va_list args
)
728 while ((id
= va_arg(args
, const char *)) != NULL
)
730 attrs
= g_list_append(attrs
, (char *)id
);
731 data
= va_arg(args
, void *);
732 attrs
= g_list_append(attrs
, data
);
734 purple_status_set_active_with_attrs_list(status
, active
, attrs
);
739 purple_status_set_active_with_attrs_list(PurpleStatus
*status
, gboolean active
,
742 gboolean changed
= FALSE
;
744 GList
*specified_attr_ids
= NULL
;
745 PurpleStatusType
*status_type
;
747 g_return_if_fail(status
!= NULL
);
749 if (!active
&& purple_status_is_exclusive(status
))
751 purple_debug_error("status",
752 "Cannot deactivate an exclusive status (%s).\n",
753 purple_status_get_id(status
));
757 if (status
->active
!= active
)
762 status
->active
= active
;
764 /* Set any attributes */
773 value
= purple_status_get_attr_value(status
, id
);
776 purple_debug_warning("status", "The attribute \"%s\" on the status \"%s\" is "
777 "not supported.\n", id
, status
->type
->name
);
778 /* Skip over the data and move on to the next attribute */
783 specified_attr_ids
= g_list_prepend(specified_attr_ids
, (gpointer
)id
);
785 if (value
->type
== PURPLE_TYPE_STRING
)
787 const gchar
*string_data
= l
->data
;
789 if (purple_strequal(string_data
, value
->data
.string_data
))
791 purple_status_set_attr_string(status
, id
, string_data
);
794 else if (value
->type
== PURPLE_TYPE_INT
)
796 int int_data
= GPOINTER_TO_INT(l
->data
);
798 if (int_data
== value
->data
.int_data
)
800 purple_status_set_attr_int(status
, id
, int_data
);
803 else if (value
->type
== PURPLE_TYPE_BOOLEAN
)
805 gboolean boolean_data
= GPOINTER_TO_INT(l
->data
);
807 if (boolean_data
== value
->data
.boolean_data
)
809 purple_status_set_attr_boolean(status
, id
, boolean_data
);
814 /* We don't know what the data is--skip over it */
819 /* Reset any unspecified attributes to their default value */
820 status_type
= purple_status_get_type(status
);
821 l
= purple_status_type_get_attrs(status_type
);
823 PurpleStatusAttr
*attr
;
828 if (!g_list_find_custom(specified_attr_ids
, attr
->id
, (GCompareFunc
)strcmp
)) {
829 PurpleValue
*default_value
;
830 default_value
= purple_status_attr_get_value(attr
);
831 if (default_value
->type
== PURPLE_TYPE_STRING
) {
832 const char *cur
= purple_status_get_attr_string(status
, attr
->id
);
833 const char *def
= purple_value_get_string(default_value
);
834 if ((cur
== NULL
&& def
== NULL
)
835 || (cur
!= NULL
&& def
!= NULL
836 && !strcmp(cur
, def
))) {
840 purple_status_set_attr_string(status
, attr
->id
, def
);
841 } else if (default_value
->type
== PURPLE_TYPE_INT
) {
842 int cur
= purple_status_get_attr_int(status
, attr
->id
);
843 int def
= purple_value_get_int(default_value
);
847 purple_status_set_attr_int(status
, attr
->id
, def
);
848 } else if (default_value
->type
== PURPLE_TYPE_BOOLEAN
) {
849 gboolean cur
= purple_status_get_attr_boolean(status
, attr
->id
);
850 gboolean def
= purple_value_get_boolean(default_value
);
854 purple_status_set_attr_boolean(status
, attr
->id
, def
);
859 g_list_free(specified_attr_ids
);
863 status_has_changed(status
);
867 purple_status_set_attr_boolean(PurpleStatus
*status
, const char *id
,
870 PurpleValue
*attr_value
;
872 g_return_if_fail(status
!= NULL
);
873 g_return_if_fail(id
!= NULL
);
875 /* Make sure this attribute exists and is the correct type. */
876 attr_value
= purple_status_get_attr_value(status
, id
);
877 g_return_if_fail(attr_value
!= NULL
);
878 g_return_if_fail(purple_value_get_type(attr_value
) == PURPLE_TYPE_BOOLEAN
);
880 purple_value_set_boolean(attr_value
, value
);
884 purple_status_set_attr_int(PurpleStatus
*status
, const char *id
, int value
)
886 PurpleValue
*attr_value
;
888 g_return_if_fail(status
!= NULL
);
889 g_return_if_fail(id
!= NULL
);
891 /* Make sure this attribute exists and is the correct type. */
892 attr_value
= purple_status_get_attr_value(status
, id
);
893 g_return_if_fail(attr_value
!= NULL
);
894 g_return_if_fail(purple_value_get_type(attr_value
) == PURPLE_TYPE_INT
);
896 purple_value_set_int(attr_value
, value
);
900 purple_status_set_attr_string(PurpleStatus
*status
, const char *id
,
903 PurpleValue
*attr_value
;
905 g_return_if_fail(status
!= NULL
);
906 g_return_if_fail(id
!= NULL
);
908 /* Make sure this attribute exists and is the correct type. */
909 attr_value
= purple_status_get_attr_value(status
, id
);
910 /* This used to be g_return_if_fail, but it's failing a LOT, so
911 * let's generate a log error for now. */
912 /* g_return_if_fail(attr_value != NULL); */
913 if (attr_value
== NULL
) {
914 purple_debug_error("status",
915 "Attempted to set status attribute '%s' for "
916 "status '%s', which is not legal. Fix "
918 purple_status_type_get_name(purple_status_get_type(status
)));
921 g_return_if_fail(purple_value_get_type(attr_value
) == PURPLE_TYPE_STRING
);
923 /* XXX: Check if the value has actually changed. If it has, and the status
924 * is active, should this trigger 'status_has_changed'? */
925 purple_value_set_string(attr_value
, value
);
929 purple_status_get_type(const PurpleStatus
*status
)
931 g_return_val_if_fail(status
!= NULL
, NULL
);
937 purple_status_get_presence(const PurpleStatus
*status
)
939 g_return_val_if_fail(status
!= NULL
, NULL
);
941 return status
->presence
;
945 purple_status_get_id(const PurpleStatus
*status
)
947 g_return_val_if_fail(status
!= NULL
, NULL
);
949 return purple_status_type_get_id(purple_status_get_type(status
));
953 purple_status_get_name(const PurpleStatus
*status
)
955 g_return_val_if_fail(status
!= NULL
, NULL
);
957 return purple_status_type_get_name(purple_status_get_type(status
));
961 purple_status_is_independent(const PurpleStatus
*status
)
963 g_return_val_if_fail(status
!= NULL
, FALSE
);
965 return purple_status_type_is_independent(purple_status_get_type(status
));
969 purple_status_is_exclusive(const PurpleStatus
*status
)
971 g_return_val_if_fail(status
!= NULL
, FALSE
);
973 return purple_status_type_is_exclusive(purple_status_get_type(status
));
977 purple_status_is_available(const PurpleStatus
*status
)
979 g_return_val_if_fail(status
!= NULL
, FALSE
);
981 return purple_status_type_is_available(purple_status_get_type(status
));
985 purple_status_is_active(const PurpleStatus
*status
)
987 g_return_val_if_fail(status
!= NULL
, FALSE
);
989 return status
->active
;
993 purple_status_is_online(const PurpleStatus
*status
)
995 PurpleStatusPrimitive primitive
;
997 g_return_val_if_fail( status
!= NULL
, FALSE
);
999 primitive
= purple_status_type_get_primitive(purple_status_get_type(status
));
1001 return (primitive
!= PURPLE_STATUS_UNSET
&&
1002 primitive
!= PURPLE_STATUS_OFFLINE
);
1006 purple_status_get_attr_value(const PurpleStatus
*status
, const char *id
)
1008 g_return_val_if_fail(status
!= NULL
, NULL
);
1009 g_return_val_if_fail(id
!= NULL
, NULL
);
1011 return (PurpleValue
*)g_hash_table_lookup(status
->attr_values
, id
);
1015 purple_status_get_attr_boolean(const PurpleStatus
*status
, const char *id
)
1017 const PurpleValue
*value
;
1019 g_return_val_if_fail(status
!= NULL
, FALSE
);
1020 g_return_val_if_fail(id
!= NULL
, FALSE
);
1022 if ((value
= purple_status_get_attr_value(status
, id
)) == NULL
)
1025 g_return_val_if_fail(purple_value_get_type(value
) == PURPLE_TYPE_BOOLEAN
, FALSE
);
1027 return purple_value_get_boolean(value
);
1031 purple_status_get_attr_int(const PurpleStatus
*status
, const char *id
)
1033 const PurpleValue
*value
;
1035 g_return_val_if_fail(status
!= NULL
, 0);
1036 g_return_val_if_fail(id
!= NULL
, 0);
1038 if ((value
= purple_status_get_attr_value(status
, id
)) == NULL
)
1041 g_return_val_if_fail(purple_value_get_type(value
) == PURPLE_TYPE_INT
, 0);
1043 return purple_value_get_int(value
);
1047 purple_status_get_attr_string(const PurpleStatus
*status
, const char *id
)
1049 const PurpleValue
*value
;
1051 g_return_val_if_fail(status
!= NULL
, NULL
);
1052 g_return_val_if_fail(id
!= NULL
, NULL
);
1054 if ((value
= purple_status_get_attr_value(status
, id
)) == NULL
)
1057 g_return_val_if_fail(purple_value_get_type(value
) == PURPLE_TYPE_STRING
, NULL
);
1059 return purple_value_get_string(value
);
1063 purple_status_compare(const PurpleStatus
*status1
, const PurpleStatus
*status2
)
1065 PurpleStatusType
*type1
, *type2
;
1066 int score1
= 0, score2
= 0;
1068 if ((status1
== NULL
&& status2
== NULL
) ||
1069 (status1
== status2
))
1073 else if (status1
== NULL
)
1075 else if (status2
== NULL
)
1078 type1
= purple_status_get_type(status1
);
1079 type2
= purple_status_get_type(status2
);
1081 if (purple_status_is_active(status1
))
1082 score1
= primitive_scores
[purple_status_type_get_primitive(type1
)];
1084 if (purple_status_is_active(status2
))
1085 score2
= primitive_scores
[purple_status_type_get_primitive(type2
)];
1087 if (score1
> score2
)
1089 else if (score1
< score2
)
1096 /**************************************************************************
1097 * PurplePresence API
1098 **************************************************************************/
1100 purple_presence_new(PurplePresenceContext context
)
1102 PurplePresence
*presence
;
1104 g_return_val_if_fail(context
!= PURPLE_PRESENCE_CONTEXT_UNSET
, NULL
);
1106 presence
= g_new0(PurplePresence
, 1);
1107 PURPLE_DBUS_REGISTER_POINTER(presence
, PurplePresence
);
1109 presence
->context
= context
;
1111 presence
->status_table
=
1112 g_hash_table_new_full(g_str_hash
, g_str_equal
,
1119 purple_presence_new_for_account(PurpleAccount
*account
)
1121 PurplePresence
*presence
= NULL
;
1122 g_return_val_if_fail(account
!= NULL
, NULL
);
1124 presence
= purple_presence_new(PURPLE_PRESENCE_CONTEXT_ACCOUNT
);
1125 presence
->u
.account
= account
;
1126 presence
->statuses
= purple_prpl_get_statuses(account
, presence
);
1132 purple_presence_new_for_conv(PurpleConversation
*conv
)
1134 PurplePresence
*presence
;
1136 g_return_val_if_fail(conv
!= NULL
, NULL
);
1138 presence
= purple_presence_new(PURPLE_PRESENCE_CONTEXT_CONV
);
1139 presence
->u
.chat
.conv
= conv
;
1140 /* presence->statuses = purple_prpl_get_statuses(conv->account, presence); ? */
1146 purple_presence_new_for_buddy(PurpleBuddy
*buddy
)
1148 PurplePresence
*presence
;
1149 PurpleAccount
*account
;
1151 g_return_val_if_fail(buddy
!= NULL
, NULL
);
1152 account
= purple_buddy_get_account(buddy
);
1154 presence
= purple_presence_new(PURPLE_PRESENCE_CONTEXT_BUDDY
);
1156 presence
->u
.buddy
.name
= g_strdup(purple_buddy_get_name(buddy
));
1157 presence
->u
.buddy
.account
= account
;
1158 presence
->statuses
= purple_prpl_get_statuses(account
, presence
);
1160 presence
->u
.buddy
.buddy
= buddy
;
1166 purple_presence_destroy(PurplePresence
*presence
)
1168 g_return_if_fail(presence
!= NULL
);
1170 if (purple_presence_get_context(presence
) == PURPLE_PRESENCE_CONTEXT_BUDDY
)
1172 g_free(presence
->u
.buddy
.name
);
1174 else if (purple_presence_get_context(presence
) == PURPLE_PRESENCE_CONTEXT_CONV
)
1176 g_free(presence
->u
.chat
.user
);
1179 g_list_foreach(presence
->statuses
, (GFunc
)purple_status_destroy
, NULL
);
1180 g_list_free(presence
->statuses
);
1182 g_hash_table_destroy(presence
->status_table
);
1184 PURPLE_DBUS_UNREGISTER_POINTER(presence
);
1189 purple_presence_add_status(PurplePresence
*presence
, PurpleStatus
*status
)
1191 g_return_if_fail(presence
!= NULL
);
1192 g_return_if_fail(status
!= NULL
);
1194 presence
->statuses
= g_list_append(presence
->statuses
, status
);
1196 g_hash_table_insert(presence
->status_table
,
1197 g_strdup(purple_status_get_id(status
)), status
);
1201 purple_presence_add_list(PurplePresence
*presence
, GList
*source_list
)
1205 g_return_if_fail(presence
!= NULL
);
1206 g_return_if_fail(source_list
!= NULL
);
1208 for (l
= source_list
; l
!= NULL
; l
= l
->next
)
1209 purple_presence_add_status(presence
, (PurpleStatus
*)l
->data
);
1213 purple_presence_set_status_active(PurplePresence
*presence
, const char *status_id
,
1216 PurpleStatus
*status
;
1218 g_return_if_fail(presence
!= NULL
);
1219 g_return_if_fail(status_id
!= NULL
);
1221 status
= purple_presence_get_status(presence
, status_id
);
1223 g_return_if_fail(status
!= NULL
);
1224 /* TODO: Should we do the following? */
1225 /* g_return_if_fail(active == status->active); */
1227 if (purple_status_is_exclusive(status
))
1231 purple_debug_warning("status",
1232 "Attempted to set a non-independent status "
1233 "(%s) inactive. Only independent statuses "
1234 "can be specifically marked inactive.",
1240 purple_status_set_active(status
, active
);
1244 purple_presence_switch_status(PurplePresence
*presence
, const char *status_id
)
1246 purple_presence_set_status_active(presence
, status_id
, TRUE
);
1250 update_buddy_idle(PurpleBuddy
*buddy
, PurplePresence
*presence
,
1251 time_t current_time
, gboolean old_idle
, gboolean idle
)
1253 PurpleBlistUiOps
*ops
= purple_blist_get_ui_ops();
1254 PurpleAccount
*account
= purple_buddy_get_account(buddy
);
1256 if (!old_idle
&& idle
)
1258 if (purple_prefs_get_bool("/purple/logging/log_system"))
1260 PurpleLog
*log
= purple_account_get_log(account
, FALSE
);
1265 tmp
= g_strdup_printf(_("%s became idle"),
1266 purple_buddy_get_alias(buddy
));
1267 tmp2
= g_markup_escape_text(tmp
, -1);
1270 purple_log_write(log
, PURPLE_MESSAGE_SYSTEM
,
1271 purple_buddy_get_alias(buddy
), current_time
, tmp2
);
1276 else if (old_idle
&& !idle
)
1278 if (purple_prefs_get_bool("/purple/logging/log_system"))
1280 PurpleLog
*log
= purple_account_get_log(account
, FALSE
);
1285 tmp
= g_strdup_printf(_("%s became unidle"),
1286 purple_buddy_get_alias(buddy
));
1287 tmp2
= g_markup_escape_text(tmp
, -1);
1290 purple_log_write(log
, PURPLE_MESSAGE_SYSTEM
,
1291 purple_buddy_get_alias(buddy
), current_time
, tmp2
);
1297 if (old_idle
!= idle
)
1298 purple_signal_emit(purple_blist_get_handle(), "buddy-idle-changed", buddy
,
1301 purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy
));
1303 /* Should this be done here? It'd perhaps make more sense to
1304 * connect to buddy-[un]idle signals and update from there
1307 if (ops
!= NULL
&& ops
->update
!= NULL
)
1308 ops
->update(purple_get_blist(), (PurpleBlistNode
*)buddy
);
1312 purple_presence_set_idle(PurplePresence
*presence
, gboolean idle
, time_t idle_time
)
1315 time_t current_time
;
1317 g_return_if_fail(presence
!= NULL
);
1319 if (presence
->idle
== idle
&& presence
->idle_time
== idle_time
)
1322 old_idle
= presence
->idle
;
1323 presence
->idle
= idle
;
1324 presence
->idle_time
= (idle
? idle_time
: 0);
1326 current_time
= time(NULL
);
1328 if (purple_presence_get_context(presence
) == PURPLE_PRESENCE_CONTEXT_BUDDY
)
1330 update_buddy_idle(purple_presence_get_buddy(presence
), presence
, current_time
,
1333 else if (purple_presence_get_context(presence
) == PURPLE_PRESENCE_CONTEXT_ACCOUNT
)
1335 PurpleAccount
*account
;
1336 PurpleConnection
*gc
= NULL
;
1337 PurplePlugin
*prpl
= NULL
;
1338 PurplePluginProtocolInfo
*prpl_info
= NULL
;
1340 account
= purple_presence_get_account(presence
);
1342 if (purple_prefs_get_bool("/purple/logging/log_system"))
1344 PurpleLog
*log
= purple_account_get_log(account
, FALSE
);
1351 tmp
= g_strdup_printf(_("+++ %s became idle"), purple_account_get_username(account
));
1353 tmp
= g_strdup_printf(_("+++ %s became unidle"), purple_account_get_username(account
));
1355 msg
= g_markup_escape_text(tmp
, -1);
1357 purple_log_write(log
, PURPLE_MESSAGE_SYSTEM
,
1358 purple_account_get_username(account
),
1359 (idle
? idle_time
: current_time
), msg
);
1364 gc
= purple_account_get_connection(account
);
1367 prpl
= purple_connection_get_prpl(gc
);
1369 if(PURPLE_CONNECTION_IS_CONNECTED(gc
) && prpl
!= NULL
)
1370 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(prpl
);
1372 if (prpl_info
&& prpl_info
->set_idle
)
1373 prpl_info
->set_idle(gc
, (idle
? (current_time
- idle_time
) : 0));
1378 purple_presence_set_login_time(PurplePresence
*presence
, time_t login_time
)
1380 g_return_if_fail(presence
!= NULL
);
1382 if (presence
->login_time
== login_time
)
1385 presence
->login_time
= login_time
;
1388 PurplePresenceContext
1389 purple_presence_get_context(const PurplePresence
*presence
)
1391 g_return_val_if_fail(presence
!= NULL
, PURPLE_PRESENCE_CONTEXT_UNSET
);
1393 return presence
->context
;
1397 purple_presence_get_account(const PurplePresence
*presence
)
1399 PurplePresenceContext context
;
1401 g_return_val_if_fail(presence
!= NULL
, NULL
);
1403 context
= purple_presence_get_context(presence
);
1405 g_return_val_if_fail(context
== PURPLE_PRESENCE_CONTEXT_ACCOUNT
||
1406 context
== PURPLE_PRESENCE_CONTEXT_BUDDY
, NULL
);
1408 return presence
->u
.account
;
1411 PurpleConversation
*
1412 purple_presence_get_conversation(const PurplePresence
*presence
)
1414 g_return_val_if_fail(presence
!= NULL
, NULL
);
1415 g_return_val_if_fail(purple_presence_get_context(presence
) ==
1416 PURPLE_PRESENCE_CONTEXT_CONV
, NULL
);
1418 return presence
->u
.chat
.conv
;
1422 purple_presence_get_chat_user(const PurplePresence
*presence
)
1424 g_return_val_if_fail(presence
!= NULL
, NULL
);
1425 g_return_val_if_fail(purple_presence_get_context(presence
) ==
1426 PURPLE_PRESENCE_CONTEXT_CONV
, NULL
);
1428 return presence
->u
.chat
.user
;
1432 purple_presence_get_buddy(const PurplePresence
*presence
)
1434 g_return_val_if_fail(presence
!= NULL
, NULL
);
1435 g_return_val_if_fail(purple_presence_get_context(presence
) ==
1436 PURPLE_PRESENCE_CONTEXT_BUDDY
, NULL
);
1438 return presence
->u
.buddy
.buddy
;
1442 purple_presence_get_statuses(const PurplePresence
*presence
)
1444 g_return_val_if_fail(presence
!= NULL
, NULL
);
1446 return presence
->statuses
;
1450 purple_presence_get_status(const PurplePresence
*presence
, const char *status_id
)
1452 PurpleStatus
*status
;
1455 g_return_val_if_fail(presence
!= NULL
, NULL
);
1456 g_return_val_if_fail(status_id
!= NULL
, NULL
);
1458 /* What's the purpose of this hash table? */
1459 status
= (PurpleStatus
*)g_hash_table_lookup(presence
->status_table
,
1462 if (status
== NULL
) {
1463 for (l
= purple_presence_get_statuses(presence
);
1464 l
!= NULL
&& status
== NULL
; l
= l
->next
)
1466 PurpleStatus
*temp_status
= l
->data
;
1468 if (purple_strequal(status_id
, purple_status_get_id(temp_status
)))
1469 status
= temp_status
;
1473 g_hash_table_insert(presence
->status_table
,
1474 g_strdup(purple_status_get_id(status
)), status
);
1481 purple_presence_get_active_status(const PurplePresence
*presence
)
1483 g_return_val_if_fail(presence
!= NULL
, NULL
);
1485 return presence
->active_status
;
1489 purple_presence_is_available(const PurplePresence
*presence
)
1491 PurpleStatus
*status
;
1493 g_return_val_if_fail(presence
!= NULL
, FALSE
);
1495 status
= purple_presence_get_active_status(presence
);
1497 return ((status
!= NULL
&& purple_status_is_available(status
)) &&
1498 !purple_presence_is_idle(presence
));
1502 purple_presence_is_online(const PurplePresence
*presence
)
1504 PurpleStatus
*status
;
1506 g_return_val_if_fail(presence
!= NULL
, FALSE
);
1508 if ((status
= purple_presence_get_active_status(presence
)) == NULL
)
1511 return purple_status_is_online(status
);
1515 purple_presence_is_status_active(const PurplePresence
*presence
,
1516 const char *status_id
)
1518 PurpleStatus
*status
;
1520 g_return_val_if_fail(presence
!= NULL
, FALSE
);
1521 g_return_val_if_fail(status_id
!= NULL
, FALSE
);
1523 status
= purple_presence_get_status(presence
, status_id
);
1525 return (status
!= NULL
&& purple_status_is_active(status
));
1529 purple_presence_is_status_primitive_active(const PurplePresence
*presence
,
1530 PurpleStatusPrimitive primitive
)
1534 g_return_val_if_fail(presence
!= NULL
, FALSE
);
1535 g_return_val_if_fail(primitive
!= PURPLE_STATUS_UNSET
, FALSE
);
1537 for (l
= purple_presence_get_statuses(presence
);
1538 l
!= NULL
; l
= l
->next
)
1540 PurpleStatus
*temp_status
= l
->data
;
1541 PurpleStatusType
*type
= purple_status_get_type(temp_status
);
1543 if (purple_status_type_get_primitive(type
) == primitive
&&
1544 purple_status_is_active(temp_status
))
1551 purple_presence_is_idle(const PurplePresence
*presence
)
1553 g_return_val_if_fail(presence
!= NULL
, FALSE
);
1555 return purple_presence_is_online(presence
) && presence
->idle
;
1559 purple_presence_get_idle_time(const PurplePresence
*presence
)
1561 g_return_val_if_fail(presence
!= NULL
, 0);
1563 return presence
->idle_time
;
1567 purple_presence_get_login_time(const PurplePresence
*presence
)
1569 g_return_val_if_fail(presence
!= NULL
, 0);
1571 return purple_presence_is_online(presence
) ? presence
->login_time
: 0;
1575 purple_presence_compute_score(const PurplePresence
*presence
)
1580 for (l
= purple_presence_get_statuses(presence
); l
!= NULL
; l
= l
->next
) {
1581 PurpleStatus
*status
= (PurpleStatus
*)l
->data
;
1582 PurpleStatusType
*type
= purple_status_get_type(status
);
1584 if (purple_status_is_active(status
)) {
1585 score
+= primitive_scores
[purple_status_type_get_primitive(type
)];
1586 if (!purple_status_is_online(status
)) {
1587 PurpleBuddy
*b
= purple_presence_get_buddy(presence
);
1588 if (b
&& purple_account_supports_offline_message(purple_buddy_get_account(b
), b
))
1589 score
+= primitive_scores
[SCORE_OFFLINE_MESSAGE
];
1593 score
+= purple_account_get_int(purple_presence_get_account(presence
), "score", 0);
1594 if (purple_presence_is_idle(presence
))
1595 score
+= primitive_scores
[SCORE_IDLE
];
1600 purple_presence_compare(const PurplePresence
*presence1
,
1601 const PurplePresence
*presence2
)
1603 time_t idle_time_1
, idle_time_2
;
1604 int score1
= 0, score2
= 0;
1606 if (presence1
== presence2
)
1608 else if (presence1
== NULL
)
1610 else if (presence2
== NULL
)
1613 if (purple_presence_is_online(presence1
) &&
1614 !purple_presence_is_online(presence2
))
1616 else if (purple_presence_is_online(presence2
) &&
1617 !purple_presence_is_online(presence1
))
1620 /* Compute the score of the first set of statuses. */
1621 score1
= purple_presence_compute_score(presence1
);
1623 /* Compute the score of the second set of statuses. */
1624 score2
= purple_presence_compute_score(presence2
);
1626 idle_time_1
= time(NULL
) - purple_presence_get_idle_time(presence1
);
1627 idle_time_2
= time(NULL
) - purple_presence_get_idle_time(presence2
);
1629 if (idle_time_1
> idle_time_2
)
1630 score1
+= primitive_scores
[SCORE_IDLE_TIME
];
1631 else if (idle_time_1
< idle_time_2
)
1632 score2
+= primitive_scores
[SCORE_IDLE_TIME
];
1634 if (score1
< score2
)
1636 else if (score1
> score2
)
1643 /**************************************************************************
1645 **************************************************************************/
1647 score_pref_changed_cb(const char *name
, PurplePrefType type
,
1648 gconstpointer value
, gpointer data
)
1650 int index
= GPOINTER_TO_INT(data
);
1652 primitive_scores
[index
] = GPOINTER_TO_INT(value
);
1656 purple_status_get_handle(void) {
1663 purple_status_init(void)
1665 void *handle
= purple_status_get_handle();
1667 purple_prefs_add_none("/purple/status");
1668 purple_prefs_add_none("/purple/status/scores");
1670 purple_prefs_add_int("/purple/status/scores/offline",
1671 primitive_scores
[PURPLE_STATUS_OFFLINE
]);
1672 purple_prefs_add_int("/purple/status/scores/available",
1673 primitive_scores
[PURPLE_STATUS_AVAILABLE
]);
1674 purple_prefs_add_int("/purple/status/scores/invisible",
1675 primitive_scores
[PURPLE_STATUS_INVISIBLE
]);
1676 purple_prefs_add_int("/purple/status/scores/away",
1677 primitive_scores
[PURPLE_STATUS_AWAY
]);
1678 purple_prefs_add_int("/purple/status/scores/extended_away",
1679 primitive_scores
[PURPLE_STATUS_EXTENDED_AWAY
]);
1680 purple_prefs_add_int("/purple/status/scores/idle",
1681 primitive_scores
[SCORE_IDLE
]);
1682 purple_prefs_add_int("/purple/status/scores/offline_msg",
1683 primitive_scores
[SCORE_OFFLINE_MESSAGE
]);
1685 purple_prefs_connect_callback(handle
, "/purple/status/scores/offline",
1686 score_pref_changed_cb
,
1687 GINT_TO_POINTER(PURPLE_STATUS_OFFLINE
));
1688 purple_prefs_connect_callback(handle
, "/purple/status/scores/available",
1689 score_pref_changed_cb
,
1690 GINT_TO_POINTER(PURPLE_STATUS_AVAILABLE
));
1691 purple_prefs_connect_callback(handle
, "/purple/status/scores/invisible",
1692 score_pref_changed_cb
,
1693 GINT_TO_POINTER(PURPLE_STATUS_INVISIBLE
));
1694 purple_prefs_connect_callback(handle
, "/purple/status/scores/away",
1695 score_pref_changed_cb
,
1696 GINT_TO_POINTER(PURPLE_STATUS_AWAY
));
1697 purple_prefs_connect_callback(handle
, "/purple/status/scores/extended_away",
1698 score_pref_changed_cb
,
1699 GINT_TO_POINTER(PURPLE_STATUS_EXTENDED_AWAY
));
1700 purple_prefs_connect_callback(handle
, "/purple/status/scores/idle",
1701 score_pref_changed_cb
,
1702 GINT_TO_POINTER(SCORE_IDLE
));
1703 purple_prefs_connect_callback(handle
, "/purple/status/scores/offline_msg",
1704 score_pref_changed_cb
,
1705 GINT_TO_POINTER(SCORE_OFFLINE_MESSAGE
));
1707 purple_prefs_trigger_callback("/purple/status/scores/offline");
1708 purple_prefs_trigger_callback("/purple/status/scores/available");
1709 purple_prefs_trigger_callback("/purple/status/scores/invisible");
1710 purple_prefs_trigger_callback("/purple/status/scores/away");
1711 purple_prefs_trigger_callback("/purple/status/scores/extended_away");
1712 purple_prefs_trigger_callback("/purple/status/scores/idle");
1713 purple_prefs_trigger_callback("/purple/status/scores/offline_msg");
1717 purple_status_uninit(void)
1719 purple_prefs_disconnect_by_handle(purple_prefs_get_handle());