Merge branch 'gnome-3-8'
[empathy.git] / ubuntu-online-accounts / cc-plugins / account-plugins / empathy-accounts-plugin-widget.c
1 /*
2  * empathy-accounts-plugin-widget.c
3  *
4  * Copyright (C) 2012 Collabora Ltd. <http://www.collabora.co.uk/>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20
21
22 #include "config.h"
23
24 #include "empathy-accounts-plugin-widget.h"
25
26 #include <glib/gi18n-lib.h>
27
28 #include <telepathy-glib/telepathy-glib.h>
29
30 #include <libaccounts-glib/ag-service.h>
31 #include <libaccounts-glib/ag-account-service.h>
32
33 #include "empathy-account-widget.h"
34
35 G_DEFINE_TYPE (EmpathyAccountsPluginWidget, empathy_accounts_plugin_widget, GTK_TYPE_BOX)
36
37 enum
38 {
39   PROP_ACCOUNT = 1,
40   N_PROPS
41 };
42
43 enum
44 {
45   SIG_DONE,
46   LAST_SIGNAL
47 };
48
49 static guint signals[LAST_SIGNAL];
50
51 struct _EmpathyAccountsPluginWidgetPriv
52 {
53   AgAccount *account;
54
55   EmpathyAccountSettings *settings;
56
57   EmpathyAccountWidget *account_widget;
58   GtkWidget *done_button;
59 };
60
61 static void
62 empathy_accounts_plugin_widget_get_property (GObject *object,
63     guint property_id,
64     GValue *value,
65     GParamSpec *pspec)
66 {
67   EmpathyAccountsPluginWidget *self = EMPATHY_ACCOUNTS_PLUGIN_WIDGET (object);
68
69   switch (property_id)
70     {
71       case PROP_ACCOUNT:
72         g_value_set_object (value, self->priv->account);
73         break;
74       default:
75         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
76         break;
77     }
78 }
79
80 static void
81 empathy_accounts_plugin_widget_set_property (GObject *object,
82     guint property_id,
83     const GValue *value,
84     GParamSpec *pspec)
85 {
86   EmpathyAccountsPluginWidget *self = EMPATHY_ACCOUNTS_PLUGIN_WIDGET (object);
87
88   switch (property_id)
89     {
90       case PROP_ACCOUNT:
91         self->priv->account = g_value_dup_object (value);
92         break;
93       default:
94         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
95         break;
96     }
97 }
98
99 static EmpathyAccountSettings *
100 create_account_settings (AgAccount *account)
101 {
102   AgService *service;
103   GList *services;
104   AgAccountService *account_service;
105   GVariant *v;
106   gchar *manager = NULL, *protocol = NULL;
107   EmpathyAccountSettings *settings;
108
109   g_assert (account->id == 0);
110
111   services = ag_account_list_services_by_type (account, "IM");
112   g_return_val_if_fail (services != NULL, NULL);
113   service = (AgService *) services->data;
114
115   account_service = ag_account_service_new (account, service);
116
117   v = ag_account_service_get_variant (account_service,
118         "telepathy/manager", NULL);
119   if (v != NULL)
120     manager = g_variant_dup_string (v, NULL);
121
122   v = ag_account_service_get_variant (account_service,
123         "telepathy/protocol", NULL);
124   if (v != NULL)
125     protocol = g_variant_dup_string (v, NULL);
126
127   g_return_val_if_fail (manager != NULL, NULL);
128   g_return_val_if_fail (protocol != NULL, NULL);
129
130   settings = empathy_account_settings_new (manager, protocol, NULL,
131       ag_service_get_display_name (service));
132
133   empathy_account_settings_set_storage_provider (settings,
134       EMPATHY_UOA_PROVIDER);
135
136   empathy_account_settings_set_icon_name_async (settings,
137     ag_service_get_icon_name (service), NULL, NULL);
138
139   g_free (manager);
140   g_free (protocol);
141
142   return settings;
143 }
144
145 static void
146 response_cb (GtkWidget *widget,
147     gint response,
148     EmpathyAccountsPluginWidget *self)
149 {
150   if (!self->priv->account_widget)
151     {
152       // widget might not be ready yet
153       g_signal_emit (self, signals[SIG_DONE], 0);
154       return;
155     }
156   if (response == GTK_RESPONSE_OK)
157     {
158       empathy_account_widget_apply_and_log_in (self->priv->account_widget);
159
160       /* Rely on account_widget_close_cb to fire the 'done' signal */
161     }
162   else
163     {
164       empathy_account_widget_discard_pending_changes (
165           self->priv->account_widget);
166
167       g_signal_emit (self, signals[SIG_DONE], 0);
168     }
169 }
170
171 static GtkWidget *
172 create_top_bar (EmpathyAccountsPluginWidget *self)
173 {
174   GtkWidget *bar, *content, *action, *label;
175   GtkCssProvider *css;
176   GError *error = NULL;
177
178   bar = gtk_info_bar_new_with_buttons (
179       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
180       NULL);
181
182   self->priv->done_button = gtk_info_bar_add_button (GTK_INFO_BAR (bar),
183       _("Done"), GTK_RESPONSE_OK);
184
185   gtk_widget_set_hexpand (bar, TRUE);
186   gtk_info_bar_set_message_type (GTK_INFO_BAR (bar), GTK_MESSAGE_QUESTION);
187   action = gtk_info_bar_get_action_area (GTK_INFO_BAR (bar));
188   gtk_orientable_set_orientation (GTK_ORIENTABLE (action),
189       GTK_ORIENTATION_HORIZONTAL);
190   gtk_widget_set_name (bar, "authorization-infobar");
191   css = gtk_css_provider_new ();
192   if (gtk_css_provider_load_from_data (css,
193           "@define-color question_bg_color rgb (222, 222, 222);"
194           "GtkInfoBar#authorization-infobar"
195           "{"
196           "  color: @fg_color;"
197           "}",
198           -1, &error))
199     {
200       GtkStyleContext *context = gtk_widget_get_style_context (bar);
201
202       gtk_style_context_add_provider (context, (GtkStyleProvider *) css,
203           GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
204     }
205   else
206     {
207       g_warning ("Error processing CSS theme override: %s", error->message);
208       g_clear_error (&error);
209     }
210   g_object_unref (css);
211
212   content = gtk_info_bar_get_content_area (GTK_INFO_BAR (bar));
213
214   label = gtk_label_new (_("Please enter your account details"));
215   gtk_container_add (GTK_CONTAINER (content), label);
216   gtk_widget_show (label);
217
218   g_signal_connect (bar, "response",
219       G_CALLBACK (response_cb), self);
220
221   return bar;
222 }
223
224 static void
225 account_widget_handle_apply_cb (EmpathyAccountWidget *widget,
226     gboolean valid,
227     EmpathyAccountsPluginWidget *self)
228 {
229   gtk_widget_set_sensitive (self->priv->done_button, valid);
230 }
231
232 static void
233 account_widget_close_cb (EmpathyAccountWidget *widget,
234     GtkResponseType response,
235     EmpathyAccountsPluginWidget *self)
236 {
237   g_signal_emit (self, signals[SIG_DONE], 0);
238 }
239
240 static void
241 add_account_widget (EmpathyAccountsPluginWidget *self)
242 {
243   GtkWidget *alig;
244   gboolean simple;
245
246   alig = gtk_alignment_new (0.5, 0, 0, 0);
247
248   gtk_box_pack_start (GTK_BOX (self), alig, TRUE, TRUE, 0);
249   gtk_widget_show (GTK_WIDGET (alig));
250
251   /* Use the simple widget only when creating the account */
252   simple = (self->priv->account->id == 0);
253
254   self->priv->account_widget = empathy_account_widget_new_for_protocol (
255       self->priv->settings, simple);
256
257   empathy_account_widget_hide_buttons (self->priv->account_widget);
258
259   gtk_widget_set_valign (GTK_WIDGET (self->priv->account_widget),
260       GTK_ALIGN_CENTER);
261
262   gtk_container_add (GTK_CONTAINER (alig),
263       GTK_WIDGET (self->priv->account_widget));
264   gtk_widget_show (GTK_WIDGET (self->priv->account_widget));
265
266   if (!empathy_account_settings_is_valid (self->priv->settings))
267     {
268       gtk_widget_set_sensitive (self->priv->done_button, FALSE);
269     }
270
271   g_signal_connect (self->priv->account_widget, "handle-apply",
272       G_CALLBACK (account_widget_handle_apply_cb), self);
273   g_signal_connect (self->priv->account_widget, "close",
274       G_CALLBACK (account_widget_close_cb), self);
275 }
276
277 static void
278 maybe_add_account_widget (EmpathyAccountsPluginWidget *self)
279 {
280   g_return_if_fail (self->priv->settings != NULL);
281
282   if (empathy_account_settings_is_ready (self->priv->settings))
283     {
284       add_account_widget (self);
285     }
286   else
287     {
288       tp_g_signal_connect_object (self->priv->settings, "notify::ready",
289           G_CALLBACK (add_account_widget), self, G_CONNECT_SWAPPED);
290     }
291 }
292
293 static void
294 manager_prepared_cb (GObject *source,
295     GAsyncResult *result,
296     gpointer user_data)
297 {
298   TpWeakRef *wr = user_data;
299   EmpathyAccountsPluginWidget *self = tp_weak_ref_dup_object (wr);
300   TpAccountManager *manager = (TpAccountManager *) source;
301   GList *accounts;
302   GError *error = NULL;
303
304   if (self == NULL)
305     {
306       tp_weak_ref_destroy (wr);
307       return;
308     }
309
310   if (!tp_proxy_prepare_finish (manager, result, &error))
311     {
312       g_debug ("Error preparing Account Manager: %s", error->message);
313       g_clear_error (&error);
314       goto out;
315     }
316
317   accounts = tp_account_manager_dup_valid_accounts (manager);
318   while (accounts != NULL)
319     {
320       TpAccount *account = accounts->data;
321       const GValue *value;
322
323       value = tp_account_get_storage_identifier (account);
324       if (G_VALUE_HOLDS_UINT (value) &&
325           g_value_get_uint (value) == self->priv->account->id)
326         {
327           self->priv->settings = empathy_account_settings_new_for_account (
328               account);
329           maybe_add_account_widget (self);
330           break;
331         }
332
333       accounts = g_list_delete_link (accounts, accounts);
334     }
335   g_list_free_full (accounts, g_object_unref);
336
337 out:
338   tp_weak_ref_destroy (wr);
339   g_object_unref (self);
340 }
341
342 static void
343 empathy_accounts_plugin_widget_constructed (GObject *object)
344 {
345   EmpathyAccountsPluginWidget *self = EMPATHY_ACCOUNTS_PLUGIN_WIDGET (object);
346   void (*chain_up) (GObject *) =
347       ((GObjectClass *) empathy_accounts_plugin_widget_parent_class)->constructed;
348   GtkWidget *top;
349
350   if (chain_up != NULL)
351     chain_up (object);
352
353   g_return_if_fail (AG_IS_ACCOUNT (self->priv->account));
354
355   /* Top bar */
356   top = create_top_bar (self);
357   gtk_widget_show (top);
358   gtk_box_pack_start (GTK_BOX (self), top, FALSE, FALSE, 0);
359
360   if (self->priv->account->id != 0)
361     {
362       TpAccountManager *manager;
363
364       /* Prepare tp's account manager to find the TpAccount corresponding to our
365        * AgAccount */
366       manager = tp_account_manager_dup ();
367
368       tp_proxy_prepare_async (manager, NULL,
369           manager_prepared_cb, tp_weak_ref_new (self, NULL, NULL));
370       g_object_unref (manager);
371       return;
372     }
373
374   self->priv->settings = create_account_settings (self->priv->account);
375   maybe_add_account_widget (self);
376 }
377
378 static void
379 empathy_accounts_plugin_widget_dispose (GObject *object)
380 {
381   EmpathyAccountsPluginWidget *self = EMPATHY_ACCOUNTS_PLUGIN_WIDGET (object);
382   void (*chain_up) (GObject *) =
383       ((GObjectClass *) empathy_accounts_plugin_widget_parent_class)->dispose;
384
385   g_clear_object (&self->priv->account);
386   g_clear_object (&self->priv->settings);
387
388   if (chain_up != NULL)
389     chain_up (object);
390 }
391
392 static void
393 empathy_accounts_plugin_widget_class_init (
394     EmpathyAccountsPluginWidgetClass *klass)
395 {
396   GObjectClass *oclass = G_OBJECT_CLASS (klass);
397   GParamSpec *spec;
398
399   oclass->get_property = empathy_accounts_plugin_widget_get_property;
400   oclass->set_property = empathy_accounts_plugin_widget_set_property;
401   oclass->constructed = empathy_accounts_plugin_widget_constructed;
402   oclass->dispose = empathy_accounts_plugin_widget_dispose;
403
404   spec = g_param_spec_object ("account", "account",
405       "AgAccount",
406       AG_TYPE_ACCOUNT,
407       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
408   g_object_class_install_property (oclass, PROP_ACCOUNT, spec);
409
410   signals[SIG_DONE] = g_signal_new ("done",
411       G_OBJECT_CLASS_TYPE (klass),
412       G_SIGNAL_RUN_LAST,
413       0, NULL, NULL, NULL,
414       G_TYPE_NONE,
415       0);
416
417   g_type_class_add_private (klass, sizeof (EmpathyAccountsPluginWidgetPriv));
418 }
419
420 static void
421 empathy_accounts_plugin_widget_init (EmpathyAccountsPluginWidget *self)
422 {
423   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
424       EMPATHY_TYPE_ACCOUNTS_PLUGIN_WIDGET, EmpathyAccountsPluginWidgetPriv);
425 }
426
427 GtkWidget *
428 empathy_accounts_plugin_widget_new (AgAccount *account)
429 {
430   return g_object_new (EMPATHY_TYPE_ACCOUNTS_PLUGIN_WIDGET,
431       "account", account,
432       "orientation", GTK_ORIENTATION_VERTICAL,
433       "spacing", 10,
434       NULL);
435 }