Coverage Report

Created: 2023-09-25 07:17

/src/neomutt/menu/functions.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file
3
 * Menu functions
4
 *
5
 * @authors
6
 * Copyright (C) 2022 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_functions Menu functions
25
 *
26
 * Menu functions
27
 */
28
29
#include "config.h"
30
#include <stddef.h>
31
#include <stdbool.h>
32
#include <stdint.h>
33
#include "mutt/lib.h"
34
#include "config/lib.h"
35
#include "core/lib.h"
36
#include "gui/lib.h"
37
#include "mutt.h"
38
#include "functions.h"
39
#include "lib.h"
40
#include "editor/lib.h"
41
#include "history/lib.h"
42
#include "protos.h"
43
#include "type.h"
44
45
extern char *SearchBuffers[];
46
47
0
#define MUTT_SEARCH_UP 1
48
0
#define MUTT_SEARCH_DOWN 2
49
50
/**
51
 * search - Search a menu
52
 * @param menu Menu to search
53
 * @param op   Search operation, e.g. OP_SEARCH_NEXT
54
 * @retval >=0 Index of matching item
55
 * @retval -1  Search failed, or was cancelled
56
 */
57
static int search(struct Menu *menu, int op)
58
0
{
59
0
  int rc = -1;
60
0
  int wrap = 0;
61
0
  int search_dir;
62
0
  regex_t re = { 0 };
63
0
  struct Buffer *buf = buf_pool_get();
64
65
0
  char *search_buf = ((menu->type < MENU_MAX)) ? SearchBuffers[menu->type] : NULL;
66
67
0
  if (!(search_buf && *search_buf) || ((op != OP_SEARCH_NEXT) && (op != OP_SEARCH_OPPOSITE)))
68
0
  {
69
0
    buf_strcpy(buf, search_buf && (search_buf[0] != '\0') ? search_buf : "");
70
0
    if ((mw_get_field(((op == OP_SEARCH) || (op == OP_SEARCH_NEXT)) ? _("Search for: ") : _("Reverse search for: "),
71
0
                      buf, MUTT_COMP_CLEAR, HC_OTHER, NULL, NULL) != 0) ||
72
0
        buf_is_empty(buf))
73
0
    {
74
0
      goto done;
75
0
    }
76
0
    if (menu->type < MENU_MAX)
77
0
    {
78
0
      mutt_str_replace(&SearchBuffers[menu->type], buf_string(buf));
79
0
      search_buf = SearchBuffers[menu->type];
80
0
    }
81
0
    menu->search_dir = ((op == OP_SEARCH) || (op == OP_SEARCH_NEXT)) ?
82
0
                           MUTT_SEARCH_DOWN :
83
0
                           MUTT_SEARCH_UP;
84
0
  }
85
86
0
  search_dir = (menu->search_dir == MUTT_SEARCH_UP) ? -1 : 1;
87
0
  if (op == OP_SEARCH_OPPOSITE)
88
0
    search_dir = -search_dir;
89
90
0
  if (search_buf)
91
0
  {
92
0
    uint16_t flags = mutt_mb_is_lower(search_buf) ? REG_ICASE : 0;
93
0
    rc = REG_COMP(&re, search_buf, REG_NOSUB | flags);
94
0
  }
95
96
0
  if (rc != 0)
97
0
  {
98
0
    regerror(rc, &re, buf->data, buf->dsize);
99
0
    mutt_error("%s", buf_string(buf));
100
0
    rc = -1;
101
0
    goto done;
102
0
  }
103
104
0
  rc = menu->current + search_dir;
105
0
search_next:
106
0
  if (wrap)
107
0
    mutt_message(_("Search wrapped to top"));
108
0
  while ((rc >= 0) && (rc < menu->max))
109
0
  {
110
0
    if (menu->search(menu, &re, rc) == 0)
111
0
    {
112
0
      regfree(&re);
113
0
      goto done;
114
0
    }
115
116
0
    rc += search_dir;
117
0
  }
118
119
0
  const bool c_wrap_search = cs_subset_bool(menu->sub, "wrap_search");
120
0
  if (c_wrap_search && (wrap++ == 0))
121
0
  {
122
0
    rc = (search_dir == 1) ? 0 : menu->max - 1;
123
0
    goto search_next;
124
0
  }
125
0
  regfree(&re);
126
0
  mutt_error(_("Not found"));
127
0
  rc = -1;
128
129
0
done:
130
0
  buf_pool_release(&buf);
131
0
  return rc;
132
0
}
133
134
// -----------------------------------------------------------------------------
135
136
/**
137
 * menu_movement - Handle all the common Menu movements - Implements ::menu_function_t - @ingroup menu_function_api
138
 */
