]> granicus.if.org Git - neomutt/commitdiff
Adds capability to edit x-labels inside mutt, and to sort by label.
authorDavid Champion <dgc@bikeshed.us>
Fri, 8 Apr 2016 00:06:05 +0000 (01:06 +0100)
committerRichard Russon <rich@flatcap.org>
Tue, 17 May 2016 16:27:37 +0000 (17:27 +0100)
17 files changed:
OPS
commands.c
copy.c
copy.h
curs_main.c
doc/manual.xml.head
functions.h
headers.c
imap/imap.c
imap/message.c
init.h
mh.c
mutt.h
pager.c
protos.h
sort.c
sort.h

diff --git a/OPS b/OPS
index 8414a8b337671ddee819f83f06b26da76559ee92..20850b2d3b02d7a8f20315eac5e1556c96fff099 100644 (file)
--- a/OPS
+++ b/OPS
@@ -56,6 +56,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"
index 2a0cdc9c70fc947601afaadb52b7a0abeb98fa92..c439d67e2537e7c35a2d2c11b79affe1236647cb 100644 (file)
@@ -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 360c1a42c88a509b8e30096d3a5313fdec0145dc..2990d6ea8d8fc9a8b96e2566c93a8a2f34a76c47 100644 (file)
--- 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);
     }
@@ -413,6 +417,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)
@@ -493,6 +506,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 & M_CM_NOHEADER) == 0)
   {
     if (flags & M_CM_PREFIX)
diff --git a/copy.h b/copy.h
index 5f12a3c1e3b9ce1eb39cc34f8b4ee228922367a8..9a98d2175a25287dd6c1820a9caeaa2dc355ea51 100644 (file)
--- 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 *);
index a76aac998e0fc94f303fa3e4730f197b56eb71f9..8eb537b8cb036d340045346be560dc9f8c8595db 100644 (file)
@@ -2045,6 +2045,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;
index b90908f001610306861479d3bc7440f848d5e17c..5cc43d11f38a93dc3de1ccff40daa4e2e82a338e 100644 (file)
@@ -5989,6 +5989,12 @@ not a standard message header field, but it can easily be inserted by
 procmail and other mail filtering agents.
 </para>
 
+<para>
+You can change or delete the <quote>X-Label:</quote> field within
+Mutt using the <quote>edit-label</quote> command, bound to the
+<quote>y</quote> key by default.  This works for tagged messages, too.
+</para>
+
 <para>
 Lastly, Mutt has the ability to <link linkend="sort">sort</link> the
 mailbox into <link linkend="threads">threads</link>.  A thread is a
index 7a1c5a9f126b7da177fbbee023acacab45af07e3..d9622d9afd6dc4f7e748648e0829a98c62edfdf3 100644 (file)
@@ -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" },
@@ -187,6 +188,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" },
index 0a759988f30fdd5f6c648f146a142457c250df84..1009b7241cd464f0cf71ab914782164d66f45249 100644 (file)
--- 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 /* | M_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),
+            M_TAG, 0);
+        }
+    }
+  }
+
+  return changed;
+}
index 1b63b3a31ca6030951ebaa84cdc5482761ef7eca..2ce4db231de4981a7b70a15c41b77c19a861cdeb 100644 (file)
@@ -1222,7 +1222,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);
@@ -1232,6 +1232,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;
       }
     }
   }
index 02a726fbdef6cb3145cff0a263f3eca861d23343..98d5baf0c0fa0c702fc373ee9d686113cb0b9870 100644 (file)
@@ -406,6 +406,7 @@ int imap_fetch_message (MESSAGE *msg, CONTEXT *ctx, int msgno)
   IMAP_CACHE *cache;
   int read;
   int rc;
+  char *x_label = NULL;
   /* Sam's weird courier server returns an OK response even when FETCH
    * fails. Thanks Sam. */
   short fetched = 0;
diff --git a/init.h b/init.h
index 5a12ec0d06b7267b630e858b8393704ff85fb5cd..749285434d0a10d04b624efd52bbcae6333f9ee4 100644 (file)
--- a/init.h
+++ b/init.h
@@ -3606,6 +3606,7 @@ const struct mapping_t SortMethods[] = {
   { "to",              SORT_TO },
   { "score",           SORT_SCORE },
   { "spam",            SORT_SPAM },
+  { "label",           SORT_LABEL },
   { NULL,               0 }
 };
 
@@ -3625,6 +3626,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 bc876607e6a2ab48edf42fc4bbdfe9af274d021e..cd17f6181e6ba5fcf88cde97507df8b9bf6d3fe2 100644 (file)
--- a/mh.c
+++ b/mh.c
@@ -1612,7 +1612,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;
@@ -1624,7 +1624,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. */
@@ -1746,6 +1746,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 == M_MAILDIR
              && (option (OPTMAILDIRTRASH) || ctx->hdrs[i]->trash)
              && (ctx->hdrs[i]->deleted != ctx->hdrs[i]->trash)))
diff --git a/mutt.h b/mutt.h
index de26fd84226daf057ddcc9841c8d0da4828956c4..ca1453ebf5db4a1d80b1140b43639f39a82c3887 100644 (file)
--- a/mutt.h
+++ b/mutt.h
@@ -736,6 +736,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 8bfe72cc9cf9de83f0884e59b89fd3d3438d2e0a..674c0c2242c0034940af48ef72d43ea56586c358 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -2744,6 +2744,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))
index 8e5f7aa9d6ba86bdfb531680103eee1d40ca7ef9..7f7a6e8c28cde6fdeb5b5912f3e9c1da9a2c6415 100644 (file)
--- a/protos.h
+++ b/protos.h
@@ -184,6 +184,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 76e9e797230a8200e03fc4435bd7260626f36e9e..7bc4aadd4045b641fb9f03588c41d276af267d6f 100644 (file)
--- 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;
+
+  /* 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 f2832b21ea28de06f4955be60bb2504f1e0d9272..9927b779f619058bdf73b7f1829e2a4874b53972 100644 (file)
--- a/sort.h
+++ b/sort.h
@@ -31,6 +31,7 @@
 #define SORT_KEYID     12
 #define SORT_TRUST     13
 #define SORT_SPAM      14
+#define SORT_LABEL     15
 /* dgc: Sort & SortAux are shorts, so I'm bumping these bitflags up from
  * bits 4 & 5 to bits 8 & 9 to make room for more sort keys in the future. */
 #define SORT_MASK      0xff