aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanielle Madeley <danielle.madeley@collabora.co.uk>2011-04-21 11:13:10 +0800
committerDanielle Madeley <danielle.madeley@collabora.co.uk>2011-04-21 11:13:10 +0800
commit93f10e9c7dad9be29c2f6248058cdd61123ed9d6 (patch)
treec0e43bbb19a6b88d675f39628666cb0967f67e41
parent462c1b74848498594239e818270efbcc0cac5170 (diff)
parentce0ef4346f2042295b18553ae47280adc708d169 (diff)
downloadgsoc2013-empathy-93f10e9c7dad9be29c2f6248058cdd61123ed9d6.tar
gsoc2013-empathy-93f10e9c7dad9be29c2f6248058cdd61123ed9d6.tar.gz
gsoc2013-empathy-93f10e9c7dad9be29c2f6248058cdd61123ed9d6.tar.bz2
gsoc2013-empathy-93f10e9c7dad9be29c2f6248058cdd61123ed9d6.tar.lz
gsoc2013-empathy-93f10e9c7dad9be29c2f6248058cdd61123ed9d6.tar.xz
gsoc2013-empathy-93f10e9c7dad9be29c2f6248058cdd61123ed9d6.tar.zst
gsoc2013-empathy-93f10e9c7dad9be29c2f6248058cdd61123ed9d6.zip
Merge branch 'balance-31-2' into empathy-skype
-rw-r--r--data/org.gnome.Empathy.gschema.xml.in5
-rw-r--r--libempathy/empathy-utils.c75
-rw-r--r--libempathy/empathy-utils.h2
-rw-r--r--src/empathy-main-window.c325
-rw-r--r--src/empathy-main-window.ui29
5 files changed, 433 insertions, 3 deletions
diff --git a/data/org.gnome.Empathy.gschema.xml.in b/data/org.gnome.Empathy.gschema.xml.in
index 27a8cb375..10001507d 100644
--- a/data/org.gnome.Empathy.gschema.xml.in
+++ b/data/org.gnome.Empathy.gschema.xml.in
@@ -55,6 +55,11 @@
<_summary>Show protocols</_summary>
<_description>Whether to show protocols for contacts in the contact list.</_description>
</key>
+ <key name="show-balance-in-roster" type="b">
+ <default>false</default>
+ <_summary>Show Balance in roster</_summary>
+ <_description>Whether to show account balances for in the contact roster.</_description>
+ </key>
<key name="compact-contact-list" type="b">
<default>false</default>
<_summary>Compact contact list</_summary>
diff --git a/libempathy/empathy-utils.c b/libempathy/empathy-utils.c
index 24d7b544b..8d4cd8d68 100644
--- a/libempathy/empathy-utils.c
+++ b/libempathy/empathy-utils.c
@@ -29,6 +29,7 @@
#include "config.h"
#include <string.h>
+#include <math.h>
#include <time.h>
#include <sys/types.h>
@@ -796,3 +797,77 @@ empathy_get_x509_certificate_hostname (gnutls_x509_crt_t cert)
return NULL;
}
+
+gchar *
+empathy_format_currency (gint amount,
+ guint scale,
+ const gchar *currency)
+{
+#define MINUS "\342\210\222"
+#define EURO "\342\202\254"
+#define YEN "\302\245"
+#define POUND "\302\243"
+
+ /* localised representations of currency */
+ /* FIXME: check these, especially negatives and decimals */
+ static const struct {
+ const char *currency;
+ const char *positive;
+ const char *negative;
+ const char *decimal;
+ } currencies[] = {
+ /* sym positive negative decimal */
+ { "EUR", EURO "%s", MINUS EURO "%s", "." },
+ { "USD", "$%s", MINUS "$%s", "." },
+ { "JPY", YEN "%s" MINUS YEN "%s", "." },
+ { "GBP", POUND "%s", MINUS POUND "%s", "." },
+ { "PLN", "%s zl", MINUS "%s zl", "." },
+ { "BRL", "R$%s", MINUS "R$%s", "." },
+ { "SEK", "%s kr", MINUS "%s kr", "." },
+ { "DKK", "kr %s", "kr " MINUS "%s", "." },
+ { "HKD", "$%s", MINUS "$%s", "." },
+ { "CHF", "%s Fr.", MINUS "%s Fr.", "." },
+ { "NOK", "kr %s", "kr" MINUS "%s", "," },
+ { "CAD", "$%s", MINUS "$%s", "." },
+ { "TWD", "$%s", MINUS "$%s", "." },
+ { "AUD", "$%s", MINUS "$%s", "." },
+ };
+
+ const char *positive = "%s";
+ const char *negative = MINUS "%s";
+ const char *decimal = ".";
+ char *fmt_amount, *money;
+ guint i;
+
+ /* get the localised currency format */
+ for (i = 0; i < G_N_ELEMENTS (currencies); i++) {
+ if (!tp_strdiff (currency, currencies[i].currency)) {
+ positive = currencies[i].positive;
+ negative = currencies[i].negative;
+ decimal = currencies[i].decimal;
+ break;
+ }
+ }
+
+ /* format the amount using the scale */
+ if (scale == 0) {
+ /* no decimal point required */
+ fmt_amount = g_strdup_printf ("%d", amount);
+ } else {
+ /* don't use floating point arithmatic, it's noisy;
+ * we take the absolute values, because we want the minus
+ * sign to appear before the $ */
+ int divisor = pow (10, scale);
+ int dollars = abs (amount / divisor);
+ int cents = abs (amount % divisor);
+
+ fmt_amount = g_strdup_printf ("%d%s%0*d",
+ dollars, decimal, scale, cents);
+ }
+
+ money = g_strdup_printf (amount < 0 ? negative : positive, fmt_amount);
+ g_free (fmt_amount);
+
+ return money;
+}
+
diff --git a/libempathy/empathy-utils.h b/libempathy/empathy-utils.h
index 2ddfb4c53..7ec96be13 100644
--- a/libempathy/empathy-utils.h
+++ b/libempathy/empathy-utils.h
@@ -102,6 +102,8 @@ gboolean empathy_folks_persona_is_interesting (FolksPersona *persona);
gchar * empathy_get_x509_certificate_hostname (gnutls_x509_crt_t cert);
+gchar *empathy_format_currency (gint amount, guint scale, const gchar *currency);
+
G_END_DECLS
#endif /* __EMPATHY_UTILS_H__ */
diff --git a/src/empathy-main-window.c b/src/empathy-main-window.c
index 864ef474e..4c1c788ed 100644
--- a/src/empathy-main-window.c
+++ b/src/empathy-main-window.c
@@ -144,6 +144,10 @@ struct _EmpathyMainWindowPriv {
GtkWidget *edit_context;
GtkWidget *edit_context_separator;
+ GtkActionGroup *balance_action_group;
+ GtkAction *view_balance_show_in_roster;
+ GtkWidget *balance_vbox;
+
guint size_timeout_id;
/* reffed TpAccount* => visible GtkInfoBar* */
@@ -904,6 +908,274 @@ main_window_update_status (EmpathyMainWindow *window)
}
}
+static char *
+main_window_account_to_action_name (TpAccount *account)
+{
+ char *r;
+
+ /* action names can't have '/' in them, replace it with '.' */
+ r = g_strdup (tp_account_get_path_suffix (account));
+ r = g_strdelimit (r, "/", '.');
+
+ return r;
+}
+
+static void
+main_window_balance_activate_cb (GtkAction *action,
+ EmpathyMainWindow *window)
+{
+ TpAccount *account = g_object_get_data (G_OBJECT (action), "account");
+ const char *protocol = tp_account_get_protocol (account);
+
+ /* FIXME: need a generic way to find out how to top-up an
+ * account that also works with arbitrary SIP and XMPP gateways --
+ * https://bugs.freedesktop.org/show_bug.cgi?id=36254 */
+ if (!tp_strdiff (protocol, "skype")) {
+ empathy_url_show (GTK_WIDGET (window),
+ "http://go.skype.com/store.buy.skypecredit");
+ } else {
+ DEBUG ("unknown protocol for top-up");
+ }
+}
+
+static void
+main_window_balance_update_balance (GtkAction *action,
+ GValueArray *balance)
+{
+ TpAccount *account = g_object_get_data (G_OBJECT (action), "account");
+ int amount = 0;
+ guint scale = G_MAXINT32;
+ const char *currency = "";
+ char *str;
+
+ if (balance != NULL)
+ tp_value_array_unpack (balance, 3,
+ &amount,
+ &scale,
+ &currency);
+
+ if (amount == 0 &&
+ scale == G_MAXINT32 &&
+ tp_str_empty (currency)) {
+ /* unknown balance */
+ str = g_strdup_printf ("%s (--)",
+ tp_account_get_display_name (account));
+ } else {
+ char *money = empathy_format_currency (amount, scale, currency);
+
+ str = g_strdup_printf ("%s (%s %s)",
+ tp_account_get_display_name (account),
+ currency, money);
+ g_free (money);
+ }
+
+ gtk_action_set_label (action, str);
+ g_free (str);
+}
+
+static void
+main_window_setup_balance_got_balance (TpProxy *conn,
+ const GValue *value,
+ const GError *in_error,
+ gpointer user_data,
+ GObject *action)
+{
+ GValueArray *balance = NULL;
+
+ if (in_error != NULL) {
+ DEBUG ("Failed to get account balance: %s",
+ in_error->message);
+ } else if (!G_VALUE_HOLDS (value, TP_STRUCT_TYPE_CURRENCY_AMOUNT)) {
+ DEBUG ("Type mismatch");
+ } else {
+ balance = g_value_get_boxed (value);
+ }
+
+ main_window_balance_update_balance (GTK_ACTION (action), balance);
+}
+
+static void
+main_window_balance_changed_cb (TpConnection *conn,
+ const GValueArray *balance,
+ gpointer user_data,
+ GObject *action)
+{
+ main_window_balance_update_balance (GTK_ACTION (action),
+ (GValueArray *) balance);
+}
+
+static GtkAction *
+main_window_setup_balance_create_action (EmpathyMainWindow *window,
+ TpAccount *account)
+{
+ EmpathyMainWindowPriv *priv = GET_PRIV (window);
+ GtkAction *action;
+ char *name, *ui;
+ guint merge_id;
+ GError *error = NULL;
+
+ /* create the action group if required */
+ if (priv->balance_action_group == NULL) {
+ priv->balance_action_group =
+ gtk_action_group_new ("balance-action-group");
+
+ gtk_ui_manager_insert_action_group (priv->ui_manager,
+ priv->balance_action_group, -1);
+ }
+
+ /* create the action */
+ name = main_window_account_to_action_name (account);
+ action = gtk_action_new (name,
+ tp_account_get_display_name (account),
+ _("Top up account credit"),
+ NULL);
+ g_object_bind_property (account, "icon-name", action, "icon-name",
+ G_BINDING_SYNC_CREATE);
+
+ g_object_set_data (G_OBJECT (action), "account", account);
+ g_signal_connect (action, "activate",
+ G_CALLBACK (main_window_balance_activate_cb), window);
+
+ gtk_action_group_add_action (priv->balance_action_group, action);
+ g_object_unref (action);
+
+ ui = g_strdup_printf (
+ "<ui>"
+ " <menubar name='menubar'>"
+ " <menu action='view'>"
+ " <placeholder name='view_balance_placeholder'>"
+ " <menuitem action='%s'/>"
+ " </placeholder>"
+ " </menu>"
+ " </menubar>"
+ "</ui>",
+ name);
+
+ merge_id = gtk_ui_manager_add_ui_from_string (priv->ui_manager,
+ ui, -1, &error);
+ if (error != NULL) {
+ DEBUG ("Failed to add balance UI for %s: %s",
+ tp_account_get_display_name (account),
+ error->message);
+ g_error_free (error);
+ }
+
+ g_object_set_data (G_OBJECT (action),
+ "merge-id", GUINT_TO_POINTER (merge_id));
+
+ g_free (name);
+ g_free (ui);
+
+ return action;
+}
+
+static GtkWidget *
+main_window_setup_balance_create_widget (EmpathyMainWindow *window,
+ GtkAction *action)
+{
+ EmpathyMainWindowPriv *priv = GET_PRIV (window);
+ GtkWidget *hbox, *image, *label, *button;
+
+ hbox = gtk_hbox_new (FALSE, 6);
+
+ image = gtk_image_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, TRUE, 0);
+ gtk_widget_show (image);
+
+ label = gtk_label_new ("");
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
+ gtk_widget_show (label);
+
+ button = gtk_button_new_with_label (_("Top Up..."));
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
+ gtk_widget_show (button);
+
+ gtk_box_pack_start (GTK_BOX (priv->balance_vbox), hbox, FALSE, TRUE, 0);
+ gtk_widget_show_all (hbox);
+
+ /* bind the properties from the action to the widgets -- I could have
+ * written a widget that implemented GtkActivatable, but effort */
+ g_object_bind_property (action, "label", label, "label",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (action, "icon-name", image, "icon-name",
+ G_BINDING_SYNC_CREATE);
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (gtk_action_activate), action);
+
+ /* tie the lifetime of the widget to the lifetime of the action */
+ g_object_weak_ref (G_OBJECT (action),
+ (GWeakNotify) gtk_widget_destroy, hbox);
+
+ return hbox;
+}
+
+static void
+main_window_setup_balance_conn_ready (GObject *conn,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ EmpathyMainWindow *window = user_data;
+ EmpathyMainWindowPriv *priv = GET_PRIV (window);
+ TpAccount *account = g_object_get_data (conn, "account");
+ GtkAction *action;
+ GtkWidget *widget;
+ GError *error = NULL;
+
+ if (!tp_proxy_prepare_finish (conn, result, &error)) {
+ DEBUG ("Failed to prepare connection: %s", error->message);
+
+ g_error_free (error);
+ return;
+ }
+
+ if (!tp_proxy_has_interface_by_id (conn,
+ TP_IFACE_QUARK_CONNECTION_INTERFACE_BALANCE)) {
+ return;
+ }
+
+ DEBUG ("Setting up balance for acct: %s",
+ tp_account_get_display_name (account));
+
+ /* create the action */
+ action = main_window_setup_balance_create_action (window, account);
+
+ if (action == NULL)
+ return;
+
+ gtk_action_set_visible (priv->view_balance_show_in_roster, TRUE);
+
+ /* create the display widget */
+ widget = main_window_setup_balance_create_widget (window, action);
+
+ /* request the current balance and monitor for any changes */
+ tp_cli_dbus_properties_call_get (conn, -1,
+ TP_IFACE_CONNECTION_INTERFACE_BALANCE,
+ "AccountBalance",
+ main_window_setup_balance_got_balance,
+ window, NULL, G_OBJECT (action));
+
+ tp_cli_connection_interface_balance_connect_to_balance_changed (
+ TP_CONNECTION (conn), main_window_balance_changed_cb,
+ window, NULL, G_OBJECT (action), NULL);
+}
+
+static void
+main_window_setup_balance (EmpathyMainWindow *window,
+ TpAccount *account)
+{
+ TpConnection *conn = tp_account_get_connection (account);
+
+ if (conn == NULL)
+ return;
+
+ /* need to prepare the connection:
+ * store the account on the connection */
+ g_object_set_data (G_OBJECT (conn), "account", account);
+ tp_proxy_prepare_async (conn, NULL,
+ main_window_setup_balance_conn_ready, window);
+}
+
static void
main_window_connection_changed_cb (TpAccount *account,
guint old_status,
@@ -913,6 +1185,8 @@ main_window_connection_changed_cb (TpAccount *account,
GHashTable *details,
EmpathyMainWindow *window)
{
+ EmpathyMainWindowPriv *priv = GET_PRIV (window);
+
main_window_update_status (window);
if (current == TP_CONNECTION_STATUS_DISCONNECTED &&
@@ -923,6 +1197,44 @@ main_window_connection_changed_cb (TpAccount *account,
if (current == TP_CONNECTION_STATUS_DISCONNECTED) {
empathy_sound_play (GTK_WIDGET (window),
EMPATHY_SOUND_ACCOUNT_DISCONNECTED);
+
+ /* remove balance action if required */
+ if (priv->balance_action_group != NULL) {
+ GtkAction *action;
+ char *name;
+ GList *a;
+
+ name = main_window_account_to_action_name (account);
+
+ action = gtk_action_group_get_action (
+ priv->balance_action_group, name);
+
+ if (action != NULL) {
+ guint merge_id;
+
+ DEBUG ("Removing action");
+
+ merge_id = GPOINTER_TO_UINT (g_object_get_data (
+ G_OBJECT (action),
+ "merge-id"));
+
+ gtk_ui_manager_remove_ui (priv->ui_manager,
+ merge_id);
+ gtk_action_group_remove_action (
+ priv->balance_action_group, action);
+ }
+
+ g_free (name);
+
+ a = gtk_action_group_list_actions (
+ priv->balance_action_group);
+
+ gtk_action_set_visible (
+ priv->view_balance_show_in_roster,
+ g_list_length (a) > 0);
+
+ g_list_free (a);
+ }
}
if (current == TP_CONNECTION_STATUS_CONNECTED) {
@@ -931,6 +1243,7 @@ main_window_connection_changed_cb (TpAccount *account,
/* Account connected without error, remove error message if any */
main_window_remove_error (window, account);
+ main_window_setup_balance (window, account);
}
}
@@ -1775,6 +2088,8 @@ account_manager_prepared_cb (GObject *source_object,
window);
g_hash_table_insert (priv->status_changed_handlers,
account, GUINT_TO_POINTER (handler_id));
+
+ main_window_setup_balance (window, account);
}
g_signal_connect (manager, "account-validity-changed",
@@ -1870,6 +2185,7 @@ empathy_main_window_init (EmpathyMainWindow *window)
filename = empathy_file_lookup ("empathy-main-window.ui", "src");
gui = empathy_builder_get_file (filename,
"main_vbox", &priv->main_vbox,
+ "balance_vbox", &priv->balance_vbox,
"errors_vbox", &priv->errors_vbox,
"auth_vbox", &priv->auth_vbox,
"ui_manager", &priv->ui_manager,
@@ -1887,6 +2203,7 @@ empathy_main_window_init (EmpathyMainWindow *window)
"notebook", &priv->notebook,
"no_entry_label", &priv->no_entry_label,
"roster_scrolledwindow", &sw,
+ "view_balance_show_in_roster", &priv->view_balance_show_in_roster,
NULL);
g_free (filename);
@@ -2044,6 +2361,14 @@ empathy_main_window_init (EmpathyMainWindow *window)
/* Set window size. */
empathy_geometry_bind (GTK_WINDOW (window), GEOMETRY_NAME);
+ /* bind view_balance_show_in_roster */
+ g_settings_bind (priv->gsettings_ui, "show-balance-in-roster",
+ priv->view_balance_show_in_roster, "active",
+ G_SETTINGS_BIND_DEFAULT);
+ g_object_bind_property (priv->view_balance_show_in_roster, "active",
+ priv->balance_vbox, "visible",
+ G_BINDING_SYNC_CREATE);
+
/* Enable event handling */
priv->call_observer = empathy_call_observer_dup_singleton ();
priv->event_manager = empathy_event_manager_dup_singleton ();
diff --git a/src/empathy-main-window.ui b/src/empathy-main-window.ui
index a538e0120..95a6ec8dd 100644
--- a/src/empathy-main-window.ui
+++ b/src/empathy-main-window.ui
@@ -69,6 +69,13 @@
</object>
</child>
<child>
+ <object class="GtkToggleAction" id="view_balance_show_in_roster">
+ <property name="name">view_balance_show_in_roster</property>
+ <property name="label" translatable="yes">Show Account _Balances in Roster</property>
+ <property name="visible">False</property>
+ </object>
+ </child>
+ <child>
<object class="GtkAction" id="view_show_map">
<property name="name">view_show_map</property>
<property name="label" translatable="yes">Contacts on a _Map</property>
@@ -256,6 +263,9 @@
<menuitem action="view_show_offline"/>
<menuitem action="view_show_protocols"/>
<separator/>
+ <menuitem action="view_balance_show_in_roster"/>
+ <placeholder name="view_balance_placeholder"/>
+ <separator/>
<menuitem action="view_sort_by_name"/>
<menuitem action="view_sort_by_status"/>
<separator/>
@@ -305,7 +315,8 @@
</packing>
</child>
<child>
- <object class="GtkVBox" id="errors_vbox">
+ <object class="GtkVBox" id="balance_vbox">
+ <property name="spacing">3</property>
<child>
<placeholder/>
</child>
@@ -317,7 +328,7 @@
</packing>
</child>
<child>
- <object class="GtkVBox" id="auth_vbox">
+ <object class="GtkVBox" id="errors_vbox">
<child>
<placeholder/>
</child>
@@ -329,6 +340,18 @@
</packing>
</child>
<child>
+ <object class="GtkVBox" id="auth_vbox">
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkNotebook" id="notebook">
<property name="visible">True</property>
<property name="can_focus">True</property>
@@ -360,7 +383,7 @@
</child>
</object>
<packing>
- <property name="position">4</property>
+ <property name="position">5</property>
</packing>
</child>
</object>