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