Coverage Report

Created: 2023-06-07 06:15

/src/neomutt/menu/draw.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file
3
 * Paint the Menu
4
 *
5
 * @authors
6
 * Copyright (C) 2021 Richard Russon <rich@flatcap.org>
7
 *
8
 * @copyright
9
 * This program is free software: you can redistribute it and/or modify it under
10
 * the terms of the GNU General Public License as published by the Free Software
11
 * Foundation, either version 2 of the License, or (at your option) any later
12
 * version.
13
 *
14
 * This program is distributed in the hope that it will be useful, but WITHOUT
15
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17
 * details.
18
 *
19
 * You should have received a copy of the GNU General Public License along with
20
 * this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
/**
24
 * @page menu_draw Paint the Menu
25
 *
26
 * Paint the Menu
27
 */
28
29
#include "config.h"
30
#include <stdbool.h>
31
#include <stdio.h>
32
#include <string.h>
33
#include <wchar.h>
34
#include "mutt/lib.h"
35
#include "config/lib.h"
36
#include "email/lib.h"
37
#include "gui/lib.h"
38
#include "color/lib.h"
39
#include "index/lib.h"
40
#include "menu/lib.h"
41
#include "pattern/lib.h"
42
#include "mutt_thread.h"
43
#include "mview.h"
44
#include "opcodes.h"
45
46
/**
47
 * get_color - Choose a colour for a line of the index
48
 * @param index Index number
49
 * @param s     String of embedded colour codes
50
 * @retval num Colour pair in an integer
51
 *
52
 * Text is coloured by inserting special characters into the string, e.g.
53
 * #MT_COLOR_INDEX_AUTHOR
54
 */
55
static struct AttrColor *get_color(int index, unsigned char *s)
56
0
{
57
0
  const int type = *s;
58
0
  struct RegexColorList *rcl = regex_colors_get_list(type);
59
0
  struct Mailbox *m_cur = get_current_mailbox();
60
0
  struct Email *e = mutt_get_virt_email(m_cur, index);
61
0
  if ((rcl == NULL) || (e == NULL))
62
0
  {
63
0
    return simple_color_get(type);
64
0
  }
65
66
0
  struct RegexColor *np = NULL;
67
68
0
  if (type == MT_COLOR_INDEX_TAG)
69
0
  {
70
0
    struct AttrColor *ac_merge = NULL;
71
0
    STAILQ_FOREACH(np, rcl, entries)
72
0
    {
73
0
      if (mutt_strn_equal((const char *) (s + 1), np->pattern, strlen(np->pattern)))
74
0
      {
75
0
        ac_merge = merged_color_overlay(ac_merge, &np->attr_color);
76
0
        continue;
77
0
      }
78
0
      const char *transform = mutt_hash_find(TagTransforms, np->pattern);
79
0
      if (transform && mutt_strn_equal((const char *) (s + 1), transform, strlen(transform)))
80
0
      {
81
0
        ac_merge = merged_color_overlay(ac_merge, &np->attr_color);
82
0
      }
83
0
    }
84
0
    return ac_merge;
85
0
  }
86
87
0
  struct AttrColor *ac_merge = NULL;
88
0
  STAILQ_FOREACH(np, rcl, entries)
89
0
  {
90
0
    if (mutt_pattern_exec(SLIST_FIRST(np->color_pattern),
91
0
                          MUTT_MATCH_FULL_ADDRESS, m_cur, e, NULL))
92
0
    {
93
0
      ac_merge = merged_color_overlay(ac_merge, &np->attr_color);
94
0
    }
95
0
  }
96
97
0
  return ac_merge;
98
0
}
99
100
/**
101
 * print_enriched_string - Display a string with embedded colours and graphics
102
 * @param win      Window
103
 * @param index    Index number
104
 * @param ac_def   Default colour for the line
105
 * @param ac_ind   Indicator colour for the line
106
 * @param s        String of embedded colour codes
107
 * @param sub      Config items
108
 */
109
static void print_enriched_string(struct MuttWindow *win, int index,
110
                                  struct AttrColor *ac_def, struct AttrColor *ac_ind,
111
                                  unsigned char *s, struct ConfigSubset *sub)
