Coverage Report

Created: 2023-06-07 06:15

/src/neomutt/sidebar/functions.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file
3
 * Sidebar functions
4
 *
5
 * @authors
6
 * Copyright (C) 2020 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 sidebar_functions Sidebar functions
25
 *
26
 * Sidebar functions
27
 */
28
29
#include "config.h"
30
#include <stddef.h>
31
#include <stdbool.h>
32
#include "private.h"
33
#include "mutt/lib.h"
34
#include "config/lib.h"
35
#include "core/lib.h"
36
#include "gui/lib.h"
37
#include "functions.h"
38
#include "lib.h"
39
#include "index/lib.h"
40
#include "opcodes.h"
41
42
/**
43
 * sb_next - Find the next unhidden Mailbox
44
 * @param wdata Sidebar data
45
 * @retval true Mailbox found
46
 */
47
bool sb_next(struct SidebarWindowData *wdata)
48
0
{
49
0
  struct SbEntry **sbep = NULL;
50
0
  ARRAY_FOREACH_FROM(sbep, &wdata->entries, wdata->hil_index + 1)
51
0
  {
52
0
    if (!(*sbep)->is_hidden)
53
0
    {
54
0
      wdata->hil_index = ARRAY_FOREACH_IDX;
55
0
      return true;
56
0
    }
57
0
  }
58
59
0
  return false;
60
0
}
61
62
/**
63
 * sb_next_new - Return the next mailbox with new messages
64
 * @param wdata Sidebar data
65
 * @param begin Starting index for searching
66
 * @param end   Ending index for searching
67
 * @retval ptr  Pointer to the first entry with new messages
68
 * @retval NULL None could be found
69
 */
70
static struct SbEntry **sb_next_new(struct SidebarWindowData *wdata, size_t begin, size_t end)
71
0
{
72
0
  struct SbEntry **sbep = NULL;
73
0
  ARRAY_FOREACH_FROM_TO(sbep, &wdata->entries, begin, end)
74
0
  {
75
0
    if ((*sbep)->mailbox->has_new || ((*sbep)->mailbox->msg_unread != 0))
76
0
      return sbep;
77
0
  }
78
0
  return NULL;
79
0
}
80
81
/**
82
 * sb_prev - Find the previous unhidden Mailbox
83
 * @param wdata Sidebar data
84
 * @retval true Mailbox found
85
 */
86
bool sb_prev(struct SidebarWindowData *wdata)
87
0
{
88
0
  struct SbEntry **sbep = NULL, **prev = NULL;
89
0
  ARRAY_FOREACH_TO(sbep, &wdata->entries, wdata->hil_index)
90
0
  {
91
0
    if (!(*sbep)->is_hidden)
92
0
      prev = sbep;
93
0
  }
94
95
0
  if (prev)
96
0
  {
97
0
    wdata->hil_index = ARRAY_IDX(&wdata->entries, prev);
98
0
    return true;
99
0
  }
100
101
0
  return false;
102
0
}
103
104
/**
105
 * sb_prev_new - Return the previous mailbox with new messages
106
 * @param wdata Sidebar data
107
 * @param begin Starting index for searching
108
 * @param end   Ending index for searching
109
 * @retval ptr  Pointer to the first entry with new messages
110
 * @retval NULL None could be found
111
 */
112
static struct SbEntry **sb_prev_new(struct SidebarWindowData *wdata, size_t begin, size_t end)
113
0
{
114
0
  struct SbEntry **sbep = NULL, **prev = NULL;
115
0
  ARRAY_FOREACH_FROM_TO(sbep, &wdata->entries, begin, end)
116
0
  {
117
0
    if ((*sbep)->mailbox->has_new || ((*sbep)->mailbox->msg_unread != 0))
118
0
      prev = sbep;
119
0
  }
120
121
0
  return prev;
122
0
}
123
124
// -----------------------------------------------------------------------------
125
126
/**
127
 * op_sidebar_first - Selects the first unhidden mailbox - Implements ::sidebar_function_t - @ingroup sidebar_function_api
128
 */
