NeoMutt  2021-02-05-329-g9e03b7
Teaching an old dog new tricks
DOXYGEN
menu.c
Go to the documentation of this file.
1 
30 #include "config.h"
31 #include <stdbool.h>
32 #include <stdint.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <wchar.h>
36 #include "mutt/lib.h"
37 #include "config/lib.h"
38 #include "email/lib.h"
39 #include "core/lib.h"
40 #include "gui/lib.h"
41 #include "mutt.h"
42 #include "menu/lib.h"
43 #include "pattern/lib.h"
44 #include "commands.h"
45 #include "context.h"
46 #include "keymap.h"
47 #include "mutt_globals.h"
48 #include "mutt_logging.h"
49 #include "mutt_mailbox.h"
50 #include "mutt_thread.h"
51 #include "muttlib.h"
52 #include "opcodes.h"
53 #include "options.h"
54 #include "protos.h"
55 
57 
58 #define DIRECTION ((neg * 2) + 1)
59 
60 #define MUTT_SEARCH_UP 1
61 #define MUTT_SEARCH_DOWN 2
62 
72 static int get_color(int index, unsigned char *s)
73 {
74  struct ColorLineList *color = NULL;
75  struct ColorLine *np = NULL;
76  struct Email *e = mutt_get_virt_email(Context->mailbox, index);
77  int type = *s;
78 
79  switch (type)
80  {
82  color = mutt_color_index_author();
83  break;
85  color = mutt_color_index_flags();
86  break;
88  color = mutt_color_index_subject();
89  break;
90  case MT_COLOR_INDEX_TAG:
92  {
93  if (mutt_strn_equal((const char *) (s + 1), np->pattern, strlen(np->pattern)))
94  return np->pair;
95  const char *transform = mutt_hash_find(TagTransforms, np->pattern);
96  if (transform && mutt_strn_equal((const char *) (s + 1), transform, strlen(transform)))
97  {
98  return np->pair;
99  }
100  }
101  return 0;
102  default:
103  return mutt_color(type);
104  }
105 
106  STAILQ_FOREACH(np, color, entries)
107  {
110  return np->pair;
111  }
112 
113  return 0;
114 }
115 
123 static void print_enriched_string(int index, int attr, unsigned char *s, bool do_color)
124 {
125  wchar_t wc;
126  size_t k;
127  size_t n = mutt_str_len((char *) s);
128  mbstate_t mbstate;
129 
130  memset(&mbstate, 0, sizeof(mbstate));
131  while (*s)
132  {
133  if (*s < MUTT_TREE_MAX)
134  {
135  if (do_color)
136 #if defined(HAVE_COLOR) && defined(HAVE_USE_DEFAULT_COLORS)
137  /* Combining tree fg color and another bg color requires having
138  * use_default_colors, because the other bg color may be undefined. */
140 #else
142 #endif
143 
144  const bool c_ascii_chars = cs_subset_bool(NeoMutt->sub, "ascii_chars");
145  while (*s && (*s < MUTT_TREE_MAX))
146  {
147  switch (*s)
148  {
149  case MUTT_TREE_LLCORNER:
150  if (c_ascii_chars)
151  mutt_window_addch('`');
152 #ifdef WACS_LLCORNER
153  else
154  add_wch(WACS_LLCORNER);
155 #else
156  else if (CharsetIsUtf8)
157  mutt_window_addstr("\342\224\224"); /* WACS_LLCORNER */
158  else
159  mutt_window_addch(ACS_LLCORNER);
160 #endif
161  break;
162  case MUTT_TREE_ULCORNER:
163  if (c_ascii_chars)
164  mutt_window_addch(',');
165 #ifdef WACS_ULCORNER
166  else
167  add_wch(WACS_ULCORNER);
168 #else
169  else if (CharsetIsUtf8)
170  mutt_window_addstr("\342\224\214"); /* WACS_ULCORNER */
171  else
172  mutt_window_addch(ACS_ULCORNER);
173 #endif
174  break;
175  case MUTT_TREE_LTEE:
176  if (c_ascii_chars)
177  mutt_window_addch('|');
178 #ifdef WACS_LTEE
179  else
180  add_wch(WACS_LTEE);
181 #else
182  else if (CharsetIsUtf8)
183  mutt_window_addstr("\342\224\234"); /* WACS_LTEE */
184  else
185  mutt_window_addch(ACS_LTEE);
186 #endif
187  break;
188  case MUTT_TREE_HLINE:
189  if (c_ascii_chars)
190  mutt_window_addch('-');
191 #ifdef WACS_HLINE
192  else
193  add_wch(WACS_HLINE);
194 #else
195  else if (CharsetIsUtf8)
196  mutt_window_addstr("\342\224\200"); /* WACS_HLINE */
197  else
198  mutt_window_addch(ACS_HLINE);
199 #endif
200  break;
201  case MUTT_TREE_VLINE:
202  if (c_ascii_chars)
203  mutt_window_addch('|');
204 #ifdef WACS_VLINE
205  else
206  add_wch(WACS_VLINE);
207 #else
208  else if (CharsetIsUtf8)
209  mutt_window_addstr("\342\224\202"); /* WACS_VLINE */
210  else
211  mutt_window_addch(ACS_VLINE);
212 #endif
213  break;
214  case MUTT_TREE_TTEE:
215  if (c_ascii_chars)
216  mutt_window_addch('-');
217 #ifdef WACS_TTEE
218  else
219  add_wch(WACS_TTEE);
220 #else
221  else if (CharsetIsUtf8)
222  mutt_window_addstr("\342\224\254"); /* WACS_TTEE */
223  else
224  mutt_window_addch(ACS_TTEE);
225 #endif
226  break;
227  case MUTT_TREE_BTEE:
228  if (c_ascii_chars)
229  mutt_window_addch('-');
230 #ifdef WACS_BTEE
231  else
232  add_wch(WACS_BTEE);
233 #else
234  else if (CharsetIsUtf8)
235  mutt_window_addstr("\342\224\264"); /* WACS_BTEE */
236  else
237  mutt_window_addch(ACS_BTEE);
238 #endif
239  break;
240  case MUTT_TREE_SPACE:
241  mutt_window_addch(' ');
242  break;
243  case MUTT_TREE_RARROW:
244  mutt_window_addch('>');
245  break;
246  case MUTT_TREE_STAR:
247  mutt_window_addch('*'); /* fake thread indicator */
248  break;
249  case MUTT_TREE_HIDDEN:
250  mutt_window_addch('&');
251  break;
252  case MUTT_TREE_EQUALS:
253  mutt_window_addch('=');
254  break;
255  case MUTT_TREE_MISSING:
256  mutt_window_addch('?');
257  break;
258  }
259  s++;
260  n--;
261  }
262  if (do_color)
263  mutt_curses_set_attr(attr);
264  }
265  else if (*s == MUTT_SPECIAL_INDEX)
266  {
267  s++;
268  if (do_color)
269  {
270  if (*s == MT_COLOR_INDEX)
271  {
272  attrset(attr);
273  }
274  else
275  {
276  if (get_color(index, s) == 0)
277  {
278  attron(attr);
279  }
280  else
281  {
282  attron(get_color(index, s));
283  }
284  }
285  }
286  s++;
287  n -= 2;
288  }
289  else if ((k = mbrtowc(&wc, (char *) s, n, &mbstate)) > 0)
290  {
291  mutt_window_addnstr((char *) s, k);
292  s += k;
293  n -= k;
294  }
295  else
296  break;
297  }
298 }
299 
307 static void make_entry(struct Menu *menu, char *buf, size_t buflen, int i)
308 {
309  if (!ARRAY_EMPTY(&menu->dialog))
310  {
311  mutt_str_copy(buf, NONULL(*ARRAY_GET(&menu->dialog, i)), buflen);
312  menu_set_index(menu, -1); /* hide menubar */
313  }
314  else
315  menu->make_entry(menu, buf, buflen, i);
316 }
317 
326 static void menu_pad_string(struct Menu *menu, char *buf, size_t buflen)
327 {
328  char *scratch = mutt_str_dup(buf);
329  const bool c_arrow_cursor = cs_subset_bool(NeoMutt->sub, "arrow_cursor");
330  const char *const c_arrow_string =
331  cs_subset_string(NeoMutt->sub, "arrow_string");
332  int shift = c_arrow_cursor ? mutt_strwidth(c_arrow_string) + 1 : 0;
333  int cols = menu->win_index->state.cols - shift;
334 
335  mutt_simple_format(buf, buflen, cols, cols, JUSTIFY_LEFT, ' ', scratch,
336  mutt_str_len(scratch), true);
337  buf[buflen - 1] = '\0';
338  FREE(&scratch);
339 }
340 
345 void menu_redraw_full(struct Menu *menu)
346 {
349 
350  window_redraw(RootWindow, true);
351  menu->pagelen = menu->win_index->state.rows;
352 
353  mutt_show_error();
354 
356 }
357 
362 void menu_redraw_status(struct Menu *menu)
363 {
364  if (!menu || !menu->win_ibar)
365  return;
366 
367  char buf[256];
368 
369  snprintf(buf, sizeof(buf), "-- NeoMutt: %s", menu->title);
371  mutt_window_move(menu->win_ibar, 0, 0);
372  mutt_paddstr(menu->win_ibar->state.cols, buf);
374  menu->redraw &= ~REDRAW_STATUS;
375 }
376 
381 void menu_redraw_index(struct Menu *menu)
382 {
383  char buf[1024];
384  bool do_color;
385  int attr;
386 
387  for (int i = menu->top; i < (menu->top + menu->pagelen); i++)
388  {
389  if (i < menu->max)
390  {
391  attr = menu->color(menu, i);
392 
393  make_entry(menu, buf, sizeof(buf), i);
394  menu_pad_string(menu, buf, sizeof(buf));
395 
396  mutt_curses_set_attr(attr);
397  mutt_window_move(menu->win_index, 0, i - menu->top);
398  do_color = true;
399 
400  const bool c_arrow_cursor = cs_subset_bool(NeoMutt->sub, "arrow_cursor");
401  const char *const c_arrow_string =
402  cs_subset_string(NeoMutt->sub, "arrow_string");
403  if (i == menu->current)
404  {
406  if (c_arrow_cursor)
407  {
408  mutt_window_addstr(c_arrow_string);
409  mutt_curses_set_attr(attr);
410  mutt_window_addch(' ');
411  }
412  else
413  do_color = false;
414  }
415  else if (c_arrow_cursor)
416  {
417  /* Print space chars to match the screen width of `$arrow_string` */
418  mutt_window_printf("%*s", mutt_strwidth(c_arrow_string) + 1, "");
419  }
420 
421  print_enriched_string(i, attr, (unsigned char *) buf, do_color);
422  }
423  else
424  {
426  mutt_window_clearline(menu->win_index, i - menu->top);
427  }
428  }
430  menu->redraw = REDRAW_NO_FLAGS;
431 
432  notify_send(menu->notify, NT_MENU, 0, NULL);
433 }
434 
439 void menu_redraw_motion(struct Menu *menu)
440 {
441  char buf[1024];
442 
443  if (!ARRAY_EMPTY(&menu->dialog))
444  {
445  menu->redraw &= ~REDRAW_MOTION;
446  return;
447  }
448 
449  /* Note: menu->color() for the index can end up retrieving a message
450  * over imap (if matching against ~h for instance). This can
451  * generate status messages. So we want to call it *before* we
452  * position the cursor for drawing. */
453  const int old_color = menu->color(menu, menu->oldcurrent);
454  mutt_window_move(menu->win_index, 0, menu->oldcurrent - menu->top);
455  mutt_curses_set_attr(old_color);
456 
457  const bool c_arrow_cursor = cs_subset_bool(NeoMutt->sub, "arrow_cursor");
458  const char *const c_arrow_string =
459  cs_subset_string(NeoMutt->sub, "arrow_string");
460  if (c_arrow_cursor)
461  {
462  /* clear the arrow */
463  /* Print space chars to match the screen width of `$arrow_string` */
464  mutt_window_printf("%*s", mutt_strwidth(c_arrow_string) + 1, "");
465 
466  make_entry(menu, buf, sizeof(buf), menu->oldcurrent);
467  menu_pad_string(menu, buf, sizeof(buf));
468  mutt_window_move(menu->win_index, mutt_strwidth(c_arrow_string) + 1,
469  menu->oldcurrent - menu->top);
470  print_enriched_string(menu->oldcurrent, old_color, (unsigned char *) buf, true);
471 
472  /* now draw it in the new location */
474  mutt_window_mvaddstr(menu->win_index, 0, menu->current - menu->top, c_arrow_string);
475  }
476  else
477  {
478  /* erase the current indicator */
479  make_entry(menu, buf, sizeof(buf), menu->oldcurrent);
480  menu_pad_string(menu, buf, sizeof(buf));
481  print_enriched_string(menu->oldcurrent, old_color, (unsigned char *) buf, true);
482 
483  /* now draw the new one to reflect the change */
484  const int cur_color = menu->color(menu, menu->current);
485  make_entry(menu, buf, sizeof(buf), menu->current);
486  menu_pad_string(menu, buf, sizeof(buf));
488  mutt_window_move(menu->win_index, 0, menu->current - menu->top);
489  print_enriched_string(menu->current, cur_color, (unsigned char *) buf, false);
490  }
491  menu->redraw &= REDRAW_STATUS;
493 
494  notify_send(menu->notify, NT_MENU, 0, NULL);
495 }
496 
501 void menu_redraw_current(struct Menu *menu)
502 {
503  char buf[1024];
504  int attr = menu->color(menu, menu->current);
505 
506  mutt_window_move(menu->win_index, 0, menu->current - menu->top);
507  make_entry(menu, buf, sizeof(buf), menu->current);
508  menu_pad_string(menu, buf, sizeof(buf));
509 
511  const bool c_arrow_cursor = cs_subset_bool(NeoMutt->sub, "arrow_cursor");
512  const char *const c_arrow_string =
513  cs_subset_string(NeoMutt->sub, "arrow_string");
514  if (c_arrow_cursor)
515  {
516  mutt_window_addstr(c_arrow_string);
517  mutt_curses_set_attr(attr);
518  mutt_window_addch(' ');
519  menu_pad_string(menu, buf, sizeof(buf));
520  print_enriched_string(menu->current, attr, (unsigned char *) buf, true);
521  }
522  else
523  print_enriched_string(menu->current, attr, (unsigned char *) buf, false);
524  menu->redraw &= REDRAW_STATUS;
526 
527  notify_send(menu->notify, NT_MENU, 0, NULL);
528 }
529 
534 static void menu_redraw_prompt(struct Menu *menu)
535 {
536  if (!menu || ARRAY_EMPTY(&menu->dialog))
537  return;
538 
539  if (OptMsgErr)
540  {
541  mutt_sleep(1);
542  OptMsgErr = false;
543  }
544 
545  if (ErrorBufMessage)
547 
550 
551  notify_send(menu->notify, NT_MENU, 0, NULL);
552 }
553 
558 void menu_check_recenter(struct Menu *menu)
559 {
560  const short c_menu_context = cs_subset_number(NeoMutt->sub, "menu_context");
561  const bool c_menu_move_off = cs_subset_bool(NeoMutt->sub, "menu_move_off");
562  const bool c_menu_scroll = cs_subset_bool(NeoMutt->sub, "menu_scroll");
563 
564  int c = MIN(c_menu_context, (menu->pagelen / 2));
565  int old_top = menu->top;
566 
567  if (!c_menu_move_off && (menu->max <= menu->pagelen)) /* less entries than lines */
568  {
569  if (menu->top != 0)
570  {
571  menu->top = 0;
572  menu->redraw |= REDRAW_INDEX;
573  }
574  }
575  else
576  {
577  if (c_menu_scroll || (menu->pagelen <= 0) || (c < c_menu_context))
578  {
579  if (menu->current < (menu->top + c))
580  menu->top = menu->current - c;
581  else if (menu->current >= (menu->top + menu->pagelen - c))
582  menu->top = menu->current - menu->pagelen + c + 1;
583  }
584  else
585  {
586  if (menu->current < menu->top + c)
587  {
588  menu->top -= (menu->pagelen - c) * ((menu->top + menu->pagelen - 1 - menu->current) /
589  (menu->pagelen - c)) -
590  c;
591  }
592  else if ((menu->current >= (menu->top + menu->pagelen - c)))
593  {
594  menu->top +=
595  (menu->pagelen - c) * ((menu->current - menu->top) / (menu->pagelen - c)) - c;
596  }
597  }
598  }
599 
600  if (!c_menu_move_off) /* make entries stick to bottom */
601  menu->top = MIN(menu->top, menu->max - menu->pagelen);
602  menu->top = MAX(menu->top, 0);
603 
604  if (menu->top != old_top)
605  menu->redraw |= REDRAW_INDEX;
606 }
607 
614 static void menu_jump(struct Menu *menu)
615 {
616  if (menu->max == 0)
617  {
618  mutt_error(_("No entries"));
619  return;
620  }
621 
623  char buf[128] = { 0 };
624  if ((mutt_get_field(_("Jump to: "), buf, sizeof(buf), MUTT_COMP_NO_FLAGS,
625  false, NULL, NULL) == 0) &&
626  (buf[0] != '\0'))
627  {
628  int n = 0;
629  if ((mutt_str_atoi(buf, &n) == 0) && (n > 0) && (n < (menu->max + 1)))
630  {
631  menu_set_index(menu, n - 1); // msg numbers are 0-based
632  }
633  else
634  mutt_error(_("Invalid index number"));
635  }
636 }
637 
642 void menu_next_line(struct Menu *menu)
643 {
644  if (menu->max == 0)
645  {
646  mutt_error(_("No entries"));
647  return;
648  }
649 
650  const short c_menu_context = cs_subset_number(NeoMutt->sub, "menu_context");
651  const bool c_menu_move_off = cs_subset_bool(NeoMutt->sub, "menu_move_off");
652  int c = MIN(c_menu_context, (menu->pagelen / 2));
653 
654  if (((menu->top + 1) < (menu->max - c)) &&
655  (c_menu_move_off ||
656  ((menu->max > menu->pagelen) && (menu->top < (menu->max - menu->pagelen)))))
657  {
658  menu->top++;
659  if ((menu->current < (menu->top + c)) && (menu->current < (menu->max - 1)))
660  menu_set_index(menu, menu->current + 1);
661  menu->redraw = REDRAW_INDEX;
662  }
663  else
664  mutt_message(_("You can't scroll down farther"));
665 }
666 
671 void menu_prev_line(struct Menu *menu)
672 {
673  if (menu->top < 1)
674  {
675  mutt_message(_("You can't scroll up farther"));
676  return;
677  }
678 
679  const short c_menu_context = cs_subset_number(NeoMutt->sub, "menu_context");
680  int c = MIN(c_menu_context, (menu->pagelen / 2));
681 
682  menu->top--;
683  if ((menu->current >= (menu->top + menu->pagelen - c)) && (menu->current > 1))
684  menu_set_index(menu, menu->current - 1);
685  menu->redraw = REDRAW_INDEX;
686 }
687 
698 static void menu_length_jump(struct Menu *menu, int jumplen)
699 {
700  if (menu->max == 0)
701  {
702  mutt_error(_("No entries"));
703  return;
704  }
705 
706  const short c_menu_context = cs_subset_number(NeoMutt->sub, "menu_context");
707  const bool c_menu_move_off = cs_subset_bool(NeoMutt->sub, "menu_move_off");
708 
709  const int neg = (jumplen >= 0) ? 0 : -1;
710  const int c = MIN(c_menu_context, (menu->pagelen / 2));
711 
712  /* possible to scroll? */
713  int tmp;
714  int index = menu->current;
715  if ((DIRECTION * menu->top) <
716  (tmp = (neg ? 0 : (menu->max /* -1 */) - (menu->pagelen /* -1 */))))
717  {
718  menu->top += jumplen;
719 
720  /* jumped too long? */
721  if ((neg || !c_menu_move_off) && ((DIRECTION * menu->top) > tmp))
722  menu->top = tmp;
723 
724  /* need to move the cursor? */
725  if ((DIRECTION *
726  (tmp = (menu->current - (menu->top + (neg ? (menu->pagelen - 1) - c : c))))) < 0)
727  {
728  index -= tmp;
729  }
730 
731  menu->redraw = REDRAW_INDEX;
732  }
733  else if ((menu->current != (neg ? 0 : menu->max - 1)) && ARRAY_EMPTY(&menu->dialog))
734  {
735  index += jumplen;
736  }
737  else
738  {
739  mutt_message(neg ? _("You are on the first page") : _("You are on the last page"));
740  }
741 
742  // Range check
743  index = MIN(index, menu->max - 1);
744  index = MAX(index, 0);
745  menu_set_index(menu, index);
746 }
747 
752 void menu_next_page(struct Menu *menu)
753 {
754  menu_length_jump(menu, MAX(menu->pagelen /* - MenuOverlap */, 0));
755 }
756 
761 void menu_prev_page(struct Menu *menu)
762 {
763  menu_length_jump(menu, 0 - MAX(menu->pagelen /* - MenuOverlap */, 0));
764 }
765 
770 void menu_half_down(struct Menu *menu)
771 {
772  menu_length_jump(menu, (menu->pagelen / 2));
773 }
774 
779 void menu_half_up(struct Menu *menu)
780 {
781  menu_length_jump(menu, 0 - (menu->pagelen / 2));
782 }
783 
788 void menu_top_page(struct Menu *menu)
789 {
790  if (menu->current == menu->top)
791  return;
792 
793  menu_set_index(menu, menu->top);
794 }
795 
800 void menu_bottom_page(struct Menu *menu)
801 {
802  if (menu->max == 0)
803  {
804  mutt_error(_("No entries"));
805  return;
806  }
807 
808  int index = menu->top + menu->pagelen - 1;
809  if (index > (menu->max - 1))
810  index = menu->max - 1;
811  menu_set_index(menu, index);
812 }
813 
818 void menu_middle_page(struct Menu *menu)
819 {
820  if (menu->max == 0)
821  {
822  mutt_error(_("No entries"));
823  return;
824  }
825 
826  int i = menu->top + menu->pagelen;
827  if (i > (menu->max - 1))
828  i = menu->max - 1;
829 
830  menu_set_index(menu, menu->top + (i - menu->top) / 2);
831 }
832 
837 void menu_first_entry(struct Menu *menu)
838 {
839  if (menu->max == 0)
840  {
841  mutt_error(_("No entries"));
842  return;
843  }
844 
845  menu_set_index(menu, 0);
846 }
847 
852 void menu_last_entry(struct Menu *menu)
853 {
854  if (menu->max == 0)
855  {
856  mutt_error(_("No entries"));
857  return;
858  }
859 
860  menu_set_index(menu, menu->max - 1);
861 }
862 
867 void menu_current_top(struct Menu *menu)
868 {
869  if (menu->max == 0)
870  {
871  mutt_error(_("No entries"));
872  return;
873  }
874 
875  menu->top = menu->current;
876  menu->redraw = REDRAW_INDEX;
877 }
878 
883 void menu_current_middle(struct Menu *menu)
884 {
885  if (menu->max == 0)
886  {
887  mutt_error(_("No entries"));
888  return;
889  }
890 
891  menu->top = menu->current - (menu->pagelen / 2);
892  if (menu->top < 0)
893  menu->top = 0;
894  menu->redraw = REDRAW_INDEX;
895 }
896 
901 void menu_current_bottom(struct Menu *menu)
902 {
903  if (menu->max == 0)
904  {
905  mutt_error(_("No entries"));
906  return;
907  }
908 
909  menu->top = menu->current - menu->pagelen + 1;
910  if (menu->top < 0)
911  menu->top = 0;
912  menu->redraw = REDRAW_INDEX;
913 }
914 
919 static void menu_next_entry(struct Menu *menu)
920 {
921  if (menu->current < (menu->max - 1))
922  {
923  menu_set_index(menu, menu->current + 1);
924  }
925  else
926  mutt_message(_("You are on the last entry"));
927 }
928 
933 static void menu_prev_entry(struct Menu *menu)
934 {
935  if (menu->current)
936  {
937  menu_set_index(menu, menu->current - 1);
938  }
939  else
940  mutt_message(_("You are on the first entry"));
941 }
942 
946 static int default_color(struct Menu *menu, int line)
947 {
948  return mutt_color(MT_COLOR_NORMAL);
949 }
950 
954 static int generic_search(struct Menu *menu, regex_t *rx, int line)
955 {
956  char buf[1024];
957 
958  make_entry(menu, buf, sizeof(buf), line);
959  return regexec(rx, buf, 0, NULL, 0);
960 }
961 
965 void menu_init(void)
966 {
967  for (int i = 0; i < MENU_MAX; i++)
968  SearchBuffers[i] = NULL;
969 }
970 
974 static int menu_color_observer(struct NotifyCallback *nc)
975 {
976  if (!nc->event_data || !nc->global_data)
977  return -1;
978  if (nc->event_type != NT_COLOR)
979  return 0;
980 
981  struct EventColor *ev_c = nc->event_data;
982 
983  int c = ev_c->color;
984 
985  // MT_COLOR_MAX is sent on `uncolor *`
986  bool simple = (c == MT_COLOR_INDEX_COLLAPSED) || (c == MT_COLOR_INDEX_DATE) ||
987  (c == MT_COLOR_INDEX_LABEL) || (c == MT_COLOR_INDEX_NUMBER) ||
988  (c == MT_COLOR_INDEX_SIZE) || (c == MT_COLOR_INDEX_TAGS) ||
989  (c == MT_COLOR_MAX);
990  bool lists = (c == MT_COLOR_ATTACH_HEADERS) || (c == MT_COLOR_BODY) ||
991  (c == MT_COLOR_HEADER) || (c == MT_COLOR_INDEX) ||
992  (c == MT_COLOR_INDEX_AUTHOR) || (c == MT_COLOR_INDEX_FLAGS) ||
993  (c == MT_COLOR_INDEX_SUBJECT) || (c == MT_COLOR_INDEX_TAG) ||
994  (c == MT_COLOR_MAX);
995 
996  // The changes aren't relevant to the index menu
997  if (!simple && !lists)
998  return 0;
999 
1000  // Colour deleted from a list
1001  struct Mailbox *m = ctx_mailbox(Context);
1002  if ((nc->event_subtype == NT_COLOR_RESET) && lists && m)
1003  {
1004  // Force re-caching of index colors
1005  for (int i = 0; i < m->msg_count; i++)
1006  {
1007  struct Email *e = m->emails[i];
1008  if (!e)
1009  break;
1010  e->pair = 0;
1011  }
1012  }
1013 
1014  struct Menu *menu = nc->global_data;
1015  menu->redraw = REDRAW_FULL;
1016 
1017  return 0;
1018 }
1019 
1023 static int menu_config_observer(struct NotifyCallback *nc)
1024 {
1025  if (!nc->event_data || !nc->global_data)
1026  return -1;
1027  if (nc->event_type != NT_CONFIG)
1028  return 0;
1029 
1030  struct EventConfig *ec = nc->event_data;
1031 
1032  const struct ConfigDef *cdef = ec->he->data;
1033  ConfigRedrawFlags flags = cdef->type & R_REDRAW_MASK;
1034 
1035  if (flags == R_REDRAW_NO_FLAGS)
1036  return 0;
1037 
1038  struct Menu *menu = nc->global_data;
1039  if ((menu->type == MENU_MAIN) && (flags & R_INDEX))
1040  menu->redraw |= REDRAW_FULL;
1041  if ((menu->type == MENU_PAGER) && (flags & R_PAGER))
1042  menu->redraw |= REDRAW_FULL;
1043  if (flags & R_PAGER_FLOW)
1044  {
1045  menu->redraw |= REDRAW_FULL | REDRAW_FLOW;
1046  }
1047 
1048  if (flags & R_RESORT_SUB)
1049  OptSortSubthreads = true;
1050  if (flags & R_RESORT)
1051  OptNeedResort = true;
1052  if (flags & R_RESORT_INIT)
1053  OptResortInit = true;
1054  if (flags & R_TREE)
1055  OptRedrawTree = true;
1056 
1057  if (flags & R_MENU)
1058  menu->redraw |= REDRAW_FULL;
1059 
1060  return 0;
1061 }
1062 
1066 static int menu_recalc(struct MuttWindow *win)
1067 {
1068  if (win->type != WT_MENU)
1069  return 0;
1070 
1071  // struct Menu *menu = win->wdata;
1072 
1073  win->actions |= WA_REPAINT;
1074  return 0;
1075 }
1076 
1080 static int menu_repaint(struct MuttWindow *win)
1081 {
1082  if (win->type != WT_MENU)
1083  return 0;
1084 
1085  // struct Menu *menu = win->wdata;
1086  // menu_redraw(menu);
1087  // menu->redraw = REDRAW_NO_FLAGS;
1088 
1089  return 0;
1090 }
1091 
1095 static int menu_window_observer(struct NotifyCallback *nc)
1096 {
1097  if (!nc->event_data || !nc->global_data)
1098  return -1;
1099  if (nc->event_type != NT_WINDOW)
1100  return 0;
1101  if (nc->event_subtype != NT_WINDOW_STATE)
1102  return 0;
1103 
1104  struct Menu *menu = nc->global_data;
1105  struct EventWindow *ev_w = nc->event_data;
1106  struct MuttWindow *win = ev_w->win;
1107 
1108  menu->pagelen = win->state.rows;
1109  menu->redraw = REDRAW_FULL;
1110 
1111  return 0;
1112 }
1113 
1119 void menu_add_dialog_row(struct Menu *menu, const char *row)
1120 {
1121  ARRAY_SET(&menu->dialog, menu->max, mutt_str_dup(row));
1122  menu->max++;
1123 }
1124 
1132 static int search(struct Menu *menu, int op)
1133 {
1134  int rc = 0, wrap = 0;
1135  int search_dir;
1136  regex_t re;
1137  char buf[128];
1138  char *search_buf = ((menu->type < MENU_MAX)) ? SearchBuffers[menu->type] : NULL;
1139 
1140  if (!(search_buf && *search_buf) || ((op != OP_SEARCH_NEXT) && (op != OP_SEARCH_OPPOSITE)))
1141  {
1142  mutt_str_copy(buf, search_buf && (search_buf[0] != '\0') ? search_buf : "",
1143  sizeof(buf));
1144  if ((mutt_get_field(((op == OP_SEARCH) || (op == OP_SEARCH_NEXT)) ?
1145  _("Search for: ") :
1146  _("Reverse search for: "),
1147  buf, sizeof(buf), MUTT_CLEAR, false, NULL, NULL) != 0) ||
1148  (buf[0] == '\0'))
1149  {
1150  return -1;
1151  }
1152  if (menu->type < MENU_MAX)
1153  {
1154  mutt_str_replace(&SearchBuffers[menu->type], buf);
1155  search_buf = SearchBuffers[menu->type];
1156  }
1157  menu->search_dir =
1158  ((op == OP_SEARCH) || (op == OP_SEARCH_NEXT)) ? MUTT_SEARCH_DOWN : MUTT_SEARCH_UP;
1159  }
1160 
1161  search_dir = (menu->search_dir == MUTT_SEARCH_UP) ? -1 : 1;
1162  if (op == OP_SEARCH_OPPOSITE)
1163  search_dir = -search_dir;
1164 
1165  if (search_buf)
1166  {
1167  uint16_t flags = mutt_mb_is_lower(search_buf) ? REG_ICASE : 0;
1168  rc = REG_COMP(&re, search_buf, REG_NOSUB | flags);
1169  }
1170 
1171  if (rc != 0)
1172  {
1173  regerror(rc, &re, buf, sizeof(buf));
1174  mutt_error("%s", buf);
1175  return -1;
1176  }
1177 
1178  rc = menu->current + search_dir;
1179 search_next:
1180  if (wrap)
1181  mutt_message(_("Search wrapped to top"));
1182  while ((rc >= 0) && (rc < menu->max))
1183  {
1184  if (menu->search(menu, &re, rc) == 0)
1185  {
1186  regfree(&re);
1187  return rc;
1188  }
1189 
1190  rc += search_dir;
1191  }
1192 
1193  const bool c_wrap_search = cs_subset_bool(NeoMutt->sub, "wrap_search");
1194  if (c_wrap_search && (wrap++ == 0))
1195  {
1196  rc = (search_dir == 1) ? 0 : menu->max - 1;
1197  goto search_next;
1198  }
1199  regfree(&re);
1200  mutt_error(_("Not found"));
1201  return -1;
1202 }
1203 
1209 static int menu_dialog_translate_op(int i)
1210 {
1211  switch (i)
1212  {
1213  case OP_NEXT_ENTRY:
1214  return OP_NEXT_LINE;
1215  case OP_PREV_ENTRY:
1216  return OP_PREV_LINE;
1217  case OP_CURRENT_TOP:
1218  case OP_FIRST_ENTRY:
1219  return OP_TOP_PAGE;
1220  case OP_CURRENT_BOTTOM:
1221  case OP_LAST_ENTRY:
1222  return OP_BOTTOM_PAGE;
1223  case OP_CURRENT_MIDDLE:
1224  return OP_MIDDLE_PAGE;
1225  }
1226 
1227  return i;
1228 }
1229 
1237 static int menu_dialog_dokey(struct Menu *menu, int *ip)
1238 {
1239  struct KeyEvent ch;
1240  char *p = NULL;
1241 
1242  do
1243  {
1244  ch = mutt_getch();
1245  } while (ch.ch == -2);
1246 
1247  if (ch.ch < 0)
1248  {
1249  *ip = -1;
1250  return 0;
1251  }
1252 
1253  if (ch.ch && (p = strchr(menu->keys, ch.ch)))
1254  {
1255  *ip = OP_MAX + (p - menu->keys + 1);
1256  return 0;
1257  }
1258  else
1259  {
1260  if (ch.op == OP_NULL)
1261  mutt_unget_event(ch.ch, 0);
1262  else
1263  mutt_unget_event(0, ch.op);
1264  return -1;
1265  }
1266 }
1267 
1274 int menu_redraw(struct Menu *menu)
1275 {
1276  if (menu->custom_redraw)
1277  {
1278  menu->custom_redraw(menu);
1279  return OP_NULL;
1280  }
1281 
1282  /* See if all or part of the screen needs to be updated. */
1283  if (menu->redraw & REDRAW_FULL)
1284  {
1285  menu_redraw_full(menu);
1286  /* allow the caller to do any local configuration */
1287  return OP_REDRAW;
1288  }
1289 
1290  if (ARRAY_EMPTY(&menu->dialog))
1291  menu_check_recenter(menu);
1292 
1293  if (menu->redraw & REDRAW_STATUS)
1294  menu_redraw_status(menu);
1295  if (menu->redraw & REDRAW_INDEX)
1296  menu_redraw_index(menu);
1297  else if (menu->redraw & REDRAW_MOTION)
1298  menu_redraw_motion(menu);
1299  else if (menu->redraw == REDRAW_CURRENT)
1300  menu_redraw_current(menu);
1301 
1302  if (!ARRAY_EMPTY(&menu->dialog))
1303  menu_redraw_prompt(menu);
1304 
1305  return OP_NULL;
1306 }
1307 
1313 int menu_loop(struct Menu *menu)
1314 {
1315  static int last_position = -1;
1316  int op = OP_NULL;
1317 
1318  if (menu->max && menu->is_mailbox_list)
1319  {
1320  if (last_position > (menu->max - 1))
1321  last_position = -1;
1322  else if (last_position >= 0)
1323  menu_set_index(menu, last_position);
1324  }
1325 
1326  while (true)
1327  {
1328  /* Clear the tag prefix unless we just started it. Don't clear
1329  * the prefix on a timeout (op==-2), but do clear on an abort (op==-1) */
1330  if (menu->tagprefix && (op != OP_TAG_PREFIX) &&
1331  (op != OP_TAG_PREFIX_COND) && (op != -2))
1332  {
1333  menu->tagprefix = false;
1334  }
1335 
1337 
1338  if (menu_redraw(menu) == OP_REDRAW)
1339  return OP_REDRAW;
1340 
1341  /* give visual indication that the next command is a tag- command */
1342  if (menu->tagprefix)
1343  {
1344  mutt_window_mvaddstr(MessageWindow, 0, 0, "tag-");
1346  }
1347 
1348  const bool c_arrow_cursor = cs_subset_bool(NeoMutt->sub, "arrow_cursor");
1349  const bool c_braille_friendly =
1350  cs_subset_bool(NeoMutt->sub, "braille_friendly");
1351 
1352  /* move the cursor out of the way */
1353  if (c_arrow_cursor)
1354  mutt_window_move(menu->win_index, 2, menu->current - menu->top);
1355  else if (c_braille_friendly)
1356  mutt_window_move(menu->win_index, 0, menu->current - menu->top);
1357  else
1358  {
1359  mutt_window_move(menu->win_index, menu->win_index->state.cols - 1,
1360  menu->current - menu->top);
1361  }
1362 
1363  mutt_refresh();
1364 
1365  /* try to catch dialog keys before ops */
1366  if (!ARRAY_EMPTY(&menu->dialog) && (menu_dialog_dokey(menu, &op) == 0))
1367  return op;
1368 
1369  const bool c_auto_tag = cs_subset_bool(NeoMutt->sub, "auto_tag");
1370  op = km_dokey(menu->type);
1371  if ((op == OP_TAG_PREFIX) || (op == OP_TAG_PREFIX_COND))
1372  {
1373  if (menu->tagprefix)
1374  {
1375  menu->tagprefix = false;
1377  continue;
1378  }
1379 
1380  if (menu->tagged)
1381  {
1382  menu->tagprefix = true;
1383  continue;
1384  }
1385  else if (op == OP_TAG_PREFIX)
1386  {
1387  mutt_error(_("No tagged entries"));
1388  op = -1;
1389  }
1390  else /* None tagged, OP_TAG_PREFIX_COND */
1391  {
1393  mutt_message(_("Nothing to do"));
1394  op = -1;
1395  }
1396  }
1397  else if (menu->tagged && c_auto_tag)
1398  menu->tagprefix = true;
1399 
1401 
1402  if (SigWinch)
1403  {
1404  SigWinch = 0;
1406  clearok(stdscr, true); /* force complete redraw */
1407  }
1408 
1409  if (op < 0)
1410  {
1411  if (menu->tagprefix)
1413  continue;
1414  }
1415 
1416  if (ARRAY_EMPTY(&menu->dialog))
1417  mutt_clear_error();
1418 
1419  /* Convert menubar movement to scrolling */
1420  if (!ARRAY_EMPTY(&menu->dialog))
1421  op = menu_dialog_translate_op(op);
1422 
1423  switch (op)
1424  {
1425  case OP_NEXT_ENTRY:
1426  menu_next_entry(menu);
1427  break;
1428  case OP_PREV_ENTRY:
1429  menu_prev_entry(menu);
1430  break;
1431  case OP_HALF_DOWN:
1432  menu_half_down(menu);
1433  break;
1434  case OP_HALF_UP:
1435  menu_half_up(menu);
1436  break;
1437  case OP_NEXT_PAGE:
1438  menu_next_page(menu);
1439  break;
1440  case OP_PREV_PAGE:
1441  menu_prev_page(menu);
1442  break;
1443  case OP_NEXT_LINE:
1444  menu_next_line(menu);
1445  break;
1446  case OP_PREV_LINE:
1447  menu_prev_line(menu);
1448  break;
1449  case OP_FIRST_ENTRY:
1450  menu_first_entry(menu);
1451  break;
1452  case OP_LAST_ENTRY:
1453  menu_last_entry(menu);
1454  break;
1455  case OP_TOP_PAGE:
1456  menu_top_page(menu);
1457  break;
1458  case OP_MIDDLE_PAGE:
1459  menu_middle_page(menu);
1460  break;
1461  case OP_BOTTOM_PAGE:
1462  menu_bottom_page(menu);
1463  break;
1464  case OP_CURRENT_TOP:
1465  menu_current_top(menu);
1466  break;
1467  case OP_CURRENT_MIDDLE:
1468  menu_current_middle(menu);
1469  break;
1470  case OP_CURRENT_BOTTOM:
1471  menu_current_bottom(menu);
1472  break;
1473  case OP_SEARCH:
1474  case OP_SEARCH_REVERSE:
1475  case OP_SEARCH_NEXT:
1476  case OP_SEARCH_OPPOSITE:
1477  if (menu->custom_search)
1478  return op;
1479  else if (menu->search && ARRAY_EMPTY(&menu->dialog)) /* Searching dialogs won't work */
1480  {
1481  int index = search(menu, op);
1482  if (index != -1)
1483  menu_set_index(menu, index);
1484  }
1485  else
1486  mutt_error(_("Search is not implemented for this menu"));
1487  break;
1488 
1489  case OP_JUMP:
1490  if (!ARRAY_EMPTY(&menu->dialog))
1491  mutt_error(_("Jumping is not implemented for dialogs"));
1492  else
1493  menu_jump(menu);
1494  break;
1495 
1496  case OP_ENTER_COMMAND:
1498  window_set_focus(menu->win_index);
1499  window_redraw(RootWindow, false);
1500  break;
1501 
1502  case OP_TAG:
1503  if (menu->tag && ARRAY_EMPTY(&menu->dialog))
1504  {
1505  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
1506 
1507  if (menu->tagprefix && !c_auto_tag)
1508  {
1509  for (int i = 0; i < menu->max; i++)
1510  menu->tagged += menu->tag(menu, i, 0);
1511  menu->redraw |= REDRAW_INDEX;
1512  }
1513  else if (menu->max != 0)
1514  {
1515  int j = menu->tag(menu, menu->current, -1);
1516  menu->tagged += j;
1517  if (j && c_resolve && (menu->current < (menu->max - 1)))
1518  {
1519  menu_set_index(menu, menu->current + 1);
1520  }
1521  else
1522  menu->redraw |= REDRAW_CURRENT;
1523  }
1524  else
1525  mutt_error(_("No entries"));
1526  }
1527  else
1528  mutt_error(_("Tagging is not supported"));
1529  break;
1530 
1531  case OP_SHELL_ESCAPE:
1532  if (mutt_shell_escape())
1533  {
1535  }
1536  break;
1537 
1538  case OP_WHAT_KEY:
1539  mutt_what_key();
1540  break;
1541 
1542  case OP_CHECK_STATS:
1544  break;
1545 
1546  case OP_REDRAW:
1547  clearok(stdscr, true);
1548  menu->redraw = REDRAW_FULL;
1549  break;
1550 
1551  case OP_HELP:
1552  mutt_help(menu->type);
1553  menu->redraw = REDRAW_FULL;
1554  break;
1555 
1556  case OP_NULL:
1557  km_error_key(menu->type);
1558  break;
1559 
1560  case OP_END_COND:
1561  break;
1562 
1563  default:
1564  if (menu->is_mailbox_list)
1565  last_position = menu->current;
1566  return op;
1567  }
1568  }
1569  /* not reached */
1570 }
1571 
1577 {
1578  struct MuttWindow *win = window_get_dialog();
1579  while (win && win->focus)
1580  win = win->focus;
1581 
1582  if (!win)
1583  return MENU_GENERIC;
1584 
1585  if (win->type != WT_MENU)
1586  return MENU_GENERIC;
1587 
1588  struct Menu *menu = win->wdata;
1589  if (!menu)
1590  return MENU_GENERIC;
1591 
1592  return menu->type;
1593 }
1594 
1598 static void menu_free_window(struct MuttWindow *win, void **ptr)
1599 {
1600  if (!ptr || !*ptr)
1601  return;
1602 
1603  struct Menu *menu = *ptr;
1604 
1608  notify_free(&menu->notify);
1609 
1610  if (menu->mdata && menu->mdata_free)
1611  menu->mdata_free(menu, &menu->mdata); // Custom function to free private data
1612 
1613  char **line = NULL;
1614  ARRAY_FOREACH(line, &menu->dialog)
1615  {
1616  FREE(line);
1617  }
1618  ARRAY_FREE(&menu->dialog);
1619 
1620  FREE(ptr);
1621 }
1622 
1629 {
1630  struct MuttWindow *win =
1633 
1634  struct Menu *menu = mutt_mem_calloc(1, sizeof(struct Menu));
1635 
1636  menu->type = type;
1637  menu->redraw = REDRAW_FULL;
1638  menu->color = default_color;
1639  menu->search = generic_search;
1640  menu->notify = notify_new();
1641  menu->win_index = win;
1642  menu->pagelen = win->state.rows;
1643 
1644  win->recalc = menu_recalc;
1645  win->repaint = menu_repaint;
1646  win->wdata = menu;
1648  notify_set_parent(menu->notify, win->notify);
1649 
1653 
1654  return win;
1655 }
1656 
1662 int menu_get_index(struct Menu *menu)
1663 {
1664  if (!menu)
1665  return -1;
1666 
1667  return menu->current;
1668 }
1669 
1676 bool menu_set_index(struct Menu *menu, int index)
1677 {
1678  if (!menu)
1679  return false;
1680 
1681  if (index < -1)
1682  return false;
1683  if (index >= menu->max)
1684  return false;
1685 
1686  menu->oldcurrent = menu->current;
1687  menu->current = index;
1688  menu->redraw |= REDRAW_MOTION;
1689  return true;
1690 }
1691 
1698 {
1699  if (!menu)
1700  return;
1701 
1702  menu->redraw |= redraw;
1703  menu->win_index->actions |= WA_RECALC;
1704 }
int km_dokey(enum MenuType menu)
Determine what a keypress should do.
Definition: keymap.c:661
void * mutt_hash_find(const struct HashTable *table, const char *strkey)
Find the HashElem data in a Hash Table element using a key.
Definition: hash.c:354
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
Convenience wrapper for the gui headers.
MIME attachment test (takes a pattern)
Definition: color.h:43
The "current" mailbox.
Definition: context.h:37
#define R_PAGER
Redraw the pager menu.
Definition: types.h:66
Star character (for threads)
Definition: mutt_thread.h:49
Index: index number.
Definition: color.h:90
Manage keymappings.
#define NONULL(x)
Definition: string2.h:37
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
int msg_count
Total number of messages.
Definition: mailbox.h:91
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:71
Ampersand character (for threads)
Definition: mutt_thread.h:50
int mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition: string.c:252
Left justify the text.
Definition: curs_lib.h:43
The envelope/body of an email.
Definition: email.h:37
#define MUTT_CLEAR
Clear input if printable character is pressed.
Definition: mutt.h:58
Index: tags field (g, J)
Definition: color.h:92
struct Email * mutt_get_virt_email(struct Mailbox *m, int vnum)
Get a virtual Email.
Definition: context.c:408
void(* wdata_free)(struct MuttWindow *win, void **ptr)
Free the private data attached to the MuttWindow.
Definition: mutt_window.h:147
#define MIN(a, b)
Definition: memory.h:31
GUI selectable list of items.
Definition: lib.h:56
#define WA_REPAINT
Redraw the contents of the Window.
Definition: mutt_window.h:110
Data passed to a notification function.
Definition: observer.h:39
struct MuttWindow * win
Window that changed.
Definition: mutt_window.h:195
struct Mailbox * ctx_mailbox(struct Context *ctx)
wrapper to get the mailbox in a Context, or NULL
Definition: context.c:440
#define R_PAGER_FLOW
Reflow line_info and redraw the pager menu.
Definition: types.h:67
void window_set_focus(struct MuttWindow *win)
Set the Window focus.
Definition: mutt_window.c:760
Structs that make up an email.
An Event that happened to a Colour.
Definition: color.h:122
An Event that happened to a Window.
Definition: mutt_window.h:193
The "currently-open" mailbox.
MenuType
Types of GUI selections.
Definition: keymap.h:72
#define mutt_message(...)
Definition: logging.h:83
Pager: highlight body of message (takes a pattern)
Definition: color.h:44
struct ColorLineList * mutt_color_index_subject(void)
Return the ColorLineList for subject in the index.
Definition: color.c:1465
Window uses all available vertical space.
Definition: mutt_window.h:36
int oldcurrent
For driver use only.
Definition: lib.h:79
int mutt_get_field(const char *field, char *buf, size_t buflen, CompletionFlags complete, bool multiple, char ***files, int *numfiles)
Ask the user for a string.
Definition: curs_lib.c:306
void notify_free(struct Notify **ptr)
Free a notification handler.
Definition: notify.c:62
void mutt_curses_set_color(enum ColorId color)
Set the current colour for text.
Definition: mutt_curses.c:56
bool mutt_shell_escape(void)
invoke a command in a subshell
Definition: commands.c:908
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:198
struct ColorLineList * mutt_color_index_flags(void)
Return the ColorLineList for flags in the index.
Definition: color.c:1456
char * prompt
Prompt for user, similar to mutt_multi_choice.
Definition: lib.h:74
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:242
struct PatternList * color_pattern
Compiled pattern to speed up index color calculation.
Definition: color.h:104
void mutt_resize_screen(void)
Update NeoMutt&#39;s opinion about the window size (CURSES)
Definition: resize.c:101
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition: array.h:206
NeoMutt Logging.
WHERE bool OptNeedResort
(pseudo) used to force a re-sort
Definition: options.h:43
#define ARRAY_GET(head, idx)
Return the element at index.
Definition: array.h:105
A config-change event.
Definition: subset.h:70
char * pattern
Pattern to match.
Definition: color.h:103
int pair
Colour pair index.
Definition: color.h:107
void mutt_enter_command(void)
enter a neomutt command
Definition: commands.c:943
struct MuttWindow * window_get_dialog(void)
Get the currently active Dialog.
Definition: mutt_window.c:801
struct ColorLineList * mutt_color_index_tags(void)
Return the ColorLineList for tags in the index.
Definition: color.c:1474
Menu has changed,.
Definition: notify_type.h:48
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
void mutt_unget_event(int ch, int op)
Return a keystroke to the input buffer.
Definition: curs_lib.c:725
#define _(a)
Definition: message.h:28
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition: regex3.h:54
int op
function op
Definition: keymap.h:66
int ch
raw key pressed
Definition: keymap.h:65
A division of the screen.
Definition: mutt_window.h:117
Index panel (list of emails)
Definition: keymap.h:80
int mutt_window_mvaddstr(struct MuttWindow *win, int col, int row, const char *str)
Move the cursor and write a fixed string to a Window.
Definition: mutt_window.c:396
Config item definition.
Definition: set.h:61
Index: number of messages in collapsed thread.
Definition: color.h:87
All user-callable functions.
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:159
void mutt_help(enum MenuType menu)
Display the help menu.
Definition: help.c:388
#define R_RESORT
Resort the mailbox.
Definition: types.h:68
Container for Accounts, Notifications.
Definition: neomutt.h:36
MuttWindow has changed, NotifyWindow, EventWindow.
Definition: notify_type.h:51
Lower left corner.
Definition: mutt_thread.h:42
Convenience wrapper for the config headers.
void mutt_paddstr(int n, const char *s)
Display a string on screen, padded if necessary.
Definition: curs_lib.c:1156
int mutt_window_move(struct MuttWindow *win, int col, int row)
Move the cursor in a Window.
Definition: mutt_window.c:382
void(* make_entry)(struct Menu *menu, char *buf, size_t buflen, int line)
Format a item for a menu.
Definition: lib.h:91
void mutt_color_observer_remove(observer_t callback, void *global_data)
Remove an observer.
Definition: color.c:1516
int event_subtype
Send: Event subtype, e.g. NT_ACCOUNT_ADD.
Definition: observer.h:43
#define R_TREE
Redraw the thread tree.
Definition: types.h:71
enum NotifyType event_type
Send: Event type, e.g. NT_ACCOUNT.
Definition: observer.h:42
Some miscellaneous functions.
#define MAX(a, b)
Definition: memory.h:30
struct MuttWindow * focus
Focussed Window.
Definition: mutt_window.h:135
WindowActionFlags actions
Actions to be performed, e.g. WA_RECALC.
Definition: mutt_window.h:127
Pager pager (email viewer)
Definition: keymap.h:81
Index: author field (takes a pattern)
Definition: color.h:82
int mutt_window_addnstr(const char *str, int num)
Write a partial string to a Window.
Definition: mutt_window.c:501
void mutt_window_clear(struct MuttWindow *win)
Clear a Window.
Definition: mutt_window.c:822
#define R_REDRAW_NO_FLAGS
No refresh/resort flags.
Definition: types.h:64
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:1461
struct HashElem * he
Config item that changed.
Definition: subset.h:74
void mutt_curses_set_cursor(enum MuttCursorState state)
Set the cursor state.
Definition: mutt_curses.c:71
struct Notify * notify
Notifications: NotifyWindow, EventWindow.
Definition: mutt_window.h:133
enum MenuType type
Menu definition for keymap entries.
Definition: lib.h:62
struct Mailbox * mailbox
Definition: context.h:49
Many unsorted constants and some structs.
Top T-piece.
Definition: mutt_thread.h:52
Message headers (takes a pattern)
Definition: color.h:53
void mutt_flush_macro_to_endcond(void)
Drop a macro from the input buffer.
Definition: curs_lib.c:781
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition: mutt.h:52
struct MuttWindow * win_ibar
Definition: lib.h:67
const char * title
Title of this menu.
Definition: lib.h:58
void mutt_window_clearline(struct MuttWindow *win, int row)
Clear a row of a Window.
Definition: mutt_window.c:230
void mutt_what_key(void)
Ask the user to press a key.
Definition: keymap.c:1687
Display a normal cursor.
Definition: mutt_curses.h:81
int LastKey
contains the last key the user pressed
Definition: keymap.c:149
Index: tag field (g, takes a pattern)
Definition: color.h:85
Convenience wrapper for the core headers.
Index: tree-drawing characters.
Definition: color.h:77
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:112
bool notify_observer_add(struct Notify *notify, enum NotifyType type, observer_t callback, void *global_data)
Add an observer to an object.
Definition: notify.c:173
void * global_data
Data from notify_observer_add()
Definition: observer.h:45
Vertical line.
Definition: mutt_thread.h:46
Plain text.
Definition: color.h:58
#define R_MENU
Redraw all menus.
Definition: types.h:72
Window state has changed, e.g. WN_VISIBLE.
Definition: mutt_window.h:185
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:58
#define R_RESORT_SUB
Resort subthreads.
Definition: types.h:69
void mutt_refresh(void)
Force a refresh of the screen.
Definition: curs_lib.c:102
struct Notify * notify_new(void)
Create a new notifications handler.
Definition: notify.c:49
Index: date field.
Definition: color.h:88
Colour has changed, NotifyColor, EventColor.
Definition: notify_type.h:39
Prototypes for many functions.
struct ColorLineList * mutt_color_index_author(void)
Return the ColorLineList for author in the index.
Definition: color.c:1447
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:122
void * mdata
Private data.
Definition: lib.h:129
struct MuttWindow * MessageWindow
Message Window, ":set", etc.
Definition: mutt_window.c:45
#define SLIST_FIRST(head)
Definition: queue.h:228
struct MuttWindow * RootWindow
Parent of all Windows.
Definition: mutt_window.c:43
Index: size field.
Definition: color.h:91
Create/manipulate threading in emails.
void km_error_key(enum MenuType menu)
Handle an unbound key sequence.
Definition: keymap.c:1147
Status bar (takes a pattern)
Definition: color.h:75
A mailbox.
Definition: mailbox.h:81
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1249
int mutt_mailbox_check(struct Mailbox *m_cur, int force)
Check all all Mailboxes for new mail.
Definition: mutt_mailbox.c:137
Right arrow.
Definition: mutt_thread.h:48
#define MUTT_WIN_SIZE_UNLIMITED
Use as much space as possible.
Definition: mutt_window.h:50
short rows
Number of rows, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:59
int top
Entry that is the top of the current page.
Definition: lib.h:78
bool tagprefix
Definition: lib.h:64
#define ARRAY_EMPTY(head)
Check if an array is empty.
Definition: array.h:70
Manage where the email is piped to external commands.
Match patterns to emails.
Bottom T-piece.
Definition: mutt_thread.h:53
bool is_mailbox_list
Definition: lib.h:65
Color has been reset/removed.
Definition: color.h:135
Selected item in list.
Definition: color.h:54
enum ColorId color
Definition: color.h:124
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:295
Left T-piece.
Definition: mutt_thread.h:44
struct HashTable * TagTransforms
Lookup table of alternative tag names.
Definition: tags.c:37
uint32_t ConfigRedrawFlags
Flags for redraw/resort, e.g. R_INDEX.
Definition: types.h:63
uint32_t type
Variable type, e.g. DT_STRING.
Definition: set.h:64
int search_dir
Direction of search.
Definition: lib.h:80
void(* custom_redraw)(struct Menu *menu)
Redraw the menu.
Definition: lib.h:125
An event such as a keypress.
Definition: keymap.h:63
struct Notify * notify
Notifications.
Definition: lib.h:127
Upper left corner.
Definition: mutt_thread.h:43
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
int(* tag)(struct Menu *menu, int sel, int act)
Tag some menu items.
Definition: lib.h:110
bool custom_search
The menu implements its own non-Menusearch()-compatible search, trickle OP_SEARCH*.
Definition: lib.h:82
struct KeyEvent mutt_getch(void)
Read a character from the input buffer.
Definition: curs_lib.c:183
int pagelen
Number of entries per screen.
Definition: lib.h:63
struct Notify * notify
Notifications handler.
Definition: neomutt.h:38
char * keys
Keys used in the prompt.
Definition: lib.h:75
WHERE bool OptRedrawTree
(pseudo) redraw the thread tree
Definition: options.h:50
int mutt_color(enum ColorId id)
Return the color of an object.
Definition: color.c:1393
WHERE bool OptResortInit
(pseudo) used to force the next resort to be from scratch
Definition: options.h:51
int tagged
Number of tagged entries.
Definition: lib.h:81
bool CharsetIsUtf8
Is the user&#39;s current character set utf-8?
Definition: charset.c:62
#define MUTT_MAILBOX_CHECK_FORCE
Definition: mutt_mailbox.h:32
void * event_data
Data from notify_send()
Definition: observer.h:44
int max
Number of entries in the menu.
Definition: lib.h:60
MuttRedrawFlags redraw
When to redraw the screen.
Definition: lib.h:61
#define ARRAY_SET(head, idx, elem)
Set an element in the array.
Definition: array.h:119
Config has changed, NotifyConfig, EventConfig.
Definition: notify_type.h:41
void * data
User-supplied data.
Definition: hash.h:47
int mutt_pattern_exec(struct Pattern *pat, PatternExecFlags flags, struct Mailbox *m, struct Email *e, struct PatternCache *cache)
Match a pattern against an email header.
Definition: exec.c:1089
int mutt_window_printf(const char *fmt,...)
Write a formatted string to a Window.
Definition: mutt_window.c:547
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:631
int mutt_window_addch(int ch)
Write one character to a Window.
Definition: mutt_window.c:489
void(* mdata_free)(struct Menu *menu, void **ptr)
Free the private data attached to the Menu.
Definition: lib.h:136
Index: label field.
Definition: color.h:89
Horizontal line.
Definition: mutt_thread.h:45
Index: subject field (takes a pattern)
Definition: color.h:84
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:716
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:593
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
#define mutt_error(...)
Definition: logging.h:84
int(* repaint)(struct MuttWindow *win)
Repaint the Window.
Definition: mutt_window.h:163
void mutt_check_stats(struct Mailbox *m)
Forcibly update mailbox stats.
Definition: commands.c:1553
int index
The absolute (unsorted) message number.
Definition: email.h:86
Colour indicator.
Definition: mutt_thread.h:57
#define FREE(x)
Definition: memory.h:40
WHERE bool OptMsgErr
(pseudo) used by mutt_error/mutt_message
Definition: options.h:41
Hide the cursor.
Definition: mutt_curses.h:80
#define R_RESORT_INIT
Resort from scratch.
Definition: types.h:70
bool notify_observer_remove(struct Notify *notify, observer_t callback, void *global_data)
Remove an observer from an object.
Definition: notify.c:212
WHERE bool OptSortSubthreads
(pseudo) used when $sort_aux changes
Definition: options.h:54
void window_redraw(struct MuttWindow *win, bool force)
Reflow, recalc and repaint a tree of Windows.
Definition: mutt_window.c:744
void mutt_show_error(void)
Show the user an error message.
Definition: curs_lib.c:554
void notify_set_parent(struct Notify *notify, struct Notify *parent)
Set the parent notification handler.
Definition: notify.c:82
int mutt_window_addstr(const char *str)
Write a string to a Window.
Definition: mutt_window.c:519
int(* recalc)(struct MuttWindow *win)
Recalculate the Window data.
Definition: mutt_window.h:155
Hundreds of global variables to back the user variables.
Handling of global boolean variables.
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
WHERE bool ErrorBufMessage
true if the last message was an error
Definition: mutt_globals.h:42
struct MuttWindow * mutt_window_new(enum WindowType type, enum MuttWindowOrientation orient, enum MuttWindowSize size, int cols, int rows)
Create a new Window.
Definition: mutt_window.c:164
Index: default colour (takes a pattern)
Definition: color.h:81
Blank space.
Definition: mutt_thread.h:47
int current
Current entry.
Definition: lib.h:59
int mutt_color_combine(uint32_t fg_attr, uint32_t bg_attr)
Combine two colours.
Definition: color.c:581
struct MuttWindow * win_index
Definition: lib.h:66
Convenience wrapper for the library headers.
void mutt_curses_set_attr(int attr)
Set the attributes for text.
Definition: mutt_curses.c:39
Window wants as much space as possible.
Definition: mutt_window.h:46
void * wdata
Private data.
Definition: mutt_window.h:140
bool mutt_mb_is_lower(const char *s)
Does a multi-byte string contain only lowercase characters?
Definition: mbyte.c:357
WHERE SIG_ATOMIC_VOLATILE_T SigWinch
true after SIGWINCH is received
Definition: mutt_globals.h:68
enum WindowType type
Window type, e.g. WT_SIDEBAR.
Definition: mutt_window.h:139
int(* search)(struct Menu *menu, regex_t *rx, int line)
Search a menu for a item matching a regex.
Definition: lib.h:101
Question mark.
Definition: mutt_thread.h:54
#define MUTT_MATCH_FULL_ADDRESS
Match the full address.
Definition: lib.h:95
An Window containing a Menu.
Definition: mutt_window.h:97
int pair
Color-pair to use when displaying in the index.
Definition: email.h:80
Mailbox helper functions.
void mutt_color_observer_add(observer_t callback, void *global_data)
Add an observer.
Definition: color.c:1506
Index: flags field (takes a pattern)
Definition: color.h:83
A regular expression and a color to highlight a line.
Definition: color.h:99
#define R_REDRAW_MASK
Mask for the Redraw Flags.
Definition: types.h:74
void mutt_simple_format(char *buf, size_t buflen, int min_width, int max_width, enum FormatJustify justify, char pad_char, const char *s, size_t n, bool arboreal)
Format a string, like snprintf()
Definition: curs_lib.c:983
Equals (for threads)
Definition: mutt_thread.h:51
#define R_INDEX
Redraw the index menu (MENU_MAIN)
Definition: types.h:65
int(* color)(struct Menu *menu, int line)
Calculate the colour for a line of the menu.
Definition: lib.h:119
Generic selection list.
Definition: keymap.h:79
bool notify_send(struct Notify *notify, enum NotifyType event_type, int event_subtype, void *event_data)
Send out a notification message.
Definition: notify.c:156
#define WA_RECALC
Recalculate the contents of the Window.
Definition: mutt_window.h:109