139
static int menu_movement(struct Menu *menu, int op)
140
0
{
141
0
  switch (op)
142
0
  {
143
0
    case OP_BOTTOM_PAGE:
144
0
      menu_bottom_page(menu);
145
0
      return FR_SUCCESS;
146
147
0
    case OP_CURRENT_BOTTOM:
148
0
      menu_current_bottom(menu);
149
0
      return FR_SUCCESS;
150
151
0
    case OP_CURRENT_MIDDLE:
152
0
      menu_current_middle(menu);
153
0
      return FR_SUCCESS;
154
155
0
    case OP_CURRENT_TOP:
156
0
      menu_current_top(menu);
157
0
      return FR_SUCCESS;
158
159
0
    case OP_FIRST_ENTRY:
160
0
      menu_first_entry(menu);
161
0
      return FR_SUCCESS;
162
163
0
    case OP_HALF_DOWN:
164
0
      menu_half_down(menu);
165
0
      return FR_SUCCESS;
166
167
0
    case OP_HALF_UP:
168
0
      menu_half_up(menu);
169
0
      return FR_SUCCESS;
170
171
0
    case OP_LAST_ENTRY:
172
0
      menu_last_entry(menu);
173
0
      return FR_SUCCESS;
174
175
0
    case OP_MIDDLE_PAGE:
176
0
      menu_middle_page(menu);
177
0
      return FR_SUCCESS;
178
179
0
    case OP_NEXT_ENTRY:
180
0
      menu_next_entry(menu);
181
0
      return FR_SUCCESS;
182
183
0
    case OP_NEXT_LINE:
184
0
      menu_next_line(menu);
185
0
      return FR_SUCCESS;
186
187
0
    case OP_NEXT_PAGE:
188
0
      menu_next_page(menu);
189
0
      return FR_SUCCESS;
190
191
0
    case OP_PREV_ENTRY:
192
0
      menu_prev_entry(menu);
193
0
      return FR_SUCCESS;
194
195
0
    case OP_PREV_LINE:
196
0
      menu_prev_line(menu);
197
0
      return FR_SUCCESS;
198
199
0
    case OP_PREV_PAGE:
200
0
      menu_prev_page(menu);
201
0
      return FR_SUCCESS;
202
203
0
    case OP_TOP_PAGE:
204
0
      menu_top_page(menu);
205
0
      return FR_SUCCESS;
206
207
0
    default:
208
0
      return FR_UNKNOWN;
209
0
  }
210
0
}
211
212
/**
213
 * menu_search - Handle Menu searching - Implements ::menu_function_t - @ingroup menu_function_api
214
 */
215
static int menu_search(struct Menu *menu, int op)
216
0
{
217
0
  if (menu->search)
218
0
  {
219
0
    int index = search(menu, op);
220
0
    if (index != -1)
221
0
      menu_set_index(menu, index);
222
0
  }
223
0
  return FR_SUCCESS;
224
0
}
225
226
/**
227
 * op_help - Show the help screen - Implements ::menu_function_t - @ingroup menu_function_api
228
 */
229
static int op_help(struct Menu *menu, int op)
230
{
231
  mutt_help(menu->type);
232
  menu->redraw = MENU_REDRAW_FULL;
233
  return FR_SUCCESS;
234
}
235
236
/**
237
 * op_jump - Jump to an index number - Implements ::menu_function_t - @ingroup menu_function_api
238
 */
