OPS | 3 + compose.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++ doc/manual.xml.head | 17 +++++ functions.h | 3 + 4 files changed, 188 insertions(+), 0 deletions(-)
# HG changeset patch # User David Champion <d...@bikeshed.us> # Date 1472600048 25200 # Tue Aug 30 16:34:08 2016 -0700 # Node ID a347a5b31a5a032d4b3753045c9fc1a73f6023f8 # Parent b8cc2b58219505f3d5f348a7dece9a3e1a3a96ee Adds compose menu bindings for grouping and moving attachments. Three new bindings: group-alternatives combine tagged items into a multipart/alternative group move-up move the current attachment up in the list move-down move the current attachment down in the list diff --git a/OPS b/OPS --- a/OPS +++ b/OPS @@ -32,7 +32,10 @@ OP_CREATE_MAILBOX "create a new mailbox (IMAP only)" OP_EDIT_TYPE "edit attachment content type" OP_COMPOSE_GET_ATTACHMENT "get a temporary copy of an attachment" +OP_COMPOSE_GROUP_ALTS "group tagged attachments as multipart/alternative" OP_COMPOSE_ISPELL "run ispell on the message" +OP_COMPOSE_MOVE_UP "move an attachment up in the attachment list" +OP_COMPOSE_MOVE_DOWN "move an attachment down in the attachment list" OP_COMPOSE_NEW_MIME "compose new attachment using mailcap entry" OP_COMPOSE_TOGGLE_RECODE "toggle recoding of this attachment" OP_COMPOSE_POSTPONE_MESSAGE "save this message to send later" diff --git a/compose.c b/compose.c --- a/compose.c +++ b/compose.c @@ -212,6 +212,8 @@ for (i = 0; i < idxlen; i++) { + if (idx[i]->content->type == TYPEMULTIPART) + continue; strfcpy(pretty, idx[i]->content->filename, sizeof(pretty)); if(stat(idx[i]->content->filename, &st) != 0) { @@ -358,6 +360,43 @@ } +/* + * compose_attach_swap: swap two adjacent entries in the attachment list. + */ +static void compose_attach_swap (BODY *msg, ATTACHPTR **idx, short first) +{ + int i; + void *saved; + BODY *part; + + /* Reorder BODY pointers. + * Must traverse msg from top since BODY * has no previous ptr. + */ + for (part = msg; part; part = part->next) + { + if (part->next == idx[first]->content) + { + idx[first]->content->next = idx[first+1]->content->next; + idx[first+1]->content->next = idx[first]->content; + part->next = idx[first+1]->content; + break; + } + } + + /* Reorder index */ + saved = idx[first]; + idx[first] = idx[first+1]; + idx[first+1] = saved; + + /* Swap ptr->num */ + i = idx[first]->num; + idx[first]->num = idx[first+1]->num; + idx[first+1]->num = i; + + return; +} + + /* * cum_attachs_size: Cumulative Attachments Size * @@ -677,6 +716,132 @@ break; + case OP_COMPOSE_MOVE_UP: + if (menu->current == 0) + { + mutt_error(_("Attachment is already at top.")); + break; + } + if (menu->current == 1) + { + mutt_error(_("The fundamental part cannot be moved.")); + break; + } + compose_attach_swap(msg->content, idx, menu->current - 1); + menu->redraw = 1; + menu->current--; + break; + + + case OP_COMPOSE_MOVE_DOWN: + if (menu->current == idxlen-1) + { + mutt_error(_("Attachment is already at bottom.")); + break; + } + if (menu->current == 0) + { + mutt_error(_("The fundamental part cannot be moved.")); + break; + } + compose_attach_swap(msg->content, idx, menu->current); + menu->redraw = 1; + menu->current++; + break; + + case OP_COMPOSE_GROUP_ALTS: + { + BODY *group, *bptr, *alts; + ATTACHPTR *gptr; + int i, j; + char *p; + + if (menu->tagged < 2) + { + mutt_error(_("Grouping alternatives requires at least 2 tagged messages.")); + break; + } + +/* need to redo using mutt_gen_attach_list() */ + + group = safe_calloc(1, sizeof(BODY)); + group->type = TYPEMULTIPART; + group->subtype = "alternative"; + + alts = NULL; + for (i = 0, bptr = msg->content; bptr && bptr->next;) + { + /* always look at bptr->next, not bptr itself */ + if (bptr->next->tagged) + { + /* untag */ + bptr->next->tagged = 0; + + /* for first match, set group desc according to match */ +# define ALTS_TAG "Alternatives for \"%s\"" + if (!group->description) + { + p = bptr->next->description; + if (!p) + p = bptr->next->filename; + if (p) + { + group->description = safe_calloc(1, + strlen(p) + strlen(ALTS_TAG) + 1); + sprintf(group->description, ALTS_TAG, p); + } + } + + /* append bptr->next to the alts list, + * and remove from the msg->content list */ + if (alts == NULL) + { + group->parts = alts = bptr->next; + bptr->next = bptr->next->next; + alts->next = NULL; + } + else + { + alts->next = bptr->next; + bptr->next = bptr->next->next; + alts = alts->next; + alts->next = NULL; + } + + /* now delink the idx entry */ + for (j = i+1; j < idxlen-1; ++j) + { + idx[j] = idx[j+1]; + } + --idxlen; + } + else + { + bptr = bptr->next; + ++i; + } + } + + /* add group to attachment list */ + for (bptr = msg->content; bptr->next; bptr = bptr->next); + bptr->next = group; + group->next = NULL; + + gptr = safe_calloc(1, sizeof(ATTACHPTR)); + gptr->content = group; + idx[idxlen] = gptr; + update_idx(menu, idx, idxlen++); + + /* add a boundary */ + mutt_generate_boundary(&group->parameter); + + /* if no group desc yet, make one up */ + if (!group->description) + group->description = strdup("unknown alternative group"); + } + menu->redraw = 1; + break; + case OP_COMPOSE_ATTACH_FILE: { char *prompt, **files; diff --git a/doc/manual.xml.head b/doc/manual.xml.head --- a/doc/manual.xml.head +++ b/doc/manual.xml.head @@ -6436,6 +6436,23 @@ functions. </para> +<para> +Mutt includes some primitive ability to compose multipart/alternative +parts. In the Compose menu, attach the two (or more) alternatives as +usual. For example, attach "invitation.html" and then "invitation.txt". +(You can reorder them using the <move-up> (-) and <move-down> (+) +bindings.) Edit the descriptions, if you wish. Then tag the attachments +that are alternatives, and press the <group-alternatives> (&) binding +to group them together. The separate parts will be replaced by a single +new part with the multipart/alternative type. From this point on, the +alternatives must be manipulated or deleted as a group. +</para> + +<para> +Beware that such messages cannot be postponed. Once two attachments are +grouped as alternatives, they must be sent or lost. +</para> + </sect2> <sect2 id="compose-menu"> diff --git a/functions.h b/functions.h --- a/functions.h +++ b/functions.h @@ -341,6 +341,9 @@ { "edit-fcc", OP_COMPOSE_EDIT_FCC, "f" }, { "filter-entry", OP_FILTER, "F" }, { "get-attachment", OP_COMPOSE_GET_ATTACHMENT, "G" }, + { "group-alternatives", OP_COMPOSE_GROUP_ALTS, "&" }, + { "move-up", OP_COMPOSE_MOVE_UP, "-" }, + { "move-down", OP_COMPOSE_MOVE_DOWN, "+" }, { "display-toggle-weed", OP_DISPLAY_HEADERS, "h" }, { "ispell", OP_COMPOSE_ISPELL, "i" }, { "print-entry", OP_PRINT, "l" },