aboutsummaryrefslogtreecommitdiffstats
path: root/mail
diff options
context:
space:
mode:
authorRodrigo Moya <rodrigo@gnome-db.org>2011-10-17 22:58:54 +0800
committerRodrigo Moya <rodrigo@gnome-db.org>2011-10-17 22:58:54 +0800
commit6ad98be88e4597f0311d746a4d872d4006096de4 (patch)
treec5a87c7ad1e916c02f17340d86c4da66af511091 /mail
parent27cea70f2b693223db47cdb974a9f1871a8ff4fb (diff)
parentfdae5a3fc42610e5d9d7123a92b6893d5092ebbe (diff)
downloadgsoc2013-evolution-6ad98be88e4597f0311d746a4d872d4006096de4.tar.gz
gsoc2013-evolution-6ad98be88e4597f0311d746a4d872d4006096de4.tar.zst
gsoc2013-evolution-6ad98be88e4597f0311d746a4d872d4006096de4.zip
Merge branch 'master' into wip/gsettings
Diffstat (limited to 'mail')
-rw-r--r--mail/e-mail-session.c120
-rw-r--r--mail/em-subscription-editor.c675
2 files changed, 708 insertions, 87 deletions
diff --git a/mail/e-mail-session.c b/mail/e-mail-session.c
index 0687d7561f..c14bdd56d8 100644
--- a/mail/e-mail-session.c
+++ b/mail/e-mail-session.c
@@ -1096,6 +1096,125 @@ mail_session_get_socks_proxy (CamelSession *session,
g_free (uri);
}
+static gboolean
+mail_session_authenticate_sync (CamelSession *session,
+ CamelService *service,
+ const gchar *mechanism,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelServiceAuthType *authtype = NULL;
+ CamelAuthenticationResult result;
+ CamelProvider *provider;
+ CamelURL *url;
+ const gchar *password;
+ guint32 password_flags;
+ GError *local_error = NULL;
+
+ /* Do not chain up. Camel's default method is only an example for
+ * subclasses to follow. Instead we mimic most of its logic here. */
+
+ url = camel_service_get_camel_url (service);
+ provider = camel_service_get_provider (service);
+
+ /* APOP is one case where a non-SASL mechanism name is passed, so
+ * don't bail if the CamelServiceAuthType struct comes back NULL. */
+ if (mechanism != NULL)
+ authtype = camel_sasl_authtype (mechanism);
+
+ /* If the SASL mechanism does not involve a user
+ * password, then it gets one shot to authenticate. */
+ if (authtype != NULL && !authtype->need_password) {
+ result = camel_service_authenticate_sync (
+ service, mechanism, cancellable, error);
+ if (result == CAMEL_AUTHENTICATION_REJECTED)
+ g_set_error (
+ error, CAMEL_SERVICE_ERROR,
+ CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
+ _("%s authentication failed"), mechanism);
+ return (result == CAMEL_AUTHENTICATION_ACCEPTED);
+ }
+
+ /* Some SASL mechanisms can attempt to authenticate without a
+ * user password being provided (e.g. single-sign-on credentials),
+ * but can fall back to a user password. Handle that case next. */
+ if (mechanism != NULL) {
+ CamelProvider *provider;
+ CamelSasl *sasl;
+ const gchar *service_name;
+ gboolean success = FALSE;
+
+ provider = camel_service_get_provider (service);
+ service_name = provider->protocol;
+
+ /* XXX Would be nice if camel_sasl_try_empty_password_sync()
+ * returned CamelAuthenticationResult so it's easier to
+ * detect errors. */
+ sasl = camel_sasl_new (service_name, mechanism, service);
+ if (sasl != NULL) {
+ success = camel_sasl_try_empty_password_sync (
+ sasl, cancellable, &local_error);
+ g_object_unref (sasl);
+ }
+
+ if (success)
+ return TRUE;
+ }
+
+ /* Abort authentication if we got cancelled.
+ * Otherwise clear any errors and press on. */
+ if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return FALSE;
+
+ g_clear_error (&local_error);
+
+ password_flags = CAMEL_SESSION_PASSWORD_SECRET;
+
+retry:
+ password = camel_service_get_password (service);
+
+ if (password == NULL) {
+ gchar *prompt;
+ gchar *new_passwd;
+
+ prompt = camel_session_build_password_prompt (
+ provider->name, url->user, url->host);
+
+ new_passwd = camel_session_get_password (
+ session, service, prompt, "password",
+ password_flags, &local_error);
+ camel_service_set_password (service, new_passwd);
+ password = camel_service_get_password (service);
+ g_free (new_passwd);
+
+ g_free (prompt);
+
+ if (local_error != NULL) {
+ g_propagate_error (error, local_error);
+ return FALSE;
+ }
+
+ if (password == NULL) {
+ g_set_error (
+ error, CAMEL_SERVICE_ERROR,
+ CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
+ _("No password was provided"));
+ return FALSE;
+ }
+ }
+
+ result = camel_service_authenticate_sync (
+ service, mechanism, cancellable, error);
+
+ if (result == CAMEL_AUTHENTICATION_REJECTED) {
+ password_flags |= CAMEL_SESSION_PASSWORD_REPROMPT;
+ camel_service_set_password (service, NULL);
+ goto retry;
+ }
+
+ return (result == CAMEL_AUTHENTICATION_ACCEPTED);
+}
+
static void
e_mail_session_class_init (EMailSessionClass *class)
{
@@ -1121,6 +1240,7 @@ e_mail_session_class_init (EMailSessionClass *class)
session_class->lookup_addressbook = mail_session_lookup_addressbook;
session_class->forward_to = mail_session_forward_to;
session_class->get_socks_proxy = mail_session_get_socks_proxy;
+ session_class->authenticate_sync = mail_session_authenticate_sync;
g_object_class_install_property (
object_class,
diff --git a/mail/em-subscription-editor.c b/mail/em-subscription-editor.c
index 09b74bcb3c..d92713f8be 100644
--- a/mail/em-subscription-editor.c
+++ b/mail/em-subscription-editor.c
@@ -48,6 +48,7 @@
((obj), EM_TYPE_SUBSCRIPTION_EDITOR, EMSubscriptionEditorPrivate))
typedef struct _AsyncContext AsyncContext;
+typedef struct _AsyncData AsyncData;
typedef struct _StoreData StoreData;
struct _EMSubscriptionEditorPrivate {
@@ -58,7 +59,9 @@ struct _EMSubscriptionEditorPrivate {
GtkWidget *entry; /* not referenced */
GtkWidget *notebook; /* not referenced */
GtkWidget *subscribe_button; /* not referenced */
+ GtkWidget *subscribe_arrow; /* not referenced */
GtkWidget *unsubscribe_button; /* not referenced */
+ GtkWidget *unsubscribe_arrow; /* not referenced */
GtkWidget *collapse_all_button; /* not referenced */
GtkWidget *expand_all_button; /* not referenced */
GtkWidget *refresh_button; /* not referenced */
@@ -76,12 +79,16 @@ struct _EMSubscriptionEditorPrivate {
guint timeout_id;
};
-struct _AsyncContext {
- EMSubscriptionEditor *editor;
+struct _AsyncData {
CamelFolderInfo *folder_info;
GtkTreeRowReference *reference;
};
+struct _AsyncContext {
+ EMSubscriptionEditor *editor;
+ GSList *async_datas; /* newly allocated AsyncData structures */
+};
+
struct _StoreData {
CamelStore *store;
GtkTreeView *tree_view;
@@ -110,10 +117,21 @@ enum {
G_DEFINE_TYPE (EMSubscriptionEditor, em_subscription_editor, GTK_TYPE_DIALOG)
static void
+async_data_free (AsyncData *data)
+{
+ g_return_if_fail (data != NULL);
+
+ gtk_tree_row_reference_free (data->reference);
+ g_free (data);
+}
+
+static void
async_context_free (AsyncContext *context)
{
g_object_unref (context->editor);
- gtk_tree_row_reference_free (context->reference);
+
+ g_slist_foreach (context->async_datas, (GFunc) async_data_free, NULL);
+ g_slist_free (context->async_datas);
g_slice_free (AsyncContext, context);
}
@@ -293,6 +311,7 @@ subscription_editor_subscribe_folder_done (CamelSubscribable *subscribable,
GtkTreeIter iter;
GdkWindow *window;
GError *error = NULL;
+ AsyncData *async_data;
camel_subscribable_subscribe_folder_finish (
subscribable, result, &error);
@@ -303,14 +322,41 @@ subscription_editor_subscribe_folder_done (CamelSubscribable *subscribable,
goto exit;
}
+ async_data = context->async_datas->data;
+ context->async_datas = g_slist_remove (context->async_datas, async_data);
+
/* XXX Do something smarter with errors. */
if (error == NULL)
- context->folder_info->flags |= CAMEL_FOLDER_SUBSCRIBED;
+ async_data->folder_info->flags |= CAMEL_FOLDER_SUBSCRIBED;
else {
g_warning ("%s", error->message);
g_error_free (error);
+ async_data_free (async_data);
+ goto exit;
+ }
+
+ /* Update the toggle renderer in the selected row. */
+ tree_model = gtk_tree_row_reference_get_model (async_data->reference);
+ path = gtk_tree_row_reference_get_path (async_data->reference);
+ gtk_tree_model_get_iter (tree_model, &iter, path);
+ gtk_tree_model_row_changed (tree_model, path, &iter);
+ gtk_tree_path_free (path);
+
+ async_data_free (async_data);
+
+ if (context->async_datas) {
+ /* continue with the next to subscribe */
+ async_data = context->async_datas->data;
+
+ camel_subscribable_subscribe_folder (
+ subscribable,
+ async_data->folder_info->full_name, G_PRIORITY_DEFAULT,
+ context->editor->priv->active->cancellable, (GAsyncReadyCallback)
+ subscription_editor_subscribe_folder_done, context);
+ return;
}
+ exit:
gtk_widget_set_sensitive (context->editor->priv->notebook, TRUE);
gtk_widget_set_sensitive (context->editor->priv->refresh_button, TRUE);
gtk_widget_set_sensitive (context->editor->priv->stop_button, FALSE);
@@ -323,15 +369,60 @@ subscription_editor_subscribe_folder_done (CamelSubscribable *subscribable,
selection = gtk_tree_view_get_selection (tree_view);
g_signal_emit_by_name (selection, "changed");
- /* Update the toggle renderer in the selected row. */
- tree_model = gtk_tree_row_reference_get_model (context->reference);
- path = gtk_tree_row_reference_get_path (context->reference);
- gtk_tree_model_get_iter (tree_model, &iter, path);
- gtk_tree_model_row_changed (tree_model, path, &iter);
- gtk_tree_path_free (path);
-
-exit:
async_context_free (context);
+
+ gtk_widget_grab_focus (GTK_WIDGET (tree_view));
+}
+
+
+static void
+subscription_editor_subscribe_many (EMSubscriptionEditor *editor,
+ GSList *async_datas)
+{
+ AsyncData *async_data;
+ AsyncContext *context;
+ CamelStore *active_store;
+ GdkCursor *cursor;
+ GdkWindow *window;
+
+ g_return_if_fail (editor != NULL);
+
+ if (!async_datas)
+ return;
+
+ async_data = async_datas->data;
+ g_return_if_fail (async_data != NULL);
+
+ /* Cancel any operation on this store still in progress. */
+ gtk_button_clicked (GTK_BUTTON (editor->priv->stop_button));
+
+ /* Start a new 'subscription' operation. */
+ editor->priv->active->cancellable = g_cancellable_new ();
+
+ gtk_widget_set_sensitive (editor->priv->notebook, FALSE);
+ gtk_widget_set_sensitive (editor->priv->subscribe_button, FALSE);
+ gtk_widget_set_sensitive (editor->priv->subscribe_arrow, FALSE);
+ gtk_widget_set_sensitive (editor->priv->unsubscribe_button, FALSE);
+ gtk_widget_set_sensitive (editor->priv->unsubscribe_arrow, FALSE);
+ gtk_widget_set_sensitive (editor->priv->refresh_button, FALSE);
+ gtk_widget_set_sensitive (editor->priv->stop_button, TRUE);
+
+ cursor = gdk_cursor_new (GDK_WATCH);
+ window = gtk_widget_get_window (GTK_WIDGET (editor));
+ gdk_window_set_cursor (window, cursor);
+ g_object_unref (cursor);
+
+ context = g_slice_new0 (AsyncContext);
+ context->editor = g_object_ref (editor);
+ context->async_datas = async_datas; /* takes ownership of the pointer */
+
+ active_store = editor->priv->active->store;
+
+ camel_subscribable_subscribe_folder (
+ CAMEL_SUBSCRIBABLE (active_store),
+ async_data->folder_info->full_name, G_PRIORITY_DEFAULT,
+ context->editor->priv->active->cancellable, (GAsyncReadyCallback)
+ subscription_editor_subscribe_folder_done, context);
}
static void
@@ -346,6 +437,7 @@ subscription_editor_unsubscribe_folder_done (CamelSubscribable *subscribable,
GtkTreeIter iter;
GdkWindow *window;
GError *error = NULL;
+ AsyncData *async_data;
camel_subscribable_unsubscribe_folder_finish (
subscribable, result, &error);
@@ -356,14 +448,40 @@ subscription_editor_unsubscribe_folder_done (CamelSubscribable *subscribable,
goto exit;
}
+ async_data = context->async_datas->data;
+ context->async_datas = g_slist_remove (context->async_datas, async_data);
+
/* XXX Do something smarter with errors. */
if (error == NULL)
- context->folder_info->flags &= ~CAMEL_FOLDER_SUBSCRIBED;
+ async_data->folder_info->flags &= ~CAMEL_FOLDER_SUBSCRIBED;
else {
g_warning ("%s", error->message);
g_error_free (error);
+ async_data_free (async_data);
+ goto exit;
}
+ /* Update the toggle renderer in the selected row. */
+ tree_model = gtk_tree_row_reference_get_model (async_data->reference);
+ path = gtk_tree_row_reference_get_path (async_data->reference);
+ gtk_tree_model_get_iter (tree_model, &iter, path);
+ gtk_tree_model_row_changed (tree_model, path, &iter);
+ gtk_tree_path_free (path);
+
+ async_data_free (async_data);
+
+ if (context->async_datas) {
+ /* continue with the next to unsubscribe */
+ async_data = context->async_datas->data;
+
+ camel_subscribable_unsubscribe_folder (
+ subscribable,
+ async_data->folder_info->full_name, G_PRIORITY_DEFAULT,
+ context->editor->priv->active->cancellable, (GAsyncReadyCallback)
+ subscription_editor_unsubscribe_folder_done, context);
+ return;
+ }
+ exit:
gtk_widget_set_sensitive (context->editor->priv->notebook, TRUE);
gtk_widget_set_sensitive (context->editor->priv->refresh_button, TRUE);
gtk_widget_set_sensitive (context->editor->priv->stop_button, FALSE);
@@ -376,39 +494,29 @@ subscription_editor_unsubscribe_folder_done (CamelSubscribable *subscribable,
selection = gtk_tree_view_get_selection (tree_view);
g_signal_emit_by_name (selection, "changed");
- /* Update the toggle renderer in the selected row. */
- tree_model = gtk_tree_row_reference_get_model (context->reference);
- path = gtk_tree_row_reference_get_path (context->reference);
- gtk_tree_model_get_iter (tree_model, &iter, path);
- gtk_tree_model_row_changed (tree_model, path, &iter);
- gtk_tree_path_free (path);
-
-exit:
async_context_free (context);
+
+ gtk_widget_grab_focus (GTK_WIDGET (tree_view));
}
+
static void
-subscription_editor_subscribe (EMSubscriptionEditor *editor)
+subscription_editor_unsubscribe_many (EMSubscriptionEditor *editor,
+ GSList *async_datas)
{
+ AsyncData *async_data;
+ AsyncContext *context;
CamelStore *active_store;
- CamelFolderInfo *folder_info;
- GtkTreeRowReference *reference;
- GtkTreeSelection *selection;
- GtkTreeModel *tree_model;
- GtkTreeView *tree_view;
- GtkTreePath *path;
- GtkTreeIter iter;
GdkCursor *cursor;
GdkWindow *window;
- AsyncContext *context;
- gboolean have_selection;
- tree_view = editor->priv->active->tree_view;
- selection = gtk_tree_view_get_selection (tree_view);
+ g_return_if_fail (editor != NULL);
- have_selection = gtk_tree_selection_get_selected (
- selection, &tree_model, &iter);
- g_return_if_fail (have_selection);
+ if (!async_datas)
+ return;
+
+ async_data = async_datas->data;
+ g_return_if_fail (async_data != NULL);
/* Cancel any operation on this store still in progress. */
gtk_button_clicked (GTK_BUTTON (editor->priv->stop_button));
@@ -418,7 +526,9 @@ subscription_editor_subscribe (EMSubscriptionEditor *editor)
gtk_widget_set_sensitive (editor->priv->notebook, FALSE);
gtk_widget_set_sensitive (editor->priv->subscribe_button, FALSE);
+ gtk_widget_set_sensitive (editor->priv->subscribe_arrow, FALSE);
gtk_widget_set_sensitive (editor->priv->unsubscribe_button, FALSE);
+ gtk_widget_set_sensitive (editor->priv->unsubscribe_arrow, FALSE);
gtk_widget_set_sensitive (editor->priv->refresh_button, FALSE);
gtk_widget_set_sensitive (editor->priv->stop_button, TRUE);
@@ -427,42 +537,252 @@ subscription_editor_subscribe (EMSubscriptionEditor *editor)
gdk_window_set_cursor (window, cursor);
g_object_unref (cursor);
- gtk_tree_model_get (
- tree_model, &iter, COL_FOLDER_INFO, &folder_info, -1);
-
- path = gtk_tree_model_get_path (tree_model, &iter);
- reference = gtk_tree_row_reference_new (tree_model, path);
- gtk_tree_path_free (path);
-
context = g_slice_new0 (AsyncContext);
context->editor = g_object_ref (editor);
- context->folder_info = folder_info;
- context->reference = reference;
+ context->async_datas = async_datas; /* takes ownership of the pointer */
active_store = editor->priv->active->store;
- camel_subscribable_subscribe_folder (
+ camel_subscribable_unsubscribe_folder (
CAMEL_SUBSCRIBABLE (active_store),
- folder_info->full_name, G_PRIORITY_DEFAULT,
+ async_data->folder_info->full_name, G_PRIORITY_DEFAULT,
editor->priv->active->cancellable, (GAsyncReadyCallback)
- subscription_editor_subscribe_folder_done, context);
+ subscription_editor_unsubscribe_folder_done, context);
+}
+
+static GtkWidget *
+subscription_editor_create_menu_item (const gchar *label,
+ gboolean sensitive,
+ GCallback activate_cb,
+ EMSubscriptionEditor *editor)
+{
+ GtkWidget *item;
+
+ item = gtk_menu_item_new_with_mnemonic (label);
+ gtk_widget_set_sensitive (item, sensitive);
+
+ gtk_widget_show (item);
+
+ g_signal_connect_swapped (item, "activate", activate_cb, editor);
+
+ return item;
}
static void
-subscription_editor_unsubscribe (EMSubscriptionEditor *editor)
+position_below_widget_cb (GtkMenu *menu,
+ gint *x,
+ gint *y,
+ gboolean *push_in,
+ gpointer under_widget)
{
- CamelStore *active_store;
- CamelFolderInfo *folder_info;
+ GtkRequisition menu_requisition;
+ GtkTextDirection direction;
+ GtkAllocation allocation;
+ GdkRectangle monitor;
+ GdkScreen *screen;
+ GdkWindow *window;
+ GtkWidget *widget;
+ gint monitor_num;
+
+ widget = under_widget;
+ gtk_widget_get_preferred_size (GTK_WIDGET (menu), &menu_requisition, NULL);
+
+ window = gtk_widget_get_parent_window (widget);
+ screen = gtk_widget_get_screen (GTK_WIDGET (menu));
+ monitor_num = gdk_screen_get_monitor_at_window (screen, window);
+ if (monitor_num < 0)
+ monitor_num = 0;
+ gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ gdk_window_get_origin (window, x, y);
+ *x += allocation.x;
+ *y += allocation.y + 2 + gtk_widget_get_allocated_height (under_widget);
+
+ direction = gtk_widget_get_direction (widget);
+ if (direction == GTK_TEXT_DIR_LTR)
+ *x += MAX (allocation.width - menu_requisition.width, 0);
+ else if (menu_requisition.width > allocation.width)
+ *x -= menu_requisition.width - allocation.width;
+
+ *push_in = FALSE;
+}
+
+static AsyncData *
+subscription_editor_async_data_from_iter (GtkTreeView *tree_view,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gboolean *is_expanded)
+{
+ AsyncData *data;
+ CamelFolderInfo *folder_info = NULL;
GtkTreeRowReference *reference;
+ GtkTreePath *path;
+
+ g_return_val_if_fail (tree_view != NULL, NULL);
+ g_return_val_if_fail (model != NULL, NULL);
+ g_return_val_if_fail (iter != NULL, NULL);
+
+ gtk_tree_model_get (
+ model, iter, COL_FOLDER_INFO, &folder_info, -1);
+
+ if (!FOLDER_CAN_SELECT (folder_info))
+ return NULL;
+
+ path = gtk_tree_model_get_path (model, iter);
+ reference = gtk_tree_row_reference_new (model, path);
+ if (is_expanded)
+ *is_expanded = gtk_tree_view_row_expanded (tree_view, path);
+ gtk_tree_path_free (path);
+
+ data = g_new0 (AsyncData, 1);
+ data->folder_info = folder_info;
+ data->reference = reference;
+
+ return data;
+}
+
+typedef enum {
+ PICK_ALL,
+ PICK_SUBSCRIBED,
+ PICK_UNSUBSCRIBED
+} EPickMode;
+
+static gboolean
+can_pick_folder_info (CamelFolderInfo *fi, EPickMode mode)
+{
+ if (!FOLDER_CAN_SELECT (fi))
+ return FALSE;
+
+ if (mode == PICK_ALL)
+ return TRUE;
+
+ return (FOLDER_SUBSCRIBED (fi) ? 1 : 0) == (mode == PICK_SUBSCRIBED ? 1 : 0);
+}
+
+struct PickAllData {
+ GtkTreeView *tree_view;
+ EPickMode mode;
+ GHashTable *skip_folder_infos;
+ GSList *async_datas;
+};
+
+static gboolean
+pick_all_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ struct PickAllData *data = user_data;
+ AsyncData *async_data;
+
+ g_return_val_if_fail (model != NULL, TRUE);
+ g_return_val_if_fail (data != NULL, TRUE);
+ g_return_val_if_fail (data->tree_view != NULL, TRUE);
+
+ async_data = subscription_editor_async_data_from_iter (data->tree_view, model, iter, NULL);
+ if (!async_data)
+ return FALSE;
+
+ if (can_pick_folder_info (async_data->folder_info, data->mode) &&
+ (data->skip_folder_infos == NULL ||
+ !g_hash_table_lookup_extended (data->skip_folder_infos, async_data->folder_info, NULL, NULL))) {
+ data->async_datas = g_slist_prepend (data->async_datas, async_data);
+ } else
+ async_data_free (async_data);
+
+ return FALSE;
+}
+
+/* skip_folder_infos contains CamelFolderInfo-s to skip;
+ these should come from the tree view; can be NULL
+ to include everything.
+*/
+static GSList *
+subscription_editor_pick_all (EMSubscriptionEditor *editor,
+ EPickMode mode,
+ GHashTable *skip_folder_infos)
+{
+ GtkTreeView *tree_view;
+ GtkTreeModel *tree_model;
+ struct PickAllData data;
+
+ tree_view = editor->priv->active->tree_view;
+ tree_model = gtk_tree_view_get_model (tree_view);
+
+ data.tree_view = tree_view;
+ data.mode = mode;
+ data.skip_folder_infos = skip_folder_infos;
+ data.async_datas = NULL;
+
+ gtk_tree_model_foreach (tree_model, pick_all_cb, &data);
+
+ return data.async_datas;
+}
+
+static GSList *
+subscription_editor_pick_shown (EMSubscriptionEditor *editor,
+ EPickMode mode)
+{
+ GtkTreeView *tree_view;
+ GtkTreeModel *tree_model;
+ GtkTreeIter iter, iter2;
+ GSList *async_datas = NULL;
+ gboolean found;
+
+ tree_view = editor->priv->active->tree_view;
+ tree_model = gtk_tree_view_get_model (tree_view);
+
+ if (!gtk_tree_model_get_iter_first (tree_model, &iter))
+ return NULL;
+
+ found = TRUE;
+ while (found) {
+ AsyncData *async_data;
+ gboolean is_expanded = FALSE;
+
+ found = FALSE;
+ async_data = subscription_editor_async_data_from_iter (tree_view, tree_model, &iter, &is_expanded);
+
+ if (async_data && can_pick_folder_info (async_data->folder_info, mode)) {
+ async_datas = g_slist_prepend (async_datas, async_data);
+ } else if (async_data) {
+ async_data_free (async_data);
+ }
+
+ if (is_expanded && gtk_tree_model_iter_children (tree_model, &iter2, &iter)) {
+ iter = iter2;
+ found = TRUE;
+ } else {
+ iter2 = iter;
+ if (gtk_tree_model_iter_next (tree_model, &iter2)) {
+ iter = iter2;
+ found = TRUE;
+ } else {
+ while (found = gtk_tree_model_iter_parent (tree_model, &iter2, &iter), found) {
+ iter = iter2;
+ if (gtk_tree_model_iter_next (tree_model, &iter2)) {
+ iter = iter2;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return async_datas;
+}
+
+static void
+subscription_editor_subscribe (EMSubscriptionEditor *editor)
+{
GtkTreeSelection *selection;
GtkTreeModel *tree_model;
GtkTreeView *tree_view;
- GtkTreePath *path;
GtkTreeIter iter;
- GdkCursor *cursor;
- GdkWindow *window;
- AsyncContext *context;
gboolean have_selection;
+ GSList *async_datas;
tree_view = editor->priv->active->tree_view;
selection = gtk_tree_view_get_selection (tree_view);
@@ -471,42 +791,167 @@ subscription_editor_unsubscribe (EMSubscriptionEditor *editor)
selection, &tree_model, &iter);
g_return_if_fail (have_selection);
- /* Cancel any operation on this store still in progress. */
- gtk_button_clicked (GTK_BUTTON (editor->priv->stop_button));
+ async_datas = g_slist_append (NULL,
+ subscription_editor_async_data_from_iter (tree_view, tree_model, &iter, NULL));
- /* Start a new 'unsubscription' operation. */
- editor->priv->active->cancellable = g_cancellable_new ();
+ subscription_editor_subscribe_many (editor, async_datas);
+}
- gtk_widget_set_sensitive (editor->priv->notebook, FALSE);
- gtk_widget_set_sensitive (editor->priv->subscribe_button, FALSE);
- gtk_widget_set_sensitive (editor->priv->unsubscribe_button, FALSE);
- gtk_widget_set_sensitive (editor->priv->refresh_button, FALSE);
- gtk_widget_set_sensitive (editor->priv->stop_button, TRUE);
+static void
+subscription_editor_subscribe_shown (EMSubscriptionEditor *editor)
+{
+ subscription_editor_subscribe_many (editor,
+ subscription_editor_pick_shown (editor, PICK_UNSUBSCRIBED));
+}
- cursor = gdk_cursor_new (GDK_WATCH);
- window = gtk_widget_get_window (GTK_WIDGET (editor));
- gdk_window_set_cursor (window, cursor);
- g_object_unref (cursor);
+static void
+subscription_editor_subscribe_all (EMSubscriptionEditor *editor)
+{
+ subscription_editor_subscribe_many (editor,
+ subscription_editor_pick_all (editor, PICK_UNSUBSCRIBED, NULL));
+}
- gtk_tree_model_get (
- tree_model, &iter, COL_FOLDER_INFO, &folder_info, -1);
+static void
+subscription_editor_subscribe_popup_cb (EMSubscriptionEditor *editor)
+{
+ GtkWidget *menu;
+ GtkTreeIter iter;
+ gboolean tree_filled;
+
+ tree_filled = editor->priv->active &&
+ gtk_tree_model_get_iter_first (
+ editor->priv->active->filtered_view
+ ? editor->priv->active->list_store
+ : editor->priv->active->tree_store,
+ &iter);
+
+ menu = gtk_menu_new ();
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+ subscription_editor_create_menu_item (
+ _("_Subscribe"),
+ gtk_widget_get_sensitive (editor->priv->subscribe_button),
+ G_CALLBACK (subscription_editor_subscribe),
+ editor));
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+ subscription_editor_create_menu_item (
+ _("Su_bscribe to shown"),
+ tree_filled,
+ G_CALLBACK (subscription_editor_subscribe_shown),
+ editor));
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+ subscription_editor_create_menu_item (
+ _("Subscribe to _all"),
+ tree_filled,
+ G_CALLBACK (subscription_editor_subscribe_all),
+ editor));
+
+ gtk_menu_popup (
+ GTK_MENU (menu), NULL, NULL, position_below_widget_cb, editor->priv->subscribe_button,
+ 0, gtk_get_current_event_time ());
+}
- path = gtk_tree_model_get_path (tree_model, &iter);
- reference = gtk_tree_row_reference_new (tree_model, path);
- gtk_tree_path_free (path);
+static void
+subscription_editor_unsubscribe_hidden (EMSubscriptionEditor *editor)
+{
+ GSList *all_shown, *ll;
+ GHashTable *skip_shown;
- context = g_slice_new0 (AsyncContext);
- context->editor = g_object_ref (editor);
- context->folder_info = folder_info;
- context->reference = reference;
+ all_shown = subscription_editor_pick_shown (editor, PICK_ALL);
+ g_return_if_fail (all_shown != NULL);
- active_store = editor->priv->active->store;
+ skip_shown = g_hash_table_new (g_direct_hash, g_direct_equal);
- camel_subscribable_unsubscribe_folder (
- CAMEL_SUBSCRIBABLE (active_store),
- folder_info->full_name, G_PRIORITY_DEFAULT,
- editor->priv->active->cancellable, (GAsyncReadyCallback)
- subscription_editor_unsubscribe_folder_done, context);
+ for (ll = all_shown; ll; ll = ll->next) {
+ AsyncData *data = ll->data;
+
+ if (!data)
+ continue;
+
+ g_hash_table_insert (skip_shown, data->folder_info, GINT_TO_POINTER (1));
+ async_data_free (data);
+ }
+
+ g_slist_free (all_shown);
+
+ subscription_editor_unsubscribe_many (editor,
+ subscription_editor_pick_all (editor, PICK_SUBSCRIBED, skip_shown));
+
+ g_hash_table_destroy (skip_shown);
+}
+
+static void
+subscription_editor_unsubscribe_all (EMSubscriptionEditor *editor)
+{
+ subscription_editor_unsubscribe_many (editor,
+ subscription_editor_pick_all (editor, PICK_SUBSCRIBED, NULL));
+}
+
+static void
+subscription_editor_unsubscribe (EMSubscriptionEditor *editor)
+{
+ GtkTreeSelection *selection;
+ GtkTreeModel *tree_model;
+ GtkTreeView *tree_view;
+ GtkTreeIter iter;
+ gboolean have_selection;
+ GSList *async_datas;
+
+ tree_view = editor->priv->active->tree_view;
+ selection = gtk_tree_view_get_selection (tree_view);
+
+ have_selection = gtk_tree_selection_get_selected (
+ selection, &tree_model, &iter);
+ g_return_if_fail (have_selection);
+
+ async_datas = g_slist_append (NULL,
+ subscription_editor_async_data_from_iter (tree_view, tree_model, &iter, NULL));
+
+ subscription_editor_unsubscribe_many (editor, async_datas);
+}
+
+static void
+subscription_editor_unsubscribe_popup_cb (EMSubscriptionEditor *editor)
+{
+ GtkWidget *menu;
+ GtkTreeIter iter;
+ gboolean tree_filled;
+
+ tree_filled = editor->priv->active &&
+ gtk_tree_model_get_iter_first (
+ editor->priv->active->filtered_view
+ ? editor->priv->active->list_store
+ : editor->priv->active->tree_store,
+ &iter);
+
+ menu = gtk_menu_new ();
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+ subscription_editor_create_menu_item (
+ _("_Unsubscribe"),
+ gtk_widget_get_sensitive (editor->priv->unsubscribe_button),
+ G_CALLBACK (subscription_editor_unsubscribe),
+ editor));
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+ subscription_editor_create_menu_item (
+ _("Unsu_bscribe from hidden"),
+ tree_filled,
+ G_CALLBACK (subscription_editor_unsubscribe_hidden),
+ editor));
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu),
+ subscription_editor_create_menu_item (
+ _("Unsubscribe from _all"),
+ tree_filled,
+ G_CALLBACK (subscription_editor_unsubscribe_all),
+ editor));
+
+ gtk_menu_popup (
+ GTK_MENU (menu), NULL, NULL, position_below_widget_cb, editor->priv->unsubscribe_button,
+ 0, gtk_get_current_event_time ());
}
static void
@@ -535,7 +980,9 @@ subscription_editor_refresh (EMSubscriptionEditor *editor)
gtk_widget_set_sensitive (editor->priv->notebook, FALSE);
gtk_widget_set_sensitive (editor->priv->subscribe_button, FALSE);
+ gtk_widget_set_sensitive (editor->priv->subscribe_arrow, FALSE);
gtk_widget_set_sensitive (editor->priv->unsubscribe_button, FALSE);
+ gtk_widget_set_sensitive (editor->priv->unsubscribe_arrow, FALSE);
gtk_widget_set_sensitive (editor->priv->refresh_button, FALSE);
gtk_widget_set_sensitive (editor->priv->stop_button, TRUE);
@@ -567,9 +1014,12 @@ subscription_editor_stop (EMSubscriptionEditor *editor)
gtk_widget_set_sensitive (editor->priv->notebook, TRUE);
gtk_widget_set_sensitive (editor->priv->subscribe_button, TRUE);
+ gtk_widget_set_sensitive (editor->priv->subscribe_arrow, TRUE);
gtk_widget_set_sensitive (editor->priv->unsubscribe_button, TRUE);
+ gtk_widget_set_sensitive (editor->priv->unsubscribe_arrow, TRUE);
gtk_widget_set_sensitive (editor->priv->refresh_button, TRUE);
gtk_widget_set_sensitive (editor->priv->stop_button, FALSE);
+ gtk_widget_grab_focus (GTK_WIDGET (editor->priv->active->tree_view));
window = gtk_widget_get_window (GTK_WIDGET (editor));
gdk_window_set_cursor (window, NULL);
@@ -811,8 +1261,11 @@ subscription_editor_selection_changed_cb (GtkTreeSelection *selection,
gtk_widget_set_sensitive (
editor->priv->subscribe_button, FALSE);
gtk_widget_set_sensitive (
- editor->priv->subscribe_button, FALSE);
+ editor->priv->unsubscribe_button, FALSE);
}
+
+ gtk_widget_set_sensitive (editor->priv->subscribe_arrow, TRUE);
+ gtk_widget_set_sensitive (editor->priv->unsubscribe_arrow, TRUE);
}
static void
@@ -1264,20 +1717,25 @@ em_subscription_editor_init (EMSubscriptionEditor *editor)
G_BINDING_BIDIRECTIONAL |
G_BINDING_SYNC_CREATE);
- widget = gtk_vbutton_box_new ();
+ widget = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
gtk_box_set_spacing (GTK_BOX (widget), 6);
gtk_button_box_set_layout (
GTK_BUTTON_BOX (widget), GTK_BUTTONBOX_START);
gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
gtk_widget_show (widget);
+ container = box = widget;
+
+ widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
+ gtk_widget_show (widget);
+
container = widget;
tooltip = _("Subscribe to the selected folder");
widget = gtk_button_new_with_mnemonic (_("Su_bscribe"));
gtk_widget_set_sensitive (widget, FALSE);
gtk_widget_set_tooltip_text (widget, tooltip);
- gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
editor->priv->subscribe_button = widget;
gtk_widget_show (widget);
@@ -1285,11 +1743,35 @@ em_subscription_editor_init (EMSubscriptionEditor *editor)
widget, "clicked",
G_CALLBACK (subscription_editor_subscribe), editor);
+ widget = gtk_button_new ();
+ gtk_button_set_image (GTK_BUTTON (widget), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
+ editor->priv->subscribe_arrow = widget;
+ gtk_widget_show (widget);
+
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (subscription_editor_subscribe_popup_cb), editor);
+
+ if (gtk_widget_get_direction (container) == GTK_TEXT_DIR_LTR) {
+ gtk_box_pack_start (GTK_BOX (container), editor->priv->subscribe_button, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (container), editor->priv->subscribe_arrow, FALSE, FALSE, 0);
+ } else {
+ gtk_box_pack_start (GTK_BOX (container), editor->priv->subscribe_arrow, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (container), editor->priv->subscribe_button, TRUE, TRUE, 0);
+ }
+
+ container = box;
+
+ widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
+ gtk_widget_show (widget);
+
+ container = widget;
+
tooltip = _("Unsubscribe from the selected folder");
widget = gtk_button_new_with_mnemonic (_("_Unsubscribe"));
gtk_widget_set_sensitive (widget, FALSE);
gtk_widget_set_tooltip_text (widget, tooltip);
- gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
editor->priv->unsubscribe_button = widget;
gtk_widget_show (widget);
@@ -1297,6 +1779,25 @@ em_subscription_editor_init (EMSubscriptionEditor *editor)
widget, "clicked",
G_CALLBACK (subscription_editor_unsubscribe), editor);
+ widget = gtk_button_new ();
+ gtk_button_set_image (GTK_BUTTON (widget), gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
+ editor->priv->unsubscribe_arrow = widget;
+ gtk_widget_show (widget);
+
+ g_signal_connect_swapped (
+ widget, "clicked",
+ G_CALLBACK (subscription_editor_unsubscribe_popup_cb), editor);
+
+ if (gtk_widget_get_direction (container) == GTK_TEXT_DIR_LTR) {
+ gtk_box_pack_start (GTK_BOX (container), editor->priv->unsubscribe_button, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (container), editor->priv->unsubscribe_arrow, FALSE, FALSE, 0);
+ } else {
+ gtk_box_pack_start (GTK_BOX (container), editor->priv->unsubscribe_arrow, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (container), editor->priv->unsubscribe_button, TRUE, TRUE, 0);
+ }
+
+ container = box;
+
tooltip = _("Collapse all folders");
widget = gtk_button_new_with_mnemonic (_("C_ollapse All"));
gtk_widget_set_tooltip_text (widget, tooltip);