PageRenderTime 65ms CodeModel.GetById 20ms app.highlight 39ms RepoModel.GetById 1ms app.codeStats 0ms

/menu.c

https://git.sr.ht/~kevin8t8/mutt/
C | 1253 lines | 1051 code | 147 blank | 55 comment | 225 complexity | 3fd9bb582fcc121fc5ee90bddd432da1 MD5 | raw file
Possible License(s): AGPL-1.0
   1/*
   2 * Copyright (C) 1996-2000,2002,2012 Michael R. Elkins <me@mutt.org>
   3 *
   4 *     This program is free software; you can redistribute it and/or modify
   5 *     it under the terms of the GNU General Public License as published by
   6 *     the Free Software Foundation; either version 2 of the License, or
   7 *     (at your option) any later version.
   8 *
   9 *     This program is distributed in the hope that it will be useful,
  10 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 *     GNU General Public License for more details.
  13 *
  14 *     You should have received a copy of the GNU General Public License
  15 *     along with this program; if not, write to the Free Software
  16 *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  17 */
  18
  19#if HAVE_CONFIG_H
  20# include "config.h"
  21#endif
  22
  23#include "mutt.h"
  24#include "mutt_curses.h"
  25#include "mutt_menu.h"
  26#include "mbyte.h"
  27#ifdef USE_SIDEBAR
  28#include "sidebar.h"
  29#endif
  30
  31char* SearchBuffers[MENU_MAX];
  32
  33/* These are used to track the active menus, for redraw operations. */
  34static size_t MenuStackCount = 0;
  35static size_t MenuStackLen = 0;
  36static MUTTMENU **MenuStack = NULL;
  37
  38static void print_enriched_string (int attr, unsigned char *s, int do_color)
  39{
  40  wchar_t wc;
  41  size_t k;
  42  size_t n = mutt_strlen ((char *)s);
  43  mbstate_t mbstate;
  44
  45  memset (&mbstate, 0, sizeof (mbstate));
  46  while (*s)
  47  {
  48    if (*s < MUTT_TREE_MAX)
  49    {
  50      if (do_color)
  51	SETCOLOR (MT_COLOR_TREE);
  52      while (*s && *s < MUTT_TREE_MAX)
  53      {
  54	switch (*s)
  55	{
  56	  case MUTT_TREE_LLCORNER:
  57	    if (option (OPTASCIICHARS))
  58	      addch ('`');
  59#ifdef WACS_LLCORNER
  60	    else
  61	      add_wch(WACS_LLCORNER);
  62#else
  63	    else if (Charset_is_utf8)
  64	      addstr ("\342\224\224"); /* WACS_LLCORNER */
  65	    else
  66	      addch (ACS_LLCORNER);
  67#endif
  68	    break;
  69	  case MUTT_TREE_ULCORNER:
  70	    if (option (OPTASCIICHARS))
  71	      addch (',');
  72#ifdef WACS_ULCORNER
  73	    else
  74	      add_wch(WACS_ULCORNER);
  75#else
  76	    else if (Charset_is_utf8)
  77	      addstr ("\342\224\214"); /* WACS_ULCORNER */
  78	    else
  79	      addch (ACS_ULCORNER);
  80#endif
  81	    break;
  82	  case MUTT_TREE_LTEE:
  83	    if (option (OPTASCIICHARS))
  84	      addch ('|');
  85#ifdef WACS_LTEE
  86	    else
  87	      add_wch(WACS_LTEE);
  88#else
  89	    else if (Charset_is_utf8)
  90	      addstr ("\342\224\234"); /* WACS_LTEE */
  91	    else
  92	      addch (ACS_LTEE);
  93#endif
  94	    break;
  95	  case MUTT_TREE_HLINE:
  96	    if (option (OPTASCIICHARS))
  97	      addch ('-');
  98#ifdef WACS_HLINE
  99	    else
 100	      add_wch(WACS_HLINE);
 101#else
 102	    else if (Charset_is_utf8)
 103	      addstr ("\342\224\200"); /* WACS_HLINE */
 104	    else
 105	      addch (ACS_HLINE);
 106#endif
 107	    break;
 108	  case MUTT_TREE_VLINE:
 109	    if (option (OPTASCIICHARS))
 110	      addch ('|');
 111#ifdef WACS_VLINE
 112	    else
 113	      add_wch(WACS_VLINE);
 114#else
 115	    else if (Charset_is_utf8)
 116	      addstr ("\342\224\202"); /* WACS_VLINE */
 117	    else
 118	      addch (ACS_VLINE);
 119#endif
 120	    break;
 121	  case MUTT_TREE_TTEE:
 122	    if (option (OPTASCIICHARS))
 123	      addch ('-');
 124#ifdef WACS_TTEE
 125	    else
 126	      add_wch(WACS_TTEE);
 127#else
 128	    else if (Charset_is_utf8)
 129	      addstr ("\342\224\254"); /* WACS_TTEE */
 130	    else
 131	      addch (ACS_TTEE);
 132#endif
 133	    break;
 134	  case MUTT_TREE_BTEE:
 135	    if (option (OPTASCIICHARS))
 136	      addch ('-');
 137#ifdef WACS_BTEE
 138	    else
 139	      add_wch(WACS_BTEE);
 140#else
 141	    else if (Charset_is_utf8)
 142	      addstr ("\342\224\264"); /* WACS_BTEE */
 143	    else
 144	      addch (ACS_BTEE);
 145#endif
 146	    break;
 147	  case MUTT_TREE_SPACE:
 148	    addch (' ');
 149	    break;
 150	  case MUTT_TREE_RARROW:
 151	    addch ('>');
 152	    break;
 153	  case MUTT_TREE_STAR:
 154	    addch ('*'); /* fake thread indicator */
 155	    break;
 156	  case MUTT_TREE_HIDDEN:
 157	    addch ('&');
 158	    break;
 159	  case MUTT_TREE_EQUALS:
 160	    addch ('=');
 161	    break;
 162	  case MUTT_TREE_MISSING:
 163	    addch ('?');
 164	    break;
 165	}
 166	s++, n--;
 167      }
 168      if (do_color) ATTRSET(attr);
 169    }
 170    else if ((k = mbrtowc (&wc, (char *)s, n, &mbstate)) > 0)
 171    {
 172      addnstr ((char *)s, k);
 173      s += k, n-= k;
 174    }
 175    else
 176      break;
 177  }
 178}
 179
 180static void menu_make_entry (char *s, int l, MUTTMENU *menu, int i)
 181{
 182  if (menu->dialog)
 183  {
 184    strncpy (s, NONULL (menu->dialog[i]), l);
 185    menu->current = -1; /* hide menubar */
 186  }
 187  else
 188    menu->make_entry (s, l, menu, i);
 189}
 190
 191static void menu_pad_string (MUTTMENU *menu, char *s, size_t n)
 192{
 193  char *scratch = safe_strdup (s);
 194  int shift = option (OPTARROWCURSOR) ? 3 : 0;
 195  int cols = menu->indexwin->cols - shift;
 196
 197  mutt_format_string (s, n, cols, cols, FMT_LEFT, ' ', scratch, mutt_strlen (scratch), 1);
 198  s[n - 1] = 0;
 199  FREE (&scratch);
 200}
 201
 202void menu_redraw_full (MUTTMENU *menu)
 203{
 204#if ! (defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM))
 205  mutt_reflow_windows ();
 206#endif
 207  NORMAL_COLOR;
 208  /* clear() doesn't optimize screen redraws */
 209  move (0, 0);
 210  clrtobot ();
 211
 212  if (option (OPTHELP))
 213  {
 214    SETCOLOR (MT_COLOR_STATUS);
 215    mutt_window_move (menu->helpwin, 0, 0);
 216    mutt_paddstr (menu->helpwin->cols, menu->help);
 217    NORMAL_COLOR;
 218  }
 219  menu->offset = 0;
 220  menu->pagelen = menu->indexwin->rows;
 221
 222  mutt_show_error ();
 223
 224  menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
 225#ifdef USE_SIDEBAR
 226  menu->redraw |= REDRAW_SIDEBAR;
 227#endif
 228}
 229
 230void menu_redraw_status (MUTTMENU *menu)
 231{
 232  char buf[STRING];
 233
 234  snprintf (buf, sizeof (buf), MUTT_MODEFMT, menu->title);
 235  SETCOLOR (MT_COLOR_STATUS);
 236  mutt_window_move (menu->statuswin, 0, 0);
 237  mutt_paddstr (menu->statuswin->cols, buf);
 238  NORMAL_COLOR;
 239  menu->redraw &= ~REDRAW_STATUS;
 240}
 241
 242#ifdef USE_SIDEBAR
 243void menu_redraw_sidebar (MUTTMENU *menu)
 244{
 245  menu->redraw &= ~REDRAW_SIDEBAR;
 246  mutt_sb_draw ();
 247}
 248#endif
 249
 250void menu_redraw_index (MUTTMENU *menu)
 251{
 252  char buf[LONG_STRING];
 253  int i;
 254  int do_color;
 255  int attr;
 256
 257  for (i = menu->top; i < menu->top + menu->pagelen; i++)
 258  {
 259    if (i < menu->max)
 260    {
 261      attr = menu->color(i);
 262
 263      menu_make_entry (buf, sizeof (buf), menu, i);
 264      menu_pad_string (menu, buf, sizeof (buf));
 265
 266      ATTRSET(attr);
 267      mutt_window_move (menu->indexwin, i - menu->top + menu->offset, 0);
 268      do_color = 1;
 269
 270      if (i == menu->current)
 271      {
 272        SETCOLOR(MT_COLOR_INDICATOR);
 273        if (option(OPTARROWCURSOR))
 274        {
 275          addstr ("->");
 276          ATTRSET(attr);
 277          addch (' ');
 278        }
 279        else
 280          do_color = 0;
 281      }
 282      else if (option(OPTARROWCURSOR))
 283	addstr("   ");
 284
 285      print_enriched_string (attr, (unsigned char *) buf, do_color);
 286    }
 287    else
 288    {
 289      NORMAL_COLOR;
 290      mutt_window_clearline (menu->indexwin, i - menu->top + menu->offset);
 291    }
 292  }
 293  NORMAL_COLOR;
 294  menu->redraw = 0;
 295}
 296
 297void menu_redraw_motion (MUTTMENU *menu)
 298{
 299  char buf[LONG_STRING];
 300  int old_color, cur_color;
 301
 302  if (menu->dialog)
 303  {
 304    menu->redraw &= ~REDRAW_MOTION;
 305    return;
 306  }
 307
 308  /* Note: menu->color() for the index can end up retrieving a message
 309   * over imap (if matching against ~h for instance).  This can
 310   * generate status messages.  So we want to call it *before* we
 311   * position the cursor for drawing. */
 312  old_color = menu->color (menu->oldcurrent);
 313  mutt_window_move (menu->indexwin, menu->oldcurrent + menu->offset - menu->top, 0);
 314  ATTRSET(old_color);
 315
 316  if (option (OPTARROWCURSOR))
 317  {
 318    /* clear the pointer */
 319    addstr ("  ");
 320
 321    if (menu->redraw & REDRAW_MOTION_RESYNCH)
 322    {
 323      menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
 324      menu_pad_string (menu, buf, sizeof (buf));
 325      mutt_window_move (menu->indexwin, menu->oldcurrent + menu->offset - menu->top, 3);
 326      print_enriched_string (old_color, (unsigned char *) buf, 1);
 327    }
 328
 329    /* now draw it in the new location */
 330    SETCOLOR(MT_COLOR_INDICATOR);
 331    mutt_window_mvaddstr (menu->indexwin, menu->current + menu->offset - menu->top, 0, "->");
 332  }
 333  else
 334  {
 335    /* erase the current indicator */
 336    menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
 337    menu_pad_string (menu, buf, sizeof (buf));
 338    print_enriched_string (old_color, (unsigned char *) buf, 1);
 339
 340    /* now draw the new one to reflect the change */
 341    cur_color = menu->color (menu->current);
 342    menu_make_entry (buf, sizeof (buf), menu, menu->current);
 343    menu_pad_string (menu, buf, sizeof (buf));
 344    SETCOLOR(MT_COLOR_INDICATOR);
 345    mutt_window_move (menu->indexwin, menu->current + menu->offset - menu->top, 0);
 346    print_enriched_string (cur_color, (unsigned char *) buf, 0);
 347  }
 348  menu->redraw &= REDRAW_STATUS;
 349  NORMAL_COLOR;
 350}
 351
 352void menu_redraw_current (MUTTMENU *menu)
 353{
 354  char buf[LONG_STRING];
 355  int attr = menu->color (menu->current);
 356
 357  mutt_window_move (menu->indexwin, menu->current + menu->offset - menu->top, 0);
 358  menu_make_entry (buf, sizeof (buf), menu, menu->current);
 359  menu_pad_string (menu, buf, sizeof (buf));
 360
 361  SETCOLOR(MT_COLOR_INDICATOR);
 362  if (option (OPTARROWCURSOR))
 363  {
 364    addstr ("->");
 365    ATTRSET(attr);
 366    addch (' ');
 367    menu_pad_string (menu, buf, sizeof (buf));
 368    print_enriched_string (attr, (unsigned char *) buf, 1);
 369  }
 370  else
 371    print_enriched_string (attr, (unsigned char *) buf, 0);
 372  menu->redraw &= REDRAW_STATUS;
 373  NORMAL_COLOR;
 374}
 375
 376static void menu_redraw_prompt (MUTTMENU *menu)
 377{
 378  if (menu->dialog)
 379  {
 380    if (option (OPTMSGERR))
 381    {
 382      mutt_sleep (1);
 383      unset_option (OPTMSGERR);
 384    }
 385
 386    if (*Errorbuf)
 387      mutt_clear_error ();
 388
 389    mutt_window_mvaddstr (menu->messagewin, 0, 0, menu->prompt);
 390    mutt_window_clrtoeol (menu->messagewin);
 391  }
 392}
 393
 394void menu_check_recenter (MUTTMENU *menu)
 395{
 396  int c = MIN (MenuContext, menu->pagelen / 2);
 397  int old_top = menu->top;
 398
 399  if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) /* less entries than lines */
 400  {
 401    if (menu->top != 0)
 402    {
 403      menu->top = 0;
 404      menu->redraw |= REDRAW_INDEX;
 405    }
 406  }
 407  else
 408  {
 409    if (option (OPTMENUSCROLL) || (menu->pagelen <= 0) || (c < MenuContext))
 410    {
 411      if (menu->current < menu->top + c)
 412	menu->top = menu->current - c;
 413      else if (menu->current >= menu->top + menu->pagelen - c)
 414	menu->top = menu->current - menu->pagelen + c + 1;
 415    }
 416    else
 417    {
 418      if (menu->current < menu->top + c)
 419	menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
 420      else if ((menu->current >= menu->top + menu->pagelen - c))
 421	menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
 422    }
 423  }
 424
 425  if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
 426    menu->top = MIN (menu->top, menu->max - menu->pagelen);
 427  menu->top = MAX (menu->top, 0);
 428
 429  if (menu->top != old_top)
 430    menu->redraw |= REDRAW_INDEX;
 431}
 432
 433void menu_jump (MUTTMENU *menu)
 434{
 435  int n;
 436  char buf[SHORT_STRING];
 437
 438  if (menu->max)
 439  {
 440    mutt_unget_event (LastKey, 0);
 441    buf[0] = 0;
 442    if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0])
 443    {
 444      if (mutt_atoi (buf, &n) == 0 && n > 0 && n < menu->max + 1)
 445      {
 446	n--;	/* msg numbers are 0-based */
 447	menu->current = n;
 448	menu->redraw = REDRAW_MOTION;
 449      }
 450      else
 451	mutt_error _("Invalid index number.");
 452    }
 453  }
 454  else
 455    mutt_error _("No entries.");
 456}
 457
 458void menu_next_line (MUTTMENU *menu)
 459{
 460  if (menu->max)
 461  {
 462    int c = MIN (MenuContext, menu->pagelen / 2);
 463
 464    if ((menu->top + 1 < menu->max - c) &&
 465        (option(OPTMENUMOVEOFF) ||
 466         (menu->max > menu->pagelen && menu->top < menu->max - menu->pagelen)))
 467    {
 468      menu->top++;
 469      if (menu->current < menu->top + c && menu->current < menu->max - 1)
 470	menu->current++;
 471      menu->redraw = REDRAW_INDEX;
 472    }
 473    else
 474      mutt_error _("You cannot scroll down farther.");
 475  }
 476  else
 477    mutt_error _("No entries.");
 478}
 479
 480void menu_prev_line (MUTTMENU *menu)
 481{
 482  if (menu->top > 0)
 483  {
 484    int c = MIN (MenuContext, menu->pagelen / 2);
 485
 486    menu->top--;
 487    if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
 488      menu->current--;
 489    menu->redraw = REDRAW_INDEX;
 490  }
 491  else
 492    mutt_error _("You cannot scroll up farther.");
 493}
 494
 495/*
 496 * pageup:   jumplen == -pagelen
 497 * pagedown: jumplen == pagelen
 498 * halfup:   jumplen == -pagelen/2
 499 * halfdown: jumplen == pagelen/2
 500 */
 501#define DIRECTION ((neg * 2) + 1)
 502static void menu_length_jump (MUTTMENU *menu, int jumplen)
 503{
 504  int tmp, neg = (jumplen >= 0) ? 0 : -1;
 505  int c = MIN (MenuContext, menu->pagelen / 2);
 506
 507  if (menu->max)
 508  {
 509    /* possible to scroll? */
 510    if (DIRECTION * menu->top <
 511	(tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/))))
 512    {
 513      menu->top += jumplen;
 514
 515      /* jumped too long? */
 516      if ((neg || !option (OPTMENUMOVEOFF)) &&
 517	  DIRECTION * menu->top > tmp)
 518	menu->top = tmp;
 519
 520      /* need to move the cursor? */
 521      if ((DIRECTION *
 522	   (tmp = (menu->current -
 523		   (menu->top + (neg ? (menu->pagelen - 1) - c : c))
 524             ))) < 0)
 525	menu->current -= tmp;
 526
 527      menu->redraw = REDRAW_INDEX;
 528    }
 529    else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog)
 530    {
 531      menu->current += jumplen;
 532      menu->redraw = REDRAW_MOTION;
 533    }
 534    else
 535      mutt_error (neg ? _("You are on the first page.")
 536                  : _("You are on the last page."));
 537
 538    menu->current = MIN (menu->current, menu->max - 1);
 539    menu->current = MAX (menu->current, 0);
 540  }
 541  else
 542    mutt_error _("No entries.");
 543}
 544#undef DIRECTION
 545
 546void menu_next_page (MUTTMENU *menu)
 547{
 548  menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
 549}
 550
 551void menu_prev_page (MUTTMENU *menu)
 552{
 553  menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
 554}
 555
 556void menu_half_down (MUTTMENU *menu)
 557{
 558  menu_length_jump (menu, menu->pagelen / 2);
 559}
 560
 561void menu_half_up (MUTTMENU *menu)
 562{
 563  menu_length_jump (menu, 0 - menu->pagelen / 2);
 564}
 565
 566void menu_top_page (MUTTMENU *menu)
 567{
 568  if (menu->current != menu->top)
 569  {
 570    menu->current = menu->top;
 571    menu->redraw = REDRAW_MOTION;
 572  }
 573}
 574
 575void menu_bottom_page (MUTTMENU *menu)
 576{
 577  if (menu->max)
 578  {
 579    menu->current = menu->top + menu->pagelen - 1;
 580    if (menu->current > menu->max - 1)
 581      menu->current = menu->max - 1;
 582    menu->redraw = REDRAW_MOTION;
 583  }
 584  else
 585    mutt_error _("No entries.");
 586}
 587
 588void menu_middle_page (MUTTMENU *menu)
 589{
 590  int i;
 591
 592  if (menu->max)
 593  {
 594    i = menu->top + menu->pagelen;
 595    if (i > menu->max - 1)
 596      i = menu->max - 1;
 597    menu->current = menu->top + (i - menu->top) / 2;
 598    menu->redraw = REDRAW_MOTION;
 599  }
 600  else
 601    mutt_error _("No entries.");
 602}
 603
 604void menu_first_entry (MUTTMENU *menu)
 605{
 606  if (menu->max)
 607  {
 608    menu->current = 0;
 609    menu->redraw = REDRAW_MOTION;
 610  }
 611  else
 612    mutt_error _("No entries.");
 613}
 614
 615void menu_last_entry (MUTTMENU *menu)
 616{
 617  if (menu->max)
 618  {
 619    menu->current = menu->max - 1;
 620    menu->redraw = REDRAW_MOTION;
 621  }
 622  else
 623    mutt_error _("No entries.");
 624}
 625
 626void menu_current_top (MUTTMENU *menu)
 627{
 628  if (menu->max)
 629  {
 630    menu->top = menu->current;
 631    menu->redraw = REDRAW_INDEX;
 632  }
 633  else
 634    mutt_error _("No entries.");
 635}
 636
 637void menu_current_middle (MUTTMENU *menu)
 638{
 639  if (menu->max)
 640  {
 641    menu->top = menu->current - menu->pagelen / 2;
 642    if (menu->top < 0)
 643      menu->top = 0;
 644    menu->redraw = REDRAW_INDEX;
 645  }
 646  else
 647    mutt_error _("No entries.");
 648}
 649
 650void menu_current_bottom (MUTTMENU *menu)
 651{
 652  if (menu->max)
 653  {
 654    menu->top = menu->current - menu->pagelen + 1;
 655    if (menu->top < 0)
 656      menu->top = 0;
 657    menu->redraw = REDRAW_INDEX;
 658  }
 659  else
 660    mutt_error _("No entries.");
 661}
 662
 663static void menu_next_entry (MUTTMENU *menu)
 664{
 665  if (menu->current < menu->max - 1)
 666  {
 667    menu->current++;
 668    menu->redraw = REDRAW_MOTION;
 669  }
 670  else
 671    mutt_error _("You are on the last entry.");
 672}
 673
 674static void menu_prev_entry (MUTTMENU *menu)
 675{
 676  if (menu->current)
 677  {
 678    menu->current--;
 679    menu->redraw = REDRAW_MOTION;
 680  }
 681  else
 682    mutt_error _("You are on the first entry.");
 683}
 684
 685static int default_color (int i)
 686{
 687  return ColorDefs[MT_COLOR_NORMAL];
 688}
 689
 690static int menu_search_generic (MUTTMENU *m, regex_t *re, int n)
 691{
 692  char buf[LONG_STRING];
 693
 694  menu_make_entry (buf, sizeof (buf), m, n);
 695  return (regexec (re, buf, 0, NULL, 0));
 696}
 697
 698void mutt_menu_init (void)
 699{
 700  int i;
 701
 702  for (i = 0; i < MENU_MAX; i++)
 703    SearchBuffers[i] = NULL;
 704}
 705
 706MUTTMENU *mutt_new_menu (int menu)
 707{
 708  MUTTMENU *p = (MUTTMENU *) safe_calloc (1, sizeof (MUTTMENU));
 709
 710  if ((menu < 0) || (menu >= MENU_MAX))
 711    menu = MENU_GENERIC;
 712
 713  p->menu = menu;
 714  p->current = 0;
 715  p->top = 0;
 716  p->offset = 0;
 717  p->redraw = REDRAW_FULL;
 718  p->pagelen = MuttIndexWindow->rows;
 719  p->indexwin = MuttIndexWindow;
 720  p->statuswin = MuttStatusWindow;
 721  p->helpwin = MuttHelpWindow;
 722  p->messagewin = MuttMessageWindow;
 723  p->color = default_color;
 724  p->search = menu_search_generic;
 725
 726  return (p);
 727}
 728
 729void mutt_menuDestroy (MUTTMENU **p)
 730{
 731  int i;
 732
 733  if ((*p)->dialog)
 734  {
 735    for (i=0; i < (*p)->max; i++)
 736      FREE (&(*p)->dialog[i]);
 737
 738    FREE (& (*p)->dialog);
 739  }
 740
 741  FREE (p);		/* __FREE_CHECKED__ */
 742}
 743
 744void mutt_menu_add_dialog_row (MUTTMENU *m, const char *row)
 745{
 746  if (m->dsize <= m->max)
 747  {
 748    m->dsize += 10;
 749    safe_realloc (&m->dialog, m->dsize * sizeof (char *));
 750  }
 751  m->dialog[m->max++] = safe_strdup (row);
 752}
 753
 754static MUTTMENU *get_current_menu (void)
 755{
 756  return MenuStackCount ? MenuStack[MenuStackCount - 1] : NULL;
 757}
 758
 759void mutt_push_current_menu (MUTTMENU *menu)
 760{
 761  if (MenuStackCount >= MenuStackLen)
 762  {
 763    MenuStackLen += 5;
 764    safe_realloc (&MenuStack, MenuStackLen * sizeof(MUTTMENU *));
 765  }
 766
 767  MenuStack[MenuStackCount++] = menu;
 768  CurrentMenu = menu->menu;
 769}
 770
 771void mutt_pop_current_menu (MUTTMENU *menu)
 772{
 773  MUTTMENU *prev_menu;
 774
 775  if (!MenuStackCount ||
 776      (MenuStack[MenuStackCount - 1] != menu))
 777  {
 778    dprint (1, (debugfile, "mutt_pop_current_menu() called with inactive menu\n"));
 779    return;
 780  }
 781
 782  MenuStackCount--;
 783  prev_menu = get_current_menu ();
 784  if (prev_menu)
 785  {
 786    CurrentMenu = prev_menu->menu;
 787    prev_menu->redraw = REDRAW_FULL;
 788  }
 789  else
 790  {
 791    CurrentMenu = MENU_MAIN;
 792    /* Clearing when Mutt exits would be an annoying change in
 793     * behavior for those who have disabled alternative screens.  The
 794     * option is currently set by autocrypt initialization which mixes
 795     * menus and prompts outside of the normal menu system state.
 796     */
 797    if (option (OPTMENUPOPCLEARSCREEN))
 798    {
 799      move (0, 0);
 800      clrtobot ();
 801    }
 802  }
 803}
 804
 805void mutt_set_current_menu_redraw (int redraw)
 806{
 807  MUTTMENU *current_menu;
 808
 809  current_menu = get_current_menu ();
 810  if (current_menu)
 811    current_menu->redraw |= redraw;
 812}
 813
 814void mutt_set_current_menu_redraw_full (void)
 815{
 816  MUTTMENU *current_menu;
 817
 818  current_menu = get_current_menu ();
 819  if (current_menu)
 820    current_menu->redraw = REDRAW_FULL;
 821}
 822
 823void mutt_set_menu_redraw (int menu_type, int redraw)
 824{
 825  if (CurrentMenu == menu_type)
 826    mutt_set_current_menu_redraw (redraw);
 827}
 828
 829void mutt_set_menu_redraw_full (int menu_type)
 830{
 831  if (CurrentMenu == menu_type)
 832    mutt_set_current_menu_redraw_full ();
 833}
 834
 835void mutt_current_menu_redraw ()
 836{
 837  MUTTMENU *current_menu;
 838
 839  current_menu = get_current_menu ();
 840  if (current_menu)
 841  {
 842    if (menu_redraw (current_menu) == OP_REDRAW)
 843      /* On a REDRAW_FULL with a non-customized redraw, menu_redraw()
 844       * will return OP_REDRAW to give the calling menu-loop a chance to
 845       * customize output.
 846       */
 847      menu_redraw (current_menu);
 848  }
 849}
 850
 851
 852#define MUTT_SEARCH_UP   1
 853#define MUTT_SEARCH_DOWN 2
 854
 855static int menu_search (MUTTMENU *menu, int op)
 856{
 857  int r, wrap = 0;
 858  int searchDir;
 859  regex_t re;
 860  char buf[SHORT_STRING];
 861  char* searchBuf = menu->menu >= 0 && menu->menu < MENU_MAX ?
 862    SearchBuffers[menu->menu] : NULL;
 863
 864  if (!(searchBuf && *searchBuf) ||
 865      (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE))
 866  {
 867    strfcpy (buf, searchBuf && *searchBuf ? searchBuf : "", sizeof (buf));
 868    if (mutt_get_field ((op == OP_SEARCH || op == OP_SEARCH_NEXT)
 869			? _("Search for: ") : _("Reverse search for: "),
 870			buf, sizeof (buf), MUTT_CLEAR) != 0 || !buf[0])
 871      return (-1);
 872    if (menu->menu >= 0 && menu->menu < MENU_MAX)
 873    {
 874      mutt_str_replace (&SearchBuffers[menu->menu], buf);
 875      searchBuf = SearchBuffers[menu->menu];
 876    }
 877    menu->searchDir = (op == OP_SEARCH || op == OP_SEARCH_NEXT) ?
 878      MUTT_SEARCH_DOWN : MUTT_SEARCH_UP;
 879  }
 880
 881  searchDir = (menu->searchDir == MUTT_SEARCH_UP) ? -1 : 1;
 882  if (op == OP_SEARCH_OPPOSITE)
 883    searchDir = -searchDir;
 884
 885  if ((r = REGCOMP (&re, searchBuf, REG_NOSUB | mutt_which_case (searchBuf))) != 0)
 886  {
 887    regerror (r, &re, buf, sizeof (buf));
 888    mutt_error ("%s", buf);
 889    return (-1);
 890  }
 891
 892  r = menu->current + searchDir;
 893search_next:
 894  if (wrap)
 895    mutt_message (_("Search wrapped to top."));
 896  while (r >= 0 && r < menu->max)
 897  {
 898    if (menu->search (menu, &re, r) == 0)
 899    {
 900      regfree (&re);
 901      return r;
 902    }
 903
 904    r += searchDir;
 905  }
 906
 907  if (option (OPTWRAPSEARCH) && wrap++ == 0)
 908  {
 909    r = searchDir == 1 ? 0 : menu->max - 1;
 910    goto search_next;
 911  }
 912  regfree (&re);
 913  mutt_error _("Not found.");
 914  return (-1);
 915}
 916
 917static int menu_dialog_translate_op (int i)
 918{
 919  switch (i)
 920  {
 921    case OP_NEXT_ENTRY:
 922      return OP_NEXT_LINE;
 923    case OP_PREV_ENTRY:
 924      return OP_PREV_LINE;
 925    case OP_CURRENT_TOP:   case OP_FIRST_ENTRY:
 926      return OP_TOP_PAGE;
 927    case OP_CURRENT_BOTTOM:    case OP_LAST_ENTRY:
 928      return OP_BOTTOM_PAGE;
 929    case OP_CURRENT_MIDDLE:
 930      return OP_MIDDLE_PAGE;
 931  }
 932
 933  return i;
 934}
 935
 936static int menu_dialog_dokey (MUTTMENU *menu, int *ip)
 937{
 938  event_t ch;
 939  char *p;
 940
 941  do
 942  {
 943    ch = mutt_getch();
 944  } while (ch.ch == -2);
 945
 946  if (ch.ch < 0)
 947  {
 948    *ip = -1;
 949    return 0;
 950  }
 951
 952  if (ch.ch && (p = strchr (menu->keys, ch.ch)))
 953  {
 954    *ip = OP_MAX + (p - menu->keys + 1);
 955    return 0;
 956  }
 957  else
 958  {
 959    mutt_unget_event (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
 960    return -1;
 961  }
 962}
 963
 964int menu_redraw (MUTTMENU *menu)
 965{
 966  if (menu->custom_menu_redraw)
 967  {
 968    menu->custom_menu_redraw (menu);
 969    return OP_NULL;
 970  }
 971
 972  /* See if all or part of the screen needs to be updated.  */
 973  if (menu->redraw & REDRAW_FULL)
 974  {
 975    menu_redraw_full (menu);
 976    /* allow the caller to do any local configuration */
 977    return (OP_REDRAW);
 978  }
 979
 980  if (!menu->dialog)
 981    menu_check_recenter (menu);
 982
 983  if (menu->redraw & REDRAW_STATUS)
 984    menu_redraw_status (menu);
 985#ifdef USE_SIDEBAR
 986  if (menu->redraw & REDRAW_SIDEBAR)
 987    menu_redraw_sidebar (menu);
 988#endif
 989  if (menu->redraw & REDRAW_INDEX)
 990    menu_redraw_index (menu);
 991  else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
 992    menu_redraw_motion (menu);
 993  else if (menu->redraw == REDRAW_CURRENT)
 994    menu_redraw_current (menu);
 995
 996  if (menu->dialog)
 997    menu_redraw_prompt (menu);
 998
 999  return OP_NULL;
1000}
1001
1002int mutt_menuLoop (MUTTMENU *menu)
1003{
1004  int i = OP_NULL;
1005
1006  FOREVER
1007  {
1008    if (option (OPTMENUCALLER))
1009    {
1010      unset_option (OPTMENUCALLER);
1011      return OP_NULL;
1012    }
1013
1014    /* Clear the tag prefix unless we just started it.  Don't clear
1015     * the prefix on a timeout (i==-2), but do clear on an abort (i==-1)
1016     */
1017    if (menu->tagprefix &&
1018        i != OP_TAG_PREFIX && i != OP_TAG_PREFIX_COND && i != -2)
1019      menu->tagprefix = 0;
1020
1021    mutt_curs_set (0);
1022
1023    if (menu->custom_menu_update)
1024      menu->custom_menu_update (menu);
1025
1026    if (menu_redraw (menu) == OP_REDRAW)
1027      return OP_REDRAW;
1028
1029    /* give visual indication that the next command is a tag- command */
1030    if (menu->tagprefix)
1031    {
1032      mutt_window_mvaddstr (menu->messagewin, 0, 0, "tag-");
1033      mutt_window_clrtoeol (menu->messagewin);
1034    }
1035
1036    menu->oldcurrent = menu->current;
1037
1038
1039    /* move the cursor out of the way */
1040    if (option (OPTARROWCURSOR))
1041      mutt_window_move (menu->indexwin, menu->current - menu->top + menu->offset, 2);
1042    else if (option (OPTBRAILLEFRIENDLY))
1043      mutt_window_move (menu->indexwin, menu->current - menu->top + menu->offset, 0);
1044    else
1045      mutt_window_move (menu->indexwin, menu->current - menu->top + menu->offset,
1046                        menu->indexwin->cols - 1);
1047
1048    mutt_refresh ();
1049
1050    /* try to catch dialog keys before ops */
1051    if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
1052      return i;
1053
1054    i = km_dokey (menu->menu);
1055    if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND)
1056    {
1057      if (menu->tagprefix)
1058      {
1059        menu->tagprefix = 0;
1060        mutt_window_clearline (menu->messagewin, 0);
1061        continue;
1062      }
1063
1064      if (menu->tagged)
1065      {
1066	menu->tagprefix = 1;
1067        continue;
1068      }
1069      else if (i == OP_TAG_PREFIX)
1070      {
1071	mutt_error _("No tagged entries.");
1072	i = -1;
1073      }
1074      else /* None tagged, OP_TAG_PREFIX_COND */
1075      {
1076	mutt_flush_macro_to_endcond ();
1077	mutt_message _("Nothing to do.");
1078	i = -1;
1079      }
1080    }
1081    else if (menu->tagged && option (OPTAUTOTAG))
1082      menu->tagprefix = 1;
1083
1084    mutt_curs_set (1);
1085
1086#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
1087    if (SigWinch)
1088    {
1089      SigWinch = 0;
1090      mutt_resize_screen ();
1091      clearok(stdscr,TRUE);/*force complete redraw*/
1092    }
1093#endif
1094
1095    if (i < 0)
1096    {
1097      if (menu->tagprefix)
1098        mutt_window_clearline (menu->messagewin, 0);
1099      continue;
1100    }
1101
1102    if (!menu->dialog)
1103      mutt_clear_error ();
1104
1105    /* Convert menubar movement to scrolling */
1106    if (menu->dialog)
1107      i = menu_dialog_translate_op (i);
1108
1109    switch (i)
1110    {
1111      case OP_NEXT_ENTRY:
1112	menu_next_entry (menu);
1113	break;
1114      case OP_PREV_ENTRY:
1115	menu_prev_entry (menu);
1116	break;
1117      case OP_HALF_DOWN:
1118	menu_half_down (menu);
1119	break;
1120      case OP_HALF_UP:
1121	menu_half_up (menu);
1122	break;
1123      case OP_NEXT_PAGE:
1124	menu_next_page (menu);
1125	break;
1126      case OP_PREV_PAGE:
1127	menu_prev_page (menu);
1128	break;
1129      case OP_NEXT_LINE:
1130	menu_next_line (menu);
1131	break;
1132      case OP_PREV_LINE:
1133	menu_prev_line (menu);
1134	break;
1135      case OP_FIRST_ENTRY:
1136	menu_first_entry (menu);
1137	break;
1138      case OP_LAST_ENTRY:
1139	menu_last_entry (menu);
1140	break;
1141      case OP_TOP_PAGE:
1142	menu_top_page (menu);
1143	break;
1144      case OP_MIDDLE_PAGE:
1145	menu_middle_page (menu);
1146	break;
1147      case OP_BOTTOM_PAGE:
1148	menu_bottom_page (menu);
1149	break;
1150      case OP_CURRENT_TOP:
1151	menu_current_top (menu);
1152	break;
1153      case OP_CURRENT_MIDDLE:
1154	menu_current_middle (menu);
1155	break;
1156      case OP_CURRENT_BOTTOM:
1157	menu_current_bottom (menu);
1158	break;
1159      case OP_SEARCH:
1160      case OP_SEARCH_REVERSE:
1161      case OP_SEARCH_NEXT:
1162      case OP_SEARCH_OPPOSITE:
1163	if (menu->search && !menu->dialog) /* Searching dialogs won't work */
1164	{
1165	  menu->oldcurrent = menu->current;
1166	  if ((menu->current = menu_search (menu, i)) != -1)
1167	    menu->redraw = REDRAW_MOTION;
1168	  else
1169	    menu->current = menu->oldcurrent;
1170	}
1171	else
1172	  mutt_error _("Search is not implemented for this menu.");
1173	break;
1174
1175      case OP_JUMP:
1176	if (menu->dialog)
1177	  mutt_error _("Jumping is not implemented for dialogs.");
1178	else
1179	  menu_jump (menu);
1180	break;
1181
1182      case OP_ENTER_COMMAND:
1183	mutt_enter_command ();
1184	break;
1185
1186      case OP_TAG:
1187	if (menu->tag && !menu->dialog)
1188	{
1189	  if (menu->tagprefix && !option (OPTAUTOTAG))
1190	  {
1191	    for (i = 0; i < menu->max; i++)
1192	      menu->tagged += menu->tag (menu, i, 0);
1193	    menu->redraw |= REDRAW_INDEX;
1194	  }
1195	  else if (menu->max)
1196	  {
1197	    int i = menu->tag (menu, menu->current, -1);
1198	    menu->tagged += i;
1199	    if (i && option (OPTRESOLVE) && menu->current < menu->max - 1)
1200	    {
1201	      menu->current++;
1202	      menu->redraw |= REDRAW_MOTION_RESYNCH;
1203	    }
1204	    else
1205	      menu->redraw |= REDRAW_CURRENT;
1206	  }
1207	  else
1208	    mutt_error _("No entries.");
1209	}
1210	else
1211	  mutt_error _("Tagging is not supported.");
1212	break;
1213
1214      case OP_SHELL_ESCAPE:
1215	mutt_shell_escape ();
1216	break;
1217
1218      case OP_WHAT_KEY:
1219	mutt_what_key ();
1220	break;
1221
1222      case OP_CHECK_STATS:
1223	mutt_check_stats ();
1224	break;
1225
1226      case OP_REDRAW:
1227	clearok (stdscr, TRUE);
1228	menu->redraw = REDRAW_FULL;
1229	break;
1230
1231      case OP_HELP:
1232	mutt_help (menu->menu);
1233	menu->redraw = REDRAW_FULL;
1234	break;
1235
1236      case OP_ERROR_HISTORY:
1237	mutt_error_history_display ();
1238	menu->redraw = REDRAW_FULL;
1239	break;
1240
1241      case OP_NULL:
1242	km_error_key (menu->menu);
1243	break;
1244
1245      case OP_END_COND:
1246	break;
1247
1248      default:
1249	return (i);
1250    }
1251  }
1252  /* not reached */
1253}