Bug #632683 - Remove-duplicates should work on selection
This commit is contained in:
@ -406,81 +406,144 @@ action_mail_remove_attachments_cb (GtkAction *action, EMailReader *reader)
|
||||
{
|
||||
CamelFolder *folder;
|
||||
GPtrArray *uids;
|
||||
gint i, j;
|
||||
|
||||
folder = e_mail_reader_get_folder (reader);
|
||||
uids = e_mail_reader_get_selected_uids (reader);
|
||||
|
||||
camel_folder_freeze (folder);
|
||||
for (i = 0; i < (uids ? uids->len : 0); i++) {
|
||||
CamelMimeMessage *message;
|
||||
CamelDataWrapper *containee;
|
||||
gchar *uid;
|
||||
mail_remove_attachments (folder, uids);
|
||||
}
|
||||
|
||||
uid = g_ptr_array_index (uids, i);
|
||||
static gchar *
|
||||
get_message_checksum (CamelFolder *folder, CamelMimeMessage *msg)
|
||||
{
|
||||
static const GChecksumType duplicate_csum = G_CHECKSUM_SHA256;
|
||||
|
||||
/* retrieve the message from the CamelFolder */
|
||||
message = camel_folder_get_message_sync (folder, uid, NULL, NULL);
|
||||
if (!message) {
|
||||
continue;
|
||||
}
|
||||
CamelDataWrapper *content;
|
||||
CamelStream *mem;
|
||||
GByteArray *buffer;
|
||||
gchar *digest = NULL;
|
||||
|
||||
containee = camel_medium_get_content (CAMEL_MEDIUM (message));
|
||||
if (containee == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
if (CAMEL_IS_MULTIPART (containee)) {
|
||||
gboolean deleted = FALSE;
|
||||
gint parts;
|
||||
/* get message contents */
|
||||
content = camel_medium_get_content ((CamelMedium *) msg);
|
||||
if (!content)
|
||||
return NULL;
|
||||
|
||||
parts = camel_multipart_get_number (CAMEL_MULTIPART (containee));
|
||||
for (j = 0; j < parts; j++) {
|
||||
CamelMimePart *mpart = camel_multipart_get_part (CAMEL_MULTIPART (containee), j);
|
||||
const gchar *disposition = camel_mime_part_get_disposition (mpart);
|
||||
if (disposition && (!strcmp (disposition, "attachment") || !strcmp (disposition, "inline"))) {
|
||||
gchar *desc;
|
||||
const gchar *filename;
|
||||
/* calculate checksum */
|
||||
mem = camel_stream_mem_new ();
|
||||
camel_data_wrapper_decode_to_stream_sync (content, mem, NULL, NULL);
|
||||
|
||||
filename = camel_mime_part_get_filename (mpart);
|
||||
desc = g_strdup_printf (_("File \"%s\" has been removed."), filename ? filename : "");
|
||||
camel_mime_part_set_disposition (mpart, "inline");
|
||||
camel_mime_part_set_content (mpart, desc, strlen (desc), "text/plain");
|
||||
camel_mime_part_set_content_type (mpart, "text/plain");
|
||||
deleted = TRUE;
|
||||
buffer = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem));
|
||||
if (buffer)
|
||||
digest = g_compute_checksum_for_data (duplicate_csum, buffer->data, buffer->len);
|
||||
|
||||
g_object_unref (mem);
|
||||
|
||||
return digest;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
message_is_duplicated (GHashTable *messages, guint64 id, gchar *digest)
|
||||
{
|
||||
gchar *hash_digest = g_hash_table_lookup (messages, &id);
|
||||
|
||||
if (!hash_digest)
|
||||
return FALSE;
|
||||
|
||||
return g_str_equal (digest, hash_digest);
|
||||
}
|
||||
|
||||
static void
|
||||
remove_duplicates_got_messages_cb (CamelFolder *folder, GPtrArray *uids, GPtrArray *msgs, gpointer data)
|
||||
{
|
||||
EMailReader *reader = data;
|
||||
GtkWindow *parent;
|
||||
GHashTable *messages;
|
||||
GPtrArray *dups;
|
||||
gint ii;
|
||||
|
||||
g_return_if_fail (folder != NULL);
|
||||
g_return_if_fail (CAMEL_IS_FOLDER (folder));
|
||||
g_return_if_fail (uids != NULL);
|
||||
g_return_if_fail (msgs != NULL);
|
||||
g_return_if_fail (msgs->len <= uids->len);
|
||||
g_return_if_fail (reader != NULL);
|
||||
g_return_if_fail (E_IS_MAIL_READER (reader));
|
||||
|
||||
parent = e_mail_reader_get_window (reader);
|
||||
|
||||
messages = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, g_free);
|
||||
dups = g_ptr_array_new ();
|
||||
|
||||
for (ii = 0; ii < msgs->len; ii++) {
|
||||
CamelMessageInfo *msg_info = camel_folder_get_message_info (folder, uids->pdata[ii]);
|
||||
const CamelSummaryMessageID *mid = camel_message_info_message_id (msg_info);
|
||||
guint32 flags = camel_message_info_flags (msg_info);
|
||||
|
||||
if (!(flags & CAMEL_MESSAGE_DELETED)) {
|
||||
gchar *digest = get_message_checksum (folder, msgs->pdata[ii]);
|
||||
|
||||
if (digest) {
|
||||
if (message_is_duplicated (messages, mid->id.id, digest)) {
|
||||
g_ptr_array_add (dups, uids->pdata[ii]);
|
||||
g_free (digest);
|
||||
} else {
|
||||
guint64 *id;
|
||||
id = g_new0 (guint64, 1);
|
||||
*id = mid->id.id;
|
||||
g_hash_table_insert (messages, id, digest);
|
||||
}
|
||||
}
|
||||
|
||||
if (deleted) {
|
||||
/* copy the original message with the deleted attachment */
|
||||
CamelMessageInfo *info, *newinfo;
|
||||
guint32 flags;
|
||||
GError *error = NULL;
|
||||
|
||||
info = camel_folder_get_message_info (folder, uid);
|
||||
newinfo = camel_message_info_new_from_header (NULL, CAMEL_MIME_PART (message)->headers);
|
||||
flags = camel_folder_get_message_flags (folder, uid);
|
||||
|
||||
/* make a copy of the message */
|
||||
camel_message_info_set_flags (newinfo, flags, flags);
|
||||
camel_folder_append_message_sync (folder, message, newinfo, NULL, NULL, &error);
|
||||
|
||||
if (!error) {
|
||||
/* marked the original message deleted */
|
||||
camel_message_info_set_flags (info, CAMEL_MESSAGE_DELETED, CAMEL_MESSAGE_DELETED);
|
||||
}
|
||||
|
||||
camel_folder_free_message_info (folder, info);
|
||||
camel_message_info_free (newinfo);
|
||||
|
||||
if (error)
|
||||
g_error_free (error);
|
||||
}
|
||||
}
|
||||
|
||||
camel_message_info_free (msg_info);
|
||||
}
|
||||
|
||||
camel_folder_synchronize_sync (folder, FALSE, NULL, NULL);
|
||||
camel_folder_thaw (folder);
|
||||
if (dups->len == 0) {
|
||||
em_utils_prompt_user (parent, NULL, "mail:info-no-remove-duplicates", camel_folder_get_name (folder), NULL);
|
||||
} else {
|
||||
gchar *msg = g_strdup_printf (ngettext (
|
||||
/* Translators: %s is replaced with a folder name
|
||||
%d with count of duplicate messages. */
|
||||
_("Folder '%s' contains %d duplicate message. Are you sure you want to delete it?"),
|
||||
_("Folder '%s' contains %d duplicate messages. Are you sure you want to delete them?"),
|
||||
dups->len),
|
||||
camel_folder_get_name (folder), dups->len);
|
||||
|
||||
if (em_utils_prompt_user (parent, NULL, "mail:ask-remove-duplicates", msg, NULL)) {
|
||||
camel_folder_freeze (folder);
|
||||
for (ii = 0; ii < dups->len; ii++)
|
||||
camel_folder_delete_message (folder, g_ptr_array_index (dups, ii));
|
||||
camel_folder_thaw (folder);
|
||||
}
|
||||
|
||||
g_free (msg);
|
||||
}
|
||||
|
||||
g_hash_table_destroy (messages);
|
||||
g_ptr_array_free (dups, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
action_mail_remove_duplicates (GtkAction *action, EMailReader *reader)
|
||||
{
|
||||
MessageList *message_list;
|
||||
CamelFolder *folder;
|
||||
GPtrArray *uids;
|
||||
|
||||
message_list = MESSAGE_LIST (e_mail_reader_get_message_list (reader));
|
||||
uids = message_list_get_selected (message_list);
|
||||
folder = message_list->folder;
|
||||
|
||||
if (!uids || uids->len <= 1) {
|
||||
if (uids)
|
||||
em_utils_uids_free (uids);
|
||||
} else {
|
||||
/* the function itself is freeing uids */
|
||||
mail_get_messages (folder, uids, remove_duplicates_got_messages_cb, reader);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -2107,6 +2170,13 @@ static GtkActionEntry mail_reader_entries[] = {
|
||||
N_("Remove attachments"),
|
||||
G_CALLBACK (action_mail_remove_attachments_cb) },
|
||||
|
||||
{ "mail-remove-duplicates",
|
||||
NULL,
|
||||
N_("Remove Du_plicate Messages"),
|
||||
NULL,
|
||||
N_("Checks selected messages for duplicates"),
|
||||
G_CALLBACK (action_mail_remove_duplicates) },
|
||||
|
||||
{ "mail-reply-all",
|
||||
NULL,
|
||||
N_("Reply to _All"),
|
||||
@ -2330,6 +2400,10 @@ static EPopupActionEntry mail_reader_popup_entries[] = {
|
||||
NULL,
|
||||
"mail-remove-attachments" },
|
||||
|
||||
{ "mail-popup-remove-duplicates",
|
||||
NULL,
|
||||
"mail-remove-duplicates" },
|
||||
|
||||
{ "mail-popup-reply-all",
|
||||
NULL,
|
||||
"mail-reply-all" },
|
||||
@ -3165,6 +3239,11 @@ mail_reader_update_actions (EMailReader *reader,
|
||||
action = e_mail_reader_get_action (reader, action_name);
|
||||
gtk_action_set_sensitive (action, sensitive);
|
||||
|
||||
action_name = "mail-remove-duplicates";
|
||||
sensitive = multiple_messages_selected;
|
||||
action = e_mail_reader_get_action (reader, action_name);
|
||||
gtk_action_set_sensitive (action, sensitive);
|
||||
|
||||
action_name = "mail-reply-all";
|
||||
sensitive = have_enabled_account && single_message_selected;
|
||||
action = e_mail_reader_get_action (reader, action_name);
|
||||
|
||||
139
mail/mail-ops.c
139
mail/mail-ops.c
@ -2599,3 +2599,142 @@ mail_disconnect_store (CamelStore *store)
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------- */
|
||||
|
||||
struct _remove_attachments_msg {
|
||||
MailMsg base;
|
||||
|
||||
CamelFolder *folder;
|
||||
GPtrArray *uids;
|
||||
};
|
||||
|
||||
static gchar *
|
||||
remove_attachments_desc (struct _remove_attachments_msg *m)
|
||||
{
|
||||
return g_strdup_printf (_("Removing attachments"));
|
||||
}
|
||||
|
||||
static void
|
||||
remove_attachments_exec (struct _remove_attachments_msg *m,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
CamelFolder *folder = m->folder;
|
||||
GPtrArray *uids = m->uids;
|
||||
gint ii, jj;
|
||||
|
||||
camel_folder_freeze (folder);
|
||||
for (ii = 0; ii < (uids ? uids->len : 0); ii++) {
|
||||
gint pc = ((ii + 1) * 100) / uids->len;
|
||||
CamelMimeMessage *message;
|
||||
CamelDataWrapper *containee;
|
||||
gchar *uid;
|
||||
|
||||
uid = g_ptr_array_index (uids, ii);
|
||||
|
||||
/* retrieve the message from the CamelFolder */
|
||||
message = camel_folder_get_message_sync (folder, uid, cancellable, NULL);
|
||||
if (!message) {
|
||||
camel_operation_progress (cancellable, pc);
|
||||
continue;
|
||||
}
|
||||
|
||||
containee = camel_medium_get_content (CAMEL_MEDIUM (message));
|
||||
if (containee == NULL) {
|
||||
camel_operation_progress (cancellable, pc);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CAMEL_IS_MULTIPART (containee)) {
|
||||
gboolean deleted = FALSE;
|
||||
gint parts;
|
||||
|
||||
parts = camel_multipart_get_number (CAMEL_MULTIPART (containee));
|
||||
for (jj = 0; jj < parts; jj++) {
|
||||
CamelMimePart *mpart = camel_multipart_get_part (CAMEL_MULTIPART (containee), jj);
|
||||
const gchar *disposition = camel_mime_part_get_disposition (mpart);
|
||||
if (disposition && (!strcmp (disposition, "attachment") || !strcmp (disposition, "inline"))) {
|
||||
gchar *desc;
|
||||
const gchar *filename;
|
||||
|
||||
filename = camel_mime_part_get_filename (mpart);
|
||||
desc = g_strdup_printf (_("File \"%s\" has been removed."), filename ? filename : "");
|
||||
camel_mime_part_set_disposition (mpart, "inline");
|
||||
camel_mime_part_set_content (mpart, desc, strlen (desc), "text/plain");
|
||||
camel_mime_part_set_content_type (mpart, "text/plain");
|
||||
deleted = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (deleted) {
|
||||
/* copy the original message with the deleted attachment */
|
||||
CamelMessageInfo *info, *newinfo;
|
||||
guint32 flags;
|
||||
GError *local_error = NULL;
|
||||
|
||||
info = camel_folder_get_message_info (folder, uid);
|
||||
newinfo = camel_message_info_new_from_header (NULL, CAMEL_MIME_PART (message)->headers);
|
||||
flags = camel_folder_get_message_flags (folder, uid);
|
||||
|
||||
/* make a copy of the message */
|
||||
camel_message_info_set_flags (newinfo, flags, flags);
|
||||
camel_folder_append_message_sync (folder, message, newinfo, NULL, cancellable, &local_error);
|
||||
|
||||
if (!local_error) {
|
||||
/* marked the original message deleted */
|
||||
camel_message_info_set_flags (info, CAMEL_MESSAGE_DELETED, CAMEL_MESSAGE_DELETED);
|
||||
}
|
||||
|
||||
camel_folder_free_message_info (folder, info);
|
||||
camel_message_info_free (newinfo);
|
||||
|
||||
if (local_error) {
|
||||
g_propagate_error (error, local_error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
camel_operation_progress (cancellable, pc);
|
||||
}
|
||||
|
||||
if (!error || !*error)
|
||||
camel_folder_synchronize_sync (folder, FALSE, cancellable, error);
|
||||
camel_folder_thaw (folder);
|
||||
}
|
||||
|
||||
static void
|
||||
remove_attachments_free (struct _remove_attachments_msg *m)
|
||||
{
|
||||
g_object_unref (m->folder);
|
||||
em_utils_uids_free (m->uids);
|
||||
}
|
||||
|
||||
static MailMsgInfo remove_attachments_info = {
|
||||
sizeof (struct _remove_attachments_msg),
|
||||
(MailMsgDescFunc) remove_attachments_desc,
|
||||
(MailMsgExecFunc) remove_attachments_exec,
|
||||
(MailMsgDoneFunc) NULL,
|
||||
(MailMsgFreeFunc) remove_attachments_free
|
||||
};
|
||||
|
||||
/* it takes ownership of 'uids' array */
|
||||
gint
|
||||
mail_remove_attachments (CamelFolder *folder, GPtrArray *uids)
|
||||
{
|
||||
struct _remove_attachments_msg *m;
|
||||
gint id;
|
||||
|
||||
g_return_val_if_fail (folder != NULL, -1);
|
||||
g_return_val_if_fail (uids != NULL, -1);
|
||||
|
||||
m = mail_msg_new (&remove_attachments_info);
|
||||
m->folder = g_object_ref (folder);
|
||||
m->uids = uids;
|
||||
|
||||
id = m->base.seq;
|
||||
mail_msg_unordered_push (m);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
@ -164,6 +164,7 @@ gint mail_check_service (EMailSession *session,
|
||||
gpointer data);
|
||||
|
||||
gint mail_disconnect_store (CamelStore *store);
|
||||
gint mail_remove_attachments (CamelFolder *folder, GPtrArray *uids);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
@ -447,133 +447,6 @@ action_mail_folder_rename_cb (GtkAction *action,
|
||||
em_folder_tree_edit_selected (folder_tree);
|
||||
}
|
||||
|
||||
static gchar *
|
||||
get_message_checksum (CamelFolder *folder, const gchar *uid)
|
||||
{
|
||||
static const GChecksumType duplicate_csum = G_CHECKSUM_SHA256;
|
||||
|
||||
CamelMimeMessage *msg;
|
||||
GError *error = NULL;
|
||||
CamelDataWrapper *content;
|
||||
CamelStream *mem;
|
||||
GByteArray *buffer;
|
||||
gchar *digest = NULL;
|
||||
|
||||
msg = camel_folder_get_message_sync (folder, uid, NULL, &error);
|
||||
|
||||
if (error || !msg) {
|
||||
if (error)
|
||||
g_error_free (error);;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* get message contents */
|
||||
content = camel_medium_get_content ((CamelMedium *) msg);
|
||||
if (!content)
|
||||
return NULL;
|
||||
|
||||
/* calculate checksum */
|
||||
mem = camel_stream_mem_new ();
|
||||
camel_data_wrapper_decode_to_stream_sync (content, mem, NULL, NULL);
|
||||
|
||||
buffer = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem));
|
||||
if (buffer)
|
||||
digest = g_compute_checksum_for_data (duplicate_csum, buffer->data, buffer->len);
|
||||
|
||||
g_object_unref (mem);
|
||||
g_object_unref (msg);
|
||||
|
||||
return digest;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
message_is_duplicated (GHashTable *messages, guint64 id, gchar *digest)
|
||||
{
|
||||
gchar *hash_digest = g_hash_table_lookup (messages, &id);
|
||||
|
||||
if (!hash_digest)
|
||||
return FALSE;
|
||||
|
||||
return g_str_equal (digest, hash_digest);
|
||||
}
|
||||
|
||||
static void
|
||||
action_mail_folder_remove_duplicates (GtkAction *action, EMailShellView *mail_shell_view)
|
||||
{
|
||||
EShellView *shell_view;
|
||||
EShellContent *shell_content;
|
||||
GtkWindow *parent;
|
||||
EMailReader *reader;
|
||||
MessageList *message_list;
|
||||
CamelFolder *folder;
|
||||
GHashTable *messages;
|
||||
GPtrArray *uids, *dups;
|
||||
gint i;
|
||||
|
||||
shell_view = E_SHELL_VIEW (mail_shell_view);
|
||||
shell_content = e_shell_view_get_shell_content (shell_view);
|
||||
parent = GTK_WINDOW (e_shell_view_get_shell_window (shell_view));
|
||||
|
||||
reader = E_MAIL_READER (shell_content);
|
||||
message_list = MESSAGE_LIST (e_mail_reader_get_message_list (reader));
|
||||
uids = message_list_get_uids (message_list);
|
||||
folder = message_list->folder;
|
||||
|
||||
messages = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, g_free);
|
||||
dups = g_ptr_array_new ();
|
||||
|
||||
for (i = 0; i < uids->len; i++) {
|
||||
CamelMessageInfo *msg_info = camel_folder_get_message_info (folder, uids->pdata[i]);
|
||||
const CamelSummaryMessageID *mid = camel_message_info_message_id (msg_info);
|
||||
guint32 flags = camel_message_info_flags (msg_info);
|
||||
|
||||
if (!(flags & CAMEL_MESSAGE_DELETED)) {
|
||||
gchar *digest = get_message_checksum (folder, uids->pdata[i]);
|
||||
|
||||
if (digest) {
|
||||
if (message_is_duplicated (messages, mid->id.id, digest)) {
|
||||
g_ptr_array_add (dups, uids->pdata[i]);
|
||||
g_free (digest);
|
||||
} else {
|
||||
guint64 *id;
|
||||
id = g_new0 (guint64, 1);
|
||||
*id = mid->id.id;
|
||||
g_hash_table_insert (messages, id, digest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
camel_message_info_free (msg_info);
|
||||
}
|
||||
|
||||
if (dups->len == 0) {
|
||||
em_utils_prompt_user (parent, NULL, "mail:info-no-remove-duplicates", camel_folder_get_name (folder), NULL);
|
||||
} else {
|
||||
gchar *msg = g_strdup_printf (ngettext (
|
||||
/* Translators: %s is replaced with a folder name
|
||||
%d with count of duplicate messages. */
|
||||
_("Folder '%s' contains %d duplicate message. Are you sure you want to delete it?"),
|
||||
_("Folder '%s' contains %d duplicate messages. Are you sure you want to delete them?"),
|
||||
dups->len),
|
||||
camel_folder_get_name (folder), dups->len);
|
||||
|
||||
if (em_utils_prompt_user (parent, NULL, "mail:ask-remove-duplicates", msg, NULL)) {
|
||||
gint ii;
|
||||
|
||||
camel_folder_freeze (folder);
|
||||
for (ii = 0; ii < dups->len; ii++)
|
||||
camel_folder_delete_message (folder, g_ptr_array_index (dups, ii));
|
||||
camel_folder_thaw (folder);
|
||||
}
|
||||
|
||||
g_free (msg);
|
||||
}
|
||||
|
||||
g_hash_table_destroy (messages);
|
||||
em_utils_uids_free (uids);
|
||||
g_ptr_array_free (dups, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
action_mail_folder_select_thread_cb (GtkAction *action,
|
||||
EMailShellView *mail_shell_view)
|
||||
@ -1249,13 +1122,6 @@ static GtkActionEntry mail_entries[] = {
|
||||
N_("Change the name of this folder"),
|
||||
G_CALLBACK (action_mail_folder_rename_cb) },
|
||||
|
||||
{ "mail-folder-remove-duplicates",
|
||||
NULL,
|
||||
N_("Remo_ve Duplicate Messages"),
|
||||
"",
|
||||
N_("Remove all duplicate messages"),
|
||||
G_CALLBACK (action_mail_folder_remove_duplicates) },
|
||||
|
||||
{ "mail-folder-select-thread",
|
||||
NULL,
|
||||
N_("Select Message _Thread"),
|
||||
|
||||
@ -85,8 +85,6 @@
|
||||
E_SHELL_WINDOW_ACTION ((window), "mail-folder-rename")
|
||||
#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_SELECT_ALL(window) \
|
||||
E_SHELL_WINDOW_ACTION ((window), "mail-folder-select-all")
|
||||
#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_REMOVE_DUPLICATES(window) \
|
||||
E_SHELL_WINDOW_ACTION ((window), "mail-folder-remove-duplicates")
|
||||
#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_SELECT_THREAD(window) \
|
||||
E_SHELL_WINDOW_ACTION ((window), "mail-folder-select-thread")
|
||||
#define E_SHELL_WINDOW_ACTION_MAIL_FOLDER_SELECT_SUBTHREAD(window) \
|
||||
|
||||
@ -97,6 +97,7 @@
|
||||
<menuitem action='mail-filters-apply'/>
|
||||
<menuitem action='mail-check-for-junk'/>
|
||||
<menuitem action='mail-remove-attachments'/>
|
||||
<menuitem action='mail-remove-duplicates'/>
|
||||
<separator/>
|
||||
<menu action='mail-create-rule-menu'>
|
||||
<menuitem action='mail-filter-on-subject'/>
|
||||
@ -159,6 +160,7 @@
|
||||
<menuitem action='mail-popup-save-as'/>
|
||||
<menuitem action='mail-popup-print'/>
|
||||
<menuitem action='mail-popup-remove-attachments'/>
|
||||
<menuitem action='mail-popup-remove-duplicates'/>
|
||||
</placeholder>
|
||||
</popup>
|
||||
<popup name='mail-preview-popup'>
|
||||
|
||||
@ -45,7 +45,6 @@
|
||||
<menuitem action='mail-folder-select-thread'/>
|
||||
<menuitem action='mail-folder-select-subthread'/>
|
||||
<menuitem action='mail-folder-mark-all-as-read'/>
|
||||
<menuitem action='mail-folder-remove-duplicates'/>
|
||||
<menuitem action='mail-folder-expunge'/>
|
||||
<separator/>
|
||||
<menuitem action='mail-folder-rename'/>
|
||||
|
||||
Reference in New Issue
Block a user