112
0
{
113
0
  wchar_t wc = 0;
114
0
  size_t k;
115
0
  size_t n = mutt_str_len((char *) s);
116
0
  mbstate_t mbstate = { 0 };
117
118
0
  const bool c_ascii_chars = cs_subset_bool(sub, "ascii_chars");
119
0
  while (*s)
120
0
  {
121
0
    if (*s < MUTT_TREE_MAX)
122
0
    {
123
0
      struct AttrColor *ac_merge = merged_color_overlay(ac_def, simple_color_get(MT_COLOR_TREE));
124
0
      ac_merge = merged_color_overlay(ac_merge, ac_ind);
125
126
      /* Combining tree fg color and another bg color requires having
127
       * use_default_colors, because the other bg color may be undefined. */
128
0
      mutt_curses_set_color(ac_merge);
129
130
0
      while (*s && (*s < MUTT_TREE_MAX))
131
0
      {
132
0
        switch (*s)
133
0
        {
134
0
          case MUTT_TREE_LLCORNER:
135
0
            if (c_ascii_chars)
136
0
              mutt_window_addch(win, '`');
137
0
#ifdef WACS_LLCORNER
138
0
            else
139
0
              add_wch(WACS_LLCORNER);
140
#else
141
            else if (CharsetIsUtf8)
142
              mutt_window_addstr(win, "\342\224\224"); /* WACS_LLCORNER */
143
            else
144
              mutt_window_addch(win, ACS_LLCORNER);
145
#endif
146
0
            break;
147
0
          case MUTT_TREE_ULCORNER:
148
0
            if (c_ascii_chars)
149
0
              mutt_window_addch(win, ',');
150
0
#ifdef WACS_ULCORNER
151
0
            else
152
0
              add_wch(WACS_ULCORNER);
153
#else
154
            else if (CharsetIsUtf8)
155
              mutt_window_addstr(win, "\342\224\214"); /* WACS_ULCORNER */
156
            else
157
              mutt_window_addch(win, ACS_ULCORNER);
158
#endif
159
0
            break;
160
0
          case MUTT_TREE_LTEE:
161
0
            if (c_ascii_chars)
162
0
              mutt_window_addch(win, '|');
163
0
#ifdef WACS_LTEE
164
0
            else
165
0
              add_wch(WACS_LTEE);
166
#else
167
            else if (CharsetIsUtf8)
168
              mutt_window_addstr(win, "\342\224\234"); /* WACS_LTEE */
169
            else
170
              mutt_window_addch(win, ACS_LTEE);
171
#endif
172
0
            break;
173
0
          case MUTT_TREE_HLINE:
174
0
            if (c_ascii_chars)
175
0
              mutt_window_addch(win, '-');
176
0
#ifdef WACS_HLINE
177
0
            else
178
0
              add_wch(WACS_HLINE);
179
#else
180
            else if (CharsetIsUtf8)
181
              mutt_window_addstr(win, "\342\224\200"); /* WACS_HLINE */
182
            else
183
              mutt_window_addch(win, ACS_HLINE);
184
#endif
185
0
            break;
186
0
          case MUTT_TREE_VLINE:
187
0
            if (c_ascii_chars)
188
0
              mutt_window_addch(win, '|');
189
0
#ifdef WACS_VLINE
190
0
            else
191
0
              add_wch(WACS_VLINE);
192
#else
193
            else if (CharsetIsUtf8)
194
              mutt_window_addstr(win, "\342\224\202"); /* WACS_VLINE */
195
            else
196
              mutt_window_addch(win, ACS_VLINE);
197
#endif
198
0
            break;
199
0
          case MUTT_TREE_TTEE:
200
0
            if (c_ascii_chars)
201
0
              mutt_window_addch(win, '-');
202
0
#ifdef WACS_TTEE
203
0
            else
204
0
              add_wch(WACS_TTEE);
205
#else
206
            else if (CharsetIsUtf8)
207
              mutt_window_addstr(win, "\342\224\254"); /* WACS_TTEE */
208
            else
209
              mutt_window_addch(win, ACS_TTEE);
210
#endif
211
0
            break;
212
0
          case MUTT_TREE_BTEE:
213
0
            if (c_ascii_chars)
214
0
              mutt_window_addch(win, '-');
215
0
#ifdef WACS_BTEE
216
0
            else
217
0
              add_wch(WACS_BTEE);
218
#else
219
            else if (CharsetIsUtf8)
220
              mutt_window_addstr(win, "\342\224\264"); /* WACS_BTEE */
221
            else
222
              mutt_window_addch(win, ACS_BTEE);
223
#endif
224
0
            break;
225
0
          case MUTT_TREE_SPACE:
226
0
            mutt_window_addch(win, ' ');
227
0
            break;
228
0
          case MUTT_TREE_RARROW:
229
0
            mutt_window_addch(win, '>');
230
0
            break;
231
0
          case MUTT_TREE_STAR:
232
0
            mutt_window_addch(win, '*'); /* fake thread indicator */
233
0
            break;
234
0
          case MUTT_TREE_HIDDEN:
235
0
            mutt_window_addch(win, '&');
236
0
            break;
237
0
          case MUTT_TREE_EQUALS:
238
0
            mutt_window_addch(win, '=');
239
0
            break;
240
0
          case MUTT_TREE_MISSING:
241
0
            mutt_window_addch(win, '?');
242
0
            break;
243
0
        }
244
0
        s++;
245
0
        n--;
246
0
      }
247
0
      ac_merge = merged_color_overlay(ac_def, ac_ind);
248
0
      mutt_curses_set_color(ac_merge);
249
0
    }
250
0
    else if (*s == MUTT_SPECIAL_INDEX)
251
0
    {
252
0
      s++;
253
0
      if (*s == MT_COLOR_INDEX)
254
0
      {
255
0
        struct AttrColor *ac_merge = merged_color_overlay(ac_def, ac_ind);
256
0
        mutt_curses_set_color(ac_merge);
257
0
      }
258
0
      else
259
0
      {
260
0
        struct AttrColor *color = get_color(index, s);
261
0
        struct AttrColor *ac_merge = merged_color_overlay(ac_def, color);
262
0
        ac_merge = merged_color_overlay(ac_merge, ac_ind);
263
264
0
        mutt_curses_set_color(ac_merge);
265
0
      }
266
0
      s++;
267
0
      n -= 2;
268
0
    }
269
0
    else if ((k = mbrtowc(&wc, (char *) s, n, &mbstate)) > 0)
270
0
    {
271
0
      mutt_window_addnstr(win, (char *) s, k);
272
0
      s += k;
273
0
      n -= k;
274
0
    }
275
0
    else
276
0
    {
277
0
      break;
278
0
    }
279
0
  }
280
0
}
281
282
/**
283
 * menu_pad_string - Pad a string with spaces for display in the Menu
284
 * @param menu   Current Menu
285
 * @param buf    Buffer containing the string
286
 * @param buflen Length of the buffer
287
 *
288
 * @note The string is padded in-place.
289
 */
