<<< Date Index >>>     <<< Thread Index >>>

ignore-thread initial patch



The inital 'ignore-thread' patch (Version 0.0.1)

PREFACE
The patch is poorly tested but the risc of demaging
anything is low because the only permanent change is
made in storing a flag in the X-Status header in 
a well-tested manner (as the 'replied' flag for example).


INTENTION
I didn't find a easy-to-use way to handle high volume 
mailing-lists with only less interesting threads. 
Deleting is not a solution because follow-ups for
this thread are not blockt nor marked.
So i tried to do this by coding a 'ignore-thread'
feature as i know from news-readers.
The ignored threads are collapsed, new messages
for this thread are also marked ignored and the
threads are ignored in many functions.
If the thread is old enough that no follow-ups are
expecet one could delete it.


DESCRIPTION

This patch adds the 'ignore-thread' feature to mutt.
It only has affects if the sort-order is 'thread'.

- A 'ignore-thread' function is added. It has no default 
  keybinding. It is available in index- and pager-mode.
- When opening a mailbox all threads marked 'ignore-thread'
  are automatically collapsed. (done by mutt_sort_headers)
- If in a thread a message is marked 'ignore-thread' all
  other messages of this thread are marked too.
  (done by mutt_sort_threads)

- If invoked the whole thread is marked 'ignore-thread'.
  and the thread is collapsed. It is a toggle-function
  and if it is invoked again the flag is removed and
  the thread is uncollapsed.

- While a thread is marked 'ignore-thread' and it is collapsed
  the thread:
  - is not automatically uncollapsed in pager-mode even if
    there are unread or new messages.
  - is ignored from the jump to the next/previos new/unread 
    message.
  - is not uncollapsed by the 'uncollapse all' function and 
    the 'uncollapse all' function cannot start on a thread
    marked 'ignore-thread'. 
    Collapse/uncollapse on a individual thread works as usual.

- The 'ignore-thread' status is shown by the following indicators:
  'i': message is read (if collapsed: all messages are read)
  'I': message is new or unread (if collapsed: one or more
       unread or new messages exist in the thread)

- The 'ignore-thread' status is made permanent by adding a
  'I' flag to the 'X-Status' header. 
  (I didn't find any standards for the use of the X-Status
  header. Maybe the char 'I' is reserved for another usage,
  so every other char is possible. Maybe the X-Status header
  is the wrong place, so a 'X-Mutt-ignore-thread' header
  would do the work too.)


OTHER CHANGES
Because the ignored threads are collapsed as often as possible
the collapse/uncollapse function is added to the pager-mode.


LICENCE
GPL


I hope the patch is usefull for mutt-users espacially 
for mailing-list users.

-- 
gerhard oettl

diff -ur -X diff.ignore cvs/OPS dev/OPS
--- cvs/OPS     2003-07-04 19:07:22.000000000 +0200
+++ dev/OPS     2003-12-13 23:06:05.000000000 +0100
@@ -174,3 +174,4 @@
 OP_MAIN_SHOW_LIMIT "show currently active limit pattern"
 OP_MAIN_COLLAPSE_THREAD "collapse/uncollapse current thread"
 OP_MAIN_COLLAPSE_ALL "collapse/uncollapse all threads"
+OP_MAIN_IGNORE_THREAD "toggle a message's 'ignore-thread' flag"
diff -ur -X diff.ignore cvs/copy.c dev/copy.c
--- cvs/copy.c  2003-09-19 15:03:25.000000000 +0200
+++ dev/copy.c  2003-12-13 23:06:05.000000000 +0100
@@ -379,7 +379,7 @@
          return (-1);
       }
 
-      if (h->flagged || h->replied)
+      if (h->flagged || h->replied || h->ignore_thread)
       {
        if (fputs ("X-Status: ", out) == EOF)
          return (-1);
@@ -395,6 +395,12 @@
          if (fputc ('F', out) == EOF)
            return (-1);
        }
+
+       if (h->ignore_thread)
+       {
+         if (fputc (M_XSTATUS_IGNORE_THREAD, out) == EOF)
+           return (-1);
+       }
        
        if (fputc ('\n', out) == EOF)
          return (-1);
