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