]> granicus.if.org Git - mutt/commitdiff
Add virtual index to actx. (see #3728)
authorKevin McCarthy <kevin@8t8.us>
Fri, 11 Aug 2017 01:18:22 +0000 (18:18 -0700)
committerKevin McCarthy <kevin@8t8.us>
Fri, 11 Aug 2017 01:18:22 +0000 (18:18 -0700)
The virtual index is modeled after the CONTEXT.  Add a CURATTACH
helper to reduce code verbosity.  Store the actx as menu->data.

Simplify and consolidate the recvattach and compose menu update code
inside a function.

Because compose and recvattach share so much code, change compose to
use the virtual index even though it has no collapse/expand
functionality.

attach.c
attach.h
compose.c
recvattach.c

index 6a12d82f2fefc076e4a1a9c3b3df71368fc738ef..f09e5acca14dd28d913984d74a8ed3455f2c9b41 100644 (file)
--- a/attach.c
+++ b/attach.c
@@ -1042,7 +1042,7 @@ int mutt_print_attachment (FILE *fp, BODY *a)
   }
 }
 
-void mutt_actx_add_attach (ATTACH_CONTEXT *actx, ATTACHPTR *attach, MUTTMENU *menu)
+void mutt_actx_add_attach (ATTACH_CONTEXT *actx, ATTACHPTR *attach)
 {
   int i;
 
@@ -1050,10 +1050,9 @@ void mutt_actx_add_attach (ATTACH_CONTEXT *actx, ATTACHPTR *attach, MUTTMENU *me
   {
     actx->idxmax += 5;
     safe_realloc (&actx->idx, sizeof (ATTACHPTR *) * actx->idxmax);
+    safe_realloc (&actx->v2r, sizeof (short) * actx->idxmax);
     for (i = actx->idxlen; i < actx->idxmax; i++)
       actx->idx[i] = NULL;
-    if (menu)
-      menu->data = actx->idx;
   }
 
   actx->idx[actx->idxlen++] = attach;
@@ -1101,6 +1100,7 @@ void mutt_actx_free_entries (ATTACH_CONTEXT *actx)
     FREE (&actx->idx[i]);
   }
   actx->idxlen = 0;
+  actx->vcount = 0;
 
   for (i = 0; i < actx->fp_len; i++)
     safe_fclose (&actx->fp_idx[i]);
@@ -1121,6 +1121,7 @@ void mutt_free_attach_context (ATTACH_CONTEXT **pactx)
   actx = *pactx;
   mutt_actx_free_entries (actx);
   FREE (&actx->idx);
+  FREE (&actx->v2r);
   FREE (&actx->fp_idx);
   FREE (&actx->body_idx);
   FREE (pactx);  /* __FREE_CHECKED__ */
index 80eed5065b9de183cbc74f133e0434aaceb69fd3..b95230bf1044c151c390c1aaf71b7b7734856c53 100644 (file)
--- a/attach.h
+++ b/attach.h
@@ -44,6 +44,9 @@ typedef struct attach_ctx
   short idxlen;
   short idxmax;
 
+  short *v2r;             /* mapping from virtual to real attachment */
+  short vcount;           /* the number of virtual attachments */
+
   FILE **fp_idx;        /* Extra FILE* used for decryption */
   short fp_len;
   short fp_max;
@@ -72,7 +75,7 @@ void mutt_attach_resend (FILE *, HEADER *, ATTACH_CONTEXT *, BODY *);
 void mutt_attach_forward (FILE *, HEADER *, ATTACH_CONTEXT *, BODY *);
 void mutt_attach_reply (FILE *, HEADER *, ATTACH_CONTEXT *, BODY *, int);
 
-void mutt_actx_add_attach (ATTACH_CONTEXT *actx, ATTACHPTR *attach, MUTTMENU *menu);
+void mutt_actx_add_attach (ATTACH_CONTEXT *actx, ATTACHPTR *attach);
 void mutt_actx_add_fp (ATTACH_CONTEXT *actx, FILE *new_fp);
 void mutt_actx_add_body (ATTACH_CONTEXT *actx, BODY *new_body);
 void mutt_actx_free_entries (ATTACH_CONTEXT *actx);
index 87bcb47a168dc06c22952921ee83f1ac1af2466d..ba6d7889aaf567f041914466e9770a9d50660636 100644 (file)
--- a/compose.c
+++ b/compose.c
@@ -48,6 +48,7 @@ static const char* There_are_no_attachments = N_("There are no attachments.");
 
 #define CHECK_COUNT if (actx->idxlen == 0) { mutt_error _(There_are_no_attachments); break; }
 
+#define CURATTACH actx->idx[actx->v2r[menu->current]]
 
 
 enum
@@ -170,8 +171,10 @@ static void init_header_padding (void)
 
 static void snd_entry (char *b, size_t blen, MUTTMENU *menu, int num)
 {
+  ATTACH_CONTEXT *actx = (ATTACH_CONTEXT *)menu->data;
+
   mutt_FormatString (b, blen, 0, MuttIndexWindow->cols, NONULL (AttachFormat), mutt_attach_fmt,
-           (unsigned long)(((ATTACHPTR **) menu->data)[num]),
+           (unsigned long)(actx->idx[actx->v2r[num]]),
            MUTT_FORMAT_STAT_FILE | MUTT_FORMAT_ARROWCURSOR);
 }
 
@@ -414,39 +417,38 @@ static void edit_address_list (int line, ADDRESS **addr)
   mutt_paddstr (W, buf);
 }
 