diff -ur -X diff.ignore cvs/curs_main.c dev/curs_main.c
--- cvs/curs_main.c     2003-09-03 19:09:09.000000000 +0200
+++ dev/curs_main.c     2003-12-14 10:37:36.000000000 +0100
@@ -313,6 +313,7 @@
   mutt_sort_headers (Context, (check == M_REOPENED));
 
   /* uncollapse threads with new mail */
+  /* let threads collapsed if they have the ignore-thread flag */
   if ((Sort & SORT_MASK) == SORT_THREADS)
   {
     if (check == M_REOPENED)
@@ -325,7 +326,8 @@
       {
        for (j = h; !j->message; j = j->child)
          ;
-       mutt_uncollapse_thread (Context, j->message);
+       if (!(j->message)->ignore_thread)
+         mutt_uncollapse_thread (Context, j->message);
       }
       mutt_set_virtual (Context);
     }
@@ -338,7 +340,7 @@
        for (k = 0; k < Context->msgcount; k++)
        {
          HEADER *h = Context->hdrs[k];
-         if (h == save_new[j] && (!Context->pattern || h->limited))
+         if (h == save_new[j] && (!Context->pattern || h->limited) && 
!h->ignore_thread)
            mutt_uncollapse_thread (Context, h);
        }
       }
@@ -412,6 +414,7 @@
 {
   char buf[LONG_STRING], helpstr[SHORT_STRING];
   int op = OP_NULL;
+  int prev_op = OP_NULL;
   int done = 0;                /* controls when to exit the "event" loop */
   int i = 0, j;
   int tag = 0;                 /* has the tag-prefix command been pressed? */
@@ -1124,13 +1127,15 @@
 
        unset_option (OPTNEEDRESORT);
 
-       if ((Sort & SORT_MASK) == SORT_THREADS && CURHDR->collapsed)
+       if ((Sort & SORT_MASK) == SORT_THREADS && CURHDR->collapsed && 
+           !CURHDR->ignore_thread && prev_op != OP_MAIN_COLLAPSE_THREAD)
        {
          mutt_uncollapse_thread (Context, CURHDR);
          mutt_set_virtual (Context);
          if (option (OPTUNCOLLAPSEJUMP))
            menu->current = mutt_thread_next_unread (Context, CURHDR);
        }
+       prev_op = OP_NULL;
  
        if ((op = mutt_display_message (CURHDR)) == -1)
        {
@@ -1346,10 +1351,14 @@
 
          if (CURHDRi->collapsed && (Sort & SORT_MASK) == SORT_THREADS)
          {
-           if (UNREAD (CURHDRi) && first_unread == -1)
-             first_unread = i;
-           if (UNREAD (CURHDRi) == 1 && first_new == -1)
-             first_new = i;
+           /* include collapsed threads only if they dont have the 
ignore-thread flag */
+           if (!CURHDRi->ignore_thread)
+           {
+             if (UNREAD (CURHDRi) && first_unread == -1)
+               first_unread = i;
+             if (UNREAD (CURHDRi) == 1 && first_new == -1)
+               first_new = i;
+           }
          }
          else if ((!CURHDRi->deleted && !CURHDRi->read))
          {
@@ -1616,6 +1625,51 @@
          break;
        }
 
+       if (menu->menu == MENU_PAGER)
+       {
+         prev_op = op;
+         op = OP_DISPLAY_MESSAGE;
+         continue;
+       }
+
+       menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
+
+       break;
+
+      case OP_MAIN_IGNORE_THREAD:
+       CHECK_MSGCOUNT;
+        CHECK_VISIBLE;
+       CHECK_READONLY;
+
+        if ((Sort & SORT_MASK) != SORT_THREADS)
+        {
+         mutt_error _("Threading is not enabled.");
+         break;
+       }
+      
+       if (CURHDR->ignore_thread)
+       {
+         mutt_unignore_thread (Context, CURHDR);
+          if (CURHDR->collapsed) 
+           menu->current = mutt_uncollapse_thread (Context, CURHDR);
+         mutt_set_virtual (Context);
+         if (option (OPTUNCOLLAPSEJUMP))
+           menu->current = mutt_thread_next_unread (Context, CURHDR);
+       }
+       else 
+       {
+         mutt_ignore_thread (Context, CURHDR);
+          if (!CURHDR->collapsed)
+           menu->current = mutt_collapse_thread (Context, CURHDR);
+         mutt_set_virtual (Context);
+       }
+
+       if (menu->menu == MENU_PAGER)
+       {
+         op = OP_DISPLAY_MESSAGE;
+         continue;
+       }
+
        menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
 
        break;
@@ -1630,6 +1684,12 @@
          break;
        }
 
+        if (CURHDR->ignore_thread)
+        {
+         mutt_error _("'un/collapse-all' cannot start from a ignored thread.");
+         break;
+       }
+
         {
          HEADER *h, *base;
          THREAD *thread, *top;
@@ -1655,7 +1715,10 @@
            if (h->collapsed != Context->collapsed)
            {
              if (h->collapsed)
-               mutt_uncollapse_thread (Context, h);
+             {
+               if (!h->ignore_thread)
+                 mutt_uncollapse_thread (Context, h);
+             }
              else if (option (OPTCOLLAPSEUNREAD) || !UNREAD (h))
                mutt_collapse_thread (Context, h);
            }
diff -ur -X diff.ignore cvs/flags.c dev/flags.c
--- cvs/flags.c 2003-01-31 00:48:36.000000000 +0100
+++ dev/flags.c 2003-12-14 11:57:59.000000000 +0100
@@ -260,6 +260,32 @@
        if (upd_ctx) ctx->tagged--;
       }
       break;
+
+    case M_IGNORE_THREAD:
+
+#ifdef USE_IMAP
+       if (ctx && ctx->magic == M_IMAP)
+               if (mutt_bit_isset (((IMAP_DATA *)ctx->data)->capabilities, 
ACL) \
+               && !mutt_bit_isset(((IMAP_DATA 
*)ctx->data)->rights,IMAP_ACL_WRITE))
+                       return;
+#endif
+
+      if (bf)
+      {
+       if (!h->ignore_thread)
+       {
+         h->ignore_thread = 1;
+         h->changed = 1;
+         if (upd_ctx) ctx->changed = 1;
+       }
+      }
+      else if (h->ignore_thread)
+      {
+       h->ignore_thread = 0;
+       h->changed = 1;
+       if (upd_ctx) ctx->changed = 1;
+      }
+      break;
   }
 
   mutt_set_header_color(ctx, h);