290
static void menu_pad_string(struct Menu *menu, char *buf, size_t buflen)
291
0
{
292
0
  char *scratch = mutt_str_dup(buf);
293
0
  const bool c_arrow_cursor = cs_subset_bool(menu->sub, "arrow_cursor");
294
0
  const char *const c_arrow_string = cs_subset_string(menu->sub, "arrow_string");
295
0
  int shift = c_arrow_cursor ? mutt_strwidth(c_arrow_string) + 1 : 0;
296
0
  int cols = menu->win->state.cols - shift;
297
298
0
  mutt_simple_format(buf, buflen, cols, cols, JUSTIFY_LEFT, ' ', scratch,
299
0
                     mutt_str_len(scratch), true);
300
0
  buf[buflen - 1] = '\0';
301
0
  FREE(&scratch);
302
0
}
303
304
/**
305
 * menu_redraw_full - Force the redraw of the Menu
306
 * @param menu Current Menu
307
 */
308
void menu_redraw_full(struct Menu *menu)
309
0
{
310
0
  mutt_curses_set_color_by_id(MT_COLOR_NORMAL);
311
0
  mutt_window_clear(menu->win);
312
313
0
  menu->page_len = menu->win->state.rows;
314
315
0
  menu->redraw = MENU_REDRAW_INDEX;
316
0
}
317
318
/**
319
 * menu_redraw_index - Force the redraw of the index
320
 * @param menu Current Menu
321
 */