-static int delete_attachment (MUTTMENU *menu, short *idxlen, int x)
+static int delete_attachment (ATTACH_CONTEXT *actx, int x)
 {
-  ATTACHPTR **idx = (ATTACHPTR **) menu->data;
+  ATTACHPTR **idx = actx->idx;
+  int rindex = actx->v2r[x];
   int y;
 
-  menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
-
-  if (x == 0 && menu->max == 1)
+  if (rindex == 0 && actx->idxlen == 1)
   {
     mutt_error _("You may not delete the only attachment.");
-    idx[x]->content->tagged = 0;
+    idx[rindex]->content->tagged = 0;
     return (-1);
   }
 
-  for (y = 0; y < *idxlen; y++)
+  for (y = 0; y < actx->idxlen; y++)
   {
-    if (idx[y]->content->next == idx[x]->content)
+    if (idx[y]->content->next == idx[rindex]->content)
     {
-      idx[y]->content->next = idx[x]->content->next;
+      idx[y]->content->next = idx[rindex]->content->next;
       break;
     }
   }
 
-  idx[x]->content->next = NULL;
-  idx[x]->content->parts = NULL;
-  mutt_free_body (&(idx[x]->content));
-  FREE (&idx[x]->tree);
-  FREE (&idx[x]);
-  for (; x < *idxlen - 1; x++)
-    idx[x] = idx[x+1];
-  idx[*idxlen - 1] = NULL;
-  menu->max = --(*idxlen);
-  
+  idx[rindex]->content->next = NULL;
+  idx[rindex]->content->parts = NULL;
+  mutt_free_body (&(idx[rindex]->content));
+  FREE (&idx[rindex]->tree);
+  FREE (&idx[rindex]);
+  for (; rindex < actx->idxlen - 1; rindex++)
+    idx[rindex] = idx[rindex+1];
+  idx[actx->idxlen - 1] = NULL;
+  actx->idxlen--;
+
   return (0);
 }
 
@@ -468,7 +470,7 @@ static void mutt_gen_compose_attach_list (ATTACH_CONTEXT *actx,
     else
     {
       new = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
-      mutt_actx_add_attach (actx, new, NULL);
+      mutt_actx_add_attach (actx, new);
       new->content = m;
       m->aptr = new;
       new->parent_type = parent_type;
@@ -477,9 +479,29 @@ static void mutt_gen_compose_attach_list (ATTACH_CONTEXT *actx,
       /* We don't support multipart messages in the compose menu yet */
     }
   }
+}
+
+static void mutt_update_compose_menu (ATTACH_CONTEXT *actx, MUTTMENU *menu, int init)
+{
+  if (init)
+  {
+    mutt_gen_compose_attach_list (actx, actx->hdr->content, -1, 0);
+    mutt_attach_init (actx);
+    menu->data = actx;
+  }
+
+  mutt_update_tree (actx);
 
-  if (level == 0)
-    mutt_update_tree (actx);
+  menu->max = actx->vcount;
+  if (menu->max)
+  {
+    if (menu->current >= menu->max)
+      menu->current = menu->max - 1;
+  }
+  else
+    menu->current = 0;
+
+  menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
 }
 
 static void update_idx (MUTTMENU *menu, ATTACH_CONTEXT *actx, ATTACHPTR *new)
@@ -488,10 +510,9 @@ static void update_idx (MUTTMENU *menu, ATTACH_CONTEXT *actx, ATTACHPTR *new)
   if (actx->idxlen)
     actx->idx[actx->idxlen - 1]->content->next = new->content;
   new->content->aptr = new;
-  mutt_actx_add_attach (actx, new, menu);
-  menu->current = actx->idxlen - 1;
-  mutt_update_tree (actx);
-  menu->max = actx->idxlen;
+  mutt_actx_add_attach (actx, new);
+  mutt_update_compose_menu (actx, menu, 0);
+  menu->current = actx->vcount - 1;
 }
 
 