129
static int op_sidebar_first(struct SidebarWindowData *wdata, int op)
130
0
{
131
0
  if (!mutt_window_is_visible(wdata->win))
132
0
    return FR_NO_ACTION;
133
134
0
  if (ARRAY_EMPTY(&wdata->entries) || (wdata->hil_index < 0))
135
0
    return FR_NO_ACTION;
136
137
0
  int orig_hil_index = wdata->hil_index;
138
139
0
  wdata->hil_index = 0;
140
0
  if ((*ARRAY_GET(&wdata->entries, wdata->hil_index))->is_hidden)
141
0
    if (!sb_next(wdata))
142
0
      wdata->hil_index = orig_hil_index;
143
144
0
  if (orig_hil_index == wdata->hil_index)
145
0
    return FR_NO_ACTION;
146
147
0
  wdata->win->actions |= WA_RECALC;
148
0
  return FR_SUCCESS;
149
0
}
150
151
/**
152
 * op_sidebar_last - Selects the last unhidden mailbox - Implements ::sidebar_function_t - @ingroup sidebar_function_api
153
 */
154
static int op_sidebar_last(struct SidebarWindowData *wdata, int op)
155
0
{
156
0
  if (!mutt_window_is_visible(wdata->win))
157
0
    return FR_NO_ACTION;
158
159
0
  if (ARRAY_EMPTY(&wdata->entries) || (wdata->hil_index < 0))
160
0
    return FR_NO_ACTION;
161
162
0
  int orig_hil_index = wdata->hil_index;
163
164
0
  wdata->hil_index = ARRAY_SIZE(&wdata->entries);
165
0
  if (!sb_prev(wdata))
166
0
    wdata->hil_index = orig_hil_index;
167
168
0
  if (orig_hil_index == wdata->hil_index)
169
0
    return FR_NO_ACTION;
170
171
0
  wdata->win->actions |= WA_RECALC;
172
0
  return FR_SUCCESS;
173
0
}
174
175
/**
176
 * op_sidebar_next - Selects the next unhidden mailbox - Implements ::sidebar_function_t - @ingroup sidebar_function_api
177
 */
178
static int op_sidebar_next(struct SidebarWindowData *wdata, int op)
179
0
{
180
0
  if (!mutt_window_is_visible(wdata->win))
181
0
    return FR_NO_ACTION;
182
183
0
  if (ARRAY_EMPTY(&wdata->entries) || (wdata->hil_index < 0))
184
0
    return FR_NO_ACTION;
185
186
0
  if (!sb_next(wdata))
187
0
    return FR_NO_ACTION;
188
189
0
  wdata->win->actions |= WA_RECALC;
190
0
  return FR_SUCCESS;
191
0
}
192
193
/**
194
 * op_sidebar_next_new - Selects the next new mailbox - Implements ::sidebar_function_t - @ingroup sidebar_function_api
195
 *
196
 * Search down the list of mail folders for one containing new mail.
197
 */
198
static int op_sidebar_next_new(struct SidebarWindowData *wdata, int op)
199
0
{
200
0
  if (!mutt_window_is_visible(wdata->win))
201
0
    return FR_NO_ACTION;
202
203
0
  const size_t max_entries = ARRAY_SIZE(&wdata->entries);
204
0
  if ((max_entries == 0) || (wdata->hil_index < 0))
205
0
    return FR_NO_ACTION;
206
207
0
  const bool c_sidebar_next_new_wrap = cs_subset_bool(NeoMutt->sub, "sidebar_next_new_wrap");
208
0
  struct SbEntry **sbep = NULL;
209
0
  if ((sbep = sb_next_new(wdata, wdata->hil_index + 1, max_entries)) ||
210
0
      (c_sidebar_next_new_wrap && (sbep = sb_next_new(wdata, 0, wdata->hil_index))))
211
0
  {
212
0
    wdata->hil_index = ARRAY_IDX(&wdata->entries, sbep);
213
0
    wdata->win->actions |= WA_RECALC;
214
0
    return FR_SUCCESS;
215
0
  }
216
217
0
  return FR_NO_ACTION;
218
0
}
219
220
/**
221
 * op_sidebar_open - Open highlighted mailbox - Implements ::sidebar_function_t - @ingroup sidebar_function_api
222
 */