322
void menu_redraw_index(struct Menu *menu)
323
0
{
324
0
  char buf[1024] = { 0 };
325
0
  struct AttrColor *ac = NULL;
326
327
0
  const bool c_arrow_cursor = cs_subset_bool(menu->sub, "arrow_cursor");
328
0
  const char *const c_arrow_string = cs_subset_string(menu->sub, "arrow_string");
329
0
  struct AttrColor *ac_ind = simple_color_get(MT_COLOR_INDICATOR);
330
0
  for (int i = menu->top; i < (menu->top + menu->page_len); i++)
331
0
  {
332
0
    if (i < menu->max)
333
0
    {
334
0
      ac = menu->color(menu, i);
335
336
0
      menu->make_entry(menu, buf, sizeof(buf), i);
337
0
      menu_pad_string(menu, buf, sizeof(buf));
338
339
0
      mutt_curses_set_color(ac);
340
0
      mutt_window_move(menu->win, 0, i - menu->top);
341
342
0
      if (i == menu->current)
343
0
        mutt_curses_set_color(ac_ind);
344
345
0
      if (c_arrow_cursor)
346
0
      {
347
0
        if (i == menu->current)
348
0
        {
349
0
          mutt_window_addstr(menu->win, c_arrow_string);
350
0
          mutt_curses_set_color(ac);
351
0
          mutt_window_addch(menu->win, ' ');
352
0
        }
353
0
        else
354
0
        {
355
          /* Print space chars to match the screen width of `$arrow_string` */
356
0
          mutt_window_printf(menu->win, "%*s", mutt_strwidth(c_arrow_string) + 1, "");
357
0
        }
358
0
      }
359
360
0
      if ((i == menu->current) && !c_arrow_cursor)
361
0
        print_enriched_string(menu->win, i, ac, ac_ind, (unsigned char *) buf, menu->sub);
362
0
      else
363
0
        print_enriched_string(menu->win, i, ac, NULL, (unsigned char *) buf, menu->sub);
364
0
    }
365
0
    else
366
0
    {
367
0
      mutt_curses_set_color_by_id(MT_COLOR_NORMAL);
368
0
      mutt_window_clearline(menu->win, i - menu->top);
369
0
    }
370
0
  }
371
0
  mutt_curses_set_color_by_id(MT_COLOR_NORMAL);
372
0
  menu->redraw = MENU_REDRAW_NO_FLAGS;
373
0
}
374
375
/**
376
 * menu_redraw_motion - Force the redraw of the list part of the menu
377
 * @param menu Current Menu
378
 */
