Skip to content

Instantly share code, notes, and snippets.

@oblitum oblitum/description.txt
Last active Aug 26, 2017

Embed
What would you like to do?
Adds compose menu bindings for grouping and moving attachments
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
index 76c2b62a..beb56144 100644
--- a/OPS
+++ b/OPS
@@ -38,7 +38,10 @@ OP_COMPOSE_EDIT_TO "edit the TO list"
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
index 0996b32b..187f1c4e 100644
--- a/compose.c
+++ b/compose.c
@@ -364,6 +364,8 @@ static int check_attachments(struct AttachPtr **idx, short idxlen)
for (int 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)
{
@@ -588,6 +590,41 @@ static void compose_menu_redraw(struct Menu *menu)
menu_redraw_current(menu);
}
+/*
+ * compose_attach_swap: swap two adjacent entries in the attachment list.
+ */
+static void compose_attach_swap(struct Body *msg, struct AttachPtr **idx,
+ short first)
+{
+ int i;
+ void *saved;
+ struct 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;
+}
+
/**
* cum_attachs_size - Cumulative Attachments Size
* @param menu Menu listing attachments
@@ -972,6 +1009,131 @@ int mutt_compose_menu(struct Header *msg, /* structure for new message */
mutt_message_hook(NULL, msg, MUTT_SEND2HOOK);
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:
+ {
+ struct Body *group, *bptr, *alts;
+ struct 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(struct 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(struct 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 = NULL, **files = NULL;
diff --git a/doc/manual.xml.head b/doc/manual.xml.head
index 0d6e6afb..22e48672 100755
--- a/doc/manual.xml.head
+++ b/doc/manual.xml.head
@@ -8201,6 +8201,18 @@ macro pager \cb |urlview\n
<para>See table
<xref linkend="tab-attachment-bindings" />for all available
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 &lt;move-up&gt; (-) and &lt;move-down&gt;
+ (+) bindings.) Edit the descriptions, if you wish. Then tag the
+ attachments that are alternatives, and press the &lt;group-alternatives&gt;
+ (&amp;) 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
index 80f1f387..7401e3cf 100644
--- a/functions.h
+++ b/functions.h
@@ -408,6 +408,9 @@ const struct Binding OpCompose[] = { /* map: compose */
{ "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" },
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.