diff -ur -X diff.ignore cvs/functions.h dev/functions.h
--- cvs/functions.h     2003-07-04 19:07:22.000000000 +0200
+++ dev/functions.h     2003-12-13 23:06:05.000000000 +0100
@@ -147,6 +147,7 @@
   { "mail-key",                        OP_MAIL_KEY,                    "\033k" 
},
   { "decrypt-copy",            OP_DECRYPT_COPY,                NULL },
   { "decrypt-save",            OP_DECRYPT_SAVE,                NULL },
+  { "ignore-thread",           OP_MAIN_IGNORE_THREAD,          NULL },
 
 
   { NULL,                      0,                              NULL }
@@ -185,6 +186,8 @@
   { "print-message",   OP_PRINT,                       "p" },
   { "previous-thread", OP_MAIN_PREV_THREAD,            "\020" },
   { "previous-subthread",OP_MAIN_PREV_SUBTHREAD,       "\033p" },
+  { "collapse-thread", OP_MAIN_COLLAPSE_THREAD,        "\033v" },
+  { "ignore-thread",   OP_MAIN_IGNORE_THREAD,          NULL },
   { "quit",            OP_QUIT,                        "Q" },
   { "exit",            OP_EXIT,                        "q" },
   { "reply",           OP_REPLY,                       "r" },
