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