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