379
void menu_redraw_motion(struct Menu *menu)
380
0
{
381
0
  char buf[1024] = { 0 };
382
383
  /* Note: menu->color() for the index can end up retrieving a message
384
   * over imap (if matching against ~h for instance).  This can
385
   * generate status messages.  So we want to call it *before* we
386
   * position the cursor for drawing. */
387
0
  struct AttrColor *old_color = menu->color(menu, menu->old_current);
388
0
  mutt_window_move(menu->win, 0, menu->old_current - menu->top);
389
0
  mutt_curses_set_color(old_color);
390
391
0
  const bool c_arrow_cursor = cs_subset_bool(menu->sub, "arrow_cursor");
392
0
  const char *const c_arrow_string = cs_subset_string(menu->sub, "arrow_string");
393
0
  struct AttrColor *ac_ind = simple_color_get(MT_COLOR_INDICATOR);
394
0
  if (c_arrow_cursor)
395
0
  {
396
    /* clear the arrow */
397
    /* Print space chars to match the screen width of `$arrow_string` */
398
0
    mutt_window_printf(menu->win, "%*s", mutt_strwidth(c_arrow_string) + 1, "");
399
0
    mutt_curses_set_color_by_id(MT_COLOR_NORMAL);
400
401
0
    menu->make_entry(menu, buf, sizeof(buf), menu->old_current);
402
0
    menu_pad_string(menu, buf, sizeof(buf));
403
0
    mutt_window_move(menu->win, mutt_strwidth(c_arrow_string) + 1,
404
0
                     menu->old_current - menu->top);
405
0
    print_enriched_string(menu->win, menu->old_current, old_color, NULL,
406
0
                          (unsigned char *) buf, menu->sub);
407
408
    /* now draw it in the new location */
409
0
    mutt_curses_set_color(ac_ind);
410
0
    mutt_window_mvaddstr(menu->win, 0, menu->current - menu->top, c_arrow_string);
411
0
  }
412
0
  else
413
0
  {
414
0
    mutt_curses_set_color_by_id(MT_COLOR_NORMAL);
415
    /* erase the current indicator */
416
0
    menu->make_entry(menu, buf, sizeof(buf), menu->old_current);
417
0
    menu_pad_string(menu, buf, sizeof(buf));
418
0
    print_enriched_string(menu->win, menu->old_current, old_color, NULL,
419
0
                          (unsigned char *) buf, menu->sub);
420
421
    /* now draw the new one to reflect the change */
422
0
    struct AttrColor *cur_color = menu->color(menu, menu->current);
423
0
    cur_color = merged_color_overlay(cur_color, ac_ind);
424
0
    menu->make_entry(menu, buf, sizeof(buf), menu->current);
425
0
    menu_pad_string(menu, buf, sizeof(buf));
426
0
    mutt_window_move(menu->win, 0, menu->current - menu->top);
427
0
    mutt_curses_set_color(cur_color);
428
0
    print_enriched_string(menu->win, menu->current, cur_color, ac_ind,
429
0
                          (unsigned char *) buf, menu->sub);
430
0
  }
431
0
  mutt_curses_set_color_by_id(MT_COLOR_NORMAL);
432
0
}
433
434
/**
435
 * menu_redraw_current - Redraw the current menu
436
 * @param menu Current Menu
437
 */
438
void menu_redraw_current(struct Menu *menu)
439
0
{
440
0
  char buf[1024] = { 0 };
441
0
  struct AttrColor *ac = menu->color(menu, menu->current);
442
443
0
  mutt_window_move(menu->win, 0, menu->current - menu->top);
444
0
  menu->make_entry(menu, buf, sizeof(buf), menu->current);
445
0
  menu_pad_string(menu, buf, sizeof(buf));
446
447
0
  struct AttrColor *ac_ind = simple_color_get(MT_COLOR_INDICATOR);
448
0
  const bool c_arrow_cursor = cs_subset_bool(menu->sub, "arrow_cursor");
449
0
  const char *const c_arrow_string = cs_subset_string(menu->sub, "arrow_string");
450
0
  if (c_arrow_cursor)
451
0
  {
452
0
    mutt_curses_set_color(ac_ind);
453
0
    mutt_window_addstr(menu->win, c_arrow_string);
454
0
    mutt_curses_set_color(ac);
455
0
    mutt_window_addch(menu->win, ' ');
456
0
    menu_pad_string(menu, buf, sizeof(buf));
457
0
    print_enriched_string(menu->win, menu->current, ac, NULL,
458
0
                          (unsigned char *) buf, menu->sub);
459
0
  }
460
0
  else
461
0
  {
462
0
    print_enriched_string(menu->win, menu->current, ac, ac_ind,
463
0
                          (unsigned char *) buf, menu->sub);
464
0
  }
465
0
  mutt_curses_set_color_by_id(MT_COLOR_NORMAL);
466
0
}
467
468
/**
469
 * menu_redraw - Redraw the parts of the screen that have been flagged to be redrawn
470
 * @param menu Menu to redraw
471
 * @retval OP_NULL   Menu was redrawn
472
 * @retval OP_REDRAW Full redraw required
473
 */
474
int menu_redraw(struct Menu *menu)
475
0
{
476
  /* See if all or part of the screen needs to be updated.  */
477
0
  if (menu->redraw & MENU_REDRAW_FULL)
478
0
    menu_redraw_full(menu);
479
480
0
  if (menu->redraw & MENU_REDRAW_INDEX)
481
0
    menu_redraw_index(menu);
482
0
  else if (menu->redraw & MENU_REDRAW_MOTION)
483
0
    menu_redraw_motion(menu);
484
0
  else if (menu->redraw == MENU_REDRAW_CURRENT)
485
0
    menu_redraw_current(menu);
486
487
0
  return OP_NULL;
488
0
}