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