]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-contact-selector.c
1fcc075ff6eac511729aae9c37ecb55de3bb47e7
[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 "config.h"
24
25 #include <glib/gi18n.h>
26 #include <gtk/gtk.h>
27
28 #include <libempathy/empathy-contact.h>
29 #include <libempathy-gtk/empathy-contact-list-store.h>
30 #include <libempathy/empathy-utils.h>
31
32 #include "empathy-contact-selector.h"
33
34 G_DEFINE_TYPE (EmpathyContactSelector, empathy_contact_selector,
35     GTK_TYPE_COMBO_BOX)
36
37 enum
38 {
39   PROP_0,
40   PROP_STORE
41 };
42
43 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyContactSelector)
44 typedef struct
45 {
46   EmpathyContactListStore *store;
47 } EmpathyContactSelectorPriv;
48
49 static void changed_cb (GtkComboBox *widget, gpointer data);
50
51
52 EmpathyContact *
53 empathy_contact_selector_get_selected (EmpathyContactSelector *selector)
54 {
55   EmpathyContactSelectorPriv *priv = GET_PRIV (selector);
56   EmpathyContact *contact = NULL;
57   GtkTreeIter iter;
58
59   if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (selector), &iter))
60     return NULL;
61
62   gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
63       EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact, -1);
64
65   return contact;
66 }
67
68
69 static guint
70 get_number_online_contacts (GtkTreeStore *store)
71 {
72   GtkTreePath *path;
73   GtkTreeIter tmp_iter;
74   gboolean is_online;
75   guint number_online_contacts = 0;
76
77   path = gtk_tree_path_new_first ();
78
79   if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &tmp_iter, path))
80     {
81       do
82         {
83           gtk_tree_model_get (GTK_TREE_MODEL (store),
84               &tmp_iter, EMPATHY_CONTACT_LIST_STORE_COL_IS_ONLINE,
85               &is_online, -1);
86           if (is_online)
87             number_online_contacts++;
88         } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &tmp_iter));
89     }
90
91   gtk_tree_path_free (path);
92
93   return number_online_contacts;
94 }
95
96
97 static gboolean
98 get_iter_for_blank_contact (GtkTreeStore *store,
99                             GtkTreeIter *blank_iter)
100 {
101   GtkTreePath *path;
102   GtkTreeIter tmp_iter;
103   EmpathyContact *tmp_contact;
104   gboolean is_present = FALSE;
105
106   path = gtk_tree_path_new_first ();
107
108   if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &tmp_iter, path))
109     {
110       do
111         {
112           gtk_tree_model_get (GTK_TREE_MODEL (store),
113               &tmp_iter, EMPATHY_CONTACT_LIST_STORE_COL_CONTACT,
114               &tmp_contact, -1);
115           if (tmp_contact == NULL)
116             {
117               *blank_iter = tmp_iter;
118               is_present = TRUE;
119               break;
120             }
121         } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &tmp_iter));
122     }
123
124   gtk_tree_path_free (path);
125
126   return is_present;
127 }
128
129
130 static void
131 add_blank_contact (EmpathyContactSelector *selector)
132 {
133   EmpathyContactSelectorPriv *priv = GET_PRIV (selector);
134   GtkTreeIter blank_iter;
135
136   gtk_tree_store_insert (GTK_TREE_STORE (priv->store), &blank_iter, NULL, 0);
137   gtk_tree_store_set (GTK_TREE_STORE (priv->store), &blank_iter,
138       EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, NULL,
139       EMPATHY_CONTACT_LIST_STORE_COL_NAME, (_("Select a contact")),
140       EMPATHY_CONTACT_LIST_STORE_COL_IS_ONLINE, FALSE, -1);
141   g_signal_handlers_block_by_func(selector, changed_cb, NULL);
142   gtk_combo_box_set_active_iter (GTK_COMBO_BOX (selector), &blank_iter);
143   g_signal_handlers_unblock_by_func(selector, changed_cb, NULL);
144 }
145
146
147 static void
148 remove_blank_contact (EmpathyContactSelector *selector)
149 {
150   EmpathyContactSelectorPriv *priv = GET_PRIV (selector);
151   GtkTreeIter blank_iter;
152
153   if (get_iter_for_blank_contact (GTK_TREE_STORE (priv->store), &blank_iter))
154     {
155       gtk_tree_store_remove (GTK_TREE_STORE (priv->store), &blank_iter);
156     }
157 }
158
159
160 static void
161 manage_sensitivity (EmpathyContactSelector *selector)
162 {
163   EmpathyContactSelectorPriv *priv = GET_PRIV (selector);
164
165   /* FIXME - make this work when offline contacts are shown.
166    * The following value needs to be the number of entries shown
167    * excluding the blank entry (if present).
168    */
169   guint number_online_contacts =
170       get_number_online_contacts (GTK_TREE_STORE (priv->store));
171
172   if (number_online_contacts)
173       gtk_widget_set_sensitive (GTK_WIDGET (selector), TRUE);
174   else
175       gtk_widget_set_sensitive (GTK_WIDGET (selector), FALSE);
176 }
177
178
179 static void
180 manage_blank_contact (EmpathyContactSelector *selector)
181 {
182   gboolean is_popup_shown;
183
184   g_object_get (selector, "popup-shown", &is_popup_shown, NULL);
185
186   if (is_popup_shown)
187     {
188       remove_blank_contact (selector);
189     }
190   else
191     {
192       if (gtk_combo_box_get_active (GTK_COMBO_BOX (selector)) == -1)
193         {
194           add_blank_contact (selector);
195         }
196       else
197         {
198           remove_blank_contact (selector);
199         }
200     }
201
202     manage_sensitivity (selector);
203 }
204
205
206 static void
207 notify_popup_shown_cb (GtkComboBox *widget,
208                        GParamSpec *property,
209                        gpointer data)
210 {
211   EmpathyContactSelector *selector = EMPATHY_CONTACT_SELECTOR (widget);
212
213   manage_blank_contact (selector);
214 }
215
216
217 static void
218 changed_cb (GtkComboBox *widget,
219             gpointer data)
220 {
221   EmpathyContactSelector *selector = EMPATHY_CONTACT_SELECTOR (widget);
222
223   manage_blank_contact (selector);
224 }
225
226
227 static void
228 empathy_store_row_changed_cb (EmpathyContactListStore *empathy_store,
229                               GtkTreePath *empathy_path,
230                               GtkTreeIter *empathy_iter,
231                               gpointer data)
232 {
233   EmpathyContactSelector *selector = EMPATHY_CONTACT_SELECTOR (data);
234
235   manage_sensitivity (selector);
236 }
237
238
239 static GObject *
240 empathy_contact_selector_constructor (GType type,
241                                       guint n_construct_params,
242                                       GObjectConstructParam *construct_params)
243 {
244   GObject *object =
245       G_OBJECT_CLASS (empathy_contact_selector_parent_class)->constructor (type,
246       n_construct_params, construct_params);
247   EmpathyContactSelector *contact_selector = EMPATHY_CONTACT_SELECTOR (object);
248   EmpathyContactSelectorPriv *priv = GET_PRIV (contact_selector);
249   GtkCellRenderer *renderer;
250
251   g_object_set (priv->store, "is-compact", TRUE, "show-avatars", FALSE,
252       "show-offline", FALSE, "show-groups", FALSE,
253       "sort-criterium", EMPATHY_CONTACT_LIST_STORE_SORT_NAME, NULL);
254
255   g_signal_connect (priv->store, "row-changed",
256       G_CALLBACK (empathy_store_row_changed_cb), (gpointer) contact_selector);
257   g_signal_connect (GTK_COMBO_BOX (contact_selector), "changed",
258       G_CALLBACK (changed_cb), NULL);
259   g_signal_connect (GTK_COMBO_BOX (contact_selector), "notify::popup-shown",
260       G_CALLBACK (notify_popup_shown_cb), NULL);
261
262   gtk_combo_box_set_model (GTK_COMBO_BOX (contact_selector),
263       GTK_TREE_MODEL (priv->store));
264   gtk_widget_set_sensitive (GTK_WIDGET (contact_selector), FALSE);
265
266   renderer = gtk_cell_renderer_pixbuf_new ();
267   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (contact_selector),
268       renderer, FALSE);
269   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (contact_selector), renderer,
270       "icon-name", EMPATHY_CONTACT_LIST_STORE_COL_ICON_STATUS, NULL);
271
272   renderer = gtk_cell_renderer_text_new ();
273   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (contact_selector),
274       renderer, TRUE);
275   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (contact_selector), renderer,
276       "text", EMPATHY_CONTACT_LIST_STORE_COL_NAME, NULL);
277
278   manage_blank_contact (contact_selector);
279   manage_sensitivity (contact_selector);
280
281   object = G_OBJECT (contact_selector);
282   return object;
283 }
284
285
286 static void
287 empathy_contact_selector_init (EmpathyContactSelector *empathy_contact_selector)
288 {
289   EmpathyContactSelectorPriv *priv =
290       G_TYPE_INSTANCE_GET_PRIVATE (empathy_contact_selector,
291       EMPATHY_TYPE_CONTACT_SELECTOR, EmpathyContactSelectorPriv);
292
293   empathy_contact_selector->priv = priv;
294 }
295
296
297 static void
298 empathy_contact_selector_set_property (GObject *object,
299                                        guint prop_id,
300                                        const GValue *value,
301                                        GParamSpec *pspec)
302 {
303   EmpathyContactSelector *contact_selector = EMPATHY_CONTACT_SELECTOR (object);
304   EmpathyContactSelectorPriv *priv = GET_PRIV (contact_selector);
305
306   switch (prop_id)
307     {
308       case PROP_STORE:
309         priv->store = g_value_dup_object (value);
310         break;
311       default:
312         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
313         break;
314     }
315 }
316
317
318 static void
319 empathy_contact_selector_get_property (GObject *object,
320                                        guint prop_id,
321                                        GValue *value,
322                                        GParamSpec *pspec)
323 {
324   EmpathyContactSelector *contact_selector = EMPATHY_CONTACT_SELECTOR (object);
325   EmpathyContactSelectorPriv *priv = GET_PRIV (contact_selector);
326
327   switch (prop_id)
328     {
329       case PROP_STORE:
330         g_value_set_object (value, priv->store);
331         break;
332       default:
333         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
334         break;
335     }
336 }
337
338
339 static void
340 empathy_contact_selector_dispose (GObject *object)
341 {
342   EmpathyContactSelector *selector = EMPATHY_CONTACT_SELECTOR (object);
343   EmpathyContactSelectorPriv *priv = GET_PRIV (selector);
344
345   if (priv->store)
346     {
347       g_object_unref (priv->store);
348       priv->store = NULL;
349     }
350
351   (G_OBJECT_CLASS (empathy_contact_selector_parent_class)->dispose) (object);
352 }
353
354
355 static void
356 empathy_contact_selector_class_init (EmpathyContactSelectorClass *klass)
357 {
358   GObjectClass *object_class = G_OBJECT_CLASS (klass);
359   object_class->constructor = empathy_contact_selector_constructor;
360   object_class->dispose = empathy_contact_selector_dispose;
361   object_class->set_property = empathy_contact_selector_set_property;
362   object_class->get_property = empathy_contact_selector_get_property;
363   g_type_class_add_private (klass, sizeof (EmpathyContactSelectorPriv));
364
365   g_object_class_install_property (object_class, PROP_STORE,
366       g_param_spec_object ("store", "store", "store",
367       EMPATHY_TYPE_CONTACT_LIST_STORE, G_PARAM_CONSTRUCT_ONLY |
368       G_PARAM_READWRITE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
369 }
370
371
372 EmpathyContactSelector *
373 empathy_contact_selector_new (EmpathyContactListStore *store)
374 {
375   return g_object_new (EMPATHY_TYPE_CONTACT_SELECTOR, "store", store, NULL);
376 }