diff -ur -X diff.ignore cvs/hdrline.c dev/hdrline.c
--- cvs/hdrline.c       2003-03-03 15:01:06.000000000 +0100
+++ dev/hdrline.c       2003-12-13 23:06:05.000000000 +0100
@@ -251,6 +251,8 @@
   int is_index = (flags & M_FORMAT_INDEX);
 #define THREAD_NEW (threads && hdr->collapsed && hdr->num_hidden > 1 && 
mutt_thread_contains_unread (ctx, hdr) == 1)
 #define THREAD_OLD (threads && hdr->collapsed && hdr->num_hidden > 1 && 
mutt_thread_contains_unread (ctx, hdr) == 2)
+#define THREAD_IGNORE_NEW (THREAD_NEW && hdr->ignore_thread)
+#define THREAD_IGNORE_UNREAD (THREAD_OLD && hdr->ignore_thread)
   size_t len;
 
   hdr = hfi->hdr;
@@ -557,6 +559,11 @@
        ch = '*';
       else if (hdr->flagged)
        ch = '!';
+      else if (hdr->ignore_thread)
+        if (hdr->read)
+         ch = 'i';
+       else
+         ch = 'I';
       else if (hdr->replied)
        ch = 'r';
       else if (hdr->read && (ctx && ctx->msgnotreadyet != hdr->msgno))
@@ -634,9 +641,12 @@
         ch = 'K';
 
       snprintf (buf2, sizeof (buf2),
-               "%c%c%c", (THREAD_NEW ? 'n' : (THREAD_OLD ? 'o' : 
+               "%c%c%c", ((THREAD_IGNORE_NEW || THREAD_IGNORE_UNREAD ||
+               (threads && hdr->ignore_thread && !hdr->read)) ? 'I' :
+               (threads && hdr->ignore_thread ? 'i' : (
+               THREAD_NEW ? 'n' : (THREAD_OLD ? 'o' :
                ((hdr->read && (ctx && ctx->msgnotreadyet != hdr->msgno))
-               ? (hdr->replied ? 'r' : ' ') : (hdr->old ? 'O' : 'N')))),
+               ? (hdr->replied ? 'r' : ' ') : (hdr->old ? 'O' : 'N')))))),
                hdr->deleted ? 'D' : (hdr->attach_del ? 'd' : ch),
                hdr->tagged ? '*' :
                (hdr->flagged ? '!' :
@@ -694,6 +704,8 @@
   return (src);
 #undef THREAD_NEW
 #undef THREAD_OLD
+#undef THREAD_IGNORE_NEW
+#undef THREAD_IGNORE_UNREAD
 }
 
 void
diff -ur -X diff.ignore cvs/mailbox.h dev/mailbox.h
--- cvs/mailbox.h       2002-11-12 08:53:09.000000000 +0100
+++ dev/mailbox.h       2003-12-13 23:06:05.000000000 +0100
@@ -28,6 +28,9 @@
                                * safe_fopen() for mbox-style folders.
                                */
 
+#define M_XSTATUS_IGNORE_THREAD  'I'  /* char for saving ignore-thread to 
X-Status */
+
+
 /* mx_open_new_message() */
 #define M_ADD_FROM     1       /* add a From_ line */
 
diff -ur -X diff.ignore cvs/mbox.c dev/mbox.c
--- cvs/mbox.c  2003-08-05 15:55:47.000000000 +0200
+++ dev/mbox.c  2003-12-13 23:06:05.000000000 +0100
@@ -1169,6 +1169,7 @@
           */
          mutt_set_flag (ctx, ctx->hdrs[i], M_FLAG, old_hdrs[j]->flagged);
          mutt_set_flag (ctx, ctx->hdrs[i], M_REPLIED, old_hdrs[j]->replied);
+         mutt_set_flag (ctx, ctx->hdrs[i], M_IGNORE_THREAD, 
old_hdrs[j]->ignore_thread);
          mutt_set_flag (ctx, ctx->hdrs[i], M_OLD, old_hdrs[j]->old);
          mutt_set_flag (ctx, ctx->hdrs[i], M_READ, old_hdrs[j]->read);
        }
