]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-roster-contact.c
rename EmpathyRosterItem to EmpathyRosterContact
[empathy.git] / libempathy-gtk / empathy-roster-contact.c
1 #include "config.h"
2
3 #include "empathy-roster-contact.h"
4
5 #include <telepathy-glib/util.h>
6
7 #include <libempathy/empathy-utils.h>
8
9 #include <libempathy-gtk/empathy-images.h>
10 #include <libempathy-gtk/empathy-ui-utils.h>
11
12 G_DEFINE_TYPE (EmpathyRosterContact, empathy_roster_contact, GTK_TYPE_ALIGNMENT)
13
14 #define AVATAR_SIZE 48
15
16 enum
17 {
18   PROP_INDIVIDIUAL = 1,
19   PROP_ONLINE,
20   PROP_ALIAS,
21   N_PROPS
22 };
23
24 /*
25 enum
26 {
27   LAST_SIGNAL
28 };
29
30 static guint signals[LAST_SIGNAL];
31 */
32
33 struct _EmpathyRosterContactPriv
34 {
35   FolksIndividual *individual;
36
37   GtkWidget *avatar;
38   GtkWidget *first_line_alig;
39   GtkWidget *alias;
40   GtkWidget *presence_msg;
41   GtkWidget *presence_icon;
42   GtkWidget *phone_icon;
43
44   gboolean online;
45 };
46
47 static const gchar *
48 get_alias (EmpathyRosterContact *self)
49 {
50   return folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (
51         self->priv->individual));
52 }
53
54 static void
55 empathy_roster_contact_get_property (GObject *object,
56     guint property_id,
57     GValue *value,
58     GParamSpec *pspec)
59 {
60   EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
61
62   switch (property_id)
63     {
64       case PROP_INDIVIDIUAL:
65         g_value_set_object (value, self->priv->individual);
66         break;
67       case PROP_ONLINE:
68         g_value_set_boolean (value, self->priv->online);
69         break;
70       case PROP_ALIAS:
71         g_value_set_string (value, get_alias (self));
72         break;
73       default:
74         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
75         break;
76     }
77 }
78
79 static void
80 empathy_roster_contact_set_property (GObject *object,
81     guint property_id,
82     const GValue *value,
83     GParamSpec *pspec)
84 {
85   EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
86
87   switch (property_id)
88     {
89       case PROP_INDIVIDIUAL:
90         g_assert (self->priv->individual == NULL); /* construct only */
91         self->priv->individual = g_value_dup_object (value);
92         break;
93       default:
94         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
95         break;
96     }
97 }
98
99 static void
100 avatar_loaded_cb (GObject *source,
101     GAsyncResult *result,
102     gpointer user_data)
103 {
104   TpWeakRef *wr = user_data;
105   EmpathyRosterContact *self;
106   GdkPixbuf *pixbuf;
107
108   self = tp_weak_ref_dup_object (wr);
109   if (self == NULL)
110     goto out;
111
112   pixbuf = empathy_pixbuf_avatar_from_individual_scaled_finish (
113       FOLKS_INDIVIDUAL (source), result, NULL);
114
115   if (pixbuf == NULL)
116     {
117       pixbuf = empathy_pixbuf_from_icon_name_sized (
118           EMPATHY_IMAGE_AVATAR_DEFAULT, AVATAR_SIZE);
119     }
120
121   gtk_image_set_from_pixbuf (GTK_IMAGE (self->priv->avatar), pixbuf);
122   g_object_unref (pixbuf);
123
124   g_object_unref (self);
125
126 out:
127   tp_weak_ref_destroy (wr);
128 }
129
130 static void
131 update_avatar (EmpathyRosterContact *self)
132 {
133   empathy_pixbuf_avatar_from_individual_scaled_async (self->priv->individual,
134       AVATAR_SIZE, AVATAR_SIZE, NULL, avatar_loaded_cb,
135       tp_weak_ref_new (self, NULL, NULL));
136 }
137
138 static void
139 avatar_changed_cb (FolksIndividual *individual,
140     GParamSpec *spec,
141     EmpathyRosterContact *self)
142 {
143   update_avatar (self);
144 }
145
146 static void
147 update_alias (EmpathyRosterContact *self)
148 {
149   gtk_label_set_text (GTK_LABEL (self->priv->alias), get_alias (self));
150
151   g_object_notify (G_OBJECT (self), "alias");
152 }
153
154 static void
155 alias_changed_cb (FolksIndividual *individual,
156     GParamSpec *spec,
157     EmpathyRosterContact *self)
158 {
159   update_alias (self);
160 }
161
162 static gboolean
163 is_phone (FolksIndividual *individual)
164 {
165   const gchar * const *types;
166
167   types = empathy_individual_get_client_types (individual);
168   if (types == NULL)
169     return FALSE;
170
171   if (g_strv_length ((GStrv) types) <= 0)
172     return FALSE;
173
174   return !tp_strdiff (types[0], "phone");
175 }
176
177 static void
178 update_presence_msg (EmpathyRosterContact *self)
179 {
180   const gchar *msg;
181
182   msg = folks_presence_details_get_presence_message (
183       FOLKS_PRESENCE_DETAILS (self->priv->individual));
184
185   if (tp_str_empty (msg))
186     {
187       /* Just display the alias in the center of the row */
188       gtk_alignment_set (GTK_ALIGNMENT (self->priv->first_line_alig),
189           0, 0.5, 1, 1);
190
191       gtk_widget_hide (self->priv->presence_msg);
192     }
193   else
194     {
195       gtk_label_set_text (GTK_LABEL (self->priv->presence_msg), msg);
196
197       gtk_alignment_set (GTK_ALIGNMENT (self->priv->first_line_alig),
198           0, 0.75, 1, 1);
199       gtk_misc_set_alignment (GTK_MISC (self->priv->presence_msg), 0, 0.25);
200
201       gtk_widget_show (self->priv->presence_msg);
202     }
203
204   gtk_widget_set_visible (self->priv->phone_icon,
205       is_phone (self->priv->individual));
206 }
207
208 static void
209 presence_message_changed_cb (FolksIndividual *individual,
210     GParamSpec *spec,
211     EmpathyRosterContact *self)
212 {
213   update_presence_msg (self);
214 }
215
216 static void
217 update_presence_icon (EmpathyRosterContact *self)
218 {
219   const gchar *icon;
220
221   icon = empathy_icon_name_for_individual (self->priv->individual);
222
223   gtk_image_set_from_icon_name (GTK_IMAGE (self->priv->presence_icon), icon,
224       GTK_ICON_SIZE_MENU);
225 }
226
227 static void
228 update_online (EmpathyRosterContact *self)
229 {
230   FolksPresenceType presence;
231   gboolean online;
232
233   presence = folks_presence_details_get_presence_type (
234       FOLKS_PRESENCE_DETAILS (self->priv->individual));
235
236   switch (presence)
237     {
238       case FOLKS_PRESENCE_TYPE_UNSET:
239       case FOLKS_PRESENCE_TYPE_OFFLINE:
240       case FOLKS_PRESENCE_TYPE_UNKNOWN:
241       case FOLKS_PRESENCE_TYPE_ERROR:
242         online = FALSE;
243         break;
244
245       case FOLKS_PRESENCE_TYPE_AVAILABLE:
246       case FOLKS_PRESENCE_TYPE_AWAY:
247       case FOLKS_PRESENCE_TYPE_EXTENDED_AWAY:
248       case FOLKS_PRESENCE_TYPE_HIDDEN:
249       case FOLKS_PRESENCE_TYPE_BUSY:
250         online = TRUE;
251         break;
252
253       default:
254         g_warning ("Unknown FolksPresenceType: %d", presence);
255         online = FALSE;
256     }
257
258   if (self->priv->online == online)
259     return;
260
261   self->priv->online = online;
262   g_object_notify (G_OBJECT (self), "online");
263 }
264
265 static void
266 presence_status_changed_cb (FolksIndividual *individual,
267     GParamSpec *spec,
268     EmpathyRosterContact *self)
269 {
270   update_presence_icon (self);
271   update_online (self);
272 }
273
274 static void
275 empathy_roster_contact_constructed (GObject *object)
276 {
277   EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
278   void (*chain_up) (GObject *) =
279       ((GObjectClass *) empathy_roster_contact_parent_class)->constructed;
280
281   if (chain_up != NULL)
282     chain_up (object);
283
284   g_assert (FOLKS_IS_INDIVIDUAL (self->priv->individual));
285
286   tp_g_signal_connect_object (self->priv->individual, "notify::avatar",
287       G_CALLBACK (avatar_changed_cb), self, 0);
288   tp_g_signal_connect_object (self->priv->individual, "notify::alias",
289       G_CALLBACK (alias_changed_cb), self, 0);
290   tp_g_signal_connect_object (self->priv->individual,
291       "notify::presence-message",
292       G_CALLBACK (presence_message_changed_cb), self, 0);
293   tp_g_signal_connect_object (self->priv->individual, "notify::presence-status",
294       G_CALLBACK (presence_status_changed_cb), self, 0);
295
296   update_avatar (self);
297   update_alias (self);
298   update_presence_msg (self);
299   update_presence_icon (self);
300
301   update_online (self);
302 }
303
304 static void
305 empathy_roster_contact_dispose (GObject *object)
306 {
307   EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
308   void (*chain_up) (GObject *) =
309       ((GObjectClass *) empathy_roster_contact_parent_class)->dispose;
310
311   g_clear_object (&self->priv->individual);
312
313   if (chain_up != NULL)
314     chain_up (object);
315 }
316
317 static void
318 empathy_roster_contact_finalize (GObject *object)
319 {
320   //EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
321   void (*chain_up) (GObject *) =
322       ((GObjectClass *) empathy_roster_contact_parent_class)->finalize;
323
324   if (chain_up != NULL)
325     chain_up (object);
326 }
327
328 static void
329 empathy_roster_contact_class_init (
330     EmpathyRosterContactClass *klass)
331 {
332   GObjectClass *oclass = G_OBJECT_CLASS (klass);
333   GParamSpec *spec;
334
335   oclass->get_property = empathy_roster_contact_get_property;
336   oclass->set_property = empathy_roster_contact_set_property;
337   oclass->constructed = empathy_roster_contact_constructed;
338   oclass->dispose = empathy_roster_contact_dispose;
339   oclass->finalize = empathy_roster_contact_finalize;
340
341   spec = g_param_spec_object ("individual", "Individual",
342       "FolksIndividual",
343       FOLKS_TYPE_INDIVIDUAL,
344       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
345   g_object_class_install_property (oclass, PROP_INDIVIDIUAL, spec);
346
347   spec = g_param_spec_boolean ("online", "Online",
348       "TRUE if Individual is online",
349       FALSE,
350       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
351   g_object_class_install_property (oclass, PROP_ONLINE, spec);
352
353   spec = g_param_spec_string ("alias", "Alias",
354       "The Alias of the individual displayed in the widget",
355       NULL,
356       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
357   g_object_class_install_property (oclass, PROP_ALIAS, spec);
358
359   g_type_class_add_private (klass, sizeof (EmpathyRosterContactPriv));
360 }
361
362 static void
363 empathy_roster_contact_init (EmpathyRosterContact *self)
364 {
365   GtkWidget *main_box, *box, *first_line_box;
366   GtkStyleContext *context;
367
368   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
369       EMPATHY_TYPE_ROSTER_CONTACT, EmpathyRosterContactPriv);
370
371   gtk_widget_set_size_request (GTK_WIDGET (self), 300, 64);
372
373   main_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
374
375   /* Avatar */
376   self->priv->avatar = gtk_image_new ();
377
378   gtk_box_pack_start (GTK_BOX (main_box), self->priv->avatar, FALSE, FALSE, 0);
379   gtk_widget_show (self->priv->avatar);
380
381   box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
382
383   /* Alias and phone icon */
384   self->priv->first_line_alig = gtk_alignment_new (0, 0.5, 1, 1);
385   first_line_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
386
387   self->priv->alias = gtk_label_new (NULL);
388   gtk_box_pack_start (GTK_BOX (first_line_box), self->priv->alias,
389       FALSE, FALSE, 0);
390   gtk_widget_show (self->priv->alias);
391
392   self->priv->phone_icon = gtk_image_new_from_icon_name ("phone-symbolic",
393       GTK_ICON_SIZE_MENU);
394   gtk_misc_set_alignment (GTK_MISC (self->priv->phone_icon), 0, 0.5);
395   gtk_box_pack_start (GTK_BOX (first_line_box), self->priv->phone_icon,
396       TRUE, TRUE, 0);
397
398   gtk_container_add (GTK_CONTAINER (self->priv->first_line_alig),
399       first_line_box);
400   gtk_widget_show (self->priv->first_line_alig);
401
402   gtk_box_pack_start (GTK_BOX (box), self->priv->first_line_alig,
403       TRUE, TRUE, 0);
404   gtk_widget_show (first_line_box);
405
406   gtk_box_pack_start (GTK_BOX (main_box), box, TRUE, TRUE, 0);
407   gtk_widget_show (box);
408
409   /* Presence */
410   self->priv->presence_msg = gtk_label_new (NULL);
411   gtk_box_pack_start (GTK_BOX (box), self->priv->presence_msg, TRUE, TRUE, 0);
412   gtk_widget_show (self->priv->presence_msg);
413
414   context = gtk_widget_get_style_context (self->priv->presence_msg);
415   gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL);
416
417   /* Presence icon */
418   self->priv->presence_icon = gtk_image_new ();
419
420   gtk_box_pack_start (GTK_BOX (main_box), self->priv->presence_icon,
421       FALSE, FALSE, 0);
422   gtk_widget_show (self->priv->presence_icon);
423
424   gtk_container_add (GTK_CONTAINER (self), main_box);
425   gtk_widget_show (main_box);
426 }
427
428 GtkWidget *
429 empathy_roster_contact_new (FolksIndividual *individual)
430 {
431   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
432
433   return g_object_new (EMPATHY_TYPE_ROSTER_CONTACT,
434       "individual", individual,
435       "bottom-padding", 8,
436       "top-padding", 8,
437       "left-padding", 8,
438       "right-padding", 8,
439       NULL);
440 }
441
442 FolksIndividual *
443 empathy_roster_contact_get_individual (EmpathyRosterContact *self)
444 {
445   return self->priv->individual;
446 }
447
448 gboolean
449 empathy_roster_contact_is_online (EmpathyRosterContact *self)
450 {
451   return self->priv->online;
452 }