223
static int op_sidebar_open(struct SidebarWindowData *wdata, int op)
224
0
{
225
0
  struct MuttWindow *win_sidebar = wdata->win;
226
0
  if (!mutt_window_is_visible(win_sidebar))
227
0
    return FR_NO_ACTION;
228
229
0
  struct MuttWindow *dlg = dialog_find(win_sidebar);
230
0
  dlg_change_folder(dlg, sb_get_highlight(win_sidebar));
231
0
  return FR_SUCCESS;
232
0
}
233
234
/**
235
 * op_sidebar_page_down - Selects the first entry in the next page of mailboxes - Implements ::sidebar_function_t - @ingroup sidebar_function_api
236
 */
237
static int op_sidebar_page_down(struct SidebarWindowData *wdata, int op)
238
0
{
239
0
  if (!mutt_window_is_visible(wdata->win))
240
0
    return FR_NO_ACTION;
241
242
0
  if (ARRAY_EMPTY(&wdata->entries) || (wdata->bot_index < 0))
243
0
    return FR_NO_ACTION;
244
245
0
  int orig_hil_index = wdata->hil_index;
246
247
0
  wdata->hil_index = wdata->bot_index;
248
0
  sb_next(wdata);
249
  /* If the rest of the entries are hidden, go up to the last unhidden one */
250
0
  if ((*ARRAY_GET(&wdata->entries, wdata->hil_index))->is_hidden)
251
0
    sb_prev(wdata);
252
253
0
  if (orig_hil_index == wdata->hil_index)
254
0
    return FR_NO_ACTION;
255
256
0
  wdata->win->actions |= WA_RECALC;
257
0
  return FR_SUCCESS;
258
0
}
259
260
/**
261
 * op_sidebar_page_up - Selects the last entry in the previous page of mailboxes - Implements ::sidebar_function_t - @ingroup sidebar_function_api
262
 */
263
static int op_sidebar_page_up(struct SidebarWindowData *wdata, int op)
264
0
{
265
0
  if (!mutt_window_is_visible(wdata->win))
266
0
    return FR_NO_ACTION;
267
268
0
  if (ARRAY_EMPTY(&wdata->entries) || (wdata->top_index < 0))
269
0
    return FR_NO_ACTION;
270
271
0
  int orig_hil_index = wdata->hil_index;
272
273
0
  wdata->hil_index = wdata->top_index;
274
0
  sb_prev(wdata);
275
  /* If the rest of the entries are hidden, go down to the last unhidden one */
276
0
  if ((*ARRAY_GET(&wdata->entries, wdata->hil_index))->is_hidden)
277
0
    sb_next(wdata);
278
279
0
  if (orig_hil_index == wdata->hil_index)
280
0
    return FR_NO_ACTION;
281
282
0
  wdata->win->actions |= WA_RECALC;
283
0
  return FR_SUCCESS;
284
0
}
285
286
/**
287
 * op_sidebar_prev - Selects the previous unhidden mailbox - Implements ::sidebar_function_t - @ingroup sidebar_function_api
288
 */
289
static int op_sidebar_prev(struct SidebarWindowData *wdata, int op)
290
0
{
291
0
  if (!mutt_window_is_visible(wdata->win))
292
0
    return FR_NO_ACTION;
293
294
0
  if (ARRAY_EMPTY(&wdata->entries) || (wdata->hil_index < 0))
295
0
    return FR_NO_ACTION;
296
297
0
  if (!sb_prev(wdata))
298
0
    return FR_NO_ACTION;
299
300
0
  wdata->win->actions |= WA_RECALC;
301
0
  return FR_SUCCESS;
302
0
}
303
304
/**
305
 * op_sidebar_prev_new - Selects the previous new mailbox - Implements ::sidebar_function_t - @ingroup sidebar_function_api
306
 *
307
 * Search up the list of mail folders for one containing new mail.
308
 */
