Remove unused purple_protocol_override.
[pidgin-git.git] / libpurple / status.c
blob83b9ffb0cdd8e01a751839ae4a1f92a104793b68
1 /* purple
3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
5 * source distribution.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
21 #include "internal.h"
22 #include "glibcompat.h"
24 #include "buddylist.h"
25 #include "core.h"
26 #include "debug.h"
27 #include "notify.h"
28 #include "prefs.h"
29 #include "status.h"
31 typedef struct _PurpleStatusPrivate PurpleStatusPrivate;
34 * A type of status.
36 struct _PurpleStatusType
38 int box_count;
40 PurpleStatusPrimitive primitive;
42 char *id;
43 char *name;
45 gboolean saveable;
46 gboolean user_settable;
47 gboolean independent;
49 GList *attrs;
53 * A status attribute.
55 struct _PurpleStatusAttribute
57 char *id;
58 char *name;
59 GValue *value_type;
62 /**
63 * PurpleStatus:
65 * A PurpleStatus can be thought of as an "instance" of a PurpleStatusType.
66 * If you're familiar with object-oriented programming languages
67 * then this should be immediately clear. Say, for example, that
68 * one of your AIM buddies has set himself as "away." You have a
69 * PurpleBuddy node for this person in your buddy list. Purple wants
70 * to mark this buddy as "away," so it creates a new PurpleStatus.
71 * The PurpleStatus has its PurpleStatusType set to the "away" state
72 * for the oscar protocol. The PurpleStatus also contains the buddy's
73 * away message. PurpleStatuses are sometimes saved, depending on
74 * the context. The current PurpleStatuses associated with each of
75 * your accounts are saved so that the next time you start Purple,
76 * your accounts will be set to their last known statuses. There
77 * is also a list of saved statuses that are written to the
78 * status.xml file. Also, each PurpleStatus has a "saveable" boolean.
79 * If "saveable" is set to FALSE then the status is NEVER saved.
80 * All PurpleStatuses should be inside a PurplePresence.
82 * A PurpleStatus is either "independent" or "exclusive."
83 * Independent statuses can be active or inactive and they don't
84 * affect anything else. However, you can only have one exclusive
85 * status per PurplePresence. If you activate one exclusive status,
86 * then the previous exclusive status is automatically deactivated.
88 * A PurplePresence is like a collection of PurpleStatuses (plus some
89 * other random info).
91 * See <link linkend="libpurple-presence">Presence API</link>
93 struct _PurpleStatus
95 GObject parent;
99 * Private data for PurpleStatus
101 struct _PurpleStatusPrivate
103 PurpleStatusType *status_type;
104 PurplePresence *presence;
106 gboolean active;
109 * The current values of the attributes for this status. The
110 * key is a string containing the name of the attribute. It is
111 * a borrowed reference from the list of attrs in the
112 * PurpleStatusType. The value is a GValue.
114 GHashTable *attr_values;
117 /* GObject property enums */
118 enum
120 PROP_0,
121 PROP_STATUS_TYPE,
122 PROP_PRESENCE,
123 PROP_ACTIVE,
124 PROP_LAST
127 typedef struct
129 PurpleAccount *account;
130 char *name;
131 } PurpleStatusBuddyKey;
133 static GParamSpec *properties[PROP_LAST];
135 G_DEFINE_TYPE_WITH_PRIVATE(PurpleStatus, purple_status, G_TYPE_OBJECT);
137 static int primitive_scores[] =
139 0, /* unset */
140 -500, /* offline */
141 100, /* available */
142 -75, /* unavailable */
143 -50, /* invisible */
144 -100, /* away */
145 -200, /* extended away */
146 -400, /* mobile */
147 0, /* tune */
148 0, /* mood */
149 -10, /* idle, special case. */
150 -5, /* idle time, special case. */
151 10 /* Offline messageable */
154 #define SCORE_IDLE 9
155 #define SCORE_IDLE_TIME 10
156 #define SCORE_OFFLINE_MESSAGE 11
158 /**************************************************************************
159 * PurpleStatusPrimitive API
160 **************************************************************************/
161 static struct PurpleStatusPrimitiveMap
163 PurpleStatusPrimitive type;
164 const char *id;
165 const char *name;
167 } const status_primitive_map[] =
169 { PURPLE_STATUS_UNSET, "unset", N_("Unset") },
170 { PURPLE_STATUS_OFFLINE, "offline", N_("Offline") },
171 { PURPLE_STATUS_AVAILABLE, "available", N_("Available") },
172 { PURPLE_STATUS_UNAVAILABLE, "unavailable", N_("Do not disturb") },
173 { PURPLE_STATUS_INVISIBLE, "invisible", N_("Invisible") },
174 { PURPLE_STATUS_AWAY, "away", N_("Away") },
175 { PURPLE_STATUS_EXTENDED_AWAY, "extended_away", N_("Extended away") },
176 { PURPLE_STATUS_MOBILE, "mobile", N_("Mobile") },
177 { PURPLE_STATUS_TUNE, "tune", N_("Listening to music"), },
178 { PURPLE_STATUS_MOOD, "mood", N_("Feeling") },
181 int *
182 _purple_statuses_get_primitive_scores(void)
184 return primitive_scores;
187 const char *
188 purple_primitive_get_id_from_type(PurpleStatusPrimitive type)
190 int i;
192 for (i = 0; i < PURPLE_STATUS_NUM_PRIMITIVES; i++)
194 if (type == status_primitive_map[i].type)
195 return status_primitive_map[i].id;
198 return status_primitive_map[0].id;
201 const char *
202 purple_primitive_get_name_from_type(PurpleStatusPrimitive type)
204 int i;
206 for (i = 0; i < PURPLE_STATUS_NUM_PRIMITIVES; i++)
208 if (type == status_primitive_map[i].type)
209 return _(status_primitive_map[i].name);
212 return _(status_primitive_map[0].name);
215 PurpleStatusPrimitive
216 purple_primitive_get_type_from_id(const char *id)
218 int i;
220 g_return_val_if_fail(id != NULL, PURPLE_STATUS_UNSET);
222 for (i = 0; i < PURPLE_STATUS_NUM_PRIMITIVES; i++)
224 if (purple_strequal(id, status_primitive_map[i].id))
225 return status_primitive_map[i].type;
228 return status_primitive_map[0].type;
232 /**************************************************************************
233 * PurpleStatusType API
234 **************************************************************************/
235 PurpleStatusType *
236 purple_status_type_new_full(PurpleStatusPrimitive primitive, const char *id,
237 const char *name, gboolean saveable,
238 gboolean user_settable, gboolean independent)
240 PurpleStatusType *status_type;
242 g_return_val_if_fail(primitive != PURPLE_STATUS_UNSET, NULL);
244 status_type = g_new0(PurpleStatusType, 1);
246 status_type->primitive = primitive;
247 status_type->saveable = saveable;
248 status_type->user_settable = user_settable;
249 status_type->independent = independent;
251 if (id != NULL)
252 status_type->id = g_strdup(id);
253 else
254 status_type->id = g_strdup(purple_primitive_get_id_from_type(primitive));
256 if (name != NULL)
257 status_type->name = g_strdup(name);
258 else
259 status_type->name = g_strdup(purple_primitive_get_name_from_type(primitive));
261 return status_type;
264 PurpleStatusType *
265 purple_status_type_new(PurpleStatusPrimitive primitive, const char *id,
266 const char *name, gboolean user_settable)
268 g_return_val_if_fail(primitive != PURPLE_STATUS_UNSET, NULL);
270 return purple_status_type_new_full(primitive, id, name, TRUE,
271 user_settable, FALSE);
274 static void
275 status_type_add_attr(PurpleStatusType *status_type, const char *id,
276 const char *name, GValue *value)
278 PurpleStatusAttribute *attr;
280 g_return_if_fail(status_type != NULL);
281 g_return_if_fail(id != NULL);
282 g_return_if_fail(name != NULL);
283 g_return_if_fail(value != NULL);
285 attr = purple_status_attribute_new(id, name, value);
287 status_type->attrs = g_list_append(status_type->attrs, attr);
290 static void
291 status_type_add_attrs_vargs(PurpleStatusType *status_type, va_list args)
293 const char *id, *name;
294 GValue *value;
296 g_return_if_fail(status_type != NULL);
298 while ((id = va_arg(args, const char *)) != NULL)
300 name = va_arg(args, const char *);
301 g_return_if_fail(name != NULL);
303 value = va_arg(args, GValue *);
304 g_return_if_fail(value != NULL);
306 status_type_add_attr(status_type, id, name, value);
310 PurpleStatusType *
311 purple_status_type_new_with_attrs(PurpleStatusPrimitive primitive,
312 const char *id, const char *name,
313 gboolean saveable, gboolean user_settable,
314 gboolean independent, const char *attr_id,
315 const char *attr_name, GValue *attr_value,
316 ...)
318 PurpleStatusType *status_type;
319 va_list args;
321 g_return_val_if_fail(primitive != PURPLE_STATUS_UNSET, NULL);
322 g_return_val_if_fail(attr_id != NULL, NULL);
323 g_return_val_if_fail(attr_name != NULL, NULL);
324 g_return_val_if_fail(attr_value != NULL, NULL);
326 status_type = purple_status_type_new_full(primitive, id, name, saveable,
327 user_settable, independent);
329 /* Add the first attribute */
330 status_type_add_attr(status_type, attr_id, attr_name, attr_value);
332 va_start(args, attr_value);
333 status_type_add_attrs_vargs(status_type, args);
334 va_end(args);
336 return status_type;
339 void
340 purple_status_type_destroy(PurpleStatusType *status_type)
342 g_return_if_fail(status_type != NULL);
344 g_free(status_type->id);
345 g_free(status_type->name);
347 g_list_foreach(status_type->attrs, (GFunc)purple_status_attribute_destroy, NULL);
348 g_list_free(status_type->attrs);
350 g_free(status_type);
353 PurpleStatusPrimitive
354 purple_status_type_get_primitive(const PurpleStatusType *status_type)
356 g_return_val_if_fail(status_type != NULL, PURPLE_STATUS_UNSET);
358 return status_type->primitive;
361 const char *
362 purple_status_type_get_id(const PurpleStatusType *status_type)
364 g_return_val_if_fail(status_type != NULL, NULL);
366 return status_type->id;
369 const char *
370 purple_status_type_get_name(const PurpleStatusType *status_type)
372 g_return_val_if_fail(status_type != NULL, NULL);
374 return status_type->name;
377 gboolean
378 purple_status_type_is_saveable(const PurpleStatusType *status_type)
380 g_return_val_if_fail(status_type != NULL, FALSE);
382 return status_type->saveable;
385 gboolean
386 purple_status_type_is_user_settable(const PurpleStatusType *status_type)
388 g_return_val_if_fail(status_type != NULL, FALSE);
390 return status_type->user_settable;
393 gboolean
394 purple_status_type_is_independent(const PurpleStatusType *status_type)
396 g_return_val_if_fail(status_type != NULL, FALSE);
398 return status_type->independent;
401 gboolean
402 purple_status_type_is_exclusive(const PurpleStatusType *status_type)
404 g_return_val_if_fail(status_type != NULL, FALSE);
406 return !status_type->independent;
409 gboolean
410 purple_status_type_is_available(const PurpleStatusType *status_type)
412 PurpleStatusPrimitive primitive;
414 g_return_val_if_fail(status_type != NULL, FALSE);
416 primitive = purple_status_type_get_primitive(status_type);
418 return (primitive == PURPLE_STATUS_AVAILABLE);
421 PurpleStatusAttribute *
422 purple_status_type_get_attr(const PurpleStatusType *status_type, const char *id)
424 GList *l;
426 g_return_val_if_fail(status_type != NULL, NULL);
427 g_return_val_if_fail(id != NULL, NULL);
429 for (l = status_type->attrs; l != NULL; l = l->next)
431 PurpleStatusAttribute *attr = (PurpleStatusAttribute *)l->data;
433 if (purple_strequal(purple_status_attribute_get_id(attr), id))
434 return attr;
437 return NULL;
440 GList *
441 purple_status_type_get_attrs(const PurpleStatusType *status_type)
443 g_return_val_if_fail(status_type != NULL, NULL);
445 return status_type->attrs;
448 const PurpleStatusType *
449 purple_status_type_find_with_id(GList *status_types, const char *id)
451 PurpleStatusType *status_type;
453 g_return_val_if_fail(id != NULL, NULL);
455 while (status_types != NULL)
457 status_type = status_types->data;
459 if (purple_strequal(id, status_type->id))
460 return status_type;
462 status_types = status_types->next;
465 return NULL;
469 /**************************************************************************
470 * PurpleStatusAttribute API
471 **************************************************************************/
472 PurpleStatusAttribute *
473 purple_status_attribute_new(const char *id, const char *name, GValue *value_type)
475 PurpleStatusAttribute *attr;
477 g_return_val_if_fail(id != NULL, NULL);
478 g_return_val_if_fail(name != NULL, NULL);
479 g_return_val_if_fail(value_type != NULL, NULL);
481 attr = g_new0(PurpleStatusAttribute, 1);
483 attr->id = g_strdup(id);
484 attr->name = g_strdup(name);
485 attr->value_type = value_type;
487 return attr;
490 void
491 purple_status_attribute_destroy(PurpleStatusAttribute *attr)
493 g_return_if_fail(attr != NULL);
495 g_free(attr->id);
496 g_free(attr->name);
498 purple_value_free(attr->value_type);
500 g_free(attr);
503 const char *
504 purple_status_attribute_get_id(const PurpleStatusAttribute *attr)
506 g_return_val_if_fail(attr != NULL, NULL);
508 return attr->id;
511 const char *
512 purple_status_attribute_get_name(const PurpleStatusAttribute *attr)
514 g_return_val_if_fail(attr != NULL, NULL);
516 return attr->name;
519 GValue *
520 purple_status_attribute_get_value(const PurpleStatusAttribute *attr)
522 g_return_val_if_fail(attr != NULL, NULL);
524 return attr->value_type;
528 /**************************************************************************
529 * PurpleStatus API
530 **************************************************************************/
531 static void
532 notify_buddy_status_update(PurpleBuddy *buddy, PurplePresence *presence,
533 PurpleStatus *old_status, PurpleStatus *new_status)
535 if (purple_prefs_get_bool("/purple/logging/log_system"))
537 GDateTime *current_time = g_date_time_new_now_utc();
538 const char *buddy_alias = purple_buddy_get_alias(buddy);
539 char *tmp, *logtmp;
540 PurpleLog *log;
542 if (old_status != NULL)
544 tmp = g_strdup_printf(_("%s (%s) changed status from %s to %s"), buddy_alias,
545 purple_buddy_get_name(buddy),
546 purple_status_get_name(old_status),
547 purple_status_get_name(new_status));
548 logtmp = g_markup_escape_text(tmp, -1);
550 else
552 /* old_status == NULL when an independent status is toggled. */
554 if (purple_status_is_active(new_status))
556 tmp = g_strdup_printf(_("%s (%s) is now %s"), buddy_alias,
557 purple_buddy_get_name(buddy),
558 purple_status_get_name(new_status));
559 logtmp = g_markup_escape_text(tmp, -1);
561 else
563 tmp = g_strdup_printf(_("%s (%s) is no longer %s"), buddy_alias,
564 purple_buddy_get_name(buddy),
565 purple_status_get_name(new_status));
566 logtmp = g_markup_escape_text(tmp, -1);
570 log = purple_account_get_log(purple_buddy_get_account(buddy), FALSE);
571 if (log != NULL)
573 purple_log_write(log, PURPLE_MESSAGE_SYSTEM, buddy_alias,
574 current_time, logtmp);
577 g_date_time_unref(current_time);
578 g_free(tmp);
579 g_free(logtmp);
583 static void
584 notify_status_update(PurplePresence *presence, PurpleStatus *old_status,
585 PurpleStatus *new_status)
587 if (PURPLE_IS_ACCOUNT_PRESENCE(presence))
589 PurpleAccount *account = purple_account_presence_get_account(
590 PURPLE_ACCOUNT_PRESENCE(presence));
591 PurpleAccountUiOps *ops = purple_accounts_get_ui_ops();
593 if (purple_account_get_enabled(account, purple_core_get_ui()))
594 purple_protocol_change_account_status(account, old_status, new_status);
596 if (ops != NULL && ops->status_changed != NULL)
598 ops->status_changed(account, new_status);
601 else if (PURPLE_IS_BUDDY_PRESENCE(presence))
603 notify_buddy_status_update(purple_buddy_presence_get_buddy(
604 PURPLE_BUDDY_PRESENCE(presence)), presence, old_status,
605 new_status);
609 static void
610 status_has_changed(PurpleStatus *status)
612 PurplePresence *presence;
613 PurpleStatus *old_status;
615 presence = purple_status_get_presence(status);
618 * If this status is exclusive, then we must be setting it to "active."
619 * Since we are setting it to active, we want to set the currently
620 * active status to "inactive."
622 if (purple_status_is_exclusive(status))
624 old_status = purple_presence_get_active_status(presence);
625 if (old_status != NULL && (old_status != status)) {
626 PurpleStatusPrivate *priv = purple_status_get_instance_private(old_status);
627 priv->active = FALSE;
628 g_object_notify_by_pspec(G_OBJECT(old_status),
629 properties[PROP_ACTIVE]);
632 else
633 old_status = NULL;
635 g_object_set(presence, "active-status", status, NULL);
636 g_object_notify_by_pspec(G_OBJECT(status), properties[PROP_ACTIVE]);
638 notify_status_update(presence, old_status, status);
641 static void
642 status_set_attr_boolean(PurpleStatus *status, const char *id,
643 gboolean value)
645 GValue *attr_value;
647 g_return_if_fail(PURPLE_IS_STATUS(status));
648 g_return_if_fail(id != NULL);
650 /* Make sure this attribute exists and is the correct type. */
651 attr_value = purple_status_get_attr_value(status, id);
652 g_return_if_fail(attr_value != NULL);
653 g_return_if_fail(G_VALUE_TYPE(attr_value) == G_TYPE_BOOLEAN);
655 g_value_set_boolean(attr_value, value);
658 static void
659 status_set_attr_int(PurpleStatus *status, const char *id, int value)
661 GValue *attr_value;
663 g_return_if_fail(PURPLE_IS_STATUS(status));
664 g_return_if_fail(id != NULL);
666 /* Make sure this attribute exists and is the correct type. */
667 attr_value = purple_status_get_attr_value(status, id);
668 g_return_if_fail(attr_value != NULL);
669 g_return_if_fail(G_VALUE_TYPE(attr_value) == G_TYPE_INT);
671 g_value_set_int(attr_value, value);
674 static void
675 status_set_attr_string(PurpleStatus *status, const char *id,
676 const char *value)
678 GValue *attr_value;
680 g_return_if_fail(PURPLE_IS_STATUS(status));
681 g_return_if_fail(id != NULL);
683 /* Make sure this attribute exists and is the correct type. */
684 attr_value = purple_status_get_attr_value(status, id);
685 /* This used to be g_return_if_fail, but it's failing a LOT, so
686 * let's generate a log error for now. */
687 /* g_return_if_fail(attr_value != NULL); */
688 if (attr_value == NULL) {
689 purple_debug_error("status",
690 "Attempted to set status attribute '%s' for "
691 "status '%s', which is not legal. Fix "
692 "this!\n", id,
693 purple_status_type_get_name(purple_status_get_status_type(status)));
694 return;
696 g_return_if_fail(G_VALUE_TYPE(attr_value) == G_TYPE_STRING);
698 /* XXX: Check if the value has actually changed. If it has, and the status
699 * is active, should this trigger 'status_has_changed'? */
700 g_value_set_string(attr_value, value);
703 void
704 purple_status_set_active(PurpleStatus *status, gboolean active)
706 purple_status_set_active_with_attrs_list(status, active, NULL);
710 * This used to parse the va_list directly, but now it creates a GList
711 * and passes it to purple_status_set_active_with_attrs_list(). That
712 * function was created because account.c needs to pass a GList of
713 * attributes to the status API.
715 void
716 purple_status_set_active_with_attrs(PurpleStatus *status, gboolean active, va_list args)
718 GList *attrs = NULL;
719 const gchar *id;
720 gpointer data;
722 while ((id = va_arg(args, const char *)) != NULL)
724 attrs = g_list_append(attrs, (char *)id);
725 data = va_arg(args, void *);
726 attrs = g_list_append(attrs, data);
728 purple_status_set_active_with_attrs_list(status, active, attrs);
729 g_list_free(attrs);
732 void
733 purple_status_set_active_with_attrs_list(PurpleStatus *status, gboolean active,
734 GList *attrs)
736 PurpleStatusPrivate *priv = NULL;
737 gboolean changed = FALSE;
738 GList *l;
739 GList *specified_attr_ids = NULL;
740 PurpleStatusType *status_type;
742 g_return_if_fail(PURPLE_IS_STATUS(status));
744 priv = purple_status_get_instance_private(status);
746 if (!active && purple_status_is_exclusive(status))
748 purple_debug_error("status",
749 "Cannot deactivate an exclusive status (%s).\n",
750 purple_status_get_id(status));
751 return;
754 if (priv->active != active)
756 changed = TRUE;
759 priv->active = active;
761 /* Set any attributes */
762 l = attrs;
763 while (l != NULL)
765 const gchar *id;
766 GValue *value;
768 id = l->data;
769 l = l->next;
770 value = purple_status_get_attr_value(status, id);
771 if (value == NULL)
773 purple_debug_warning("status", "The attribute \"%s\" on the status \"%s\" is "
774 "not supported.\n", id, priv->status_type->name);
775 /* Skip over the data and move on to the next attribute */
776 l = l->next;
777 continue;
780 specified_attr_ids = g_list_prepend(specified_attr_ids, (gpointer)id);
782 if (G_VALUE_TYPE(value) == G_TYPE_STRING)
784 const gchar *string_data = l->data;
785 l = l->next;
786 if (purple_strequal(string_data, g_value_get_string(value)))
787 continue;
788 status_set_attr_string(status, id, string_data);
789 changed = TRUE;
791 else if (G_VALUE_TYPE(value) == G_TYPE_INT)
793 int int_data = GPOINTER_TO_INT(l->data);
794 l = l->next;
795 if (int_data == g_value_get_int(value))
796 continue;
797 status_set_attr_int(status, id, int_data);
798 changed = TRUE;
800 else if (G_VALUE_TYPE(value) == G_TYPE_BOOLEAN)
802 gboolean boolean_data = GPOINTER_TO_INT(l->data);
803 l = l->next;
804 if (boolean_data == g_value_get_boolean(value))
805 continue;
806 status_set_attr_boolean(status, id, boolean_data);
807 changed = TRUE;
809 else
811 /* We don't know what the data is--skip over it */
812 l = l->next;
816 /* Reset any unspecified attributes to their default value */
817 status_type = purple_status_get_status_type(status);
818 l = purple_status_type_get_attrs(status_type);
819 while (l != NULL) {
820 PurpleStatusAttribute *attr;
822 attr = l->data;
823 l = l->next;
825 if (!g_list_find_custom(specified_attr_ids, attr->id, (GCompareFunc)strcmp)) {
826 GValue *default_value;
827 default_value = purple_status_attribute_get_value(attr);
828 if (G_VALUE_TYPE(default_value) == G_TYPE_STRING) {
829 const char *cur = purple_status_get_attr_string(status, attr->id);
830 const char *def = g_value_get_string(default_value);
831 if (purple_strequal(cur, def)) {
832 continue;
835 status_set_attr_string(status, attr->id, def);
836 } else if (G_VALUE_TYPE(default_value) == G_TYPE_INT) {
837 int cur = purple_status_get_attr_int(status, attr->id);
838 int def = g_value_get_int(default_value);
839 if (cur == def)
840 continue;
842 status_set_attr_int(status, attr->id, def);
843 } else if (G_VALUE_TYPE(default_value) == G_TYPE_BOOLEAN) {
844 gboolean cur = purple_status_get_attr_boolean(status, attr->id);
845 gboolean def = g_value_get_boolean(default_value);
846 if (cur == def)
847 continue;
849 status_set_attr_boolean(status, attr->id, def);
851 changed = TRUE;
854 g_list_free(specified_attr_ids);
856 if (!changed)
857 return;
858 status_has_changed(status);
861 PurpleStatusType *
862 purple_status_get_status_type(PurpleStatus *status)
864 PurpleStatusPrivate *priv = NULL;
866 g_return_val_if_fail(PURPLE_IS_STATUS(status), NULL);
868 priv = purple_status_get_instance_private(status);
869 return priv->status_type;
872 PurplePresence *
873 purple_status_get_presence(PurpleStatus *status)
875 PurpleStatusPrivate *priv = NULL;
877 g_return_val_if_fail(PURPLE_IS_STATUS(status), NULL);
879 priv = purple_status_get_instance_private(status);
880 return priv->presence;
883 const char *
884 purple_status_get_id(PurpleStatus *status)
886 g_return_val_if_fail(PURPLE_IS_STATUS(status), NULL);
888 return purple_status_type_get_id(purple_status_get_status_type(status));
891 const char *
892 purple_status_get_name(PurpleStatus *status)
894 g_return_val_if_fail(PURPLE_IS_STATUS(status), NULL);
896 return purple_status_type_get_name(purple_status_get_status_type(status));
899 gboolean
900 purple_status_is_independent(PurpleStatus *status)
902 g_return_val_if_fail(PURPLE_IS_STATUS(status), FALSE);
904 return purple_status_type_is_independent(purple_status_get_status_type(status));
907 gboolean
908 purple_status_is_exclusive(PurpleStatus *status)
910 g_return_val_if_fail(PURPLE_IS_STATUS(status), FALSE);
912 return purple_status_type_is_exclusive(purple_status_get_status_type(status));
915 gboolean
916 purple_status_is_available(PurpleStatus *status)
918 g_return_val_if_fail(PURPLE_IS_STATUS(status), FALSE);
920 return purple_status_type_is_available(purple_status_get_status_type(status));
923 gboolean
924 purple_status_is_active(PurpleStatus *status)
926 PurpleStatusPrivate *priv = NULL;
928 g_return_val_if_fail(PURPLE_IS_STATUS(status), FALSE);
930 priv = purple_status_get_instance_private(status);
931 return priv->active;
934 gboolean
935 purple_status_is_online(PurpleStatus *status)
937 PurpleStatusPrimitive primitive;
939 g_return_val_if_fail(PURPLE_IS_STATUS(status), FALSE);
941 primitive = purple_status_type_get_primitive(purple_status_get_status_type(status));
943 return (primitive != PURPLE_STATUS_UNSET &&
944 primitive != PURPLE_STATUS_OFFLINE);
947 GValue *
948 purple_status_get_attr_value(PurpleStatus *status, const char *id)
950 PurpleStatusPrivate *priv = NULL;
952 g_return_val_if_fail(PURPLE_IS_STATUS(status), NULL);
953 g_return_val_if_fail(id != NULL, NULL);
955 priv = purple_status_get_instance_private(status);
956 return (GValue *)g_hash_table_lookup(priv->attr_values, id);
959 gboolean
960 purple_status_get_attr_boolean(PurpleStatus *status, const char *id)
962 const GValue *value;
964 g_return_val_if_fail(PURPLE_IS_STATUS(status), FALSE);
965 g_return_val_if_fail(id != NULL, FALSE);
967 if ((value = purple_status_get_attr_value(status, id)) == NULL)
968 return FALSE;
970 g_return_val_if_fail(G_VALUE_TYPE(value) == G_TYPE_BOOLEAN, FALSE);
972 return g_value_get_boolean(value);
976 purple_status_get_attr_int(PurpleStatus *status, const char *id)
978 const GValue *value;
980 g_return_val_if_fail(PURPLE_IS_STATUS(status), 0);
981 g_return_val_if_fail(id != NULL, 0);
983 if ((value = purple_status_get_attr_value(status, id)) == NULL)
984 return 0;
986 g_return_val_if_fail(G_VALUE_TYPE(value) == G_TYPE_INT, 0);
988 return g_value_get_int(value);
991 const char *
992 purple_status_get_attr_string(PurpleStatus *status, const char *id)
994 const GValue *value;
996 g_return_val_if_fail(PURPLE_IS_STATUS(status), NULL);
997 g_return_val_if_fail(id != NULL, NULL);
999 if ((value = purple_status_get_attr_value(status, id)) == NULL)
1000 return NULL;
1002 g_return_val_if_fail(G_VALUE_TYPE(value) == G_TYPE_STRING, NULL);
1004 return g_value_get_string(value);
1007 gint
1008 purple_status_compare(PurpleStatus *status1, PurpleStatus *status2)
1010 PurpleStatusType *type1, *type2;
1011 int score1 = 0, score2 = 0;
1013 if ((status1 == NULL && status2 == NULL) ||
1014 (status1 == status2))
1016 return 0;
1018 else if (status1 == NULL)
1019 return 1;
1020 else if (status2 == NULL)
1021 return -1;
1023 type1 = purple_status_get_status_type(status1);
1024 type2 = purple_status_get_status_type(status2);
1026 if (purple_status_is_active(status1))
1027 score1 = primitive_scores[purple_status_type_get_primitive(type1)];
1029 if (purple_status_is_active(status2))
1030 score2 = primitive_scores[purple_status_type_get_primitive(type2)];
1032 if (score1 > score2)
1033 return -1;
1034 else if (score1 < score2)
1035 return 1;
1037 return 0;
1041 /**************************************************************************
1042 * GBoxed code for PurpleStatusType
1043 **************************************************************************/
1044 static PurpleStatusType *
1045 purple_status_type_ref(PurpleStatusType *status_type)
1047 g_return_val_if_fail(status_type != NULL, NULL);
1049 status_type->box_count++;
1051 return status_type;
1054 static void
1055 purple_status_type_unref(PurpleStatusType *status_type)
1057 g_return_if_fail(status_type != NULL);
1058 g_return_if_fail(status_type->box_count >= 0);
1060 if (!status_type->box_count--)
1061 purple_status_type_destroy(status_type);
1064 GType
1065 purple_status_type_get_type(void)
1067 static GType type = 0;
1069 if (type == 0) {
1070 type = g_boxed_type_register_static("PurpleStatusType",
1071 (GBoxedCopyFunc)purple_status_type_ref,
1072 (GBoxedFreeFunc)purple_status_type_unref);
1075 return type;
1078 /**************************************************************************
1079 * GBoxed code for PurpleStatusAttribute
1080 **************************************************************************/
1081 static PurpleStatusAttribute *
1082 purple_status_attribute_copy(PurpleStatusAttribute *status_attr)
1084 g_return_val_if_fail(status_attr != NULL, NULL);
1086 return purple_status_attribute_new(status_attr->id,
1087 status_attr->name,
1088 purple_value_dup(status_attr->value_type));
1091 GType
1092 purple_status_attribute_get_type(void)
1094 static GType type = 0;
1096 if (type == 0) {
1097 type = g_boxed_type_register_static("PurpleStatusAttribute",
1098 (GBoxedCopyFunc)purple_status_attribute_copy,
1099 (GBoxedFreeFunc)purple_status_attribute_destroy);
1102 return type;
1105 /**************************************************************************
1106 * GBoxed code for PurpleMood
1107 **************************************************************************/
1108 static PurpleMood *
1109 purple_mood_copy(PurpleMood *mood)
1111 PurpleMood *mood_copy;
1113 g_return_val_if_fail(mood != NULL, NULL);
1115 mood_copy = g_new(PurpleMood, 1);
1117 mood_copy->mood = g_strdup(mood->mood);
1118 mood_copy->description = g_strdup(mood->description);
1120 return mood_copy;
1123 static void
1124 purple_mood_free(PurpleMood *mood)
1126 g_free((gchar *)mood->mood);
1127 g_free((gchar *)mood->description);
1129 g_free(mood);
1132 GType
1133 purple_mood_get_type(void)
1135 static GType type = 0;
1137 if (type == 0) {
1138 type = g_boxed_type_register_static("PurpleMood",
1139 (GBoxedCopyFunc)purple_mood_copy,
1140 (GBoxedFreeFunc)purple_mood_free);
1143 return type;
1147 /**************************************************************************
1148 * GObject code
1149 **************************************************************************/
1151 /* Set method for GObject properties */
1152 static void
1153 purple_status_set_property(GObject *obj, guint param_id, const GValue *value,
1154 GParamSpec *pspec)
1156 PurpleStatus *status = PURPLE_STATUS(obj);
1157 PurpleStatusPrivate *priv = purple_status_get_instance_private(status);
1159 switch (param_id) {
1160 case PROP_STATUS_TYPE:
1161 priv->status_type = g_value_get_pointer(value);
1162 break;
1163 case PROP_PRESENCE:
1164 priv->presence = g_value_get_object(value);
1165 break;
1166 case PROP_ACTIVE:
1167 purple_status_set_active(status, g_value_get_boolean(value));
1168 break;
1169 default:
1170 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
1171 break;
1175 /* Get method for GObject properties */
1176 static void
1177 purple_status_get_property(GObject *obj, guint param_id, GValue *value,
1178 GParamSpec *pspec)
1180 PurpleStatus *status = PURPLE_STATUS(obj);
1182 switch (param_id) {
1183 case PROP_STATUS_TYPE:
1184 g_value_set_pointer(value, purple_status_get_status_type(status));
1185 break;
1186 case PROP_PRESENCE:
1187 g_value_set_object(value, purple_status_get_presence(status));
1188 break;
1189 case PROP_ACTIVE:
1190 g_value_set_boolean(value, purple_status_is_active(status));
1191 break;
1192 default:
1193 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
1194 break;
1198 /* GObject initialization function */
1199 static void
1200 purple_status_init(PurpleStatus *status)
1202 PurpleStatusPrivate *priv = purple_status_get_instance_private(status);
1204 priv->attr_values =
1205 g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
1206 (GDestroyNotify)purple_value_free);
1209 /* Called when done constructing */
1210 static void
1211 purple_status_constructed(GObject *object)
1213 GList *l;
1214 PurpleStatusPrivate *priv = purple_status_get_instance_private(PURPLE_STATUS(object));
1216 G_OBJECT_CLASS(purple_status_parent_class)->constructed(object);
1218 for (l = purple_status_type_get_attrs(priv->status_type); l != NULL; l = l->next)
1220 PurpleStatusAttribute *attr = (PurpleStatusAttribute *)l->data;
1221 GValue *value = purple_status_attribute_get_value(attr);
1222 GValue *new_value = purple_value_dup(value);
1224 g_hash_table_insert(priv->attr_values,
1225 (char *)purple_status_attribute_get_id(attr),
1226 new_value);
1231 * GObject finalize function
1232 * TODO: If the PurpleStatus is in a PurplePresence, then
1233 * remove it from the PurplePresence?
1235 static void
1236 purple_status_finalize(GObject *obj)
1238 PurpleStatusPrivate *priv = purple_status_get_instance_private(PURPLE_STATUS(obj));
1239 g_hash_table_destroy(priv->attr_values);
1241 G_OBJECT_CLASS(purple_status_parent_class)->finalize(obj);
1244 /* Class initializer function */
1245 static void
1246 purple_status_class_init(PurpleStatusClass *klass)
1248 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
1250 obj_class->finalize = purple_status_finalize;
1251 obj_class->constructed = purple_status_constructed;
1253 /* Setup properties */
1254 obj_class->get_property = purple_status_get_property;
1255 obj_class->set_property = purple_status_set_property;
1257 properties[PROP_STATUS_TYPE] = g_param_spec_pointer("status-type",
1258 "Status type",
1259 "The PurpleStatusType of the status.",
1260 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
1261 G_PARAM_STATIC_STRINGS);
1263 properties[PROP_PRESENCE] = g_param_spec_object("presence", "Presence",
1264 "The presence that the status belongs to.",
1265 PURPLE_TYPE_PRESENCE,
1266 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
1267 G_PARAM_STATIC_STRINGS);
1269 properties[PROP_ACTIVE] = g_param_spec_boolean("active", "Active",
1270 "Whether the status is active or not.", FALSE,
1271 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1273 g_object_class_install_properties(obj_class, PROP_LAST, properties);
1276 PurpleStatus *
1277 purple_status_new(PurpleStatusType *status_type, PurplePresence *presence)
1279 g_return_val_if_fail(status_type != NULL, NULL);
1280 g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), NULL);
1282 return g_object_new(PURPLE_TYPE_STATUS,
1283 "status-type", status_type,
1284 "presence", presence,
1285 NULL);
1289 /**************************************************************************
1290 * Status subsystem
1291 **************************************************************************/
1292 static void
1293 score_pref_changed_cb(const char *name, PurplePrefType type,
1294 gconstpointer value, gpointer data)
1296 int index = GPOINTER_TO_INT(data);
1298 primitive_scores[index] = GPOINTER_TO_INT(value);
1301 void *
1302 purple_statuses_get_handle(void) {
1303 static int handle;
1305 return &handle;
1308 void
1309 purple_statuses_init(void)
1311 void *handle = purple_statuses_get_handle();
1313 purple_prefs_add_none("/purple/status");
1314 purple_prefs_add_none("/purple/status/scores");
1316 purple_prefs_add_int("/purple/status/scores/offline",
1317 primitive_scores[PURPLE_STATUS_OFFLINE]);
1318 purple_prefs_add_int("/purple/status/scores/available",
1319 primitive_scores[PURPLE_STATUS_AVAILABLE]);
1320 purple_prefs_add_int("/purple/status/scores/invisible",
1321 primitive_scores[PURPLE_STATUS_INVISIBLE]);
1322 purple_prefs_add_int("/purple/status/scores/away",
1323 primitive_scores[PURPLE_STATUS_AWAY]);
1324 purple_prefs_add_int("/purple/status/scores/extended_away",
1325 primitive_scores[PURPLE_STATUS_EXTENDED_AWAY]);
1326 purple_prefs_add_int("/purple/status/scores/idle",
1327 primitive_scores[SCORE_IDLE]);
1328 purple_prefs_add_int("/purple/status/scores/idle_time",
1329 primitive_scores[SCORE_IDLE_TIME]);
1330 purple_prefs_add_int("/purple/status/scores/offline_msg",
1331 primitive_scores[SCORE_OFFLINE_MESSAGE]);
1333 purple_prefs_connect_callback(handle, "/purple/status/scores/offline",
1334 score_pref_changed_cb,
1335 GINT_TO_POINTER(PURPLE_STATUS_OFFLINE));
1336 purple_prefs_connect_callback(handle, "/purple/status/scores/available",
1337 score_pref_changed_cb,
1338 GINT_TO_POINTER(PURPLE_STATUS_AVAILABLE));
1339 purple_prefs_connect_callback(handle, "/purple/status/scores/invisible",
1340 score_pref_changed_cb,
1341 GINT_TO_POINTER(PURPLE_STATUS_INVISIBLE));
1342 purple_prefs_connect_callback(handle, "/purple/status/scores/away",
1343 score_pref_changed_cb,
1344 GINT_TO_POINTER(PURPLE_STATUS_AWAY));
1345 purple_prefs_connect_callback(handle, "/purple/status/scores/extended_away",
1346 score_pref_changed_cb,
1347 GINT_TO_POINTER(PURPLE_STATUS_EXTENDED_AWAY));
1348 purple_prefs_connect_callback(handle, "/purple/status/scores/idle",
1349 score_pref_changed_cb,
1350 GINT_TO_POINTER(SCORE_IDLE));
1351 purple_prefs_connect_callback(handle, "/purple/status/scores/idle_time",
1352 score_pref_changed_cb,
1353 GINT_TO_POINTER(SCORE_IDLE_TIME));
1354 purple_prefs_connect_callback(handle, "/purple/status/scores/offline_msg",
1355 score_pref_changed_cb,
1356 GINT_TO_POINTER(SCORE_OFFLINE_MESSAGE));
1358 purple_prefs_trigger_callback("/purple/status/scores/offline");
1359 purple_prefs_trigger_callback("/purple/status/scores/available");
1360 purple_prefs_trigger_callback("/purple/status/scores/invisible");
1361 purple_prefs_trigger_callback("/purple/status/scores/away");
1362 purple_prefs_trigger_callback("/purple/status/scores/extended_away");
1363 purple_prefs_trigger_callback("/purple/status/scores/idle");
1364 purple_prefs_trigger_callback("/purple/status/scores/idle_time");
1365 purple_prefs_trigger_callback("/purple/status/scores/offline_msg");
1368 void
1369 purple_statuses_uninit(void)
1371 purple_prefs_disconnect_by_handle(purple_prefs_get_handle());