@@ -508,11 +529,12 @@ static unsigned long cum_attachs_size (MUTTMENU *menu)
 {
   size_t s;
   unsigned short i;
-  ATTACHPTR **idx = menu->data;
+  ATTACH_CONTEXT *actx = menu->data;
+  ATTACHPTR **idx = actx->idx;
   CONTENT *info;
   BODY *b;
   
-  for (i = 0, s = 0; i < menu->max; i++)
+  for (i = 0, s = 0; i < actx->idxlen; i++)
   {
     b = idx[i]->content;
 
@@ -696,21 +718,19 @@ int mutt_compose_menu (HEADER *msg,   /* structure for new message */
   rd.msg = msg;
   rd.fcc = fcc;
 
-  actx = safe_calloc (sizeof(ATTACH_CONTEXT), 1);
-  mutt_gen_compose_attach_list (actx, msg->content, -1, 0);
-  mutt_attach_init (actx);
-
   menu = mutt_new_menu (MENU_COMPOSE);
   menu->offset = HDR_ATTACH;
-  menu->max = actx->idxlen;
   menu->make_entry = snd_entry;
   menu->tag = mutt_tag_attach;
-  menu->data = actx->idx;
   menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_COMPOSE, ComposeHelp);
   menu->custom_menu_redraw = compose_menu_redraw;
   menu->redraw_data = &rd;
   mutt_push_current_menu (menu);
 
+  actx = safe_calloc (sizeof(ATTACH_CONTEXT), 1);
+  actx->hdr = msg;
+  mutt_update_compose_menu (actx, menu, 1);
+
   while (loop)
   {
     switch (op = mutt_menuLoop (menu))
@@ -819,9 +839,7 @@ int mutt_compose_menu (HEADER *msg,   /* structure for new message */
        if (actx->idxlen && actx->idx[actx->idxlen - 1]->content->next)
        {
           mutt_actx_free_entries (actx);
-         mutt_gen_compose_attach_list (actx, msg->content, -1, 0);
-         menu->data = actx->idx;
-         menu->max = actx->idxlen;
+          mutt_update_compose_menu (actx, menu, 1);
        }
 
         menu->redraw = REDRAW_FULL;
@@ -994,38 +1012,27 @@ int mutt_compose_menu (HEADER *msg,   /* structure for new message */
 
       case OP_DELETE:
        CHECK_COUNT;
-        if (actx->idx[menu->current]->unowned)
-          actx->idx[menu->current]->content->unlink = 0;
-       if (delete_attachment (menu, &actx->idxlen, menu->current) == -1)
+        if (CURATTACH->unowned)
+          CURATTACH->content->unlink = 0;
+       if (delete_attachment (actx, menu->current) == -1)
          break;
-       mutt_update_tree (actx);
-       if (actx->idxlen)
-       {
-         if (menu->current > actx->idxlen - 1)
-           menu->current = actx->idxlen - 1;
-       }
-       else
-         menu->current = 0;
-
+       mutt_update_compose_menu (actx, menu, 0);
        if (menu->current == 0)
          msg->content = actx->idx[0]->content;
 
-        menu->redraw |= REDRAW_STATUS;
         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
         break;
 
-#define CURRENT actx->idx[menu->current]->content
-      
       case OP_COMPOSE_TOGGLE_RECODE:
-      {      
+      {
         CHECK_COUNT;
-        if (!mutt_is_text_part (CURRENT))
+        if (!mutt_is_text_part (CURATTACH->content))
         {
          mutt_error (_("Recoding only affects text attachments."));
          break;
        }
-        CURRENT->noconv = !CURRENT->noconv;
-        if (CURRENT->noconv)
+        CURATTACH->content->noconv = !CURATTACH->content->noconv;
+        if (CURATTACH->content->noconv)
          mutt_message (_("The current attachment won't be converted."));
         else
          mutt_message (_("The current attachment will be converted."));
@@ -1033,18 +1040,17 @@ int mutt_compose_menu (HEADER *msg,   /* structure for new message */
         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
         break;
       }
-#undef CURRENT
 
       case OP_COMPOSE_EDIT_DESCRIPTION:
        CHECK_COUNT;
        strfcpy (buf,
-                actx->idx[menu->current]->content->description ?
-                actx->idx[menu->current]->content->description : "",
+                CURATTACH->content->description ?
+                CURATTACH->content->description : "",
                 sizeof (buf));
        /* header names should not be translated */
        if (mutt_get_field ("Description: ", buf, sizeof (buf), 0) == 0)
        {
-         mutt_str_replace (&actx->idx[menu->current]->content->description, buf);
+         mutt_str_replace (&CURATTACH->content->description, buf);
          menu->redraw = REDRAW_CURRENT;
        }
         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
@@ -1064,7 +1070,7 @@ int mutt_compose_menu (HEADER *msg,   /* structure for new message */
        }
         else
         {
-          mutt_update_encoding(actx->idx[menu->current]->content);
+          mutt_update_encoding(CURATTACH->content);
          menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
        }
         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
@@ -1072,17 +1078,17 @@ int mutt_compose_menu (HEADER *msg,   /* structure for new message */
       
       case OP_COMPOSE_TOGGLE_DISPOSITION:
        /* toggle the content-disposition between inline/attachment */
-       actx->idx[menu->current]->content->disposition = (actx->idx[menu->current]->content->disposition == DISPINLINE) ? DISPATTACH : DISPINLINE;
+       CURATTACH->content->disposition = (CURATTACH->content->disposition == DISPINLINE) ? DISPATTACH : DISPINLINE;
        menu->redraw = REDRAW_CURRENT;
        break;
 
       case OP_EDIT_TYPE:
        CHECK_COUNT;
         {
-         mutt_edit_content_type (NULL, actx->idx[menu->current]->content, NULL);
+         mutt_edit_content_type (NULL, CURATTACH->content, NULL);
 
          /* this may have been a change to text/something */
-         mutt_update_encoding (actx->idx[menu->current]->content);
+         mutt_update_encoding (CURATTACH->content);
 
          menu->redraw = REDRAW_CURRENT;
        }
@@ -1091,14 +1097,14 @@ int mutt_compose_menu (HEADER *msg,   /* structure for new message */
 
       case OP_COMPOSE_EDIT_ENCODING:
        CHECK_COUNT;
-       strfcpy (buf, ENCODING (actx->idx[menu->current]->content->encoding),
+       strfcpy (buf, ENCODING (CURATTACH->content->encoding),
                                                              sizeof (buf));
        if (mutt_get_field ("Content-Transfer-Encoding: ", buf,
                                            sizeof (buf), 0) == 0 && buf[0])
        {
          if ((i = mutt_check_encoding (buf)) != ENCOTHER && i != ENCUUENCODED)
          {
-           actx->idx[menu->current]->content->encoding = i;
+           CURATTACH->content->encoding = i;
            menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
            mutt_clear_error();
          }
@@ -1141,15 +1147,15 @@ int mutt_compose_menu (HEADER *msg,   /* structure for new message */
 
       case OP_COMPOSE_EDIT_FILE:
        CHECK_COUNT;
-       mutt_edit_file (NONULL(Editor), actx->idx[menu->current]->content->filename);
-       mutt_update_encoding (actx->idx[menu->current]->content);
+       mutt_edit_file (NONULL(Editor), CURATTACH->content->filename);
+       mutt_update_encoding (CURATTACH->content);
        menu->redraw = REDRAW_CURRENT | REDRAW_STATUS;
         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
        break;
 
       case OP_COMPOSE_TOGGLE_UNLINK:
        CHECK_COUNT;
-       actx->idx[menu->current]->content->unlink = !actx->idx[menu->current]->content->unlink;
+       CURATTACH->content->unlink = !CURATTACH->content->unlink;
 
 #if 0
         /* OPTRESOLVE is otherwise ignored on this menu.
@@ -1175,7 +1181,7 @@ int mutt_compose_menu (HEADER *msg,   /* structure for new message */
          }
          menu->redraw = REDRAW_FULL;
        }
-        else if (mutt_get_tmp_attachment(actx->idx[menu->current]->content) == 0)
+        else if (mutt_get_tmp_attachment(CURATTACH->content) == 0)
          menu->redraw = REDRAW_CURRENT;
 
         /* No send2hook since this doesn't change the message. */
@@ -1187,10 +1193,10 @@ int mutt_compose_menu (HEADER *msg,   /* structure for new message */
           int ret;
 
           CHECK_COUNT;
-          if (actx->idx[menu->current]->content->d_filename)
-            src = actx->idx[menu->current]->content->d_filename;
+          if (CURATTACH->content->d_filename)
+            src = CURATTACH->content->d_filename;
           else
-            src = actx->idx[menu->current]->content->filename;
+            src = CURATTACH->content->filename;
           strfcpy (fname, mutt_basename (NONULL (src)), sizeof (fname));
           ret = mutt_get_field (_("Send attachment with name: "),
                                 fname, sizeof (fname), MUTT_FILE);
@@ -1200,7 +1206,7 @@ int mutt_compose_menu (HEADER *msg,   /* structure for new message */
              * As opposed to RENAME_FILE, we don't check fname[0] because it's
              * valid to set an empty string here, to erase what was set
              */
-            mutt_str_replace (&actx->idx[menu->current]->content->d_filename, fname);
+            mutt_str_replace (&CURATTACH->content->d_filename, fname);
             menu->redraw = REDRAW_CURRENT;
           }
         }
@@ -1208,12 +1214,12 @@ int mutt_compose_menu (HEADER *msg,   /* structure for new message */
 
       case OP_COMPOSE_RENAME_FILE:
        CHECK_COUNT;
-       strfcpy (fname, actx->idx[menu->current]->content->filename, sizeof (fname));
+       strfcpy (fname, CURATTACH->content->filename, sizeof (fname));
        mutt_pretty_mailbox (fname, sizeof (fname));
        if (mutt_get_field (_("Rename to: "), fname, sizeof (fname), MUTT_FILE)
                                                        == 0 && fname[0])
        {
-         if (stat(actx->idx[menu->current]->content->filename, &st) == -1)
+         if (stat(CURATTACH->content->filename, &st) == -1)
          {
             /* L10N:
                "stat" is a system call. Do "man 2 stat" for more information. */
@@ -1222,14 +1228,14 @@ int mutt_compose_menu (HEADER *msg,   /* structure for new message */
          }
 
          mutt_expand_path (fname, sizeof (fname));
-         if(mutt_rename_file (actx->idx[menu->current]->content->filename, fname))
+         if(mutt_rename_file (CURATTACH->content->filename, fname))
            break;
          
-         mutt_str_replace (&actx->idx[menu->current]->content->filename, fname);
+         mutt_str_replace (&CURATTACH->content->filename, fname);
          menu->redraw = REDRAW_CURRENT;
 
-         if(actx->idx[menu->current]->content->stamp >= st.st_mtime)
-           mutt_stamp_attachment(actx->idx[menu->current]->content);
+         if(CURATTACH->content->stamp >= st.st_mtime)
+           mutt_stamp_attachment(CURATTACH->content);
          
        }
         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
@@ -1285,14 +1291,14 @@ int mutt_compose_menu (HEADER *msg,   /* structure for new message */
          }
          update_idx (menu, actx, new);
 
-         actx->idx[menu->current]->content->type = itype;
-         mutt_str_replace (&actx->idx[menu->current]->content->subtype, p);
-         actx->idx[menu->current]->content->unlink = 1;
+         CURATTACH->content->type = itype;
+         mutt_str_replace (&CURATTACH->content->subtype, p);
+         CURATTACH->content->unlink = 1;
          menu->redraw |= REDRAW_INDEX | REDRAW_STATUS;
 
-         if (mutt_compose_attachment (actx->idx[menu->current]->content))
+         if (mutt_compose_attachment (CURATTACH->content))
          {
-           mutt_update_encoding (actx->idx[menu->current]->content);
+           mutt_update_encoding (CURATTACH->content);
            menu->redraw = REDRAW_FULL;
          }
        }
@@ -1301,9 +1307,9 @@ int mutt_compose_menu (HEADER *msg,   /* structure for new message */
 
       case OP_COMPOSE_EDIT_MIME:
        CHECK_COUNT;
-       if (mutt_edit_attachment (actx->idx[menu->current]->content))
+       if (mutt_edit_attachment (CURATTACH->content))
        {
-         mutt_update_encoding (actx->idx[menu->current]->content);
+         mutt_update_encoding (CURATTACH->content);
          menu->redraw = REDRAW_FULL;
        }
         mutt_message_hook (NULL, msg, MUTT_SEND2HOOK);
@@ -1319,20 +1325,20 @@ int mutt_compose_menu (HEADER *msg,   /* structure for new message */
 
       case OP_SAVE:
        CHECK_COUNT;
-       mutt_save_attachment_list (NULL, menu->tagprefix, menu->tagprefix ?  msg->content : actx->idx[menu->current]->content, NULL, menu);
+       mutt_save_attachment_list (NULL, menu->tagprefix, menu->tagprefix ?  msg->content : CURATTACH->content, NULL, menu);
         /* no send2hook, since this doesn't modify the message */
        break;
 
       case OP_PRINT:
        CHECK_COUNT;
-       mutt_print_attachment_list (NULL, menu->tagprefix, menu->tagprefix ? msg->content : actx->idx[menu->current]->content);
+       mutt_print_attachment_list (NULL, menu->tagprefix, menu->tagprefix ? msg->content : CURATTACH->content);
         /* no send2hook, since this doesn't modify the message */
        break;
 
       case OP_PIPE:
       case OP_FILTER:
         CHECK_COUNT;
-       mutt_pipe_attachment_list (NULL, menu->tagprefix, menu->tagprefix ? msg->content : actx->idx[menu->current]->content, op == OP_FILTER);
+       mutt_pipe_attachment_list (NULL, menu->tagprefix, menu->tagprefix ? msg->content : CURATTACH->content, op == OP_FILTER);
        if (op == OP_FILTER) /* cte might have changed */
          menu->redraw = menu->tagprefix ? REDRAW_FULL : REDRAW_CURRENT;
         menu->redraw |= REDRAW_STATUS;
index a0d44e1592a7305d9c8660fbc039156bc7f0b279..630f85949621c22cfb64dd0f94a004719b47087d 100644 (file)
@@ -51,6 +51,8 @@ static const char *Mailbox_is_read_only = N_("Mailbox is read-only.");
     break; \
 }
 
+#define CURATTACH actx->idx[actx->v2r[menu->current]]
+
 static const struct mapping_t AttachHelp[] = {
   { N_("Exit"),  OP_EXIT },
   { N_("Save"),  OP_SAVE },
@@ -60,21 +62,48 @@ static const struct mapping_t AttachHelp[] = {
   { NULL,        0 }
 };
 
+static void mutt_update_v2r (ATTACH_CONTEXT *actx)
+{
+  int vindex, rindex, curlevel;
+
+  vindex = rindex = 0;
+
+  while (rindex < actx->idxlen)
+  {
+    actx->v2r[vindex++] = rindex;
+    if (actx->idx[rindex]->content->collapsed)
+    {
+      curlevel = actx->idx[rindex]->level;
+      do
+        rindex++;
+      while ((rindex < actx->idxlen) &&
+             (actx->idx[rindex]->level > curlevel));
+    }
+    else
+      rindex++;
+  }
+
+  actx->vcount = vindex;
+}
+
 void mutt_update_tree (ATTACH_CONTEXT *actx)
 {
   char buf[STRING];
   char *s;
-  int x;
+  int rindex, vindex;
 
-  for (x = 0; x < actx->idxlen; x++)
+  mutt_update_v2r (actx);
+
+  for (vindex = 0; vindex < actx->vcount; vindex++)
   {
-    actx->idx[x]->num = x;
-    if (2 * (actx->idx[x]->level + 2) < sizeof (buf))
+    rindex = actx->v2r[vindex];
+    actx->idx[rindex]->num = vindex;
+    if (2 * (actx->idx[rindex]->level + 2) < sizeof (buf))
     {
-      if (actx->idx[x]->level)
+      if (actx->idx[rindex]->level)
       {
-       s = buf + 2 * (actx->idx[x]->level - 1);
-       *s++ = (actx->idx[x]->content->next) ? MUTT_TREE_LTEE : MUTT_TREE_LLCORNER;
+       s = buf + 2 * (actx->idx[rindex]->level - 1);
+       *s++ = (actx->idx[rindex]->content->next) ? MUTT_TREE_LTEE : MUTT_TREE_LLCORNER;
        *s++ = MUTT_TREE_HLINE;
        *s++ = MUTT_TREE_RARROW;
       }
@@ -83,18 +112,18 @@ void mutt_update_tree (ATTACH_CONTEXT *actx)
       *s = 0;
     }
 
-    if (actx->idx[x]->tree)
+    if (actx->idx[rindex]->tree)
     {
-      if (mutt_strcmp (actx->idx[x]->tree, buf) != 0)
-       mutt_str_replace (&actx->idx[x]->tree, buf);
+      if (mutt_strcmp (actx->idx[rindex]->tree, buf) != 0)
+       mutt_str_replace (&actx->idx[rindex]->tree, buf);
     }
     else
-      actx->idx[x]->tree = safe_strdup (buf);
+      actx->idx[rindex]->tree = safe_strdup (buf);
 
-    if (2 * (actx->idx[x]->level + 2) < sizeof (buf) && actx->idx[x]->level)
+    if (2 * (actx->idx[rindex]->level + 2) < sizeof (buf) && actx->idx[rindex]->level)
     {
-      s = buf + 2 * (actx->idx[x]->level - 1);
-      *s++ = (actx->idx[x]->content->next) ? '\005' : '\006';
+      s = buf + 2 * (actx->idx[rindex]->level - 1);
+      *s++ = (actx->idx[rindex]->content->next) ? '\005' : '\006';
       *s++ = '\006';
     }
   }
@@ -335,14 +364,18 @@ const char *mutt_attach_fmt (char *dest,
 
 static void attach_entry (char *b, size_t blen, MUTTMENU *menu, int num)
 {
-  mutt_FormatString (b, blen, 0, MuttIndexWindow->cols, NONULL (AttachFormat), mutt_attach_fmt, (unsigned long) (((ATTACHPTR **)menu->data)[num]), MUTT_FORMAT_ARROWCURSOR);
+  ATTACH_CONTEXT *actx = (ATTACH_CONTEXT *)menu->data;
+
+  mutt_FormatString (b, blen, 0, MuttIndexWindow->cols, NONULL (AttachFormat), mutt_attach_fmt,
+                     (unsigned long) (actx->idx[actx->v2r[num]]), MUTT_FORMAT_ARROWCURSOR);
 }
 
 int mutt_tag_attach (MUTTMENU *menu, int n, int m)
 {
-  BODY *cur = ((ATTACHPTR **) menu->data)[n]->content;
+  ATTACH_CONTEXT *actx = (ATTACH_CONTEXT *)menu->data;
+  BODY *cur = actx->idx[actx->v2r[n]]->content;
   int ot = cur->tagged;
-  
+
   cur->tagged = (m >= 0 ? m : !cur->tagged);
   return cur->tagged - ot;
 }
@@ -771,7 +804,7 @@ mutt_attach_display_loop (MUTTMENU *menu, int op, FILE *fp, HEADER *hdr,
        /* fall through */
 
       case OP_VIEW_ATTACH:
-       op = mutt_view_attachment (fp, actx->idx[menu->current]->content, MUTT_REGULAR,
+       op = mutt_view_attachment (fp, CURATTACH->content, MUTT_REGULAR,
                                   hdr, actx);
        break;
 
@@ -798,7 +831,7 @@ mutt_attach_display_loop (MUTTMENU *menu, int op, FILE *fp, HEADER *hdr,
       case OP_EDIT_TYPE:
        /* when we edit the content-type, we should redisplay the attachment
           immediately */
-       mutt_edit_content_type (hdr, actx->idx[menu->current]->content, fp);
+       mutt_edit_content_type (hdr, CURATTACH->content, fp);
         if (recv)
         {
           /* Editing the content type can rewrite the body structure. */
@@ -922,7 +955,7 @@ decrypt_failed:
     else
     {
       new = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
-      mutt_actx_add_attach (actx, new, NULL);
+      mutt_actx_add_attach (actx, new);
 
       new->content = m;
       new->fp = fp;
@@ -954,14 +987,16 @@ void mutt_attach_init (ATTACH_CONTEXT *actx)
 static void mutt_update_recvattach_menu (ATTACH_CONTEXT *actx, MUTTMENU *menu, int init)
 {
   if (init)
+  {
     mutt_generate_recvattach_list (actx, actx->hdr, actx->hdr->content,
                                    actx->root_fp, -1, 0, 0);
+    mutt_attach_init (actx);
+    menu->data = actx;
+  }
 
-  /* TODO: regenerate virtual index */
   mutt_update_tree (actx);
 
-  menu->max  = actx->idxlen;
-  menu->data = actx->idx;
+  menu->max  = actx->vcount;
 
   if (menu->current >= menu->max)
     menu->current = menu->max - 1;
@@ -1030,7 +1065,6 @@ void mutt_view_attachments (HEADER *hdr)
   actx->hdr = hdr;
   actx->root_fp = msg->fp;
   mutt_update_recvattach_menu (actx, menu, 1);
-  mutt_attach_init (actx);
 
   FOREVER
   {
@@ -1039,13 +1073,13 @@ void mutt_view_attachments (HEADER *hdr)
     switch (op)
     {
       case OP_ATTACH_VIEW_MAILCAP:
-       mutt_view_attachment (fp, actx->idx[menu->current]->content, MUTT_MAILCAP,
+       mutt_view_attachment (fp, CURATTACH->content, MUTT_MAILCAP,
                              hdr, actx);
        menu->redraw = REDRAW_FULL;
        break;
 
       case OP_ATTACH_VIEW_TEXT:
-       mutt_view_attachment (fp, actx->idx[menu->current]->content, MUTT_AS_TEXT,
+       mutt_view_attachment (fp, CURATTACH->content, MUTT_AS_TEXT,
                              hdr, actx);
        menu->redraw = REDRAW_FULL;
        break;
@@ -1057,15 +1091,15 @@ void mutt_view_attachments (HEADER *hdr)
         continue;
 
       case OP_ATTACH_COLLAPSE:
-        if (!actx->idx[menu->current]->content->parts)
+        if (!CURATTACH->content->parts)
         {
          mutt_error _("There are no subparts to show!");
          break;
        }
-        if (!actx->idx[menu->current]->content->collapsed)
-         attach_collapse (actx->idx[menu->current]->content, 1, 0, 1);
+        if (!CURATTACH->content->collapsed)
+         attach_collapse (CURATTACH->content, 1, 0, 1);
         else
-         attach_collapse (actx->idx[menu->current]->content, 0, 1, 1);
+         attach_collapse (CURATTACH->content, 0, 1, 1);
         mutt_update_recvattach_menu (actx, menu, 0);
         break;
       
@@ -1077,7 +1111,7 @@ void mutt_view_attachments (HEADER *hdr)
         if ((WithCrypto & APPLICATION_PGP))
         {
           crypt_pgp_extract_keys_from_attachment_list (fp, menu->tagprefix, 
-                   menu->tagprefix ? cur : actx->idx[menu->current]->content);
+                   menu->tagprefix ? cur : CURATTACH->content);
           menu->redraw = REDRAW_FULL;
         }
         break;
@@ -1085,7 +1119,7 @@ void mutt_view_attachments (HEADER *hdr)
       case OP_CHECK_TRADITIONAL:
         if ((WithCrypto & APPLICATION_PGP)
             && crypt_pgp_check_traditional (fp, menu->tagprefix ? cur
-                                              : actx->idx[menu->current]->content,
+                                              : CURATTACH->content,
                                       menu->tagprefix))
         {
          hdr->security = crypt_query (cur);
@@ -1095,17 +1129,17 @@ void mutt_view_attachments (HEADER *hdr)
 
       case OP_PRINT:
        mutt_print_attachment_list (fp, menu->tagprefix, 
-                 menu->tagprefix ? cur : actx->idx[menu->current]->content);
+                 menu->tagprefix ? cur : CURATTACH->content);
        break;
 
       case OP_PIPE:
        mutt_pipe_attachment_list (fp, menu->tagprefix, 
-                 menu->tagprefix ? cur : actx->idx[menu->current]->content, 0);
+                 menu->tagprefix ? cur : CURATTACH->content, 0);
        break;
 
       case OP_SAVE:
        mutt_save_attachment_list (fp, menu->tagprefix, 
-                 menu->tagprefix ?  cur : actx->idx[menu->current]->content, hdr, menu);
+                 menu->tagprefix ?  cur : CURATTACH->content, hdr, menu);
 
         if (!menu->tagprefix && option (OPTRESOLVE) && menu->current < menu->max - 1)
          menu->current++;
@@ -1138,9 +1172,9 @@ void mutt_view_attachments (HEADER *hdr)
         }
         if (!menu->tagprefix)
         {
-          if (actx->idx[menu->current]->parent_type == TYPEMULTIPART)
+          if (CURATTACH->parent_type == TYPEMULTIPART)
           {
-            actx->idx[menu->current]->content->deleted = 1;
+            CURATTACH->content->deleted = 1;
             if (option (OPTRESOLVE) && menu->current < menu->max - 1)
             {
               menu->current++;
@@ -1178,7 +1212,7 @@ void mutt_view_attachments (HEADER *hdr)
        CHECK_READONLY;
        if (!menu->tagprefix)
        {
-        actx->idx[menu->current]->content->deleted = 0;
+        CURATTACH->content->deleted = 0;
         if (option (OPTRESOLVE) && menu->current < menu->max - 1)
         {
           menu->current++;
@@ -1205,21 +1239,21 @@ void mutt_view_attachments (HEADER *hdr)
       case OP_RESEND:
         CHECK_ATTACH;
         mutt_attach_resend (fp, hdr, actx,
-                            menu->tagprefix ? NULL : actx->idx[menu->current]->content);
+                            menu->tagprefix ? NULL : CURATTACH->content);
         menu->redraw = REDRAW_FULL;
        break;
       
       case OP_BOUNCE_MESSAGE:
         CHECK_ATTACH;
         mutt_attach_bounce (fp, hdr, actx,
-                            menu->tagprefix ? NULL : actx->idx[menu->current]->content);
+                            menu->tagprefix ? NULL : CURATTACH->content);
         menu->redraw = REDRAW_FULL;
        break;
 
       case OP_FORWARD_MESSAGE:
         CHECK_ATTACH;
         mutt_attach_forward (fp, hdr, actx,
-                            menu->tagprefix ? NULL : actx->idx[menu->current]->content);
+                            menu->tagprefix ? NULL : CURATTACH->content);
         menu->redraw = REDRAW_FULL;
         break;
       
@@ -1233,12 +1267,12 @@ void mutt_view_attachments (HEADER *hdr)
          (op == OP_GROUP_REPLY ? SENDGROUPREPLY : 0) |
          (op == OP_LIST_REPLY ? SENDLISTREPLY : 0);
         mutt_attach_reply (fp, hdr, actx,
-                          menu->tagprefix ? NULL : actx->idx[menu->current]->content, flags);
+                          menu->tagprefix ? NULL : CURATTACH->content, flags);
        menu->redraw = REDRAW_FULL;
        break;
 
       case OP_EDIT_TYPE:
-       mutt_edit_content_type (hdr, actx->idx[menu->current]->content, fp);
+       mutt_edit_content_type (hdr, CURATTACH->content, fp);
         /* Editing the content type can rewrite the body structure. */
         for (i = 0; i < actx->idxlen; i++)
           actx->idx[i]->content = NULL;