/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with the program; if not, see * * * Authors: * Michael Zucchi * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ #ifdef HAVE_CONFIG_H #include #endif #include "em-folder-properties.h" #include #include #include #include #include #include #include "e-mail-backend.h" #include "e-mail-ui-session.h" #include "em-config.h" #include "mail-vfolder-ui.h" typedef struct _AsyncContext AsyncContext; struct _AsyncContext { EActivity *activity; CamelFolder *folder; GtkWindow *parent_window; CamelFolderQuotaInfo *quota_info; gint total; gint unread; }; static void async_context_free (AsyncContext *context) { if (context->activity != NULL) g_object_unref (context->activity); if (context->folder != NULL) g_object_unref (context->folder); if (context->parent_window != NULL) g_object_unref (context->parent_window); if (context->quota_info != NULL) camel_folder_quota_info_free (context->quota_info); g_slice_free (AsyncContext, context); } static void emfp_free (EConfig *ec, GSList *items, gpointer data) { g_slist_free (items); } static gint add_numbered_row (GtkTable *table, gint row, const gchar *description, const gchar *format, gint num) { gchar *str; GtkWidget *label; g_return_val_if_fail (table != NULL, row); g_return_val_if_fail (description != NULL, row); g_return_val_if_fail (format != NULL, row); label = gtk_label_new (description); gtk_widget_show (label); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_table_attach ( table, label, 0, 1, row, row + 1, GTK_FILL, 0, 0, 0); str = g_strdup_printf (format, num); label = gtk_label_new (str); gtk_widget_show (label); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_table_attach ( table, label, 1, 2, row, row + 1, GTK_FILL | GTK_EXPAND, 0, 0, 0); g_free (str); return row + 1; } static GtkWidget * emfp_get_folder_item (EConfig *ec, EConfigItem *item, GtkWidget *parent, GtkWidget *old, gint position, gpointer data) { GObjectClass *class; GParamSpec **properties; GtkWidget *widget, *table; AsyncContext *context = data; guint ii, n_properties; gint row = 0; gboolean can_apply_filters = FALSE; if (old) return old; table = gtk_table_new (2, 2, FALSE); gtk_table_set_row_spacings ((GtkTable *) table, 6); gtk_table_set_col_spacings ((GtkTable *) table, 12); gtk_widget_show (table); gtk_box_pack_start ((GtkBox *) parent, table, TRUE, TRUE, 0); /* To be on the safe side, ngettext is used here, * see e.g. comment #3 at bug 272567 */ row = add_numbered_row ( GTK_TABLE (table), row, ngettext ( "Unread messages:", "Unread messages:", context->unread), "%d", context->unread); /* TODO: can this be done in a loop? */ /* To be on the safe side, ngettext is used here, * see e.g. comment #3 at bug 272567 */ row = add_numbered_row ( GTK_TABLE (table), row, ngettext ( "Total messages:", "Total messages:", context->total), "%d", context->total); if (context->quota_info) { CamelFolderQuotaInfo *info; CamelFolderQuotaInfo *quota = context->quota_info; for (info = quota; info; info = info->next) { gchar *descr; gint procs; /* should not happen, but anyway... */ if (!info->total) continue; /* Show quota name only when available and we * have more than one quota info. */ if (info->name && quota->next) descr = g_strdup_printf ( _("Quota usage (%s):"), _(info->name)); else descr = g_strdup_printf (_("Quota usage")); procs = (gint) ((((gdouble) info->used) / ((gdouble) info->total)) * 100.0 + 0.5); row = add_numbered_row ( GTK_TABLE (table), row, descr, "%d%%", procs); g_free (descr); } } if (context->folder != NULL) { CamelStore *store; CamelSession *session; CamelFolderInfoFlags fi_flags = 0; const gchar *folder_name; MailFolderCache *folder_cache; gboolean have_flags; store = camel_folder_get_parent_store (context->folder); folder_name = camel_folder_get_full_name (context->folder); session = camel_service_ref_session (CAMEL_SERVICE (store)); folder_cache = e_mail_session_get_folder_cache ( E_MAIL_SESSION (session)); have_flags = mail_folder_cache_get_folder_info_flags ( folder_cache, store, folder_name, &fi_flags); can_apply_filters = !CAMEL_IS_VEE_FOLDER (context->folder) && have_flags && (fi_flags & CAMEL_FOLDER_TYPE_MASK) != CAMEL_FOLDER_TYPE_INBOX; g_object_unref (session); } class = G_OBJECT_GET_CLASS (context->folder); properties = g_object_class_list_properties (class, &n_properties); for (ii = 0; ii < n_properties; ii++) { const gchar *blurb; if ((properties[ii]->flags & CAMEL_PARAM_PERSISTENT) == 0) continue; if (!can_apply_filters && g_strcmp0 (properties[ii]->name, "apply-filters") == 0) continue; blurb = g_param_spec_get_blurb (properties[ii]); switch (properties[ii]->value_type) { case G_TYPE_BOOLEAN: widget = gtk_check_button_new_with_mnemonic (blurb); g_object_bind_property ( context->folder, properties[ii]->name, widget, "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); gtk_widget_show (widget); gtk_table_attach ( GTK_TABLE (table), widget, 0, 2, row, row + 1, GTK_FILL | GTK_EXPAND, 0, 0, 0); row++; break; default: g_warn_if_reached (); break; } } g_free (properties); return table; } #define EMFP_FOLDER_SECTION (2) static EMConfigItem emfp_items[] = { { E_CONFIG_BOOK, (gchar *) "", NULL }, { E_CONFIG_PAGE, (gchar *) "00.general", (gchar *) N_("General") }, { E_CONFIG_SECTION, (gchar *) "00.general/00.folder", NULL /* set by code */ }, { E_CONFIG_ITEM, (gchar *) "00.general/00.folder/00.info", NULL, emfp_get_folder_item }, }; static gboolean emfp_items_translated = FALSE; static void emfp_dialog_run (AsyncContext *context) { GtkWidget *dialog, *w; GtkWidget *content_area; GSList *l; gint32 i,deleted; EMConfig *ec; EMConfigTargetFolder *target; CamelStore *parent_store; CamelFolderSummary *summary; gboolean store_is_local; gboolean hide_deleted; GSettings *settings; const gchar *name; const gchar *uid; parent_store = camel_folder_get_parent_store (context->folder); /* Get number of VISIBLE and DELETED messages, instead of TOTAL * messages. VISIBLE+DELETED gives the correct count that matches * the label below the Send & Receive button. */ summary = context->folder->summary; context->total = camel_folder_summary_get_visible_count (summary); context->unread = camel_folder_summary_get_unread_count (summary); deleted = camel_folder_summary_get_deleted_count (summary); settings = g_settings_new ("org.gnome.evolution.mail"); hide_deleted = !g_settings_get_boolean (settings, "show-deleted"); g_object_unref (settings); /* * Do the calculation only for those accounts that support VTRASHes */ if (parent_store->flags & CAMEL_STORE_VTRASH) { if (CAMEL_IS_VTRASH_FOLDER (context->folder)) context->total += deleted; else if (!hide_deleted && deleted > 0) context->total += deleted; } /* * If the folder is junk folder, get total number of mails. */ if (parent_store->flags & CAMEL_STORE_VJUNK) context->total = camel_folder_summary_count ( context->folder->summary); name = camel_folder_get_display_name (context->folder); uid = camel_service_get_uid (CAMEL_SERVICE (parent_store)); store_is_local = (g_strcmp0 (uid, E_MAIL_SESSION_LOCAL_UID) == 0); if (store_is_local && (!strcmp (name, "Drafts") || !strcmp (name, "Templates") || !strcmp (name, "Inbox") || !strcmp (name, "Outbox") || !strcmp (name, "Sent"))) { emfp_items[EMFP_FOLDER_SECTION].label = gettext (name); if (!emfp_items_translated) { for (i = 0; i < G_N_ELEMENTS (emfp_items); i++) { if (emfp_items[i].label) emfp_items[i].label = _(emfp_items[i].label); } emfp_items_translated = TRUE; } } else if (!strcmp (name, "INBOX")) emfp_items[EMFP_FOLDER_SECTION].label = _("Inbox"); else emfp_items[EMFP_FOLDER_SECTION].label = (gchar *) name; dialog = gtk_dialog_new_with_buttons ( _("Folder Properties"), context->parent_window, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_OK, NULL); gtk_window_set_default_size ((GtkWindow *) dialog, 192, 160); gtk_widget_ensure_style (dialog); content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); gtk_container_set_border_width (GTK_CONTAINER (content_area), 12); /** @HookPoint-EMConfig: Folder Properties Window * @Id: org.gnome.evolution.mail.folderConfig * @Type: E_CONFIG_BOOK * @Class: org.gnome.evolution.mail.config:1.0 * @Target: EMConfigTargetFolder * * The folder properties window. */ ec = em_config_new ("org.gnome.evolution.mail.folderConfig"); l = NULL; for (i = 0; i < G_N_ELEMENTS (emfp_items); i++) l = g_slist_prepend (l, &emfp_items[i]); e_config_add_items ((EConfig *) ec, l, emfp_free, context); target = em_config_target_new_folder (ec, context->folder); e_config_set_target ((EConfig *) ec, (EConfigTarget *) target); w = e_config_create_widget ((EConfig *) ec); gtk_box_pack_start (GTK_BOX (content_area), w, TRUE, TRUE, 0); /* We do 'apply on ok', since instant apply may start some * very long running tasks. */ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { e_config_commit ((EConfig *) ec); camel_object_state_write (CAMEL_OBJECT (context->folder)); } else e_config_abort ((EConfig *) ec); gtk_widget_destroy (dialog); } static void emfp_dialog_got_quota_info (CamelFolder *folder, GAsyncResult *result, AsyncContext *context) { EAlertSink *alert_sink; GError *error = NULL; alert_sink = e_activity_get_alert_sink (context->activity); context->quota_info = camel_folder_get_quota_info_finish (folder, result, &error); /* If the folder does not implement quota info, just continue. */ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) { g_warn_if_fail (context->quota_info == NULL); g_error_free (error); } else if (e_activity_handle_cancellation (context->activity, error)) { g_warn_if_fail (context->quota_info == NULL); async_context_free (context); g_error_free (error); return; /* FIXME Add an EAlert for failing to get quota info. */ } else if (error != NULL) { g_warn_if_fail (context->folder == NULL); e_alert_submit ( alert_sink, "mail:folder-open", error->message, NULL); async_context_free (context); g_error_free (error); return; } /* Quota info may still be NULL here if not supported. */ /* Finalize the activity here so we don't leave a message * in the task bar while the properties window is shown. */ e_activity_set_state (context->activity, E_ACTIVITY_COMPLETED); g_object_unref (context->activity); context->activity = NULL; emfp_dialog_run (context); async_context_free (context); } static void emfp_dialog_got_folder (CamelStore *store, GAsyncResult *result, AsyncContext *context) { EAlertSink *alert_sink; GCancellable *cancellable; GError *error = NULL; alert_sink = e_activity_get_alert_sink (context->activity); cancellable = e_activity_get_cancellable (context->activity); context->folder = camel_store_get_folder_finish ( store, result, &error); if (e_activity_handle_cancellation (context->activity, error)) { g_warn_if_fail (context->folder == NULL); async_context_free (context); g_error_free (error); return; } else if (error != NULL) { g_warn_if_fail (context->folder == NULL); e_alert_submit ( alert_sink, "mail:folder-open", error->message, NULL); async_context_free (context); g_error_free (error); return; } g_return_if_fail (CAMEL_IS_FOLDER (context->folder)); camel_folder_get_quota_info ( context->folder, G_PRIORITY_DEFAULT, cancellable, (GAsyncReadyCallback) emfp_dialog_got_quota_info, context); } /** * em_folder_properties_show: * @store: a #CamelStore * @folder_name: a folder name * @alert_sink: an #EAlertSink * @parent_window: a parent #GtkWindow * * Show folder properties for @folder_name. **/ void em_folder_properties_show (CamelStore *store, const gchar *folder_name, EAlertSink *alert_sink, GtkWindow *parent_window) { CamelService *service; CamelSession *session; GCancellable *cancellable; AsyncContext *context; const gchar *uid; g_return_if_fail (CAMEL_IS_STORE (store)); g_return_if_fail (folder_name != NULL); g_return_if_fail (E_IS_ALERT_SINK (alert_sink)); g_return_if_fail (GTK_IS_WINDOW (parent_window)); service = CAMEL_SERVICE (store); uid = camel_service_get_uid (service); session = camel_service_ref_session (service); /* Show the Edit Rule dialog for Search Folders, but not "Unmatched". * "Unmatched" is a special Search Folder which can't be modified. */ if (g_strcmp0 (uid, E_MAIL_SESSION_VFOLDER_UID) == 0) { if (g_strcmp0 (folder_name, CAMEL_UNMATCHED_NAME) != 0) { gchar *folder_uri; folder_uri = e_mail_folder_uri_build ( store, folder_name); vfolder_edit_rule ( E_MAIL_SESSION (session), folder_uri, alert_sink); g_free (folder_uri); goto exit; } } /* Open the folder asynchronously. */ context = g_slice_new0 (AsyncContext); context->activity = e_activity_new (); context->parent_window = g_object_ref (parent_window); e_activity_set_alert_sink (context->activity, alert_sink); cancellable = camel_operation_new (); e_activity_set_cancellable (context->activity, cancellable); e_mail_ui_session_add_activity ( E_MAIL_UI_SESSION (session), context->activity); camel_store_get_folder ( store, folder_name, 0, G_PRIORITY_DEFAULT, cancellable, (GAsyncReadyCallback) emfp_dialog_got_folder, context); g_object_unref (cancellable); exit: g_object_unref (session); }