[darcs-to-svn @ initial import]
[empathy.git] / libempathy / gossip-presence.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2004-2007 Imendio AB
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Authors: Mikael Hallendal <micke@imendio.com>
21  */
22
23 #include "config.h"
24
25 #include <string.h>
26
27 #include <glib/gi18n.h>
28
29 #include "gossip-presence.h"
30 #include "gossip-time.h"
31
32 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_PRESENCE, GossipPresencePriv))
33
34 typedef struct _GossipPresencePriv GossipPresencePriv;
35
36 struct _GossipPresencePriv {
37         GossipPresenceState  state;
38
39         gchar               *status;
40         gchar               *resource;
41
42         gint                 priority;
43         GossipTime           timestamp;
44 };
45
46 static void         presence_finalize           (GObject             *object);
47 static void         presence_get_property       (GObject             *object,
48                                                  guint                param_id,
49                                                  GValue              *value,
50                                                  GParamSpec          *pspec);
51 static void         presence_set_property       (GObject             *object,
52                                                  guint                param_id,
53                                                  const GValue        *value,
54                                                  GParamSpec          *pspec);
55
56 enum {
57         PROP_0,
58         PROP_STATE,
59         PROP_STATUS,
60         PROP_RESOURCE,
61         PROP_PRIORITY
62 };
63
64 G_DEFINE_TYPE (GossipPresence, gossip_presence, G_TYPE_OBJECT);
65
66 static void
67 gossip_presence_class_init (GossipPresenceClass *class)
68 {
69         GObjectClass *object_class;
70
71         object_class = G_OBJECT_CLASS (class);
72
73         object_class->finalize     = presence_finalize;
74         object_class->get_property = presence_get_property;
75         object_class->set_property = presence_set_property;
76
77         g_object_class_install_property (object_class,
78                                          PROP_STATE,
79                                          g_param_spec_int ("state",
80                                                            "Presence State",
81                                                            "The current state of the presence",
82                                                            GOSSIP_PRESENCE_STATE_AVAILABLE,
83                                                            GOSSIP_PRESENCE_STATE_EXT_AWAY,
84                                                            GOSSIP_PRESENCE_STATE_AVAILABLE,
85                                                            G_PARAM_READWRITE));
86         g_object_class_install_property (object_class,
87                                          PROP_STATUS,
88                                          g_param_spec_string ("status",
89                                                               "Presence Status",
90                                                               "Status string set on presence",
91                                                               NULL,
92                                                               G_PARAM_READWRITE));
93         g_object_class_install_property (object_class,
94                                          PROP_RESOURCE,
95                                          g_param_spec_string ("resource",
96                                                               "Presence Resource",
97                                                               "Resource that this presence is for",
98                                                               NULL,
99                                                               G_PARAM_READWRITE));
100         g_object_class_install_property (object_class,
101                                          PROP_PRIORITY,
102                                          g_param_spec_int ("priority",
103                                                            "Presence Priority",
104                                                            "Priority value of presence",
105                                                            G_MININT,
106                                                            G_MAXINT,
107                                                            0,
108                                                            G_PARAM_READWRITE));
109
110         g_type_class_add_private (object_class, sizeof (GossipPresencePriv));
111 }
112
113 static void
114 gossip_presence_init (GossipPresence *presence)
115 {
116         GossipPresencePriv *priv;
117
118         priv = GET_PRIV (presence);
119
120         priv->state = GOSSIP_PRESENCE_STATE_AVAILABLE;
121
122         priv->status = NULL;
123         priv->resource = NULL;
124
125         priv->priority = 0;
126
127         priv->timestamp = gossip_time_get_current ();
128 }
129
130 static void
131 presence_finalize (GObject *object)
132 {
133         GossipPresencePriv *priv;
134
135         priv = GET_PRIV (object);
136
137         g_free (priv->status);
138         g_free (priv->resource);
139
140         (G_OBJECT_CLASS (gossip_presence_parent_class)->finalize) (object);
141 }
142
143 static void
144 presence_get_property (GObject    *object,
145                        guint       param_id,
146                        GValue     *value,
147                        GParamSpec *pspec)
148 {
149         GossipPresencePriv *priv;
150
151         priv = GET_PRIV (object);
152
153         switch (param_id) {
154         case PROP_STATE:
155                 g_value_set_int (value, priv->state);
156                 break;
157         case PROP_STATUS:
158                 g_value_set_string (value,
159                                     gossip_presence_get_status (GOSSIP_PRESENCE (object)));
160                 break;
161         case PROP_RESOURCE:
162                 g_value_set_string (value,
163                                     gossip_presence_get_resource (GOSSIP_PRESENCE (object)));
164                 break;
165         case PROP_PRIORITY:
166                 g_value_set_int (value, priv->priority);
167                 break;
168         default:
169                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
170                 break;
171         }
172 }
173 static void
174 presence_set_property (GObject      *object,
175                        guint         param_id,
176                        const GValue *value,
177                        GParamSpec   *pspec)
178 {
179         GossipPresencePriv *priv;
180
181         priv = GET_PRIV (object);
182
183         switch (param_id) {
184         case PROP_STATE:
185                 priv->state = g_value_get_int (value);
186                 break;
187         case PROP_STATUS:
188                 gossip_presence_set_status (GOSSIP_PRESENCE (object),
189                                             g_value_get_string (value));
190                 break;
191         case PROP_RESOURCE:
192                 gossip_presence_set_resource (GOSSIP_PRESENCE (object),
193                                               g_value_get_string (value));
194                 break;
195         case PROP_PRIORITY:
196                 priv->priority = g_value_get_int (value);
197                 break;
198         default:
199                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
200                 break;
201         }
202 }
203
204 GossipPresence *
205 gossip_presence_new (void)
206 {
207         return g_object_new (GOSSIP_TYPE_PRESENCE, NULL);
208 }
209
210 GossipPresence *
211 gossip_presence_new_full (GossipPresenceState  state,
212                           const gchar         *status)
213 {
214         return g_object_new (GOSSIP_TYPE_PRESENCE,
215                              "state", state,
216                              "status", status,
217                              NULL);
218 }
219
220 const gchar *
221 gossip_presence_get_resource (GossipPresence *presence)
222 {
223         GossipPresencePriv *priv;
224
225         g_return_val_if_fail (GOSSIP_IS_PRESENCE (presence), NULL);
226
227         priv = GET_PRIV (presence);
228
229         if (priv->resource) {
230                 return priv->resource;
231         }
232
233         return NULL;
234 }
235
236 const gchar *
237 gossip_presence_get_status (GossipPresence *presence)
238 {
239         GossipPresencePriv *priv;
240
241         g_return_val_if_fail (GOSSIP_IS_PRESENCE (presence),
242                               _("Offline"));
243
244         priv = GET_PRIV (presence);
245
246         return priv->status;
247 }
248
249 gint
250 gossip_presence_get_priority (GossipPresence *presence)
251 {
252         GossipPresencePriv *priv;
253
254         priv = GET_PRIV (presence);
255         g_return_val_if_fail (GOSSIP_IS_PRESENCE (presence), 0);
256
257         return priv->priority;
258 }
259
260 void
261 gossip_presence_set_resource (GossipPresence *presence,
262                               const gchar    *resource)
263 {
264         GossipPresencePriv *priv;
265
266         g_return_if_fail (GOSSIP_IS_PRESENCE (presence));
267         g_return_if_fail (resource != NULL);
268
269         priv = GET_PRIV (presence);
270
271         g_free (priv->resource);
272         priv->resource = g_strdup (resource);
273
274         g_object_notify (G_OBJECT (presence), "resource");
275 }
276
277 GossipPresenceState
278 gossip_presence_get_state (GossipPresence *presence)
279 {
280         GossipPresencePriv *priv;
281
282         g_return_val_if_fail (GOSSIP_IS_PRESENCE (presence),
283                               GOSSIP_PRESENCE_STATE_AVAILABLE);
284
285         priv = GET_PRIV (presence);
286
287         return priv->state;
288 }
289
290 void
291 gossip_presence_set_state (GossipPresence      *presence,
292                            GossipPresenceState  state)
293 {
294         GossipPresencePriv *priv;
295
296         g_return_if_fail (GOSSIP_IS_PRESENCE (presence));
297
298         priv = GET_PRIV (presence);
299
300         priv->state = state;
301
302         g_object_notify (G_OBJECT (presence), "state");
303 }
304
305 void
306 gossip_presence_set_status (GossipPresence *presence,
307                             const gchar    *status)
308 {
309         GossipPresencePriv *priv;
310
311         priv = GET_PRIV (presence);
312         g_return_if_fail (GOSSIP_IS_PRESENCE (presence));
313
314         g_free (priv->status);
315
316         if (status) {
317                 priv->status = g_strdup (status);
318         } else {
319                 priv->status = NULL;
320         }
321
322         g_object_notify (G_OBJECT (presence), "status");
323 }
324
325 void
326 gossip_presence_set_priority (GossipPresence *presence,
327                               gint            priority)
328 {
329         GossipPresencePriv *priv;
330
331         g_return_if_fail (GOSSIP_IS_PRESENCE (presence));
332
333         priv = GET_PRIV (presence);
334
335         priv->priority = priority;
336
337         g_object_notify (G_OBJECT (presence), "priority");
338 }
339
340 gboolean
341 gossip_presence_resource_equal (gconstpointer a,
342                                 gconstpointer b)
343 {
344         GossipPresencePriv *priv1;
345         GossipPresencePriv *priv2;
346
347         g_return_val_if_fail (GOSSIP_IS_PRESENCE (a), FALSE);
348         g_return_val_if_fail (GOSSIP_IS_PRESENCE (b), FALSE);
349
350         priv1 = GET_PRIV (a);
351         priv2 = GET_PRIV (b);
352
353         if (!priv1->resource) {
354                 if (!priv2->resource) {
355                         return TRUE;
356                 }
357
358                 return FALSE;
359         }
360
361         if (!priv2->resource) {
362                 return FALSE;
363         }
364
365         if (strcmp (priv1->resource, priv2->resource) == 0) {
366                 return TRUE;
367         }
368
369         return FALSE;
370 }
371
372 gint
373 gossip_presence_sort_func (gconstpointer a,
374                            gconstpointer b)
375 {
376         GossipPresencePriv *priv_a;
377         GossipPresencePriv *priv_b;
378         gint                diff;
379
380         g_return_val_if_fail (GOSSIP_IS_PRESENCE (a), 0);
381         g_return_val_if_fail (GOSSIP_IS_PRESENCE (b), 0);
382
383         /* We sort here by priority AND status, in theory, the
384          * priority would be enough for JUST Jabber contacts which
385          * actually abide to the protocol, but for other protocols and
386          * dodgy clients, we will sort by:
387          *   
388          *    1. State
389          *    2. Priority
390          *    3. Time it was set (most recent first).
391          */
392          
393         priv_a = GET_PRIV (a);
394         priv_b = GET_PRIV (b);
395
396         /* 1. State */
397         diff = priv_a->state - priv_b->state;
398         if (diff != 0) {
399                 return diff < 1 ? -1 : +1;
400         }
401
402         /* 2. Priority */
403         diff = priv_a->priority - priv_b->priority;
404         if (diff != 0) {
405                 return diff < 1 ? -1 : +1;
406         }
407
408         /* 3. Time (newest first) */
409         diff = priv_b->timestamp - priv_a->timestamp;
410         if (diff != 0) {
411                 return diff < 1 ? -1 : +1;
412         }
413                 
414         /* No real difference, except maybe resource */
415         return 0;
416 }
417
418 const gchar *
419 gossip_presence_state_get_default_status (GossipPresenceState state)
420 {
421         switch (state) {
422         case GOSSIP_PRESENCE_STATE_AVAILABLE:
423                 return _("Available");
424                 break;
425
426         case GOSSIP_PRESENCE_STATE_BUSY:
427                 return _("Busy");
428                 break;
429
430         case GOSSIP_PRESENCE_STATE_AWAY:
431         case GOSSIP_PRESENCE_STATE_EXT_AWAY:
432                 return _("Away");
433                 break;
434
435         case GOSSIP_PRESENCE_STATE_HIDDEN:
436         case GOSSIP_PRESENCE_STATE_UNAVAILABLE:
437                 return _("Unavailable");
438         }
439
440         return _("Available");
441 }