From: David Champion Date: Sun, 29 Jan 2017 02:47:15 +0000 (-0800) Subject: Adds capability to edit x-labels inside mutt, and to sort by label. X-Git-Tag: mutt-1-8-rel~36 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8583edba96dc050315dba9bfa9dcad17996f2d9a;p=mutt Adds capability to edit x-labels inside mutt, and to sort by label. --- diff --git a/OPS b/OPS index 9fb29acc..083a08e3 100644 --- a/OPS +++ b/OPS @@ -57,6 +57,7 @@ OP_DELETE_THREAD "delete all messages in thread" OP_DISPLAY_ADDRESS "display full address of sender" OP_DISPLAY_HEADERS "display message and toggle header weeding" OP_DISPLAY_MESSAGE "display a message" +OP_EDIT_LABEL "add, change, or delete a message's label" OP_EDIT_MESSAGE "edit the raw message" OP_EDITOR_BACKSPACE "delete the char in front of the cursor" OP_EDITOR_BACKWARD_CHAR "move the cursor one character to the left" diff --git a/commands.c b/commands.c index 2202a673..e734bbfe 100644 --- a/commands.c +++ b/commands.c @@ -533,9 +533,9 @@ int mutt_select_sort (int reverse) int method = Sort; /* save the current method in case of abort */ switch (mutt_multi_choice (reverse ? - _("Rev-Sort (d)ate/(f)rm/(r)ecv/(s)ubj/t(o)/(t)hread/(u)nsort/si(z)e/s(c)ore/s(p)am?: ") : - _("Sort (d)ate/(f)rm/(r)ecv/(s)ubj/t(o)/(t)hread/(u)nsort/si(z)e/s(c)ore/s(p)am?: "), - _("dfrsotuzcp"))) + _("Rev-Sort Date/Frm/Recv/Subj/tO/Thread/Unsort/siZe/sCore/sPam/Label?: ") : + _("Sort Date/Frm/Recv/Subj/tO/Thread/Unsort/siZe/sCore/sPam/Label?: "), + _("dfrsotuzcpl"))) { case -1: /* abort - don't resort */ return -1; @@ -579,6 +579,10 @@ int mutt_select_sort (int reverse) case 10: /* s(p)am */ Sort = SORT_SPAM; break; + + case 11: /* (l)abel */ + Sort = SORT_LABEL; + break; } if (reverse) Sort |= SORT_REVERSE; diff --git a/copy.c b/copy.c index 1ef441f4..3ba0bad0 100644 --- a/copy.c +++ b/copy.c @@ -111,6 +111,10 @@ mutt_copy_hdr (FILE *in, FILE *out, LOFF_T off_start, LOFF_T off_end, int flags, ignore = 0; } + if (flags & CH_UPDATE_LABEL && + mutt_strncasecmp ("X-Label:", buf, 8) == 0) + continue; + if (!ignore && fputs (buf, out) == EOF) return (-1); } @@ -414,6 +418,15 @@ mutt_copy_header (FILE *in, HEADER *h, FILE *out, int flags, const char *prefix) fprintf (out, "Lines: %d\n", h->lines); } + if (flags & CH_UPDATE_LABEL && h->xlabel_changed) + { + h->xlabel_changed = 0; + if (h->env->x_label != NULL) + if (fprintf(out, "X-Label: %s\n", h->env->x_label) != + 10 + strlen(h->env->x_label)) + return -1; + } + if ((flags & CH_NONEWLINE) == 0) { if (flags & CH_PREFIX) @@ -494,6 +507,9 @@ _mutt_copy_message (FILE *fpout, FILE *fpin, HEADER *hdr, BODY *body, _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context, hdr, 0); } + if (hdr->xlabel_changed) + chflags |= CH_UPDATE_LABEL; + if ((flags & MUTT_CM_NOHEADER) == 0) { if (flags & MUTT_CM_PREFIX) diff --git a/copy.h b/copy.h index 4d6fc54a..2ba54143 100644 --- a/copy.h +++ b/copy.h @@ -53,6 +53,7 @@ #define CH_UPDATE_IRT (1<<16) /* update In-Reply-To: */ #define CH_UPDATE_REFS (1<<17) /* update References: */ #define CH_DISPLAY (1<<18) /* display result to user */ +#define CH_UPDATE_LABEL (1<<19) /* update X-Label: from hdr->env->x_label? */ int mutt_copy_hdr (FILE *, FILE *, LOFF_T, LOFF_T, int, const char *); diff --git a/curs_main.c b/curs_main.c index 227f567d..6362073b 100644 --- a/curs_main.c +++ b/curs_main.c @@ -2093,6 +2093,21 @@ int mutt_index_menu (void) menu->redraw = REDRAW_FULL; break; + case OP_EDIT_LABEL: + + CHECK_MSGCOUNT; + CHECK_READONLY; + rc = mutt_label_message(tag ? NULL : CURHDR); + if (rc > 0) { + Context->changed = 1; + menu->redraw = REDRAW_FULL; + mutt_message ("%d label%s changed.", rc, rc == 1 ? "" : "s"); + } + else { + mutt_message _("No labels changed."); + } + break; + case OP_LIST_REPLY: CHECK_ATTACH; diff --git a/doc/manual.xml.head b/doc/manual.xml.head index 44e38082..f49862c9 100644 --- a/doc/manual.xml.head +++ b/doc/manual.xml.head @@ -6063,6 +6063,12 @@ not a standard message header field, but it can easily be inserted by procmail and other mail filtering agents. + +You can change or delete the X-Label: field within +Mutt using the edit-label command, bound to the +y key by default. This works for tagged messages, too. + + Lastly, Mutt has the ability to sort the mailbox into threads. A thread is a diff --git a/functions.h b/functions.h index b5033a84..25a1306f 100644 --- a/functions.h +++ b/functions.h @@ -99,6 +99,7 @@ const struct binding_t OpMain[] = { /* map: index */ { "delete-thread", OP_DELETE_THREAD, "\004" }, { "delete-subthread", OP_DELETE_SUBTHREAD, "\033d" }, { "edit", OP_EDIT_MESSAGE, "e" }, + { "edit-label", OP_EDIT_LABEL, "y" }, { "edit-type", OP_EDIT_TYPE, "\005" }, { "forward-message", OP_FORWARD_MESSAGE, "f" }, { "flag-message", OP_FLAG_MESSAGE, "F" }, @@ -200,6 +201,7 @@ const struct binding_t OpPager[] = { /* map: pager */ { "set-flag", OP_MAIN_SET_FLAG, "w" }, { "clear-flag", OP_MAIN_CLEAR_FLAG, "W" }, { "edit", OP_EDIT_MESSAGE, "e" }, + { "edit-label", OP_EDIT_LABEL, "y" }, { "edit-type", OP_EDIT_TYPE, "\005" }, { "forward-message", OP_FORWARD_MESSAGE, "f" }, { "flag-message", OP_FLAG_MESSAGE, "F" }, diff --git a/headers.c b/headers.c index 0a759988..48195bd4 100644 --- a/headers.c +++ b/headers.c @@ -211,3 +211,61 @@ void mutt_edit_headers (const char *editor, } } } + +/* + * add an X-Label: field. + */ +static int label_message(HEADER *hdr, char *new) +{ + if (hdr == NULL) + return 0; + if (hdr->env->x_label == NULL && new == NULL) + return 0; + if (hdr->env->x_label != NULL && new != NULL && + strcmp(hdr->env->x_label, new) == 0) + return 0; + if (hdr->env->x_label != NULL) + FREE(&hdr->env->x_label); + if (new == NULL) + hdr->env->x_label = NULL; + else + hdr->env->x_label = safe_strdup(new); + return hdr->changed = hdr->xlabel_changed = 1; +} + +int mutt_label_message(HEADER *hdr) +{ + char buf[LONG_STRING], *new; + int i; + int changed; + + *buf = '\0'; + if (hdr != NULL && hdr->env->x_label != NULL) { + strncpy(buf, hdr->env->x_label, LONG_STRING); + } + + if (mutt_get_field("Label: ", buf, sizeof(buf), 0 /* | MUTT_CLEAR */) != 0) + return 0; + + new = buf; + SKIPWS(new); + if (*new == '\0') + new = NULL; + + changed = 0; + if (hdr != NULL) { + changed += label_message(hdr, new); + } else { +#define HDR_OF(index) Context->hdrs[Context->v2r[(index)]] + for (i = 0; i < Context->vcount; ++i) { + if (HDR_OF(i)->tagged) + if (label_message(HDR_OF(i), new)) { + ++changed; + mutt_set_flag(Context, HDR_OF(i), + MUTT_TAG, 0); + } + } + } + + return changed; +} diff --git a/imap/imap.c b/imap/imap.c index 36f94aa3..e4412bb1 100644 --- a/imap/imap.c +++ b/imap/imap.c @@ -1242,7 +1242,7 @@ int imap_sync_mailbox (CONTEXT* ctx, int expunge, int* index_hint) * we delete the message and reupload it. * This works better if we're expunging, of course. */ if ((h->env && (h->env->refs_changed || h->env->irt_changed)) || - h->attach_del) + h->attach_del || h->xlabel_changed) { mutt_message (_("Saving changed messages... [%d/%d]"), n+1, ctx->msgcount); @@ -1252,6 +1252,7 @@ int imap_sync_mailbox (CONTEXT* ctx, int expunge, int* index_hint) dprint (1, (debugfile, "imap_sync_mailbox: Error opening mailbox in append mode\n")); else _mutt_save_message (h, appendctx, 1, 0, 0); + h->xlabel_changed = 0; } } } diff --git a/init.h b/init.h index 5b25761a..2cffbc2e 100644 --- a/init.h +++ b/init.h @@ -3813,6 +3813,7 @@ const struct mapping_t SortMethods[] = { { "to", SORT_TO }, { "score", SORT_SCORE }, { "spam", SORT_SPAM }, + { "label", SORT_LABEL }, { NULL, 0 } }; @@ -3832,6 +3833,7 @@ const struct mapping_t SortAuxMethods[] = { { "to", SORT_TO }, { "score", SORT_SCORE }, { "spam", SORT_SPAM }, + { "label", SORT_LABEL }, { NULL, 0 } }; diff --git a/mh.c b/mh.c index a96d09c1..0833b025 100644 --- a/mh.c +++ b/mh.c @@ -1793,7 +1793,7 @@ static int mh_sync_message (CONTEXT * ctx, int msgno) { HEADER *h = ctx->hdrs[msgno]; - if (h->attach_del || + if (h->attach_del || h->xlabel_changed || (h->env && (h->env->refs_changed || h->env->irt_changed))) if (mh_rewrite_message (ctx, msgno) != 0) return -1; @@ -1805,7 +1805,7 @@ static int maildir_sync_message (CONTEXT * ctx, int msgno) { HEADER *h = ctx->hdrs[msgno]; - if (h->attach_del || + if (h->attach_del || h->xlabel_changed || (h->env && (h->env->refs_changed || h->env->irt_changed))) { /* when doing attachment deletion/rethreading, fall back to the MH case. */ @@ -1927,6 +1927,7 @@ int mh_sync_mailbox (CONTEXT * ctx, int *index_hint) } } else if (ctx->hdrs[i]->changed || ctx->hdrs[i]->attach_del || + ctx->hdrs[i]->xlabel_changed || (ctx->magic == MUTT_MAILDIR && (option (OPTMAILDIRTRASH) || ctx->hdrs[i]->trash) && (ctx->hdrs[i]->deleted != ctx->hdrs[i]->trash))) diff --git a/mutt.h b/mutt.h index c67c260d..8a8a0266 100644 --- a/mutt.h +++ b/mutt.h @@ -760,6 +760,7 @@ typedef struct header * This flag is used by the maildir_trash * option. */ + unsigned int xlabel_changed : 1; /* editable - used for syncing */ /* timezone of the sender of this message */ unsigned int zhours : 5; diff --git a/pager.c b/pager.c index c0fbbe39..96f0d2c6 100644 --- a/pager.c +++ b/pager.c @@ -2814,6 +2814,18 @@ search_next: redraw = REDRAW_FULL; break; + case OP_EDIT_LABEL: + CHECK_MODE(IsHeader (extra)); + rc = mutt_label_message(extra->hdr); + if (rc > 0) { + Context->changed = 1; + redraw = REDRAW_FULL; + mutt_message ("%d label%s changed.", rc, rc == 1 ? "" : "s"); + } + else { + mutt_message _("No labels changed."); + } + break; case OP_MAIL_KEY: if (!(WithCrypto & APPLICATION_PGP)) diff --git a/protos.h b/protos.h index ce8f0201..a989c5f8 100644 --- a/protos.h +++ b/protos.h @@ -186,6 +186,7 @@ void mutt_edit_content_type (HEADER *, BODY *, FILE *); void mutt_edit_file (const char *, const char *); void mutt_edit_headers (const char *, const char *, HEADER *, char *, size_t); int mutt_filter_unprintable (char **); +int mutt_label_message (HEADER *); void mutt_curses_error (const char *, ...); void mutt_curses_message (const char *, ...); void mutt_encode_descriptions (BODY *, short); diff --git a/sort.c b/sort.c index 76e9e797..7be810dc 100644 --- a/sort.c +++ b/sort.c @@ -210,6 +210,36 @@ static int compare_spam (const void *a, const void *b) return (SORTCODE(result)); } +int compare_label (const void *a, const void *b) +{ + HEADER **ppa = (HEADER **) a; + HEADER **ppb = (HEADER **) b; + int ahas, bhas, result = 0; + + /* As with compare_spam, not all messages will have the x-label + * property. Blank X-Labels are treated as null in the index + * display, so we'll consider them as null for sort, too. */ + ahas = (*ppa)->env && (*ppa)->env->x_label && *((*ppa)->env->x_label); + bhas = (*ppb)->env && (*ppb)->env->x_label && *((*ppb)->env->x_label); + + /* First we bias toward a message with a label, if the other does not. */ + if (ahas && !bhas) + return (SORTCODE(-1)); + if (!ahas && bhas) + return (SORTCODE(1)); + + /* If neither has a label, use aux sort. */ + if (!ahas && !bhas) + { + AUXSORT(result, a, b); + return (SORTCODE(result)); + } + + /* If both have a label, we just do a lexical compare. */ + result = mutt_strcasecmp((*ppa)->env->x_label, (*ppb)->env->x_label); + return (SORTCODE(result)); +} + sort_t *mutt_get_sort_func (int method) { switch (method & SORT_MASK) @@ -232,6 +262,8 @@ sort_t *mutt_get_sort_func (int method) return (compare_score); case SORT_SPAM: return (compare_spam); + case SORT_LABEL: + return (compare_label); default: return (NULL); } diff --git a/sort.h b/sort.h index 9b87af96..b5bf9c87 100644 --- a/sort.h +++ b/sort.h @@ -35,6 +35,7 @@ #define SORT_UNREAD 16 #define SORT_FLAGGED 17 #define SORT_PATH 18 +#define SORT_LABEL 19 /* Sort and sort_aux are shorts, and are a composite of a * constant sort operation number and a set of compounded