From 435a811e440c3a5a79f504fa6f2850e63673f49a Mon Sep 17 00:00:00 2001 From: Jonathan Tellier Date: Wed, 26 Aug 2009 12:52:45 -0400 Subject: Asking for confirmation before losing pending accounts changes. If an account has pending changes, we now ask for a confirmation before: - Adding a new account - Closing the accounts dialog - Selecting a new account. Those are all options which discard pending changes. --- libempathy-gtk/empathy-account-widget.c | 33 ++++- libempathy-gtk/empathy-account-widget.h | 5 + libempathy-gtk/empathy-ui-utils.c | 28 ++++ libempathy-gtk/empathy-ui-utils.h | 6 + src/empathy-accounts-dialog.c | 243 +++++++++++++++++++++++++++++--- 5 files changed, 294 insertions(+), 21 deletions(-) diff --git a/libempathy-gtk/empathy-account-widget.c b/libempathy-gtk/empathy-account-widget.c index 02ca44cf3..684ff4c9c 100644 --- a/libempathy-gtk/empathy-account-widget.c +++ b/libempathy-gtk/empathy-account-widget.c @@ -61,6 +61,9 @@ typedef struct { gboolean simple; + gboolean contains_pending_changes; + gboolean original_enabled_checkbox_value; + /* An EmpathyAccountWidget can be used to either create an account or * modify it. When we are creating an account, this member is set to TRUE */ gboolean creating_account; @@ -97,6 +100,7 @@ account_widget_set_control_buttons_sensitivity (EmpathyAccountWidget *self, { gtk_widget_set_sensitive (priv->apply_button, sensitive); gtk_widget_set_sensitive (priv->cancel_button, sensitive); + priv->contains_pending_changes = sensitive; } } @@ -109,10 +113,7 @@ account_widget_handle_control_buttons_sensitivity (EmpathyAccountWidget *self) is_valid = empathy_account_settings_is_valid (priv->settings); if (!priv->simple) - { - gtk_widget_set_sensitive (priv->apply_button, is_valid); - gtk_widget_set_sensitive (priv->cancel_button, is_valid); - } + account_widget_set_control_buttons_sensitivity (self, is_valid); g_signal_emit (self, signals[HANDLE_APPLY], 0, is_valid); } @@ -1237,9 +1238,11 @@ do_constructed (GObject *obj) priv->enabled_checkbox = gtk_check_button_new_with_label (_("Enabled")); + priv->original_enabled_checkbox_value = + empathy_account_is_enabled (account); gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON (priv->enabled_checkbox), - empathy_account_is_enabled (account)); + priv->original_enabled_checkbox_value); g_object_get (priv->table_common_settings, "n-rows", &nb_rows, "n-columns", &nb_columns, NULL); @@ -1387,6 +1390,26 @@ empathy_account_widget_init (EmpathyAccountWidget *self) /* public methods */ +void +empathy_account_widget_discard_pending_changes + (EmpathyAccountWidget *widget) +{ + EmpathyAccountWidgetPriv *priv = GET_PRIV (widget); + + empathy_account_settings_discard_changes (priv->settings); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->enabled_checkbox), + priv->original_enabled_checkbox_value); + priv->contains_pending_changes = FALSE; +} + +gboolean +empathy_account_widget_contains_pending_changes (EmpathyAccountWidget *widget) +{ + EmpathyAccountWidgetPriv *priv = GET_PRIV (widget); + + return priv->contains_pending_changes; +} + void empathy_account_widget_handle_params (EmpathyAccountWidget *self, const gchar *first_widget, diff --git a/libempathy-gtk/empathy-account-widget.h b/libempathy-gtk/empathy-account-widget.h index d4111eba2..75214fac8 100644 --- a/libempathy-gtk/empathy-account-widget.h +++ b/libempathy-gtk/empathy-account-widget.h @@ -65,6 +65,11 @@ EmpathyAccountWidget * empathy_account_widget_new_for_protocol ( EmpathyAccountSettings *settings, gboolean simple); +gboolean empathy_account_widget_contains_pending_changes + (EmpathyAccountWidget *widget); +void empathy_account_widget_discard_pending_changes + (EmpathyAccountWidget *widget); + G_END_DECLS #endif /* __EMPATHY_ACCOUNT_WIDGET_H__ */ diff --git a/libempathy-gtk/empathy-ui-utils.c b/libempathy-gtk/empathy-ui-utils.c index 3089c46ea..e7b8fce74 100644 --- a/libempathy-gtk/empathy-ui-utils.c +++ b/libempathy-gtk/empathy-ui-utils.c @@ -1551,3 +1551,31 @@ empathy_receive_file_with_file_chooser (EmpathyFTHandler *handler) gtk_widget_show (widget); } + +/** empathy_show_yes_no_question_dialog: + * @parent: The parent of the message dialog + * @message: The question message + * @response_callback: The callback connected to the "response" signal of + * the message dialog. + * @user_data: User data to pass to the @response_callback. + * + * A simple utility function to create a modal yes/no question message dialog + * and hooking to its "response" signal. + */ +void empathy_show_yes_no_question_dialog (GtkWindow *parent, + gchar *message, + GCallback response_callback, + gpointer user_data) +{ + GtkWidget *message_dialog; + + message_dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_YES_NO, + message); + + g_signal_connect (message_dialog, "response", response_callback, user_data); + + gtk_widget_show (message_dialog); +} diff --git a/libempathy-gtk/empathy-ui-utils.h b/libempathy-gtk/empathy-ui-utils.h index 39baeaf41..2d54f987c 100644 --- a/libempathy-gtk/empathy-ui-utils.h +++ b/libempathy-gtk/empathy-ui-utils.h @@ -113,6 +113,12 @@ gchar * empathy_make_absolute_url (const gchar *url); gchar * empathy_make_absolute_url_len (const gchar *url, guint len); +/* Message Dialogs */ +void empathy_show_yes_no_question_dialog (GtkWindow *parent, + gchar *message, + GCallback response_callback, + gpointer user_data); + G_END_DECLS #endif /* __EMPATHY_UI_UTILS_H__ */ diff --git a/src/empathy-accounts-dialog.c b/src/empathy-accounts-dialog.c index 0a193e34e..b146396c9 100644 --- a/src/empathy-accounts-dialog.c +++ b/src/empathy-accounts-dialog.c @@ -87,6 +87,14 @@ typedef struct { GtkWidget *label_type; GtkWidget *settings_widget; + /* We have to keep a reference on the actual EmpathyAccountWidget, not just + * his GtkWidget. it is the only reliable source we can query to know if + * there are any unsaved changes to the currently selected account. We can't + * look at the account settings because it does not contain everything that + * can be changed using the EmpathyAccountWidget. For instance, it does not + * contain the state of the "Enabled" checkbox. */ + EmpathyAccountWidget *setting_widget_object; + gboolean connecting_show; guint connecting_id; @@ -98,6 +106,16 @@ typedef struct { GtkWindow *parent_window; EmpathyAccount *initial_selection; + + /* Those are needed when changing the selected row. When a user selects + * another account and there are unsaved changes on the currently selected + * one, a confirmation message box is presented to him. Since his answer + * is retrieved asynchronously, we keep some information as member of the + * EmpathyAccountsDialog object. */ + gboolean force_change_row; + gchar *destination_path; + + } EmpathyAccountsDialogPriv; enum { @@ -120,6 +138,11 @@ static void accounts_dialog_account_display_name_changed_cb ( static EmpathyAccountSettings * accounts_dialog_model_get_selected_settings ( EmpathyAccountsDialog *dialog); +static gboolean accounts_dialog_get_settings_iter ( + EmpathyAccountsDialog *dialog, + EmpathyAccountSettings *settings, + GtkTreeIter *iter); + static void accounts_dialog_model_select_first (EmpathyAccountsDialog *dialog); static void accounts_dialog_update (EmpathyAccountsDialog *dialog, @@ -163,7 +186,7 @@ empathy_account_dialog_widget_cancelled_cb (EmpathyAccountWidget *widget_object, COL_ACCOUNT_SETTINGS_POINTER, &settings, COL_ACCOUNT_POINTER, &account, -1); - empathy_account_settings_discard_changes (settings); + empathy_account_widget_discard_pending_changes (priv->setting_widget_object); if (account == NULL) { @@ -249,17 +272,13 @@ static void account_dialog_create_settings_widget (EmpathyAccountsDialog *dialog, EmpathyAccountSettings *settings) { - EmpathyAccountWidget *widget_object = NULL; EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); gchar *icon_name; - widget_object = empathy_account_widget_new_for_protocol (settings, FALSE); - priv->settings_widget = empathy_account_widget_get_widget (widget_object); - - g_signal_connect (widget_object, "account-created", + g_signal_connect (priv->setting_widget_object, "account-created", G_CALLBACK (empathy_account_dialog_account_created_cb), dialog); - g_signal_connect (widget_object, "cancelled", + g_signal_connect (priv->setting_widget_object, "cancelled", G_CALLBACK (empathy_account_dialog_widget_cancelled_cb), dialog); gtk_container_add (GTK_CONTAINER (priv->alignment_settings), @@ -311,6 +330,31 @@ accounts_dialog_model_select_first (EmpathyAccountsDialog *dialog) } } +static gboolean +accounts_dialog_has_pending_change (EmpathyAccountsDialog *dialog, + EmpathyAccount **account) +{ + gboolean has_pending_changes; + GtkTreeIter iter; + GtkTreeModel *model; + EmpathyAccountSettings *settings; + EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview)); + settings = accounts_dialog_model_get_selected_settings (dialog); + + if (accounts_dialog_get_settings_iter (dialog, settings, &iter)) + gtk_tree_model_get (model, &iter, COL_ACCOUNT_POINTER, account, -1); + + has_pending_changes = account != NULL && priv->setting_widget_object != NULL + && empathy_account_widget_contains_pending_changes ( + priv->setting_widget_object); + + g_object_unref (settings); + + return has_pending_changes; +} + static void accounts_dialog_protocol_changed_cb (GtkWidget *widget, EmpathyAccountsDialog *dialog) @@ -345,11 +389,10 @@ accounts_dialog_protocol_changed_cb (GtkWidget *widget, } static void -accounts_dialog_button_add_clicked_cb (GtkWidget *button, - EmpathyAccountsDialog *dialog) +accounts_dialog_setup_ui_to_add_account (EmpathyAccountsDialog *dialog) { - GtkTreeView *view; - GtkTreeModel *model; + GtkTreeView *view; + GtkTreeModel *model; EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); view = GTK_TREE_VIEW (priv->treeview); @@ -373,6 +416,54 @@ accounts_dialog_button_add_clicked_cb (GtkWidget *button, gtk_widget_grab_focus (priv->combobox_protocol); } +static void +accounts_dialog_add_pending_changes_response_cb (GtkDialog *message_dialog, + gint response_id, + gpointer *user_data) +{ + EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data); + EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); + + gtk_widget_destroy (GTK_WIDGET (message_dialog)); + + if (response_id == GTK_RESPONSE_YES) + { + empathy_account_widget_discard_pending_changes ( + priv->setting_widget_object); + accounts_dialog_setup_ui_to_add_account (dialog); + } +} + +static void +accounts_dialog_button_add_clicked_cb (GtkWidget *button, + EmpathyAccountsDialog *dialog) +{ + EmpathyAccount *account; + EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); + + if (accounts_dialog_has_pending_change (dialog, &account)) + { + gchar *message; + + message = g_strdup_printf ( + _("There are unsaved modification regarding your %s account.\n" + "You are about to create a new account, which will discard\n" + "your changes. Are you sure you want to proceed?"), + empathy_account_get_display_name (account)); + + empathy_show_yes_no_question_dialog (GTK_WINDOW (priv->window), + message, + G_CALLBACK (accounts_dialog_add_pending_changes_response_cb), + dialog); + + g_free (message); + } + else + { + accounts_dialog_setup_ui_to_add_account (dialog); + } +} + static void accounts_dialog_update_settings (EmpathyAccountsDialog *dialog, EmpathyAccountSettings *settings) @@ -761,16 +852,103 @@ accounts_dialog_model_selection_changed (GtkTreeSelection *selection, is_selection = gtk_tree_selection_get_selected (selection, &model, &iter); settings = accounts_dialog_model_get_selected_settings (dialog); - - if (settings != NULL) - empathy_account_settings_discard_changes (settings); - accounts_dialog_update_settings (dialog, settings); if (settings != NULL) g_object_unref (settings); } +static void +accounts_dialog_selection_change_response_cb (GtkDialog *message_dialog, + gint response_id, + gpointer *user_data) +{ + EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (user_data); + EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); + + gtk_widget_destroy (GTK_WIDGET (message_dialog)); + + if (response_id == GTK_RESPONSE_YES) + { + /* The user wants to lose unsaved changes to the currently selected + * account and select another account. We discard the changes and + * select the other account. */ + GtkTreeIter iter; + GtkTreeSelection *selection; + GtkTreeModel *model; + + priv->force_change_row = TRUE; + empathy_account_widget_discard_pending_changes ( + priv->setting_widget_object); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->treeview)); + selection = gtk_tree_view_get_selection ( + GTK_TREE_VIEW (priv->treeview)); + + if (gtk_tree_model_get_iter_from_string (model, + &iter, priv->destination_path)) + { + /* This will trigger a call to + * accounts_dialog_account_selection_change() */ + gtk_tree_selection_select_iter (selection, &iter); + } + } + else + { + priv->force_change_row = FALSE; + } +} + +static gboolean +accounts_dialog_account_selection_change (GtkTreeSelection *selection, + GtkTreeModel *model, + GtkTreePath *path, + gboolean path_currently_selected, + gpointer data) +{ + g_message ("path_currently_selected: %d - path: %s", path_currently_selected, gtk_tree_path_to_string (path)); + + EmpathyAccount *account; + EmpathyAccountsDialog *dialog = EMPATHY_ACCOUNTS_DIALOG (data); + EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); + + if (priv->force_change_row) + { + /* We came back here because the user wants to discard changes to his + * modified account. The changes have already been discarded so we + * just change the selected row. */ + priv->force_change_row = FALSE; + return TRUE; + } + + if (accounts_dialog_has_pending_change (dialog, &account)) + { + /* The currently selected account has some unsaved changes. We ask + * the user if he really wants to lose his changes and select another + * account */ + priv->destination_path = gtk_tree_path_to_string (path); + gchar *message; + + message = g_strdup_printf ( + _("There are unsaved modification regarding your %s account.\n" + "You are about to select another account, which will discard\n" + "your changes. Are you sure you want to proceed?"), + empathy_account_get_display_name (account)); + + empathy_show_yes_no_question_dialog (GTK_WINDOW (priv->window), + message, G_CALLBACK (accounts_dialog_selection_change_response_cb), + dialog); + + g_free (message); + } + else + { + return TRUE; + } + + return FALSE; +} + static void accounts_dialog_model_setup (EmpathyAccountsDialog *dialog) { @@ -789,6 +967,8 @@ accounts_dialog_model_setup (EmpathyAccountsDialog *dialog) selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview)); gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); + gtk_tree_selection_set_select_function (selection, + accounts_dialog_account_selection_change, dialog, NULL); g_signal_connect (selection, "changed", G_CALLBACK (accounts_dialog_model_selection_changed), @@ -1211,12 +1391,43 @@ accounts_dialog_button_help_clicked_cb (GtkWidget *button, empathy_url_show (button, "ghelp:empathy?accounts-window"); } +static void +accounts_dialog_close_response_cb (GtkDialog *message_dialog, + gint response_id, + gpointer user_data) +{ + GtkWidget *account_dialog = GTK_WIDGET (user_data); + + gtk_widget_destroy (GTK_WIDGET (message_dialog)); + + if (response_id == GTK_RESPONSE_YES) + gtk_widget_destroy (account_dialog); +} + static void accounts_dialog_response_cb (GtkWidget *widget, gint response, EmpathyAccountsDialog *dialog) { - if (response == GTK_RESPONSE_CLOSE) + EmpathyAccount *account; + EmpathyAccountsDialogPriv *priv = GET_PRIV (dialog); + + if (accounts_dialog_has_pending_change (dialog, &account)) + { + gchar *message; + + message = g_strdup_printf ( + _("There are unsaved modifications regarding your %s account.\n" + "Are you sure you want to close the window? "), + empathy_account_get_display_name (account)); + + empathy_show_yes_no_question_dialog (GTK_WINDOW (priv->window), + message, G_CALLBACK (accounts_dialog_close_response_cb), + widget); + + g_free (message); + } + else if (response == GTK_RESPONSE_CLOSE) gtk_widget_destroy (widget); } -- cgit v1.2.3