]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-protocol-chooser.c
include telepathy-glib.h
[empathy.git] / libempathy-gtk / empathy-protocol-chooser.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
2 /*
3  * Copyright (C) 2007-2009 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: Xavier Claessens <xclaesse@gmail.com>
20  *          Jonny Lamb <jonny.lamb@collabora.co.uk>
21  */
22
23 #include <config.h>
24
25 #include <string.h>
26
27 #include <gtk/gtk.h>
28
29 #include <glib/gi18n-lib.h>
30
31 #include <libempathy/empathy-utils.h>
32 #include <libempathy/empathy-connection-managers.h>
33
34 #include "empathy-protocol-chooser.h"
35 #include "empathy-ui-utils.h"
36
37 #define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT
38 #include <libempathy/empathy-debug.h>
39
40 /**
41  * SECTION:empathy-protocol-chooser
42  * @title: EmpathyProtocolChooser
43  * @short_description: A widget used to choose from a list of protocols
44  * @include: libempathy-gtk/empathy-protocol-chooser.h
45  *
46  * #EmpathyProtocolChooser is a widget which extends #GtkComboBox to provides a
47  * chooser of available protocols.
48  */
49
50 /**
51  * EmpathyProtocolChooser:
52  * @parent: parent object
53  *
54  * Widget which extends #GtkComboBox to provide a chooser of available
55  * protocols.
56  */
57
58 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyProtocolChooser)
59 typedef struct
60 {
61   GtkListStore *store;
62
63   gboolean dispose_run;
64   EmpathyConnectionManagers *cms;
65
66   EmpathyProtocolChooserFilterFunc filter_func;
67   gpointer filter_user_data;
68
69   GHashTable *protocols;
70 } EmpathyProtocolChooserPriv;
71
72 enum
73 {
74   COL_ICON,
75   COL_LABEL,
76   COL_CM,
77   COL_PROTOCOL_NAME,
78   COL_SERVICE,
79   COL_COUNT
80 };
81
82 G_DEFINE_TYPE (EmpathyProtocolChooser, empathy_protocol_chooser,
83     GTK_TYPE_COMBO_BOX);
84
85 static gint
86 protocol_chooser_sort_protocol_value (const gchar *protocol_name)
87 {
88   guint i;
89   const gchar *names[] = {
90     "jabber",
91     "local-xmpp",
92     "gtalk",
93     NULL
94   };
95
96   for (i = 0 ; names[i]; i++)
97     {
98       if (strcmp (protocol_name, names[i]) == 0)
99         return i;
100     }
101
102   return i;
103 }
104
105 static gint
106 protocol_chooser_sort_func (GtkTreeModel *model,
107     GtkTreeIter  *iter_a,
108     GtkTreeIter  *iter_b,
109     gpointer      user_data)
110 {
111   gchar *protocol_a;
112   gchar *protocol_b;
113   gint cmp = 0;
114
115   gtk_tree_model_get (model, iter_a,
116       COL_PROTOCOL_NAME, &protocol_a,
117       -1);
118   gtk_tree_model_get (model, iter_b,
119       COL_PROTOCOL_NAME, &protocol_b,
120       -1);
121
122   cmp = protocol_chooser_sort_protocol_value (protocol_a);
123   cmp -= protocol_chooser_sort_protocol_value (protocol_b);
124   if (cmp == 0)
125     {
126       cmp = strcmp (protocol_a, protocol_b);
127       /* only happens for jabber where there is one entry for gtalk and one for
128        * non-gtalk */
129       if (cmp == 0)
130         {
131           gchar *service;
132
133           gtk_tree_model_get (model, iter_a,
134             COL_SERVICE, &service,
135             -1);
136
137           if (service != NULL)
138             cmp = 1;
139           else
140             cmp = -1;
141
142           g_free (service);
143         }
144     }
145
146   g_free (protocol_a);
147   g_free (protocol_b);
148   return cmp;
149 }
150
151 static void
152 protocol_choosers_add_cm (EmpathyProtocolChooser *chooser,
153     TpConnectionManager *cm)
154 {
155   EmpathyProtocolChooserPriv *priv = GET_PRIV (chooser);
156   GList *protocols, *l;
157   const gchar *cm_name;
158
159   cm_name = tp_connection_manager_get_name (cm);
160
161   protocols = tp_connection_manager_dup_protocols (cm);
162
163   for (l = protocols; l != NULL; l = g_list_next (l))
164     {
165       TpProtocol *protocol = l->data;
166       gchar *icon_name;
167       const gchar *display_name;
168       const gchar *saved_cm_name;
169       const gchar *proto_name;
170       GdkPixbuf *pixbuf;
171
172       proto_name = tp_protocol_get_name (protocol);
173       saved_cm_name = g_hash_table_lookup (priv->protocols, proto_name);
174
175       if (!tp_strdiff (cm_name, "haze") && saved_cm_name != NULL &&
176           tp_strdiff (saved_cm_name, "haze"))
177         /* the CM we're adding is a haze implementation of something we already
178          * have; drop it.
179          */
180         continue;
181
182       if (!tp_strdiff (cm_name, "haze") &&
183           !tp_strdiff (proto_name, "facebook"))
184         /* Facebook now supports XMPP so drop the purple facebook plugin; user
185          * should use Gabble */
186         continue;
187
188       if (!tp_strdiff (cm_name, "haze") &&
189           !tp_strdiff (proto_name, "sip"))
190         /* Haze's SIP implementation is pretty useless (bgo #629736) */
191         continue;
192
193       if (!tp_strdiff (cm_name, "butterfly"))
194         /* Butterfly isn't supported any more */
195         continue;
196
197       if (tp_strdiff (cm_name, "haze") && !tp_strdiff (saved_cm_name, "haze"))
198         {
199           GtkTreeIter titer;
200           gboolean valid;
201           TpConnectionManager *haze_cm;
202
203           /* let's this CM replace the haze implementation */
204           valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->store),
205               &titer);
206
207           while (valid)
208             {
209               gchar *haze_proto_name = NULL;
210
211               gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &titer,
212                   COL_PROTOCOL_NAME, &haze_proto_name,
213                   COL_CM, &haze_cm, -1);
214
215               if (haze_cm == NULL)
216                 continue;
217
218               if (!tp_strdiff (tp_connection_manager_get_name (haze_cm), "haze")
219                   && !tp_strdiff (haze_proto_name, proto_name))
220                 {
221                   gtk_list_store_remove (priv->store, &titer);
222                   g_object_unref (haze_cm);
223                   g_free (haze_proto_name);
224                   break;
225                 }
226
227               g_object_unref (haze_cm);
228               g_free (haze_proto_name);
229               valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store),
230                   &titer);
231             }
232         }
233
234       g_hash_table_insert (priv->protocols,
235           g_strdup (proto_name), g_strdup (cm_name));
236
237       icon_name = empathy_protocol_icon_name (proto_name);
238       pixbuf = empathy_pixbuf_from_icon_name (icon_name, GTK_ICON_SIZE_BUTTON);
239
240       display_name = empathy_protocol_name_to_display_name (proto_name);
241
242       gtk_list_store_insert_with_values (priv->store,
243           NULL, 0,
244           COL_ICON, pixbuf,
245           COL_LABEL, display_name,
246           COL_CM, cm,
247           COL_PROTOCOL_NAME, proto_name,
248           -1);
249
250       g_clear_object (&pixbuf);
251
252       if (!tp_strdiff (proto_name, "jabber") &&
253           !tp_strdiff (cm_name, "gabble"))
254         {
255           display_name = empathy_service_name_to_display_name ("google-talk");
256           pixbuf = empathy_pixbuf_from_icon_name ("im-google-talk",
257                   GTK_ICON_SIZE_BUTTON);
258
259           gtk_list_store_insert_with_values (priv->store,
260              NULL, 0,
261              COL_ICON, pixbuf,
262              COL_LABEL, display_name,
263              COL_CM, cm,
264              COL_PROTOCOL_NAME, proto_name,
265              COL_SERVICE, "google-talk",
266              -1);
267
268           g_clear_object (&pixbuf);
269
270           display_name = empathy_service_name_to_display_name ("facebook");
271           pixbuf = empathy_pixbuf_from_icon_name ("im-facebook",
272                   GTK_ICON_SIZE_BUTTON);
273
274           gtk_list_store_insert_with_values (priv->store,
275              NULL, 0,
276              COL_ICON, pixbuf,
277              COL_LABEL, display_name,
278              COL_CM, cm,
279              COL_PROTOCOL_NAME, proto_name,
280              COL_SERVICE, "facebook",
281              -1);
282
283           g_clear_object (&pixbuf);
284         }
285
286       g_free (icon_name);
287     }
288
289   g_list_free_full (protocols, g_object_unref);
290 }
291
292 static void
293 protocol_chooser_add_cms_list (EmpathyProtocolChooser *protocol_chooser,
294     GList *cms)
295 {
296   GList *l;
297
298   for (l = cms; l != NULL; l = l->next)
299     protocol_choosers_add_cm (protocol_chooser, l->data);
300
301   gtk_combo_box_set_active (GTK_COMBO_BOX (protocol_chooser), 0);
302 }
303
304 static void
305 protocol_chooser_cms_prepare_cb (GObject *source,
306     GAsyncResult *result,
307     gpointer user_data)
308 {
309   EmpathyConnectionManagers *cms = EMPATHY_CONNECTION_MANAGERS (source);
310   EmpathyProtocolChooser *protocol_chooser = user_data;
311
312   if (!empathy_connection_managers_prepare_finish (cms, result, NULL))
313     return;
314
315   protocol_chooser_add_cms_list (protocol_chooser,
316       empathy_connection_managers_get_cms (cms));
317 }
318
319 static void
320 protocol_chooser_constructed (GObject *object)
321 {
322   EmpathyProtocolChooser *protocol_chooser;
323   EmpathyProtocolChooserPriv *priv;
324   GtkCellRenderer *renderer;
325
326   priv = GET_PRIV (object);
327   protocol_chooser = EMPATHY_PROTOCOL_CHOOSER (object);
328
329   /* set up combo box with new store */
330   priv->store = gtk_list_store_new (COL_COUNT,
331           GDK_TYPE_PIXBUF,  /* Icon */
332           G_TYPE_STRING,    /* Label     */
333           G_TYPE_OBJECT,    /* CM */
334           G_TYPE_STRING,    /* protocol name  */
335           G_TYPE_STRING);   /* service */
336
337   /* Set the protocol sort function */
338   gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->store),
339       COL_PROTOCOL_NAME,
340       protocol_chooser_sort_func,
341       NULL, NULL);
342   gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->store),
343       COL_PROTOCOL_NAME,
344       GTK_SORT_ASCENDING);
345
346   gtk_combo_box_set_model (GTK_COMBO_BOX (object),
347       GTK_TREE_MODEL (priv->store));
348
349   renderer = gtk_cell_renderer_pixbuf_new ();
350   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (object), renderer, FALSE);
351   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (object), renderer,
352       "pixbuf", COL_ICON,
353       NULL);
354
355   renderer = gtk_cell_renderer_text_new ();
356   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (object), renderer, TRUE);
357   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (object), renderer,
358       "text", COL_LABEL,
359       NULL);
360
361   empathy_connection_managers_prepare_async (priv->cms,
362       protocol_chooser_cms_prepare_cb, protocol_chooser);
363
364   if (G_OBJECT_CLASS (empathy_protocol_chooser_parent_class)->constructed)
365     G_OBJECT_CLASS
366       (empathy_protocol_chooser_parent_class)->constructed (object);
367 }
368
369 static void
370 empathy_protocol_chooser_init (EmpathyProtocolChooser *protocol_chooser)
371 {
372   EmpathyProtocolChooserPriv *priv =
373     G_TYPE_INSTANCE_GET_PRIVATE (protocol_chooser,
374         EMPATHY_TYPE_PROTOCOL_CHOOSER, EmpathyProtocolChooserPriv);
375
376   priv->dispose_run = FALSE;
377   priv->cms = empathy_connection_managers_dup_singleton ();
378   priv->protocols = g_hash_table_new_full (g_str_hash, g_str_equal,
379       g_free, g_free);
380
381   protocol_chooser->priv = priv;
382 }
383
384 static void
385 protocol_chooser_finalize (GObject *object)
386 {
387   EmpathyProtocolChooser *protocol_chooser = EMPATHY_PROTOCOL_CHOOSER (object);
388   EmpathyProtocolChooserPriv *priv = GET_PRIV (protocol_chooser);
389
390   if (priv->protocols)
391     {
392       g_hash_table_unref (priv->protocols);
393       priv->protocols = NULL;
394     }
395
396   (G_OBJECT_CLASS (empathy_protocol_chooser_parent_class)->finalize) (object);
397 }
398
399 static void
400 protocol_chooser_dispose (GObject *object)
401 {
402   EmpathyProtocolChooser *protocol_chooser = EMPATHY_PROTOCOL_CHOOSER (object);
403   EmpathyProtocolChooserPriv *priv = GET_PRIV (protocol_chooser);
404
405   if (priv->dispose_run)
406     return;
407
408   priv->dispose_run = TRUE;
409
410   if (priv->store)
411     {
412       g_object_unref (priv->store);
413       priv->store = NULL;
414     }
415
416   if (priv->cms)
417     {
418       g_object_unref (priv->cms);
419       priv->cms = NULL;
420     }
421
422   (G_OBJECT_CLASS (empathy_protocol_chooser_parent_class)->dispose) (object);
423 }
424
425 static void
426 empathy_protocol_chooser_class_init (EmpathyProtocolChooserClass *klass)
427 {
428   GObjectClass *object_class = G_OBJECT_CLASS (klass);
429
430   object_class->constructed = protocol_chooser_constructed;
431   object_class->dispose = protocol_chooser_dispose;
432   object_class->finalize = protocol_chooser_finalize;
433
434   g_type_class_add_private (object_class, sizeof (EmpathyProtocolChooserPriv));
435 }
436
437 static gboolean
438 protocol_chooser_filter_visible_func (GtkTreeModel *model,
439     GtkTreeIter *iter,
440     gpointer user_data)
441 {
442   EmpathyProtocolChooser *protocol_chooser = user_data;
443   EmpathyProtocolChooserPriv *priv = GET_PRIV (protocol_chooser);
444   TpConnectionManager *cm = NULL;
445   gchar *protocol_name = NULL;
446   gboolean visible = FALSE;
447   gchar *service;
448
449   gtk_tree_model_get (model, iter,
450       COL_CM, &cm,
451       COL_PROTOCOL_NAME, &protocol_name,
452       COL_SERVICE, &service,
453       -1);
454
455   if (cm != NULL && protocol_name != NULL)
456     {
457       TpProtocol *protocol;
458
459       protocol = tp_connection_manager_get_protocol_object (cm, protocol_name);
460
461       if (protocol != NULL)
462         {
463           visible = priv->filter_func (cm, protocol, service,
464               priv->filter_user_data);
465         }
466     }
467
468   if (cm != NULL)
469     g_object_unref (cm);
470
471   g_free (service);
472   return visible;
473 }
474
475 /* public methods */
476
477 /**
478  * empathy_protocol_chooser_get_selected_protocol:
479  * @protocol_chooser: an #EmpathyProtocolChooser
480  *
481  * Returns a pointer to the selected #TpConnectionManagerProtocol in
482  * @protocol_chooser.
483  *
484  * Return value: a pointer to the selected #TpConnectionManagerProtocol
485  */
486 TpConnectionManager *
487 empathy_protocol_chooser_dup_selected (
488     EmpathyProtocolChooser *protocol_chooser,
489     TpProtocol **protocol,
490     gchar **service)
491 {
492   GtkTreeIter iter;
493   TpConnectionManager *cm = NULL;
494   GtkTreeModel *cur_model;
495
496   g_return_val_if_fail (EMPATHY_IS_PROTOCOL_CHOOSER (protocol_chooser), NULL);
497
498   /* get the current model from the chooser, as we could either be filtering
499    * or not.
500    */
501   cur_model = gtk_combo_box_get_model (GTK_COMBO_BOX (protocol_chooser));
502
503   if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (protocol_chooser), &iter))
504     {
505       gtk_tree_model_get (GTK_TREE_MODEL (cur_model), &iter,
506           COL_CM, &cm,
507           -1);
508
509       if (protocol != NULL)
510         {
511           gchar *protocol_name = NULL;
512
513           gtk_tree_model_get (GTK_TREE_MODEL (cur_model), &iter,
514               COL_PROTOCOL_NAME, &protocol_name,
515               -1);
516
517           *protocol = tp_connection_manager_get_protocol_object (cm,
518               protocol_name);
519
520           g_free (protocol_name);
521
522           if (*protocol == NULL)
523             {
524               /* For some reason the CM doesn't know about this protocol
525                * any more */
526               g_object_unref (cm);
527               return NULL;
528             }
529         }
530
531       if (service != NULL)
532         {
533           gtk_tree_model_get (GTK_TREE_MODEL (cur_model), &iter,
534               COL_SERVICE, service,
535               -1);
536         }
537     }
538
539   return cm;
540 }
541
542 /**
543  * empathy_protocol_chooser_new:
544  *
545  * Triggers the creation of a new #EmpathyProtocolChooser.
546  *
547  * Return value: a new #EmpathyProtocolChooser widget
548  */
549
550 GtkWidget *
551 empathy_protocol_chooser_new (void)
552 {
553   return GTK_WIDGET (g_object_new (EMPATHY_TYPE_PROTOCOL_CHOOSER, NULL));
554 }
555
556 void
557 empathy_protocol_chooser_set_visible (EmpathyProtocolChooser *protocol_chooser,
558     EmpathyProtocolChooserFilterFunc func,
559     gpointer user_data)
560 {
561   EmpathyProtocolChooserPriv *priv;
562   GtkTreeModel *filter_model;
563
564   g_return_if_fail (EMPATHY_IS_PROTOCOL_CHOOSER (protocol_chooser));
565
566   priv = GET_PRIV (protocol_chooser);
567   priv->filter_func = func;
568   priv->filter_user_data = user_data;
569
570   filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (priv->store),
571       NULL);
572   gtk_combo_box_set_model (GTK_COMBO_BOX (protocol_chooser), filter_model);
573   g_object_unref (filter_model);
574
575   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER
576       (filter_model), protocol_chooser_filter_visible_func,
577       protocol_chooser, NULL);
578
579   gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filter_model));
580
581   gtk_combo_box_set_active (GTK_COMBO_BOX (protocol_chooser), 0);
582 }
583
584 EmpathyAccountSettings *
585 empathy_protocol_chooser_create_account_settings (EmpathyProtocolChooser *self)
586 {
587   EmpathyAccountSettings *settings = NULL;
588   gchar *str;
589   const gchar *display_name;
590   TpConnectionManager *cm;
591   TpProtocol *proto;
592   gchar *service = NULL;
593
594   cm = empathy_protocol_chooser_dup_selected (self, &proto, &service);
595   if (cm == NULL || proto == NULL)
596     goto out;
597
598   if (service != NULL)
599     display_name = empathy_service_name_to_display_name (service);
600   else
601     display_name = empathy_protocol_name_to_display_name (
602         tp_protocol_get_name (proto));
603
604   /* Create account */
605   /* To translator: %s is the name of the protocol, such as "Google Talk" or
606    * "Yahoo!"
607    */
608   str = g_strdup_printf (_("New %s account"), display_name);
609
610   settings = empathy_account_settings_new (tp_connection_manager_get_name (cm),
611       tp_protocol_get_name (proto), service, str);
612
613   g_free (str);
614
615   if (!tp_strdiff (service, "google-talk"))
616     {
617       const gchar *fallback_servers[] = {
618           "talkx.l.google.com",
619           "talkx.l.google.com:443,oldssl",
620           "talkx.l.google.com:80",
621           NULL};
622
623       const gchar *extra_certificate_identities[] = {
624           "talk.google.com",
625           NULL};
626
627       empathy_account_settings_set_icon_name_async (settings, "im-google-talk",
628           NULL, NULL);
629       empathy_account_settings_set (settings, "server",
630           g_variant_new_string (extra_certificate_identities[0]));
631       empathy_account_settings_set (settings, "require-encryption",
632           g_variant_new_boolean (TRUE));
633       empathy_account_settings_set (settings, "fallback-servers",
634           g_variant_new_strv (fallback_servers, -1));
635
636       if (empathy_account_settings_have_tp_param (settings,
637               "extra-certificate-identities"))
638         {
639           empathy_account_settings_set (settings,
640               "extra-certificate-identities",
641               g_variant_new_strv (extra_certificate_identities, -1));
642         }
643     }
644   else if (!tp_strdiff (service, "facebook"))
645     {
646       const gchar *fallback_servers[] = {
647           "chat.facebook.com:443",
648           NULL };
649
650       empathy_account_settings_set_icon_name_async (settings, "im-facebook",
651           NULL, NULL);
652       empathy_account_settings_set (settings, "require-encryption",
653           g_variant_new_boolean (TRUE));
654       empathy_account_settings_set (settings, "server",
655           g_variant_new_string ("chat.facebook.com"));
656       empathy_account_settings_set (settings, "fallback-servers",
657           g_variant_new_strv (fallback_servers, -1));
658     }
659
660 out:
661   tp_clear_object (&cm);
662   g_free (service);
663   return settings;
664 }