PageRenderTime 56ms CodeModel.GetById 13ms app.highlight 35ms RepoModel.GetById 0ms app.codeStats 1ms

/menu.c

https://bitbucket.org/mutt/mutt
C | 1210 lines | 1021 code | 143 blank | 46 comment | 223 complexity | fa55f6af88adddb8c9e56c4153bb5c17 MD5 | raw file
Possible License(s): AGPL-1.0, GPL-2.0, LGPL-2.0, LGPL-2.1
   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, 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
 301  if (menu->dialog) 
 302  {
 303    menu->redraw &= ~REDRAW_MOTION;
 304    return;
 305  }
 306  
 307  mutt_window_move (menu->indexwin, menu->oldcurrent + menu->offset - menu->top, 0);
 308  ATTRSET(menu->color (menu->oldcurrent));
 309
 310  if (option (OPTARROWCURSOR))
 311  {
 312    /* clear the pointer */
 313    addstr ("  ");
 314
 315    if (menu->redraw & REDRAW_MOTION_RESYNCH)
 316    {
 317      menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
 318      menu_pad_string (menu, buf, sizeof (buf));
 319      mutt_window_move (menu->indexwin, menu->oldcurrent + menu->offset - menu->top, 3);
 320      print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1);
 321    }
 322
 323    /* now draw it in the new location */
 324    SETCOLOR(MT_COLOR_INDICATOR);
 325    mutt_window_mvaddstr (menu->indexwin, menu->current + menu->offset - menu->top, 0, "->");
 326  }
 327  else
 328  {
 329    /* erase the current indicator */
 330    menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
 331    menu_pad_string (menu, buf, sizeof (buf));
 332    print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1);
 333
 334    /* now draw the new one to reflect the change */
 335    menu_make_entry (buf, sizeof (buf), menu, menu->current);
 336    menu_pad_string (menu, buf, sizeof (buf));
 337    SETCOLOR(MT_COLOR_INDICATOR);
 338    mutt_window_move (menu->indexwin, menu->current + menu->offset - menu->top, 0);
 339    print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0);
 340  }
 341  menu->redraw &= REDRAW_STATUS;
 342  NORMAL_COLOR;
 343}
 344
 345void menu_redraw_current (MUTTMENU *menu)
 346{
 347  char buf[LONG_STRING];
 348  int attr = menu->color (menu->current);
 349
 350  mutt_window_move (menu->indexwin, menu->current + menu->offset - menu->top, 0);
 351  menu_make_entry (buf, sizeof (buf), menu, menu->current);
 352  menu_pad_string (menu, buf, sizeof (buf));
 353
 354  SETCOLOR(MT_COLOR_INDICATOR);
 355  if (option (OPTARROWCURSOR))
 356  {
 357    addstr ("->");
 358    ATTRSET(attr);
 359    addch (' ');
 360    menu_pad_string (menu, buf, sizeof (buf));
 361    print_enriched_string (attr, (unsigned char *) buf, 1);
 362  }
 363  else
 364    print_enriched_string (attr, (unsigned char *) buf, 0);
 365  menu->redraw &= REDRAW_STATUS;
 366  NORMAL_COLOR;
 367}
 368
 369static void menu_redraw_prompt (MUTTMENU *menu)
 370{
 371  if (menu->dialog) 
 372  {
 373    if (option (OPTMSGERR)) 
 374    {
 375      mutt_sleep (1);
 376      unset_option (OPTMSGERR);
 377    }
 378
 379    if (*Errorbuf)
 380      mutt_clear_error ();
 381
 382    mutt_window_mvaddstr (menu->messagewin, 0, 0, menu->prompt);
 383    mutt_window_clrtoeol (menu->messagewin);
 384  }
 385}
 386
 387void menu_check_recenter (MUTTMENU *menu)
 388{
 389  int c = MIN (MenuContext, menu->pagelen / 2);
 390  int old_top = menu->top;
 391
 392  if (!option (OPTMENUMOVEOFF) && menu->max <= menu->pagelen) /* less entries than lines */
 393  {
 394    if (menu->top != 0) 
 395    {
 396      menu->top = 0;
 397      menu->redraw |= REDRAW_INDEX;
 398    }
 399  }
 400  else 
 401  {
 402    if (option (OPTMENUSCROLL) || (menu->pagelen <= 0) || (c < MenuContext))
 403    {
 404      if (menu->current < menu->top + c)
 405	menu->top = menu->current - c;
 406      else if (menu->current >= menu->top + menu->pagelen - c)
 407	menu->top = menu->current - menu->pagelen + c + 1;
 408    }
 409    else
 410    {
 411      if (menu->current < menu->top + c)
 412	menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) / (menu->pagelen - c)) - c;
 413      else if ((menu->current >= menu->top + menu->pagelen - c))
 414	menu->top += (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;	
 415    }
 416  }
 417
 418  if (!option (OPTMENUMOVEOFF)) /* make entries stick to bottom */
 419    menu->top = MIN (menu->top, menu->max - menu->pagelen);
 420  menu->top = MAX (menu->top, 0);
 421
 422  if (menu->top != old_top)
 423    menu->redraw |= REDRAW_INDEX;
 424}
 425
 426void menu_jump (MUTTMENU *menu)
 427{
 428  int n;
 429  char buf[SHORT_STRING];
 430
 431  if (menu->max)
 432  {
 433    mutt_unget_event (LastKey, 0);
 434    buf[0] = 0;
 435    if (mutt_get_field (_("Jump to: "), buf, sizeof (buf), 0) == 0 && buf[0])
 436    {
 437      if (mutt_atoi (buf, &n) == 0 && n > 0 && n < menu->max + 1)
 438      {
 439	n--;	/* msg numbers are 0-based */
 440	menu->current = n;
 441	menu->redraw = REDRAW_MOTION;
 442      }
 443      else
 444	mutt_error _("Invalid index number.");
 445    }
 446  }
 447  else
 448    mutt_error _("No entries.");
 449}
 450
 451void menu_next_line (MUTTMENU *menu)
 452{
 453  if (menu->max)
 454  {
 455    int c = MIN (MenuContext, menu->pagelen / 2);
 456
 457    if (menu->top + 1 < menu->max - c
 458      && (option(OPTMENUMOVEOFF) || (menu->max > menu->pagelen && menu->top < menu->max - menu->pagelen)))
 459    {
 460      menu->top++;
 461      if (menu->current < menu->top + c && menu->current < menu->max - 1)
 462	menu->current++;
 463      menu->redraw = REDRAW_INDEX;
 464    }
 465    else
 466      mutt_error _("You cannot scroll down farther.");
 467  }
 468  else
 469    mutt_error _("No entries.");
 470}
 471
 472void menu_prev_line (MUTTMENU *menu)
 473{
 474  if (menu->top > 0)
 475  {
 476    int c = MIN (MenuContext, menu->pagelen / 2);
 477
 478    menu->top--;
 479    if (menu->current >= menu->top + menu->pagelen - c && menu->current > 1)
 480      menu->current--;
 481    menu->redraw = REDRAW_INDEX;
 482  }
 483  else
 484    mutt_error _("You cannot scroll up farther.");
 485}
 486
 487/* 
 488 * pageup:   jumplen == -pagelen
 489 * pagedown: jumplen == pagelen
 490 * halfup:   jumplen == -pagelen/2
 491 * halfdown: jumplen == pagelen/2
 492 */
 493#define DIRECTION ((neg * 2) + 1)
 494static void menu_length_jump (MUTTMENU *menu, int jumplen)
 495{
 496  int tmp, neg = (jumplen >= 0) ? 0 : -1;
 497  int c = MIN (MenuContext, menu->pagelen / 2);
 498
 499  if (menu->max)
 500  {
 501    /* possible to scroll? */
 502    if (DIRECTION * menu->top <
 503	(tmp = (neg ? 0 : (menu->max /*-1*/) - (menu->pagelen /*-1*/))))
 504    {
 505      menu->top += jumplen;
 506
 507      /* jumped too long? */
 508      if ((neg || !option (OPTMENUMOVEOFF)) &&
 509	  DIRECTION * menu->top > tmp)
 510	menu->top = tmp;
 511
 512      /* need to move the cursor? */
 513      if ((DIRECTION *
 514	   (tmp = (menu->current -
 515		   (menu->top + (neg ? (menu->pagelen - 1) - c : c))
 516	  ))) < 0)
 517	menu->current -= tmp;
 518
 519      menu->redraw = REDRAW_INDEX;
 520    }
 521    else if (menu->current != (neg ? 0 : menu->max - 1) && !menu->dialog)
 522    {
 523      menu->current += jumplen;
 524      menu->redraw = REDRAW_MOTION;
 525    }
 526    else
 527      mutt_error (neg ? _("You are on the first page.")
 528		      : _("You are on the last page."));
 529
 530    menu->current = MIN (menu->current, menu->max - 1);
 531    menu->current = MAX (menu->current, 0);
 532  }
 533  else
 534    mutt_error _("No entries.");
 535}
 536#undef DIRECTION
 537
 538void menu_next_page (MUTTMENU *menu)
 539{
 540  menu_length_jump (menu, MAX (menu->pagelen /* - MenuOverlap */, 0));
 541}
 542
 543void menu_prev_page (MUTTMENU *menu)
 544{
 545  menu_length_jump (menu, 0 - MAX (menu->pagelen /* - MenuOverlap */, 0));
 546}
 547
 548void menu_half_down (MUTTMENU *menu)
 549{
 550  menu_length_jump (menu, menu->pagelen / 2);
 551}
 552
 553void menu_half_up (MUTTMENU *menu)
 554{
 555  menu_length_jump (menu, 0 - menu->pagelen / 2);
 556}
 557
 558void menu_top_page (MUTTMENU *menu)
 559{
 560  if (menu->current != menu->top)
 561  {
 562    menu->current = menu->top;
 563    menu->redraw = REDRAW_MOTION;
 564  }
 565}
 566
 567void menu_bottom_page (MUTTMENU *menu)
 568{
 569  if (menu->max)
 570  {
 571    menu->current = menu->top + menu->pagelen - 1;
 572    if (menu->current > menu->max - 1)
 573      menu->current = menu->max - 1;
 574    menu->redraw = REDRAW_MOTION;
 575  }
 576  else
 577    mutt_error _("No entries.");
 578}
 579
 580void menu_middle_page (MUTTMENU *menu)
 581{
 582  int i;
 583
 584  if (menu->max)
 585  {
 586    i = menu->top + menu->pagelen;
 587    if (i > menu->max - 1)
 588      i = menu->max - 1;
 589    menu->current = menu->top + (i - menu->top) / 2;
 590    menu->redraw = REDRAW_MOTION;
 591  }
 592  else
 593    mutt_error _("No entries.");
 594}
 595
 596void menu_first_entry (MUTTMENU *menu)
 597{
 598  if (menu->max)
 599  {
 600    menu->current = 0;
 601    menu->redraw = REDRAW_MOTION;
 602  }
 603  else
 604    mutt_error _("No entries.");
 605}
 606
 607void menu_last_entry (MUTTMENU *menu)
 608{
 609  if (menu->max)
 610  {
 611    menu->current = menu->max - 1;
 612    menu->redraw = REDRAW_MOTION;
 613  }
 614  else
 615    mutt_error _("No entries.");
 616}
 617
 618void menu_current_top (MUTTMENU *menu)
 619{
 620  if (menu->max)
 621  {
 622    menu->top = menu->current;
 623    menu->redraw = REDRAW_INDEX;
 624  }
 625  else
 626    mutt_error _("No entries.");
 627}
 628
 629void menu_current_middle (MUTTMENU *menu)
 630{
 631  if (menu->max)
 632  {
 633    menu->top = menu->current - menu->pagelen / 2;
 634    if (menu->top < 0)
 635      menu->top = 0;
 636    menu->redraw = REDRAW_INDEX;
 637  }
 638  else
 639    mutt_error _("No entries.");
 640}
 641
 642void menu_current_bottom (MUTTMENU *menu)
 643{
 644  if (menu->max)
 645  {
 646    menu->top = menu->current - menu->pagelen + 1;
 647    if (menu->top < 0)
 648      menu->top = 0;
 649    menu->redraw = REDRAW_INDEX;
 650  }
 651  else
 652    mutt_error _("No entries.");
 653}
 654
 655static void menu_next_entry (MUTTMENU *menu)
 656{
 657  if (menu->current < menu->max - 1)
 658  {
 659    menu->current++;
 660    menu->redraw = REDRAW_MOTION;
 661  }
 662  else
 663    mutt_error _("You are on the last entry.");
 664}
 665
 666static void menu_prev_entry (MUTTMENU *menu)
 667{
 668  if (menu->current)
 669  {
 670    menu->current--;
 671    menu->redraw = REDRAW_MOTION;
 672  }
 673  else
 674    mutt_error _("You are on the first entry.");
 675}
 676
 677static int default_color (int i)
 678{
 679   return ColorDefs[MT_COLOR_NORMAL];
 680}
 681
 682static int menu_search_generic (MUTTMENU *m, regex_t *re, int n)
 683{
 684  char buf[LONG_STRING];
 685
 686  menu_make_entry (buf, sizeof (buf), m, n);
 687  return (regexec (re, buf, 0, NULL, 0));
 688}
 689
 690void mutt_menu_init (void)
 691{
 692  int i;
 693
 694  for (i = 0; i < MENU_MAX; i++)
 695    SearchBuffers[i] = NULL;
 696}
 697
 698MUTTMENU *mutt_new_menu (int menu)
 699{
 700  MUTTMENU *p = (MUTTMENU *) safe_calloc (1, sizeof (MUTTMENU));
 701
 702  if ((menu < 0) || (menu >= MENU_MAX))
 703    menu = MENU_GENERIC;
 704
 705  p->menu = menu;
 706  p->current = 0;
 707  p->top = 0;
 708  p->offset = 0;
 709  p->redraw = REDRAW_FULL;
 710  p->pagelen = MuttIndexWindow->rows;
 711  p->indexwin = MuttIndexWindow;
 712  p->statuswin = MuttStatusWindow;
 713  p->helpwin = MuttHelpWindow;
 714  p->messagewin = MuttMessageWindow;
 715  p->color = default_color;
 716  p->search = menu_search_generic;
 717
 718  return (p);
 719}
 720
 721void mutt_menuDestroy (MUTTMENU **p)
 722{
 723  int i;
 724
 725  if ((*p)->dialog)
 726  {
 727    for (i=0; i < (*p)->max; i++)
 728      FREE (&(*p)->dialog[i]);
 729
 730    FREE (& (*p)->dialog);
 731  }
 732
 733  FREE (p);		/* __FREE_CHECKED__ */
 734}
 735
 736static MUTTMENU *get_current_menu (void)
 737{
 738  return MenuStackCount ? MenuStack[MenuStackCount - 1] : NULL;
 739}
 740
 741void mutt_push_current_menu (MUTTMENU *menu)
 742{
 743  if (MenuStackCount >= MenuStackLen)
 744  {
 745    MenuStackLen += 5;
 746    safe_realloc (&MenuStack, MenuStackLen * sizeof(MUTTMENU *));
 747  }
 748
 749  MenuStack[MenuStackCount++] = menu;
 750  CurrentMenu = menu->menu;
 751}
 752
 753void mutt_pop_current_menu (MUTTMENU *menu)
 754{
 755  MUTTMENU *prev_menu;
 756
 757  if (!MenuStackCount ||
 758      (MenuStack[MenuStackCount - 1] != menu))
 759  {
 760    dprint (1, (debugfile, "mutt_pop_current_menu() called with inactive menu\n"));
 761    return;
 762  }
 763
 764  MenuStackCount--;
 765  prev_menu = get_current_menu ();
 766  if (prev_menu)
 767  {
 768    CurrentMenu = prev_menu->menu;
 769    prev_menu->redraw = REDRAW_FULL;
 770  }
 771  else
 772  {
 773    CurrentMenu = MENU_MAIN;
 774  }
 775}
 776
 777void mutt_set_current_menu_redraw (int redraw)
 778{
 779  MUTTMENU *current_menu;
 780
 781  current_menu = get_current_menu ();
 782  if (current_menu)
 783    current_menu->redraw |= redraw;
 784}
 785
 786void mutt_set_current_menu_redraw_full (void)
 787{
 788  MUTTMENU *current_menu;
 789
 790  current_menu = get_current_menu ();
 791  if (current_menu)
 792    current_menu->redraw = REDRAW_FULL;
 793}
 794
 795void mutt_set_menu_redraw (int menu_type, int redraw)
 796{
 797  if (CurrentMenu == menu_type)
 798    mutt_set_current_menu_redraw (redraw);
 799}
 800
 801void mutt_set_menu_redraw_full (int menu_type)
 802{
 803  if (CurrentMenu == menu_type)
 804    mutt_set_current_menu_redraw_full ();
 805}
 806
 807void mutt_current_menu_redraw ()
 808{
 809  MUTTMENU *current_menu;
 810
 811  current_menu = get_current_menu ();
 812  if (current_menu)
 813  {
 814    if (menu_redraw (current_menu) == OP_REDRAW)
 815    /* On a REDRAW_FULL with a non-customized redraw, menu_redraw()
 816     * will return OP_REDRAW to give the calling menu-loop a chance to
 817     * customize output.
 818     */
 819       menu_redraw (current_menu);
 820  }
 821}
 822
 823
 824#define MUTT_SEARCH_UP   1
 825#define MUTT_SEARCH_DOWN 2
 826
 827static int menu_search (MUTTMENU *menu, int op)
 828{
 829  int r, wrap = 0;
 830  int searchDir;
 831  regex_t re;
 832  char buf[SHORT_STRING];
 833  char* searchBuf = menu->menu >= 0 && menu->menu < MENU_MAX ?
 834                    SearchBuffers[menu->menu] : NULL;
 835
 836  if (!(searchBuf && *searchBuf) ||
 837      (op != OP_SEARCH_NEXT && op != OP_SEARCH_OPPOSITE))
 838  {
 839    strfcpy (buf, searchBuf && *searchBuf ? searchBuf : "", sizeof (buf));
 840    if (mutt_get_field ((op == OP_SEARCH || op == OP_SEARCH_NEXT)
 841			? _("Search for: ") : _("Reverse search for: "),
 842			buf, sizeof (buf), MUTT_CLEAR) != 0 || !buf[0])
 843      return (-1);
 844    if (menu->menu >= 0 && menu->menu < MENU_MAX)
 845    {
 846      mutt_str_replace (&SearchBuffers[menu->menu], buf);
 847      searchBuf = SearchBuffers[menu->menu];
 848    }
 849    menu->searchDir = (op == OP_SEARCH || op == OP_SEARCH_NEXT) ?
 850		       MUTT_SEARCH_DOWN : MUTT_SEARCH_UP;
 851  }
 852
 853  searchDir = (menu->searchDir == MUTT_SEARCH_UP) ? -1 : 1;
 854  if (op == OP_SEARCH_OPPOSITE)
 855    searchDir = -searchDir;
 856
 857  if ((r = REGCOMP (&re, searchBuf, REG_NOSUB | mutt_which_case (searchBuf))) != 0)
 858  {
 859    regerror (r, &re, buf, sizeof (buf));
 860    mutt_error ("%s", buf);
 861    return (-1);
 862  }
 863
 864  r = menu->current + searchDir;
 865search_next:
 866  if (wrap)
 867    mutt_message (_("Search wrapped to top."));
 868  while (r >= 0 && r < menu->max)
 869  {
 870    if (menu->search (menu, &re, r) == 0)
 871    {
 872      regfree (&re);
 873      return r;
 874    }
 875
 876    r += searchDir;
 877  }
 878
 879  if (option (OPTWRAPSEARCH) && wrap++ == 0)
 880  {
 881    r = searchDir == 1 ? 0 : menu->max - 1;
 882    goto search_next;
 883  }
 884  regfree (&re);
 885  mutt_error _("Not found.");
 886  return (-1);
 887}
 888
 889static int menu_dialog_translate_op (int i)
 890{
 891  switch (i)
 892  {
 893    case OP_NEXT_ENTRY:   
 894      return OP_NEXT_LINE;
 895    case OP_PREV_ENTRY:	  
 896      return OP_PREV_LINE;
 897    case OP_CURRENT_TOP:   case OP_FIRST_ENTRY:  
 898      return OP_TOP_PAGE;
 899    case OP_CURRENT_BOTTOM:    case OP_LAST_ENTRY:	  
 900      return OP_BOTTOM_PAGE;
 901    case OP_CURRENT_MIDDLE: 
 902      return OP_MIDDLE_PAGE; 
 903  }
 904  
 905  return i;
 906}
 907
 908static int menu_dialog_dokey (MUTTMENU *menu, int *ip)
 909{
 910  event_t ch;
 911  char *p;
 912
 913  ch = mutt_getch ();
 914
 915  if (ch.ch < 0)
 916  {
 917    *ip = -1;
 918    return 0;
 919  }
 920
 921  if (ch.ch && (p = strchr (menu->keys, ch.ch)))
 922  {
 923    *ip = OP_MAX + (p - menu->keys + 1);
 924    return 0;
 925  }
 926  else
 927  {
 928    mutt_unget_event (ch.op ? 0 : ch.ch, ch.op ? ch.op : 0);
 929    return -1;
 930  }
 931}
 932
 933int menu_redraw (MUTTMENU *menu)
 934{
 935  if (menu->custom_menu_redraw)
 936  {
 937    menu->custom_menu_redraw (menu);
 938    return OP_NULL;
 939  }
 940
 941  /* See if all or part of the screen needs to be updated.  */
 942  if (menu->redraw & REDRAW_FULL)
 943  {
 944    menu_redraw_full (menu);
 945    /* allow the caller to do any local configuration */
 946    return (OP_REDRAW);
 947  }
 948  
 949  if (!menu->dialog)
 950    menu_check_recenter (menu);
 951  
 952  if (menu->redraw & REDRAW_STATUS)
 953    menu_redraw_status (menu);
 954#ifdef USE_SIDEBAR
 955  if (menu->redraw & REDRAW_SIDEBAR)
 956    menu_redraw_sidebar (menu);
 957#endif
 958  if (menu->redraw & REDRAW_INDEX)
 959    menu_redraw_index (menu);
 960  else if (menu->redraw & (REDRAW_MOTION | REDRAW_MOTION_RESYNCH))
 961    menu_redraw_motion (menu);
 962  else if (menu->redraw == REDRAW_CURRENT)
 963    menu_redraw_current (menu);
 964  
 965  if (menu->dialog)
 966    menu_redraw_prompt (menu);
 967  
 968  return OP_NULL;
 969}
 970
 971int mutt_menuLoop (MUTTMENU *menu)
 972{
 973  int i = OP_NULL;
 974
 975  FOREVER
 976  {
 977    if (option (OPTMENUCALLER))
 978    {
 979      unset_option (OPTMENUCALLER);
 980      return OP_NULL;
 981    }
 982
 983    /* Clear the tag prefix unless we just started it.  Don't clear
 984     * the prefix on a timeout (i==-2), but do clear on an abort (i==-1)
 985     */
 986    if (menu->tagprefix &&
 987        i != OP_TAG_PREFIX && i != OP_TAG_PREFIX_COND && i != -2)
 988      menu->tagprefix = 0;
 989
 990    mutt_curs_set (0);
 991
 992    if (menu_redraw (menu) == OP_REDRAW)
 993      return OP_REDRAW;
 994
 995    /* give visual indication that the next command is a tag- command */
 996    if (menu->tagprefix)
 997    {
 998      mutt_window_mvaddstr (menu->messagewin, 0, 0, "tag-");
 999      mutt_window_clrtoeol (menu->messagewin);
1000    }
1001
1002    menu->oldcurrent = menu->current;
1003
1004
1005    /* move the cursor out of the way */
1006    if (option (OPTARROWCURSOR))
1007      mutt_window_move (menu->indexwin, menu->current - menu->top + menu->offset, 2);
1008    else if (option (OPTBRAILLEFRIENDLY))
1009      mutt_window_move (menu->indexwin, menu->current - menu->top + menu->offset, 0);
1010    else
1011      mutt_window_move (menu->indexwin, menu->current - menu->top + menu->offset,
1012                        menu->indexwin->cols - 1);
1013
1014    mutt_refresh ();
1015
1016    /* try to catch dialog keys before ops */
1017    if (menu->dialog && menu_dialog_dokey (menu, &i) == 0)
1018      return i;
1019
1020    i = km_dokey (menu->menu);
1021    if (i == OP_TAG_PREFIX || i == OP_TAG_PREFIX_COND)
1022    {
1023      if (menu->tagprefix)
1024      {
1025        menu->tagprefix = 0;
1026        mutt_window_clearline (menu->messagewin, 0);
1027        continue;
1028      }
1029
1030      if (menu->tagged)
1031      {
1032	menu->tagprefix = 1;
1033        continue;
1034      }
1035      else if (i == OP_TAG_PREFIX)
1036      {
1037	mutt_error _("No tagged entries.");
1038	i = -1;
1039      }
1040      else /* None tagged, OP_TAG_PREFIX_COND */
1041      {
1042	mutt_flush_macro_to_endcond ();
1043	mutt_message _("Nothing to do.");
1044	i = -1;
1045      }
1046    }
1047    else if (menu->tagged && option (OPTAUTOTAG))
1048      menu->tagprefix = 1;
1049
1050    mutt_curs_set (1);
1051
1052#if defined (USE_SLANG_CURSES) || defined (HAVE_RESIZETERM)
1053    if (SigWinch)
1054    {
1055      mutt_resize_screen ();
1056      SigWinch = 0;
1057      clearok(stdscr,TRUE);/*force complete redraw*/
1058    }
1059#endif
1060
1061    if (i < 0)
1062    {
1063      if (menu->tagprefix)
1064        mutt_window_clearline (menu->messagewin, 0);
1065      continue;
1066    }
1067
1068    if (!menu->dialog)
1069      mutt_clear_error ();
1070
1071    /* Convert menubar movement to scrolling */
1072    if (menu->dialog) 
1073      i = menu_dialog_translate_op (i);
1074
1075    switch (i)
1076    {
1077      case OP_NEXT_ENTRY:
1078	menu_next_entry (menu);
1079	break;
1080      case OP_PREV_ENTRY:
1081	menu_prev_entry (menu);
1082	break;
1083      case OP_HALF_DOWN:
1084	menu_half_down (menu);
1085	break;
1086      case OP_HALF_UP:
1087	menu_half_up (menu);
1088	break;
1089      case OP_NEXT_PAGE:
1090	menu_next_page (menu);
1091	break;
1092      case OP_PREV_PAGE:
1093	menu_prev_page (menu);
1094	break;
1095      case OP_NEXT_LINE:
1096	menu_next_line (menu);
1097	break;
1098      case OP_PREV_LINE:
1099	menu_prev_line (menu);
1100	break;
1101      case OP_FIRST_ENTRY:
1102	menu_first_entry (menu);
1103	break;
1104      case OP_LAST_ENTRY:
1105	menu_last_entry (menu);
1106	break;
1107      case OP_TOP_PAGE:
1108	menu_top_page (menu);
1109	break;
1110      case OP_MIDDLE_PAGE:
1111	menu_middle_page (menu);
1112	break;
1113      case OP_BOTTOM_PAGE:
1114	menu_bottom_page (menu);
1115	break;
1116      case OP_CURRENT_TOP:
1117	menu_current_top (menu);
1118	break;
1119      case OP_CURRENT_MIDDLE:
1120	menu_current_middle (menu);
1121	break;
1122      case OP_CURRENT_BOTTOM:
1123	menu_current_bottom (menu);
1124	break;
1125      case OP_SEARCH:
1126      case OP_SEARCH_REVERSE:
1127      case OP_SEARCH_NEXT:
1128      case OP_SEARCH_OPPOSITE:
1129	if (menu->search && !menu->dialog) /* Searching dialogs won't work */
1130	{
1131	  menu->oldcurrent = menu->current;
1132	  if ((menu->current = menu_search (menu, i)) != -1)
1133	    menu->redraw = REDRAW_MOTION;
1134	  else
1135	    menu->current = menu->oldcurrent;
1136	}
1137	else
1138	  mutt_error _("Search is not implemented for this menu.");
1139	break;
1140
1141      case OP_JUMP:
1142	if (menu->dialog)
1143	  mutt_error _("Jumping is not implemented for dialogs.");
1144	else
1145	  menu_jump (menu);
1146	break;
1147
1148      case OP_ENTER_COMMAND:
1149	mutt_enter_command ();
1150	break;
1151
1152      case OP_TAG:
1153	if (menu->tag && !menu->dialog)
1154	{
1155	  if (menu->tagprefix && !option (OPTAUTOTAG))
1156	  {
1157	    for (i = 0; i < menu->max; i++)
1158	      menu->tagged += menu->tag (menu, i, 0);
1159	    menu->redraw |= REDRAW_INDEX;
1160	  }
1161	  else if (menu->max)
1162	  {
1163	    int i = menu->tag (menu, menu->current, -1);
1164	    menu->tagged += i;
1165	    if (i && option (OPTRESOLVE) && menu->current < menu->max - 1)
1166	    {
1167	      menu->current++;
1168	      menu->redraw |= REDRAW_MOTION_RESYNCH;
1169	    }
1170	    else
1171	      menu->redraw |= REDRAW_CURRENT;
1172	  }
1173	  else
1174	    mutt_error _("No entries.");
1175	}
1176	else
1177	  mutt_error _("Tagging is not supported.");
1178	break;
1179
1180      case OP_SHELL_ESCAPE:
1181	mutt_shell_escape ();
1182	break;
1183
1184      case OP_WHAT_KEY:
1185	mutt_what_key ();
1186	break;
1187
1188      case OP_REDRAW:
1189	clearok (stdscr, TRUE);
1190	menu->redraw = REDRAW_FULL;
1191	break;
1192
1193      case OP_HELP:
1194	mutt_help (menu->menu);
1195	menu->redraw = REDRAW_FULL;
1196	break;
1197
1198      case OP_NULL:
1199	km_error_key (menu->menu);
1200	break;
1201
1202      case OP_END_COND:
1203	break;
1204
1205      default:
1206	return (i);
1207    }
1208  }
1209  /* not reached */
1210}