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