309
static int op_sidebar_prev_new(struct SidebarWindowData *wdata, int op)
310
0
{
311
0
  if (!mutt_window_is_visible(wdata->win))
312
0
    return FR_NO_ACTION;
313
314
0
  const size_t max_entries = ARRAY_SIZE(&wdata->entries);
315
0
  if ((max_entries == 0) || (wdata->hil_index < 0))
316
0
    return FR_NO_ACTION;
317
318
0
  const bool c_sidebar_next_new_wrap = cs_subset_bool(NeoMutt->sub, "sidebar_next_new_wrap");
319
0
  struct SbEntry **sbep = NULL;
320
0
  if ((sbep = sb_prev_new(wdata, 0, wdata->hil_index)) ||
321
0
      (c_sidebar_next_new_wrap &&
322
0
       (sbep = sb_prev_new(wdata, wdata->hil_index + 1, max_entries))))
323
0
  {
324
0
    wdata->hil_index = ARRAY_IDX(&wdata->entries, sbep);
325
0
    wdata->win->actions |= WA_RECALC;
326
0
    return FR_SUCCESS;
327
0
  }
328
329
0
  return FR_NO_ACTION;
330
0
}
331
332
/**
333
 * op_sidebar_toggle_visible - Make the sidebar (in)visible - Implements ::sidebar_function_t - @ingroup sidebar_function_api
334
 */
335
static int op_sidebar_toggle_visible(struct SidebarWindowData *wdata, int op)
336
0
{
337
0
  bool_str_toggle(NeoMutt->sub, "sidebar_visible", NULL);
338
0
  mutt_window_reflow(NULL);
339
0
  return FR_SUCCESS;
340
0
}
341
342
/**
343
 * op_sidebar_toggle_virtual - Deprecated - Implements ::sidebar_function_t - @ingroup sidebar_function_api
344
 */
345
static int op_sidebar_toggle_virtual(struct SidebarWindowData *wdata, int op)
346
0
{
347
0
  return FR_SUCCESS;
348
0
}
349
350
// -----------------------------------------------------------------------------
351
352
/**
353
 * SidebarFunctions - All the NeoMutt functions that the Sidebar supports
354
 */
355
static const struct SidebarFunction SidebarFunctions[] = {
356
  // clang-format off
357
  { OP_SIDEBAR_FIRST,          op_sidebar_first },
358
  { OP_SIDEBAR_LAST,           op_sidebar_last },
359
  { OP_SIDEBAR_NEXT,           op_sidebar_next },
360
  { OP_SIDEBAR_NEXT_NEW,       op_sidebar_next_new },
361
  { OP_SIDEBAR_OPEN,           op_sidebar_open },
362
  { OP_SIDEBAR_PAGE_DOWN,      op_sidebar_page_down },
363
  { OP_SIDEBAR_PAGE_UP,        op_sidebar_page_up },
364
  { OP_SIDEBAR_PREV,           op_sidebar_prev },
365
  { OP_SIDEBAR_PREV_NEW,       op_sidebar_prev_new },
366
  { OP_SIDEBAR_TOGGLE_VIRTUAL, op_sidebar_toggle_virtual },
367
  { OP_SIDEBAR_TOGGLE_VISIBLE, op_sidebar_toggle_visible },
368
  { 0, NULL },
369
  // clang-format on
370
};
371
372
/**
373
 * sb_function_dispatcher - Perform a Sidebar function - Implements ::function_dispatcher_t - @ingroup dispatcher_api
374
 */
375
int sb_function_dispatcher(struct MuttWindow *win, int op)
376
0
{
377
0
  if (!win || !win->wdata)
378
0
    return FR_UNKNOWN;
379
380
0
  struct SidebarWindowData *wdata = win->wdata;
381
0
  int rc = FR_UNKNOWN;
382
0
  for (size_t i = 0; SidebarFunctions[i].op != OP_NULL; i++)
383
0
  {
384
0
    const struct SidebarFunction *fn = &SidebarFunctions[i];
385
0
    if (fn->op == op)
386
0
    {
387
0
      rc = fn->function(wdata, op);
388
0
      break;
389
0
    }
390
0
  }
391
392
0
  if (rc == FR_UNKNOWN) // Not our function
393
0
    return rc;
394
395
0
  const char *result = dispacher_get_retval_name(rc);
396
0
  mutt_debug(LL_DEBUG1, "Handled %s (%d) -> %s\n", opcodes_get_name(op), op, NONULL(result));
397
398
0
  return FR_SUCCESS; // Whatever the outcome, we handled it
399
0
}