239
static int op_jump(struct Menu *menu, int op)
240
{
241
  if (menu->max == 0)
242
  {
243
    mutt_error(_("No entries"));
244
    return FR_SUCCESS;
245
  }
246
247
  const int digit = op - OP_JUMP;
248
  if ((digit > 0) && (digit < 10))
249
  {
250
    mutt_unget_ch('0' + digit);
251
  }
252
253
  struct Buffer *buf = buf_pool_get();
254
  if ((mw_get_field(_("Jump to: "), buf, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) == 0) &&
255
      !buf_is_empty(buf))
256
  {
257
    int n = 0;
258
    if (mutt_str_atoi_full(buf_string(buf), &n) && (n > 0) && (n < (menu->max + 1)))
259
    {
260
      menu_set_index(menu, n - 1); // msg numbers are 0-based
261
    }
262
    else
263
    {
264
      mutt_error(_("Invalid index number"));
265
    }
266
  }
267
268
  buf_pool_release(&buf);
269
  return FR_SUCCESS;
270
}
271
272
// -----------------------------------------------------------------------------
273
274
/**
275
 * MenuFunctions - All the NeoMutt functions that the Menu supports
276
 */
277
static const struct MenuFunction MenuFunctions[] = {
278
  // clang-format off
279
  { OP_BOTTOM_PAGE,            menu_movement },
280
  { OP_CURRENT_BOTTOM,         menu_movement },
281
  { OP_CURRENT_MIDDLE,         menu_movement },
282
  { OP_CURRENT_TOP,            menu_movement },
283
  { OP_FIRST_ENTRY,            menu_movement },
284
  { OP_HALF_DOWN,              menu_movement },
285
  { OP_HALF_UP,                menu_movement },
286
  { OP_HELP,                   op_help },
287
  { OP_JUMP,                   op_jump },
288
  { OP_JUMP_1,                 op_jump },
289
  { OP_JUMP_2,                 op_jump },
290
  { OP_JUMP_3,                 op_jump },
291
  { OP_JUMP_4,                 op_jump },
292
  { OP_JUMP_5,                 op_jump },
293
  { OP_JUMP_6,                 op_jump },
294
  { OP_JUMP_7,                 op_jump },
295
  { OP_JUMP_8,                 op_jump },
296
  { OP_JUMP_9,                 op_jump },
297
  { OP_LAST_ENTRY,             menu_movement },
298
  { OP_MIDDLE_PAGE,            menu_movement },
299
  { OP_NEXT_ENTRY,             menu_movement },
300
  { OP_NEXT_LINE,              menu_movement },
301
  { OP_NEXT_PAGE,              menu_movement },
302
  { OP_PREV_ENTRY,             menu_movement },
303
  { OP_PREV_LINE,              menu_movement },
304
  { OP_PREV_PAGE,              menu_movement },
305
  { OP_SEARCH,                 menu_search },
306
  { OP_SEARCH_NEXT,            menu_search },
307
  { OP_SEARCH_OPPOSITE,        menu_search },
308
  { OP_SEARCH_REVERSE,         menu_search },
309
  { OP_TOP_PAGE,               menu_movement },
310
  { 0, NULL },
311
  // clang-format on
312
};
313
314
/**
315
 * menu_function_dispatcher - Perform a Menu function - Implements ::function_dispatcher_t - @ingroup dispatcher_api
316
 */
317
int menu_function_dispatcher(struct MuttWindow *win, int op)
318
0
{
319
0
  if (!win || !win->wdata)
320
0
    return FR_UNKNOWN;
321
322
0
  struct Menu *menu = win->wdata;
323
324
0
  int rc = FR_UNKNOWN;
325
0
  for (size_t i = 0; MenuFunctions[i].op != OP_NULL; i++)
326
0
  {
327
0
    const struct MenuFunction *fn = &MenuFunctions[i];
328
0
    if (fn->op == op)
329
0
    {
330
0
      rc = fn->function(menu, op);
331
0
      break;
332
0
    }
333
0
  }
334
335
0
  if (rc == FR_UNKNOWN) // Not our function
336
0
    return rc;
337
338
0
  const char *result = dispatcher_get_retval_name(rc);
339
0
  mutt_debug(LL_DEBUG1, "Handled %s (%d) -> %s\n", opcodes_get_name(op), op, NONULL(result));
340
341
0
  return rc;
342
0
}