]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-contact-selector.c
update authors
[empathy.git] / libempathy-gtk / empathy-contact-selector.c
1 /*
2 *  Copyright (C) 2007 Marco Barisione <marco@barisione.org>
3 *  Copyright (C) 2008 Collabora Ltd.
4 *
5 *  This library is free software; you can redistribute it and/or
6 *  modify it under the terms of the GNU Lesser General Public
7 *  License as published by the Free Software Foundation; either
8 *  version 2.1 of the License, or (at your option) any later version.
9 *
10 *  This library 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 *  Lesser General Public License for more details.
14 *
15 *  You should have received a copy of the GNU Lesser General Public
16 *  License along with this library; if not, write to the Free Software
17 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 *
19 *  Authors: Marco Barisione <marco@barisione.org>
20 *           Elliot Fairweather <elliot.fairweather@collabora.co.uk>
21 */
22
23 #include <gtk/gtk.h>
24
25 #include <libempathy/empathy-contact.h>
26 #include <libempathy-gtk/empathy-contact-list-store.h>
27 #include "empathy-contact-selector.h"
28
29 G_DEFINE_TYPE (EmpathyContactSelector, empathy_contact_selector,
30     GTK_TYPE_COMBO_BOX)
31
32 #define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE \
33     ((object), EMPATHY_TYPE_CONTACT_SELECTOR, EmpathyContactSelectorPriv))
34
35 enum {
36   CONTACT_COL,
37   NAME_COL,
38   STATUS_ICON_NAME_COL,
39   NUM_COLS
40 };
41
42 enum
43 {
44   PROP_0,
45   PROP_STORE
46 };
47
48 typedef struct _EmpathyContactSelectorPriv EmpathyContactSelectorPriv;
49
50 struct _EmpathyContactSelectorPriv
51 {
52   EmpathyContactListStore *store;
53   GtkListStore *list_store;
54 };
55
56
57
58 EmpathyContact *
59 empathy_contact_selector_get_selected (EmpathyContactSelector *selector)
60 {
61   EmpathyContactSelectorPriv *priv = GET_PRIV (selector);
62   EmpathyContact *contact = NULL;
63   GtkTreeIter iter;
64
65   if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (selector), &iter))
66     {
67       /* FIXME what to do? */
68       return NULL;
69     }
70
71   gtk_tree_model_get (GTK_TREE_MODEL (priv->list_store), &iter,
72       CONTACT_COL, &contact, -1);
73
74   return contact;
75 }
76
77
78 static void
79 get_iter_from_contact (GtkListStore *list_store,
80                        GtkTreeIter *list_iter,
81                        EmpathyContact *contact)
82 {
83   GtkTreePath *path;
84   GtkTreeIter tmp_iter;
85   EmpathyContact *tmp_contact;
86   gboolean found = FALSE;
87
88   /* Do a linear search to find the row with CONTACT_COL set to contact. */
89   path = gtk_tree_path_new_first ();
90   if (gtk_tree_model_get_iter (GTK_TREE_MODEL (list_store), &tmp_iter, path))
91     {
92       do
93         {
94           gtk_tree_model_get (GTK_TREE_MODEL (list_store),
95               &tmp_iter, CONTACT_COL, &tmp_contact, -1);
96           found = (tmp_contact == contact);
97           g_object_unref (tmp_contact);
98           if (found)
99             {
100               *list_iter = tmp_iter;
101               break;
102             }
103         } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (list_store),
104               &tmp_iter));
105     }
106
107   /* The store does not contain the contact, so create a new row and set it. */
108   if (!found)
109     {
110       gtk_list_store_append (list_store, list_iter);
111       gtk_list_store_set (list_store, list_iter, CONTACT_COL, contact, -1);
112     }
113
114   gtk_tree_path_free (path);
115 }
116
117
118 static void
119 empathy_store_row_changed_cb (EmpathyContactListStore *empathy_store,
120                               GtkTreePath *empathy_path,
121                               GtkTreeIter *empathy_iter,
122                               gpointer data)
123 {
124   EmpathyContactSelector *selector = EMPATHY_CONTACT_SELECTOR (data);
125   EmpathyContactSelectorPriv *priv = GET_PRIV (selector);
126   GtkTreeIter list_iter;
127   EmpathyContact *contact;
128   gchar *name;
129   gchar *icon_name;
130   gboolean is_online;
131   gboolean had_active;
132
133   /* Something is currently selected or not. */
134   had_active = gtk_combo_box_get_active (GTK_COMBO_BOX (selector)) != -1;
135
136   /* Synchronize the GtkListStore with the EmpathyContactListStore. */
137   gtk_tree_model_get (GTK_TREE_MODEL (empathy_store), empathy_iter,
138       EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
139       EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
140       EMPATHY_CONTACT_LIST_STORE_COL_ICON_STATUS, &icon_name,
141       EMPATHY_CONTACT_LIST_STORE_COL_IS_ONLINE, &is_online, -1);
142
143   if (!contact)
144     {
145       if (contact)
146         g_object_unref (contact);
147       g_free (name);
148       g_free (icon_name);
149       return;
150     }
151
152   get_iter_from_contact (priv->list_store, &list_iter, contact);
153
154   if (is_online)
155     {
156       gtk_list_store_set (priv->list_store, &list_iter, NAME_COL, name,
157           STATUS_ICON_NAME_COL, icon_name, -1);
158     }
159   else
160     {
161       /* We display only online contacts. */
162       gtk_list_store_remove (priv->list_store, &list_iter);
163
164       if (had_active &&
165           gtk_combo_box_get_active (GTK_COMBO_BOX (selector)) == -1)
166         {
167           /* There was an active contact but we removed it from
168            * the list, so select the first one.
169            */
170           gtk_combo_box_set_active (GTK_COMBO_BOX (selector), 0);
171         }
172     }
173
174   if (!gtk_tree_model_iter_n_children (GTK_TREE_MODEL (priv->list_store),
175         NULL))
176       gtk_widget_set_sensitive (GTK_WIDGET (selector), FALSE);
177   else
178       gtk_widget_set_sensitive (GTK_WIDGET (selector), TRUE);
179 }
180
181 static gboolean
182 select_first_timeout_cb (gpointer data)
183 {
184   /* If there is not an active contact select the first element in the list.
185    * Contacts are not added in alphabetical order to the list store, so
186    * we cannot select the first element in empathy_store_row_changed_cb()
187    * because we would probably select a random contact.
188    */
189   EmpathyContactSelector *selector = EMPATHY_CONTACT_SELECTOR (data);
190
191   if (gtk_combo_box_get_active (GTK_COMBO_BOX (selector)) == 0)
192     gtk_combo_box_set_active (GTK_COMBO_BOX (selector), 0);
193
194   return FALSE;
195 }
196
197
198 static GObject *
199 empathy_contact_selector_constructor (GType type,
200                                       guint n_construct_params,
201                                       GObjectConstructParam *construct_params)
202 {
203   GObject *object =
204       G_OBJECT_CLASS (empathy_contact_selector_parent_class)->constructor (type,
205       n_construct_params, construct_params);
206   EmpathyContactSelector *contact_selector = EMPATHY_CONTACT_SELECTOR (object);
207   EmpathyContactSelectorPriv *priv = GET_PRIV (contact_selector);
208   GtkCellRenderer *renderer;
209
210   g_object_set (priv->store, "is-compact", TRUE, "show-avatars", FALSE,
211       "show-offline", FALSE, "sort-criterium",
212       EMPATHY_CONTACT_LIST_STORE_SORT_NAME, NULL);
213
214   priv->list_store = gtk_list_store_new (NUM_COLS, EMPATHY_TYPE_CONTACT,
215         G_TYPE_STRING, G_TYPE_STRING);
216   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->list_store),
217         NAME_COL, GTK_SORT_ASCENDING);
218
219   g_signal_connect (priv->store, "row-changed",
220       G_CALLBACK (empathy_store_row_changed_cb), (gpointer) contact_selector);
221
222   gtk_combo_box_set_model (GTK_COMBO_BOX (contact_selector),
223       GTK_TREE_MODEL (priv->list_store));
224   gtk_widget_set_sensitive (GTK_WIDGET (contact_selector), FALSE);
225
226   /* Status icon */
227   renderer = gtk_cell_renderer_pixbuf_new ();
228   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (contact_selector),
229       renderer, FALSE);
230   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (contact_selector), renderer,
231       "icon-name", STATUS_ICON_NAME_COL, NULL);
232
233   /* Contact name */
234   renderer = gtk_cell_renderer_text_new ();
235   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (contact_selector),
236       renderer, TRUE);
237   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (contact_selector), renderer,
238       "text", NAME_COL, NULL);
239
240   g_timeout_add (200, select_first_timeout_cb, (gpointer) contact_selector);
241
242   object = G_OBJECT (contact_selector);
243   return object;
244 }
245
246
247 static void
248 empathy_contact_selector_init (EmpathyContactSelector *empathy_contact_selector)
249 {
250 }
251
252
253 static void
254 empathy_contact_selector_set_property (GObject *object,
255                                        guint prop_id,
256                                        const GValue *value,
257                                        GParamSpec *pspec)
258 {
259   EmpathyContactSelector *contact_selector = EMPATHY_CONTACT_SELECTOR (object);
260   EmpathyContactSelectorPriv *priv = GET_PRIV (contact_selector);
261
262   switch (prop_id)
263     {
264       case PROP_STORE:
265         priv->store = g_value_dup_object (value);
266         break;
267       default:
268         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
269         break;
270     }
271 }
272
273
274 static void
275 empathy_contact_selector_get_property (GObject *object,
276                                        guint prop_id,
277                                        GValue *value,
278                                        GParamSpec *pspec)
279 {
280   EmpathyContactSelector *contact_selector = EMPATHY_CONTACT_SELECTOR (object);
281   EmpathyContactSelectorPriv *priv = GET_PRIV (contact_selector);
282
283   switch (prop_id)
284     {
285       case PROP_STORE:
286         g_value_set_object (value, priv->store);
287         break;
288       default:
289         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
290         break;
291     }
292 }
293
294
295 static void
296 empathy_contact_selector_dispose (GObject *object)
297 {
298   g_debug ("EmpathyContactSelector - dispose: %p",  object);
299
300   (G_OBJECT_CLASS (empathy_contact_selector_parent_class)->dispose) (object);
301 }
302
303
304 static void
305 empathy_contact_selector_finalize (GObject *object)
306 {
307   g_debug ("EmpathyContactSelector - finalize: %p",  object);
308
309   (G_OBJECT_CLASS (empathy_contact_selector_parent_class)->finalize) (object);
310 }
311
312
313 static void
314 empathy_contact_selector_class_init (EmpathyContactSelectorClass *klass)
315 {
316   GObjectClass *object_class = G_OBJECT_CLASS (klass);
317   object_class->constructor = empathy_contact_selector_constructor;
318   object_class->dispose = empathy_contact_selector_dispose;
319   object_class->finalize = empathy_contact_selector_finalize;
320   object_class->set_property = empathy_contact_selector_set_property;
321   object_class->get_property = empathy_contact_selector_get_property;
322   g_type_class_add_private (klass, sizeof (EmpathyContactSelectorPriv));
323
324   g_object_class_install_property (object_class, PROP_STORE,
325       g_param_spec_object ("store", "store", "store",
326       EMPATHY_TYPE_CONTACT_LIST_STORE, G_PARAM_CONSTRUCT_ONLY |
327       G_PARAM_READWRITE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
328 }
329
330
331 EmpathyContactSelector *
332 empathy_contact_selector_new (EmpathyContactListStore *store)
333 {
334   return g_object_new (EMPATHY_TYPE_CONTACT_SELECTOR, "store", store, NULL);
335 }