diff -ur -X diff.ignore cvs/mutt.h dev/mutt.h
--- cvs/mutt.h  2003-10-04 22:34:59.000000000 +0200
+++ dev/mutt.h  2003-12-13 23:06:05.000000000 +0100
@@ -178,6 +178,9 @@
 #define M_THREAD_GET_HIDDEN    (1<<2)
 #define M_THREAD_UNREAD                (1<<3)
 #define M_THREAD_NEXT_UNREAD   (1<<4)
+#define M_THREAD_IGNORE        (1<<5)
+#define M_THREAD_UNIGNORE      (1<<6)
+
 
 enum
 {
@@ -203,6 +206,7 @@
   M_LIMIT,
   M_EXPIRED,
   M_SUPERSEDED,
+  M_IGNORE_THREAD,
 
   /* actions for mutt_pattern_comp/mutt_pattern_exec */
   M_AND,
@@ -666,6 +670,7 @@
   /* the following are used to support collapsing threads  */
   unsigned int collapsed : 1;  /* is this message part of a collapsed thread? 
*/
   unsigned int limited : 1;    /* is this message in a limited view?  */
+  unsigned int ignore_thread : 1;   /* is this message part of a ignored 
thread?  */
   size_t num_hidden;           /* number of hidden messages in this view */
 
   short recipient;             /* user_is_recipient()'s return value, cached */
diff -ur -X diff.ignore cvs/pager.c dev/pager.c
--- cvs/pager.c 2003-12-03 09:19:11.000000000 +0100
+++ dev/pager.c 2003-12-13 23:06:05.000000000 +0100
@@ -2146,6 +2146,19 @@
        km_error_key (MENU_PAGER);
        break;
 
+      case OP_MAIN_COLLAPSE_THREAD:
+       CHECK_MODE(IsHeader (extra));
+       rc = OP_MAIN_COLLAPSE_THREAD;
+       ch = -1;
+       break;
+
+      case OP_MAIN_IGNORE_THREAD:
+       CHECK_MODE(IsHeader (extra));
+       CHECK_READONLY;
+       rc = OP_MAIN_IGNORE_THREAD;
+       ch = -1;
+       break;
+
        /* --------------------------------------------------------------------
         * The following are operations on the current message rather than
         * adjusting the view of the message.
diff -ur -X diff.ignore cvs/parse.c dev/parse.c
--- cvs/parse.c 2003-09-19 15:03:26.000000000 +0200
+++ dev/parse.c 2003-12-13 23:06:05.000000000 +0100
@@ -1192,6 +1192,9 @@
            case 'F':
            hdr->flagged = 1;
            break;
+           case M_XSTATUS_IGNORE_THREAD:
+           hdr->ignore_thread = 1;
+           break;
            default:
            break;
          }
diff -ur -X diff.ignore cvs/protos.h dev/protos.h
--- cvs/protos.h        2003-10-04 22:34:59.000000000 +0200
+++ dev/protos.h        2003-12-13 23:06:05.000000000 +0100
@@ -50,6 +50,8 @@
 #define mutt_get_hidden(x,y)_mutt_traverse_thread (x,y,M_THREAD_GET_HIDDEN) 
 #define mutt_thread_contains_unread(x,y) _mutt_traverse_thread 
(x,y,M_THREAD_UNREAD)
 #define mutt_thread_next_unread(x,y) 
_mutt_traverse_thread(x,y,M_THREAD_NEXT_UNREAD)
+#define mutt_ignore_thread(x,y) _mutt_traverse_thread (x,y,M_THREAD_IGNORE)
+#define mutt_unignore_thread(x,y) _mutt_traverse_thread (x,y,M_THREAD_UNIGNORE)
 int _mutt_traverse_thread (CONTEXT *ctx, HEADER *hdr, int flag);
 
 
diff -ur -X diff.ignore cvs/sort.c dev/sort.c
--- cvs/sort.c  2003-11-12 12:40:27.000000000 +0100
+++ dev/sort.c  2003-12-13 23:06:05.000000000 +0100
@@ -258,6 +258,7 @@
   }
 
   /* re-collapse threads marked as collapsed */
+  /* and threads marked for ignoring if it is a "init-sort" */
   if ((Sort & SORT_MASK) == SORT_THREADS)
   {
     top = ctx->tree;
@@ -267,7 +268,7 @@
        thread = thread->child;
       h = thread->message;
 
-      if (h->collapsed)
+      if (h->collapsed || (h->ignore_thread && init))
        mutt_collapse_thread (ctx, h);
       top = top->next;
     }
diff -ur -X diff.ignore cvs/thread.c dev/thread.c
--- cvs/thread.c        2003-12-13 21:50:08.000000000 +0100
+++ dev/thread.c        2003-12-13 23:10:50.000000000 +0100
@@ -108,6 +108,69 @@
   }
 }
 
