X-Git-Url: https://git.0d.be/?p=empathy.git;a=blobdiff_plain;f=src%2Fempathy-main-window.c;h=f3722524bb83abbe2f2994f5f8e0ce41c6c7f9ff;hp=b82c077f89a5db3c61cf0d968062bff71b652f9e;hb=e3598826ad6ede2f72fbee91c528e621f6dce926;hpb=d9db9822240a5aece8ee660bebb2afe7202a7f53 diff --git a/src/empathy-main-window.c b/src/empathy-main-window.c index b82c077f..f3722524 100644 --- a/src/empathy-main-window.c +++ b/src/empathy-main-window.c @@ -34,9 +34,8 @@ #include #include -#include #include -#include +#include #include #include #include @@ -51,6 +50,8 @@ #include #include #include +#include +#include #include #include #include @@ -60,10 +61,11 @@ #include #include #include -#include +#include #include #include "empathy-accounts-dialog.h" +#include "empathy-call-observer.h" #include "empathy-chat-manager.h" #include "empathy-main-window.h" #include "empathy-preferences.h" @@ -91,18 +93,27 @@ /* Name in the geometry file */ #define GEOMETRY_NAME "main-window" +enum { + PAGE_CONTACT_LIST = 0, + PAGE_NO_MATCH +}; + G_DEFINE_TYPE (EmpathyMainWindow, empathy_main_window, GTK_TYPE_WINDOW); #define GET_PRIV(self) ((EmpathyMainWindowPriv *)((EmpathyMainWindow *) self)->priv) struct _EmpathyMainWindowPriv { + EmpathyContactList *contact_manager; EmpathyIndividualStore *individual_store; EmpathyIndividualView *individual_view; TpAccountManager *account_manager; EmpathyChatroomManager *chatroom_manager; EmpathyEventManager *event_manager; + EmpathySoundManager *sound_mgr; + EmpathyCallObserver *call_observer; guint flash_timeout_id; gboolean flash_on; + gboolean empty; GSettings *gsettings_ui; GSettings *gsettings_contacts; @@ -114,6 +125,7 @@ struct _EmpathyMainWindowPriv { GtkWidget *presence_toolbar; GtkWidget *presence_chooser; GtkWidget *errors_vbox; + GtkWidget *auth_vbox; GtkWidget *search_bar; GtkWidget *notebook; GtkWidget *no_entry_label; @@ -133,9 +145,18 @@ 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* */ GHashTable *errors; + /* EmpathyEvent* => visible GtkInfoBar* */ + GHashTable *auths; + /* stores a mapping from TpAccount to Handler ID to prevent * to listen more than once to the status-changed signal */ GHashTable *status_changed_handlers; @@ -183,18 +204,15 @@ main_window_flash_foreach (GtkTreeModel *model, GdkPixbuf *pixbuf = NULL; gtk_tree_model_get (model, iter, - EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, - &individual, + EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual, -1); if (individual == NULL) return FALSE; contact = empathy_contact_dup_from_folks_individual (individual); - if (contact != data->event->contact) { - tp_clear_object (&contact); - return FALSE; - } + if (contact != data->event->contact) + goto out; if (data->on) { icon_name = data->event->icon_name; @@ -203,6 +221,8 @@ main_window_flash_foreach (GtkTreeModel *model, pixbuf = empathy_individual_store_get_individual_status_icon ( GET_PRIV (data->window)->individual_store, individual); + if (pixbuf != NULL) + g_object_ref (pixbuf); } gtk_tree_store_set (GTK_TREE_STORE (model), iter, @@ -221,8 +241,10 @@ main_window_flash_foreach (GtkTreeModel *model, gtk_tree_path_free (parent_path); } +out: g_object_unref (individual); tp_clear_object (&contact); + tp_clear_object (&pixbuf); return FALSE; } @@ -277,13 +299,229 @@ main_window_flash_start (EmpathyMainWindow *window) main_window_flash_cb (window); } +static void +main_window_remove_auth (EmpathyMainWindow *window, + EmpathyEvent *event) +{ + EmpathyMainWindowPriv *priv = GET_PRIV (window); + GtkWidget *error_widget; + + error_widget = g_hash_table_lookup (priv->auths, event); + if (error_widget != NULL) { + gtk_widget_destroy (error_widget); + g_hash_table_remove (priv->auths, event); + } +} + +static void +main_window_auth_add_clicked_cb (GtkButton *button, + EmpathyMainWindow *window) +{ + EmpathyEvent *event; + + event = g_object_get_data (G_OBJECT (button), "event"); + + empathy_event_approve (event); + + main_window_remove_auth (window, event); +} + +static void +main_window_auth_close_clicked_cb (GtkButton *button, + EmpathyMainWindow *window) +{ + EmpathyEvent *event; + + event = g_object_get_data (G_OBJECT (button), "event"); + + empathy_event_decline (event); + main_window_remove_auth (window, event); +} + +static void +main_window_auth_display (EmpathyMainWindow *window, + EmpathyEvent *event) +{ + EmpathyMainWindowPriv *priv = GET_PRIV (window); + TpAccount *account = event->account; + GtkWidget *info_bar; + GtkWidget *content_area; + GtkWidget *image; + GtkWidget *label; + GtkWidget *add_button; + GtkWidget *close_button; + GtkWidget *action_area; + GtkWidget *action_table; + const gchar *icon_name; + gchar *str; + + if (g_hash_table_lookup (priv->auths, event) != NULL) { + return; + } + + info_bar = gtk_info_bar_new (); + gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_QUESTION); + + gtk_widget_set_no_show_all (info_bar, TRUE); + gtk_box_pack_start (GTK_BOX (priv->auth_vbox), info_bar, FALSE, TRUE, 0); + gtk_widget_show (info_bar); + + icon_name = tp_account_get_icon_name (account); + image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_widget_show (image); + + str = g_markup_printf_escaped ("%s\n%s", + tp_account_get_display_name (account), + _("Password required")); + + label = gtk_label_new (str); + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END); + gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); + gtk_widget_show (label); + + g_free (str); + + content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar)); + gtk_box_pack_start (GTK_BOX (content_area), image, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (content_area), label, TRUE, TRUE, 0); + + image = gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON); + add_button = gtk_button_new (); + gtk_button_set_image (GTK_BUTTON (add_button), image); + gtk_widget_set_tooltip_text (add_button, _("Provide Password")); + gtk_widget_show (add_button); + + image = gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_BUTTON); + close_button = gtk_button_new (); + gtk_button_set_image (GTK_BUTTON (close_button), image); + gtk_widget_set_tooltip_text (close_button, _("Disconnect")); + gtk_widget_show (close_button); + + action_table = gtk_table_new (1, 2, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (action_table), 6); + gtk_widget_show (action_table); + + action_area = gtk_info_bar_get_action_area (GTK_INFO_BAR (info_bar)); + gtk_box_pack_start (GTK_BOX (action_area), action_table, FALSE, FALSE, 0); + + gtk_table_attach (GTK_TABLE (action_table), add_button, 0, 1, 0, 1, + (GtkAttachOptions) (GTK_SHRINK), + (GtkAttachOptions) (GTK_SHRINK), 0, 0); + gtk_table_attach (GTK_TABLE (action_table), close_button, 1, 2, 0, 1, + (GtkAttachOptions) (GTK_SHRINK), + (GtkAttachOptions) (GTK_SHRINK), 0, 0); + + g_object_set_data_full (G_OBJECT (info_bar), + "event", event, NULL); + g_object_set_data_full (G_OBJECT (add_button), + "event", event, NULL); + g_object_set_data_full (G_OBJECT (close_button), + "event", event, NULL); + + g_signal_connect (add_button, "clicked", + G_CALLBACK (main_window_auth_add_clicked_cb), + window); + g_signal_connect (close_button, "clicked", + G_CALLBACK (main_window_auth_close_clicked_cb), + window); + + gtk_widget_show (priv->auth_vbox); + + g_hash_table_insert (priv->auths, event, info_bar); +} + +static void +modify_event_count (GtkTreeModel *model, + GtkTreeIter *iter, + EmpathyEvent *event, + gboolean increase) +{ + FolksIndividual *individual; + EmpathyContact *contact; + guint count; + + gtk_tree_model_get (model, iter, + EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual, + EMPATHY_INDIVIDUAL_STORE_COL_EVENT_COUNT, &count, + -1); + + if (individual == NULL) + return; + + increase ? count++ : count--; + + contact = empathy_contact_dup_from_folks_individual (individual); + if (contact == event->contact) { + gtk_tree_store_set (GTK_TREE_STORE (model), iter, + EMPATHY_INDIVIDUAL_STORE_COL_EVENT_COUNT, count, -1); + } + + tp_clear_object (&contact); + g_object_unref (individual); +} + +static gboolean +increase_event_count_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + EmpathyEvent *event = user_data; + + modify_event_count (model, iter, event, TRUE); + + return FALSE; +} + +static void +increase_event_count (EmpathyMainWindow *self, + EmpathyEvent *event) +{ + EmpathyMainWindowPriv *priv = GET_PRIV (self); + GtkTreeModel *model; + + model = GTK_TREE_MODEL (priv->individual_store); + + gtk_tree_model_foreach (model, increase_event_count_foreach, event); +} + +static gboolean +decrease_event_count_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + EmpathyEvent *event = user_data; + + modify_event_count (model, iter, event, FALSE); + + return FALSE; +} + +static void +decrease_event_count (EmpathyMainWindow *self, + EmpathyEvent *event) +{ + EmpathyMainWindowPriv *priv = GET_PRIV (self); + GtkTreeModel *model; + + model = GTK_TREE_MODEL (priv->individual_store); + + gtk_tree_model_foreach (model, decrease_event_count_foreach, event); +} + static void main_window_event_added_cb (EmpathyEventManager *manager, EmpathyEvent *event, EmpathyMainWindow *window) { if (event->contact) { + increase_event_count (window, event); + main_window_flash_start (window); + } else if (event->type == EMPATHY_EVENT_TYPE_AUTH) { + main_window_auth_display (window, event); } } @@ -295,10 +533,17 @@ main_window_event_removed_cb (EmpathyEventManager *manager, EmpathyMainWindowPriv *priv = GET_PRIV (window); FlashForeachData data; + if (event->type == EMPATHY_EVENT_TYPE_AUTH) { + main_window_remove_auth (window, event); + return; + } + if (!event->contact) { return; } + decrease_event_count (window, event); + data.on = FALSE; data.event = event; data.window = window; @@ -358,6 +603,52 @@ OUT: tp_clear_object (&individual); } +static void +main_window_row_deleted_cb (GtkTreeModel *model, + GtkTreePath *path, + EmpathyMainWindow *window) +{ + EmpathyMainWindowPriv *priv = GET_PRIV (window); + GtkTreeIter help_iter; + + if (!gtk_tree_model_get_iter_first (model, &help_iter)) { + priv->empty = TRUE; + + if (empathy_individual_view_is_searching ( + priv->individual_view)) { + gchar *tmp; + + tmp = g_strdup_printf ("%s", + _("No match found")); + + gtk_label_set_markup (GTK_LABEL (priv->no_entry_label), tmp); + g_free (tmp); + + gtk_label_set_line_wrap (GTK_LABEL (priv->no_entry_label), + TRUE); + + gtk_notebook_set_current_page ( + GTK_NOTEBOOK (priv->notebook), PAGE_NO_MATCH); + } + } +} + +static void +main_window_row_inserted_cb (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + EmpathyMainWindow *window) +{ + EmpathyMainWindowPriv *priv = GET_PRIV (window); + + if (priv->empty) { + priv->empty = FALSE; + gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), + PAGE_CONTACT_LIST); + gtk_widget_grab_focus (GTK_WIDGET (priv->individual_view)); + } +} + static void main_window_remove_error (EmpathyMainWindow *window, TpAccount *account) @@ -433,10 +724,19 @@ main_window_error_display (EmpathyMainWindow *window, GtkWidget *action_table; gchar *str; const gchar *icon_name; + const gchar *error_message; + gboolean user_requested; + + error_message = + empathy_account_get_error_message (account, &user_requested); + + if (user_requested) { + return; + } str = g_markup_printf_escaped ("%s\n%s", tp_account_get_display_name (account), - empathy_account_get_error_message (account)); + error_message); info_bar = g_hash_table_lookup (priv->errors, account); if (info_bar) { @@ -530,6 +830,7 @@ main_window_error_display (EmpathyMainWindow *window, G_CALLBACK (main_window_error_retry_clicked_cb), window); + gtk_widget_set_tooltip_text (priv->errors_vbox, error_message); gtk_widget_show (priv->errors_vbox); g_hash_table_insert (priv->errors, g_object_ref (account), info_bar); @@ -540,7 +841,7 @@ main_window_update_status (EmpathyMainWindow *window) { EmpathyMainWindowPriv *priv = GET_PRIV (window); gboolean connected, connecting; - GList *l; + GList *l, *children; connected = empathy_account_manager_get_accounts_connected (&connecting); @@ -557,6 +858,291 @@ main_window_update_status (EmpathyMainWindow *window) for (l = priv->actions_connected; l; l = l->next) { gtk_action_set_sensitive (l->data, connected); } + + /* Update favourite rooms sensitivity */ + children = gtk_container_get_children (GTK_CONTAINER (priv->room_menu)); + for (l = children; l != NULL; l = l->next) { + if (g_object_get_data (G_OBJECT (l->data), "is_favorite") != NULL) { + gtk_widget_set_sensitive (GTK_WIDGET (l->data), connected); + } + } + g_list_free (children); +} + +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) +{ + const char *uri; + + uri = g_object_get_data (G_OBJECT (action), "manage-credit-uri"); + + if (!tp_str_empty (uri)) { + DEBUG ("Top-up credit URI: %s", uri); + empathy_url_show (GTK_WIDGET (window), uri); + } else { + DEBUG ("unknown protocol for top-up"); + } +} + +static void +main_window_balance_update_balance (GtkAction *action, + TpConnection *conn) +{ + TpAccount *account = tp_connection_get_account (conn); + GtkWidget *label; + int amount = 0; + guint scale = G_MAXINT32; + const gchar *currency = ""; + char *money, *str; + + if (!tp_connection_get_balance (conn, &amount, &scale, ¤cy)) + return; + + if (amount == 0 && + scale == G_MAXINT32 && + tp_str_empty (currency)) { + /* unknown balance */ + money = g_strdup ("--"); + } else { + char *tmp = empathy_format_currency (amount, scale, currency); + + money = g_strdup_printf ("%s %s", currency, tmp); + g_free (tmp); + } + + /* Translators: this string will be something like: + * Top up My Account ($1.23)..." */ + str = g_strdup_printf (_("Top up %s (%s)..."), + tp_account_get_display_name (account), + money); + + gtk_action_set_label (action, str); + g_free (str); + + /* update the money label in the roster */ + label = g_object_get_data (G_OBJECT (action), "money-label"); + + gtk_label_set_text (GTK_LABEL (label), money); + g_free (money); +} + +static void +main_window_balance_changed_cb (TpConnection *conn, + guint balance, + guint scale, + const gchar *currency, + GtkAction *action) +{ + main_window_balance_update_balance (action, conn); +} + +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_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 ( + "" + " " + " " + " " + " " + " " + " " + " " + "", + 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, + TpAccount *account) +{ + EmpathyMainWindowPriv *priv = GET_PRIV (window); + GtkWidget *hbox, *image, *label, *button; + + hbox = gtk_hbox_new (FALSE, 6); + + /* protocol icon */ + image = gtk_image_new (); + gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, TRUE, 0); + g_object_bind_property (action, "icon-name", image, "icon-name", + G_BINDING_SYNC_CREATE); + + /* account name label */ + 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); + g_object_bind_property (account, "display-name", label, "label", + G_BINDING_SYNC_CREATE); + + /* balance label */ + label = gtk_label_new (""); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0); + g_object_set_data (G_OBJECT (action), "money-label", label); + + /* top up button */ + button = gtk_button_new_with_label (_("Top Up...")); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0); + g_signal_connect_swapped (button, "clicked", + G_CALLBACK (gtk_action_activate), action); + + gtk_box_pack_start (GTK_BOX (priv->balance_vbox), hbox, FALSE, TRUE, 0); + gtk_widget_show_all (hbox); + + /* 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 (EmpathyMainWindow *window, + TpAccount *account) +{ + EmpathyMainWindowPriv *priv = GET_PRIV (window); + TpConnection *conn = tp_account_get_connection (account); + GtkAction *action; + const gchar *uri; + + if (conn == NULL) + return; + + if (!tp_proxy_is_prepared (conn, TP_CONNECTION_FEATURE_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 */ + main_window_setup_balance_create_widget (window, action, account); + + /* check the current balance and monitor for any changes */ + uri = tp_connection_get_balance_uri (conn); + + g_object_set_data_full (G_OBJECT (action), "manage-credit-uri", + g_strdup (uri), g_free); + gtk_action_set_sensitive (GTK_ACTION (action), !tp_str_empty (uri)); + + main_window_balance_update_balance (GTK_ACTION (action), conn); + + g_signal_connect (conn, "balance-changed", + G_CALLBACK (main_window_balance_changed_cb), action); + +} + +static void +main_window_remove_balance_action (EmpathyMainWindow *window, + TpAccount *account) +{ + EmpathyMainWindowPriv *priv = GET_PRIV (window); + GtkAction *action; + char *name; + GList *a; + + if (priv->balance_action_group == NULL) + return; + + 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); } static void @@ -568,6 +1154,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 && @@ -576,16 +1164,20 @@ main_window_connection_changed_cb (TpAccount *account, } if (current == TP_CONNECTION_STATUS_DISCONNECTED) { - empathy_sound_play (GTK_WIDGET (window), + empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (window), EMPATHY_SOUND_ACCOUNT_DISCONNECTED); + + /* remove balance action if required */ + main_window_remove_balance_action (window, account); } if (current == TP_CONNECTION_STATUS_CONNECTED) { - empathy_sound_play (GTK_WIDGET (window), + empathy_sound_manager_play (priv->sound_mgr, GTK_WIDGET (window), EMPATHY_SOUND_ACCOUNT_CONNECTED); /* Account connected without error, remove error message if any */ main_window_remove_error (window, account); + main_window_setup_balance (window, account); } } @@ -634,7 +1226,10 @@ empathy_main_window_finalize (GObject *window) g_object_unref (priv->account_manager); g_object_unref (priv->individual_store); + g_object_unref (priv->contact_manager); + g_object_unref (priv->sound_mgr); g_hash_table_destroy (priv->errors); + g_hash_table_destroy (priv->auths); /* disconnect all handlers of status-changed signal */ g_hash_table_iter_init (&iter, priv->status_changed_handlers); @@ -650,6 +1245,7 @@ empathy_main_window_finalize (GObject *window) g_signal_handlers_disconnect_by_func (priv->event_manager, main_window_event_removed_cb, window); + g_object_unref (priv->call_observer); g_object_unref (priv->event_manager); g_object_unref (priv->ui_manager); g_object_unref (priv->chatroom_manager); @@ -665,14 +1261,10 @@ main_window_key_press_event_cb (GtkWidget *window, GdkEventKey *event, gpointer user_data) { - EmpathyChatManager *chat_manager; - - if (event->keyval == GDK_T + if (event->keyval == GDK_KEY_T && event->state & GDK_SHIFT_MASK && event->state & GDK_CONTROL_MASK) { - chat_manager = empathy_chat_manager_dup_singleton (); - empathy_chat_manager_undo_closed_chat (chat_manager); - g_object_unref (chat_manager); + empathy_chat_manager_call_undo_closed_chat (); } return FALSE; } @@ -712,6 +1304,15 @@ main_window_chat_add_contact_cb (GtkAction *action, empathy_new_individual_dialog_show (GTK_WINDOW (window)); } +static void +main_window_chat_search_contacts_cb (GtkAction *action, + EmpathyMainWindow *window) +{ + GtkWidget *dialog = empathy_contact_search_dialog_new ( + GTK_WINDOW (window)); + gtk_widget_show (dialog); +} + static void main_window_view_show_ft_manager (GtkAction *action, EmpathyMainWindow *window) @@ -892,11 +1493,20 @@ main_window_notify_contact_list_size_cb (GSettings *gsettings, gtk_radio_action_set_current_value (priv->normal_with_avatars, value); } +static void +main_window_edit_search_contacts_cb (GtkCheckMenuItem *item, + EmpathyMainWindow *window) +{ + EmpathyMainWindowPriv *priv = GET_PRIV (window); + + empathy_individual_view_start_search (priv->individual_view); +} + static void main_window_view_show_map_cb (GtkCheckMenuItem *item, EmpathyMainWindow *window) { -#if HAVE_LIBCHAMPLAIN +#ifdef HAVE_LIBCHAMPLAIN empathy_map_view_show (); #endif } @@ -906,16 +1516,13 @@ join_chatroom (EmpathyChatroom *chatroom, gint64 timestamp) { TpAccount *account; - TpConnection *connection; const gchar *room; account = empathy_chatroom_get_account (chatroom); - connection = tp_account_get_connection (account); - g_assert (connection != NULL); room = empathy_chatroom_get_room (chatroom); DEBUG ("Requesting channel for '%s'", room); - empathy_dispatcher_join_muc (connection, room, timestamp); + empathy_join_muc (account, room, timestamp); } typedef struct @@ -973,6 +1580,9 @@ account_status_changed_cb (TpAccount *account, case TP_CONNECTION_STATUS_CONNECTED: /* We can join the room */ break; + + default: + g_assert_not_reached (); } join_chatroom (ctx->chatroom, ctx->timestamp); @@ -1005,7 +1615,7 @@ main_window_favorite_chatroom_join (EmpathyChatroom *chatroom) join_fav_account_sig_ctx *ctx; ctx = join_fav_account_sig_ctx_new (account, chatroom, - gtk_get_current_event_time ()); + empathy_get_current_action_time ()); ctx->sig_id = g_signal_connect_data (account, "status-changed", G_CALLBACK (account_status_changed_cb), ctx, @@ -1016,7 +1626,7 @@ main_window_favorite_chatroom_join (EmpathyChatroom *chatroom) return; } - join_chatroom (chatroom, gtk_get_current_event_time ()); + join_chatroom (chatroom, empathy_get_current_action_time ()); } static void @@ -1032,14 +1642,22 @@ main_window_favorite_chatroom_menu_add (EmpathyMainWindow *window, { EmpathyMainWindowPriv *priv = GET_PRIV (window); GtkWidget *menu_item; - const gchar *name; + const gchar *name, *account_name; + gchar *label; + if (g_object_get_data (G_OBJECT (chatroom), "menu_item")) { return; } name = empathy_chatroom_get_name (chatroom); - menu_item = gtk_menu_item_new_with_label (name); + account_name = tp_account_get_display_name ( + empathy_chatroom_get_account (chatroom)); + label = g_strdup_printf ("%s (%s)", name, account_name); + menu_item = gtk_menu_item_new_with_label (label); + g_free (label); + g_object_set_data (G_OBJECT (menu_item), "is_favorite", + GUINT_TO_POINTER (TRUE)); g_object_set_data (G_OBJECT (chatroom), "menu_item", menu_item); g_signal_connect (menu_item, "activate", @@ -1218,8 +1836,20 @@ main_window_edit_personal_information_cb (GtkAction *action, } static void -main_window_edit_preferences_cb (GtkAction *action, - EmpathyMainWindow *window) +main_window_edit_blocked_contacts_cb (GtkAction *action, + EmpathyMainWindow *window) +{ + GtkWidget *dialog; + + dialog = empathy_contact_blocking_dialog_new (GTK_WINDOW (window)); + gtk_widget_show (dialog); + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); +} + +void +empathy_main_window_show_preferences (EmpathyMainWindow *window, + const gchar *tab) { EmpathyMainWindowPriv *priv = GET_PRIV (window); @@ -1232,6 +1862,17 @@ main_window_edit_preferences_cb (GtkAction *action, } else { gtk_window_present (GTK_WINDOW (priv->preferences)); } + + if (tab != NULL) + empathy_preferences_show_tab ( + EMPATHY_PREFERENCES (priv->preferences), tab); +} + +static void +main_window_edit_preferences_cb (GtkAction *action, + EmpathyMainWindow *window) +{ + empathy_main_window_show_preferences (window, NULL); } static void @@ -1245,35 +1886,7 @@ static void main_window_help_debug_cb (GtkAction *action, EmpathyMainWindow *window) { - GdkScreen *screen = gdk_screen_get_default (); - GError *error = NULL; - gchar *argv[2] = { NULL, }; - gint i = 0; - gchar *path; - - g_return_if_fail (GDK_IS_SCREEN (screen)); - - /* Try to run from source directory if possible */ - path = g_build_filename (g_getenv ("EMPATHY_SRCDIR"), "src", - "empathy-debugger", NULL); - - if (!g_file_test (path, G_FILE_TEST_EXISTS)) { - g_free (path); - path = g_build_filename (BIN_DIR, "empathy-debugger", NULL); - } - - argv[i++] = path; - - gdk_spawn_on_screen (screen, NULL, argv, NULL, - G_SPAWN_SEARCH_PATH, - NULL, NULL, NULL, &error); - - if (error) { - g_warning ("Failed to open debug window: %s", error->message); - g_error_free (error); - } - - g_free (path); + empathy_launch_program (BIN_DIR, "empathy-debugger", NULL); } static void @@ -1317,6 +1930,9 @@ main_window_account_removed_cb (TpAccountManager *manager, /* remove errors if any */ main_window_remove_error (window, account); + + /* remove the balance action if required */ + main_window_remove_balance_action (window, account); } static void @@ -1364,11 +1980,15 @@ main_window_connection_items_setup (EmpathyMainWindow *window, GObject *action; guint i; const gchar *actions_connected[] = { - "room", + "room_join_new", + "room_join_favorites", "chat_new_message", "chat_new_call", + "chat_search_contacts", "chat_add_contact", - "edit_personal_information" + "edit_personal_information", + "edit_blocked_contacts", + "edit_search_contacts" }; for (i = 0, list = NULL; i < G_N_ELEMENTS (actions_connected); i++) { @@ -1390,7 +2010,7 @@ account_manager_prepared_cb (GObject *source_object, EmpathyMainWindowPriv *priv = GET_PRIV (window); GError *error = NULL; - if (!tp_account_manager_prepare_finish (manager, result, &error)) { + if (!tp_proxy_prepare_finish (manager, result, &error)) { DEBUG ("Failed to prepare account manager: %s", error->message); g_error_free (error); return; @@ -1406,6 +2026,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", @@ -1475,9 +2097,8 @@ static void empathy_main_window_init (EmpathyMainWindow *window) { EmpathyMainWindowPriv *priv; - EmpathyContactList *list_iface; EmpathyIndividualManager *individual_manager; - GtkBuilder *gui; + GtkBuilder *gui, *gui_mgr; GtkWidget *sw; GtkToggleAction *show_offline_widget; GtkAction *show_map_widget; @@ -1485,6 +2106,9 @@ empathy_main_window_init (EmpathyMainWindow *window) gboolean show_offline; gchar *filename; GSList *l; + GtkTreeModel *model; + GtkWidget *search_vbox; + GtkWidget *menubar; priv = window->priv = G_TYPE_INSTANCE_GET_PRIVATE (window, EMPATHY_TYPE_MAIN_WINDOW, EmpathyMainWindowPriv); @@ -1492,15 +2116,34 @@ empathy_main_window_init (EmpathyMainWindow *window) priv->gsettings_ui = g_settings_new (EMPATHY_PREFS_UI_SCHEMA); priv->gsettings_contacts = g_settings_new (EMPATHY_PREFS_CONTACTS_SCHEMA); + priv->sound_mgr = empathy_sound_manager_dup_singleton (); + gtk_window_set_title (GTK_WINDOW (window), _("Contact List")); gtk_window_set_role (GTK_WINDOW (window), "contact_list"); gtk_window_set_default_size (GTK_WINDOW (window), 225, 325); + /* don't finalize the widget on delete-event, just hide it */ + g_signal_connect (window, "delete-event", + G_CALLBACK (gtk_widget_hide_on_delete), NULL); + /* Set up interface */ 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, + "search_vbox", &search_vbox, + "presence_toolbar", &priv->presence_toolbar, + "notebook", &priv->notebook, + "no_entry_label", &priv->no_entry_label, + "roster_scrolledwindow", &sw, + NULL); + g_free (filename); + + /* Set UI manager */ + filename = empathy_file_lookup ("empathy-main-window-menubar.ui", "src"); + gui_mgr = empathy_builder_get_file (filename, "ui_manager", &priv->ui_manager, "view_show_offline", &show_offline_widget, "view_show_protocols", &priv->show_protocols, @@ -1512,20 +2155,24 @@ empathy_main_window_init (EmpathyMainWindow *window) "view_history", &priv->view_history, "view_show_map", &show_map_widget, "room_join_favorites", &priv->room_join_favorites, - "presence_toolbar", &priv->presence_toolbar, - "notebook", &priv->notebook, - "no_entry_label", &priv->no_entry_label, - "roster_scrolledwindow", &sw, + "view_balance_show_in_roster", &priv->view_balance_show_in_roster, + "menubar", &menubar, NULL); g_free (filename); + /* The UI manager is living in its own .ui file as Glade doesn't support + * those. The GtkMenubar has to be in this file as well to we manually add + * it to the first position of the vbox. */ + gtk_box_pack_start (GTK_BOX (priv->main_vbox), menubar, FALSE, FALSE, 0); + gtk_box_reorder_child (GTK_BOX (priv->main_vbox), menubar, 0); + gtk_container_add (GTK_CONTAINER (window), priv->main_vbox); gtk_widget_show (priv->main_vbox); g_signal_connect (window, "key-press-event", G_CALLBACK (main_window_key_press_event_cb), NULL); - empathy_builder_connect (gui, window, + empathy_builder_connect (gui_mgr, window, "chat_quit", "activate", main_window_chat_quit_cb, "chat_new_message", "activate", main_window_chat_new_message_cb, "chat_new_call", "activate", main_window_chat_new_call_cb, @@ -1534,6 +2181,7 @@ empathy_main_window_init (EmpathyMainWindow *window) "room_join_favorites", "activate", main_window_room_join_favorites_cb, "room_manage_favorites", "activate", main_window_room_manage_favorites_cb, "chat_add_contact", "activate", main_window_chat_add_contact_cb, + "chat_search_contacts", "activate", main_window_chat_search_contacts_cb, "view_show_ft_manager", "activate", main_window_view_show_ft_manager, "view_show_offline", "toggled", main_window_view_show_offline_cb, "view_show_protocols", "toggled", main_window_view_show_protocols_cb, @@ -1543,25 +2191,28 @@ empathy_main_window_init (EmpathyMainWindow *window) "edit", "activate", main_window_edit_cb, "edit_accounts", "activate", main_window_edit_accounts_cb, "edit_personal_information", "activate", main_window_edit_personal_information_cb, + "edit_blocked_contacts", "activate", main_window_edit_blocked_contacts_cb, "edit_preferences", "activate", main_window_edit_preferences_cb, + "edit_search_contacts", "activate", main_window_edit_search_contacts_cb, "help_about", "activate", main_window_help_about_cb, "help_debug", "activate", main_window_help_debug_cb, "help_contents", "activate", main_window_help_contents_cb, NULL); /* Set up connection related widgets. */ - main_window_connection_items_setup (window, gui); + main_window_connection_items_setup (window, gui_mgr); g_object_ref (priv->ui_manager); g_object_unref (gui); + g_object_unref (gui_mgr); -#if !HAVE_LIBCHAMPLAIN +#ifndef HAVE_LIBCHAMPLAIN gtk_action_set_visible (show_map_widget, FALSE); #endif priv->account_manager = tp_account_manager_dup (); - tp_account_manager_prepare_async (priv->account_manager, NULL, + tp_proxy_prepare_async (priv->account_manager, NULL, account_manager_prepared_cb, window); priv->errors = g_hash_table_new_full (g_direct_hash, @@ -1569,6 +2220,8 @@ empathy_main_window_init (EmpathyMainWindow *window) g_object_unref, NULL); + priv->auths = g_hash_table_new (NULL, NULL); + priv->status_changed_handlers = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, @@ -1593,6 +2246,7 @@ empathy_main_window_init (EmpathyMainWindow *window) gtk_widget_show (priv->presence_chooser); item = gtk_tool_item_new (); gtk_widget_show (GTK_WIDGET (item)); + gtk_widget_set_size_request (priv->presence_chooser, 10, -1); gtk_container_add (GTK_CONTAINER (item), priv->presence_chooser); gtk_tool_item_set_is_important (item, TRUE); gtk_tool_item_set_expand (item, TRUE); @@ -1601,8 +2255,6 @@ empathy_main_window_init (EmpathyMainWindow *window) /* Set up the throbber */ priv->throbber = gtk_spinner_new (); gtk_widget_set_size_request (priv->throbber, 16, -1); - gtk_widget_set_tooltip_text (priv->throbber, _("Show and edit accounts")); - gtk_widget_set_has_window (GTK_WIDGET (priv->throbber), TRUE); gtk_widget_set_events (priv->throbber, GDK_BUTTON_PRESS_MASK); g_signal_connect (priv->throbber, "button-press-event", G_CALLBACK (main_window_throbber_button_press_event_cb), @@ -1615,23 +2267,29 @@ empathy_main_window_init (EmpathyMainWindow *window) gtk_container_add (GTK_CONTAINER (item), priv->throbber); priv->throbber_tool_item = GTK_WIDGET (item); - list_iface = EMPATHY_CONTACT_LIST (empathy_contact_manager_dup_singleton ()); + /* XXX: this class is designed to live for the duration of the program, + * so it's got a race condition between its signal handlers and its + * finalization. The class is planned to be removed, so we won't fix + * this before then. */ + priv->contact_manager = EMPATHY_CONTACT_LIST ( + empathy_contact_manager_dup_singleton ()); individual_manager = empathy_individual_manager_dup_singleton (); priv->individual_store = empathy_individual_store_new ( individual_manager); g_object_unref (individual_manager); + /* For the moment, we disallow Persona drops onto the main contact list (e.g. from things such as + * the EmpathyPersonaView in the linking dialogue). No code is hooked up to do anything on a Persona + * drop, so allowing them would achieve nothing except confusion. */ priv->individual_view = empathy_individual_view_new ( priv->individual_store, - EMPATHY_INDIVIDUAL_VIEW_FEATURE_ALL, + EMPATHY_INDIVIDUAL_VIEW_FEATURE_ALL ^ EMPATHY_INDIVIDUAL_VIEW_FEATURE_PERSONA_DROP, EMPATHY_INDIVIDUAL_FEATURE_ALL); priv->butterfly_log_migration_members_changed_id = g_signal_connect ( - list_iface, "members-changed", + priv->contact_manager, "members-changed", G_CALLBACK (main_window_members_changed_cb), window); - g_object_unref (list_iface); - gtk_widget_show (GTK_WIDGET (priv->individual_view)); gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (priv->individual_view)); @@ -1644,15 +2302,21 @@ empathy_main_window_init (EmpathyMainWindow *window) GTK_WIDGET (priv->individual_view)); empathy_individual_view_set_live_search (priv->individual_view, EMPATHY_LIVE_SEARCH (priv->search_bar)); - gtk_box_pack_start (GTK_BOX (priv->main_vbox), priv->search_bar, + gtk_box_pack_start (GTK_BOX (search_vbox), priv->search_bar, FALSE, TRUE, 0); + g_signal_connect_swapped (window, "map", G_CALLBACK (gtk_widget_grab_focus), priv->individual_view); - /* TODO: Set up the TreeView Notebook */ - // have to detect when the contact list is empty (gtk_tree_model_get_iter_first) - //if we are searching display: no match found, otherwise: your contact list is empty - //hook to row-added and row removed? fire own signals when empty, not empty? + /* Connect to proper signals to check if contact list is empty or not */ + model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->individual_view)); + priv->empty = TRUE; + g_signal_connect (model, "row-inserted", + G_CALLBACK (main_window_row_inserted_cb), + window); + g_signal_connect (model, "row-deleted", + G_CALLBACK (main_window_row_deleted_cb), + window); /* Load user-defined accelerators. */ main_window_accels_load (); @@ -1660,7 +2324,16 @@ 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 (); g_signal_connect (priv->event_manager, "event-added",