+/* check if all messages in a ignored thread have the ignore-tread status */
+static void mutt_check_ignore_thread (CONTEXT *ctx)
+{
+  THREAD *thread, *top;
+  HEADER *roothdr;
+  int ignore_thread, missing_count;
+      
+  thread = ctx->tree; 
+  while (thread)
+  {
+    ignore_thread = 0;
+    missing_count = 0;
+    roothdr = NULL;
+
+    top = thread;
+    while (!thread->message)
+      thread = thread->child;
+ 
+    FOREVER
+    {
+      if (thread->message)
+      {
+       if (!roothdr)
+         roothdr = thread->message;
+
+       if ((thread->message)->ignore_thread)
+         ignore_thread = 1;
+       else
+         missing_count++;
+      }
+
+      if (thread->child)
+       thread = thread->child;
+      else if (thread == top)
+       break;
+      else if (thread->next)
+       thread = thread->next;
+      else
+      {
+       int done = 0;
+       while (!thread->next)
+       {
+         thread = thread->parent;
+         if (thread == top)
+         {
+           done = 1;
+           break;
+         }
+       }
+       if (done)
+         break;
+       thread = thread->next;
+      }
+    }
+    if (ignore_thread && missing_count)
+    {
+      mutt_ignore_thread(ctx, roothdr);
+    }
+    thread = thread->next;
+  }
+}
+
+
 /* this calculates whether a node is the root of a subtree that has visible
  * nodes, whether a node itself is visible, whether, if invisible, it has
  * depth anyway, and whether any of its later siblings are roots of visible
@@ -959,6 +1022,10 @@
 
     /* Draw the thread tree. */
     mutt_draw_tree (ctx);
+ 
+    /* check if all messages in a ignored tree have the ignore-thread status */
+    mutt_check_ignore_thread(ctx);
+ 
   }
 }
 
@@ -1163,6 +1230,13 @@
     }
   }
 
+  if (flag & (M_THREAD_IGNORE | M_THREAD_UNIGNORE))
+  {
+    cur->pair = 0; /* force index entry's color to be re-evaluated */
+    mutt_set_flag (ctx, cur, M_IGNORE_THREAD, flag & M_THREAD_IGNORE);
+  }
+
+
   if (thread == top && (thread = thread->child) == NULL)
   {
     /* return value depends on action requested */
@@ -1174,6 +1248,8 @@
       return (num_hidden);
     else if (flag & M_THREAD_NEXT_UNREAD)
       return (min_unread);
+    else if (flag & (M_THREAD_IGNORE | M_THREAD_UNIGNORE))
+      return (1);
   }
   
   FOREVER
@@ -1212,6 +1288,13 @@
       }
 
 
+      if (flag & (M_THREAD_IGNORE | M_THREAD_UNIGNORE))
+      {
+       cur->pair = 0; /* force index entry's color to be re-evaluated */
+       mutt_set_flag (ctx, cur, M_IGNORE_THREAD, flag & M_THREAD_IGNORE);
+      }
+
+
       if (!cur->read && CHECK_LIMIT)
       {
        if (cur->old)
@@ -1260,6 +1343,8 @@
     return (num_hidden+1);
   else if (flag & M_THREAD_NEXT_UNREAD)
     return (min_unread);
+  else if (flag & (M_THREAD_IGNORE | M_THREAD_UNIGNORE))
+    return (1);
 
   return (0);
 #undef CHECK_LIMIT