Coverage Report

Created: 2023-06-07 06:15

/src/neomutt/index/index.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file
3
 * Index Window
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 index_index Index Window
25
 *
26
 * The Index Window displays a list of emails to the user.
27
 *
28
 * ## Windows
29
 *
30
 * | Name         | Type         | See Also           |
31
 * | :----------- | :----------- | :----------------- |
32
 * | Index Window | WT_DLG_INDEX | index_window_new() |
33
 *
34
 * **Parent**
35
 * - @ref index_ipanel
36
 *
37
 * **Children**
38
 *
39
 * None.
40
 *
41
 * ## Data
42
 * - #IndexPrivateData
43
 *
44
 * The Index Window stores its data (#IndexPrivateData) in MuttWindow::wdata.
45
 *
46
 * ## Events
47
 *
48
 * Once constructed, it is controlled by the following events:
49
 *
50
 * | Event Type            | Handler                 |
51
 * | :-------------------- | :---------------------- |
52
 * | #NT_ALTERN            | index_altern_observer() |
53
 * | #NT_ATTACH            | index_attach_observer() |
54
 * | #NT_COLOR             | index_color_observer()  |
55
 * | #NT_CONFIG            | index_config_observer() |
56
 * | #NT_MENU              | index_menu_observer()   |
57
 * | #NT_SCORE             | index_score_observer()  |
58
 * | #NT_SUBJRX            | index_subjrx_observer() |
59
 * | #NT_WINDOW            | index_window_observer() |
60
 * | MuttWindow::recalc()  | index_recalc()          |
61
 * | MuttWindow::repaint() | index_repaint()         |
62
 *
63
 * The Index Window does not implement MuttWindow::recalc() or MuttWindow::repaint().
64
 *
65
 * Some other events are handled by the window's children.
66
 */
67
68
#include "config.h"
69
#include <stddef.h>
70
#include <stdbool.h>
71
#include "mutt/lib.h"
72
#include "config/lib.h"
73
#include "email/lib.h"
74
#include "core/lib.h"
75
#include "gui/lib.h"
76
#include "attach/lib.h"
77
#include "color/lib.h"
78
#include "menu/lib.h"
79
#include "postpone/lib.h"
80
#include "alternates.h"
81
#include "globals.h" // IWYU pragma: keep
82
#include "mutt_thread.h"
83
#include "muttlib.h"
84
#include "mview.h"
85
#include "private_data.h"
86
#include "score.h"
87
#include "shared_data.h"
88
#include "subjectrx.h"
89
90
/**
91
 * sort_use_threads_warn - Alert the user to odd $sort settings
92
 */
93
static void sort_use_threads_warn(void)
94
0
{
95
0
  static bool warned = false;
96
0
  if (!warned)
97
0
  {
98
0
    mutt_warning(_("Changing threaded display should prefer $use_threads over $sort"));
99
0
    warned = true;
100
0
    mutt_sleep(0);
101
0
  }
102
0
}
103
104
/**
105
 * config_sort - React to changes to "sort"
106
 * @param sub the config subset that was just updated
107
 * @retval  0 Successfully handled
108
 * @retval -1 Error
109
 */
110
static int config_sort(const struct ConfigSubset *sub)
111
0
{
112
0
  const enum SortType c_sort = cs_subset_sort(sub, "sort");
113
0
  const unsigned char c_use_threads = cs_subset_enum(sub, "use_threads");
114
115
0
  if (((c_sort & SORT_MASK) != SORT_THREADS) || (c_use_threads == UT_UNSET))
116
0
    return 0;
117
118
0
  sort_use_threads_warn();
119
120
  /* Note: changing a config variable here kicks off a second round of
121
   * observers before the first round has completed. Be careful that
122
   * changes made here do not cause an infinite loop of toggling
123
   * adjustments - the early exit above when $sort no longer uses
124
   * SORT_THREADS ends the recursion.
125
   */
126
0
  int rc;
127
0
  if ((c_use_threads == UT_FLAT) ||
128
0
      (!(c_sort & SORT_REVERSE) == (c_use_threads == UT_REVERSE)))
129
0
  {
130
    /* If we were flat or the user wants to change thread
131
     * directions, then leave $sort alone for now and change
132
     * $use_threads to match the desired outcome.  The 2nd-level
133
     * observer for $use_threads will then adjust $sort, and our
134
     * 3rd-level observer for $sort will be a no-op.
135
     */
136
0
    rc = cs_subset_str_native_set(sub, "use_threads",
137
0
                                  (c_sort & SORT_REVERSE) ? UT_REVERSE : UT_THREADS, NULL);
138
0
  }
139
0
  else
140
0
  {
141
    /* We were threaded, and the user still wants the same thread
142
     * direction. Adjust $sort based on $sort_aux, and the 2nd-level
143
     * observer for $sort will be a no-op.
144
     */
145
0
    enum SortType c_sort_aux = cs_subset_sort(sub, "sort_aux");
146
0
    c_sort_aux ^= (c_sort & SORT_REVERSE);
147
0
    rc = cs_subset_str_native_set(sub, "sort", c_sort_aux, NULL);
148
0
  }
149
0
  return (CSR_RESULT(rc) == CSR_SUCCESS) ? 0 : -1;
150
0
}
151
152
/**
153
 * config_use_threads - React to changes to "use_threads"
154
 * @param sub the config subset that was just updated
155
 * @retval  0 Successfully handled
156
 * @retval -1 Error
157
 */
158
static int config_use_threads(const struct ConfigSubset *sub)
159
0
{
160
0
  const enum SortType c_sort = cs_subset_sort(sub, "sort");
161
0
  const unsigned char c_use_threads = cs_subset_enum(sub, "use_threads");
162
163
0
  if (((c_sort & SORT_MASK) != SORT_THREADS) || (c_use_threads == UT_UNSET))
164
0
    return 0;
165
166
0
  sort_use_threads_warn();
167
168
  /* Note: changing a config variable here kicks off a second round of
169
   * observers before the first round has completed. But since we
170
   * aren't setting $sort to threads, the 2nd-level observer will be a
171
   * no-op.
172
   */
173
0
  const enum SortType c_sort_aux = cs_subset_sort(sub, "sort_aux");
174
0
  int rc = cs_subset_str_native_set(sub, "sort", c_sort_aux, NULL);
175
0
  return (CSR_RESULT(rc) == CSR_SUCCESS) ? 0 : -1;
176
0
}
177
178
/**
179
 * index_adjust_sort_threads - Adjust use_threads/sort/sort_aux
180
 * @param sub the config subset that was just updated
181
 */
182
void index_adjust_sort_threads(const struct ConfigSubset *sub)
183
0
{
184
  /* For lack of a better way, we fake a "set use_threads" */
185
0
  config_use_threads(sub);
186
0
}
187
188
/**
189
 * config_reply_regex - React to changes to $reply_regex
190
 * @param mv Mailbox View
191
 * @retval  0 Successfully handled
192
 * @retval -1 Error
193
 */
194
static int config_reply_regex(struct MailboxView *mv)
195
0
{
196
0
  if (!mv || !mv->mailbox)
197
0
    return 0;
198
199
0
  struct Mailbox *m = mv->mailbox;
200
201
0
  regmatch_t pmatch[1];
202
203
0
  const struct Regex *c_reply_regex = cs_subset_regex(NeoMutt->sub, "reply_regex");
204
0
  for (int i = 0; i < m->msg_count; i++)
205
0
  {
206
0
    struct Email *e = m->emails[i];
207
0
    if (!e)
208
0
      break;
209
0
    struct Envelope *env = e->env;
210
0
    if (!env || !env->subject)
211
0
      continue;
212
213
0
    if (mutt_regex_capture(c_reply_regex, env->subject, 1, pmatch))
214
0
    {
215
0
      env->real_subj = env->subject + pmatch[0].rm_eo;
216
0
      if (env->real_subj[0] == '\0')
217
0
        env->real_subj = NULL;
218
0
      continue;
219
0
    }
220
221
0
    env->real_subj = env->subject;
222
0
  }
223
224
0
  OptResortInit = true; /* trigger a redraw of the index */
225
0
  return 0;
226
0
}
227
228
/**
229
 * index_altern_observer - Notification that an 'alternates' command has occurred - Implements ::observer_t - @ingroup observer_api
230
 */
231
static int index_altern_observer(struct NotifyCallback *nc)
232
0
{
233
0
  if (nc->event_type != NT_ALTERN)
234
0
    return 0;
235
0
  if (!nc->global_data)
236
0
    return -1;
237
238
0
  struct MuttWindow *win = nc->global_data;
239
0
  struct MuttWindow *dlg = dialog_find(win);
240
0
  struct IndexSharedData *shared = dlg->wdata;
241
242
0
  mutt_alternates_reset(shared->mailbox_view);
243
0
  mutt_debug(LL_DEBUG5, "alternates done\n");
244
0
  return 0;
245
0
}
246
247
/**
248
 * index_attach_observer - Notification that an 'attachments' command has occurred - Implements ::observer_t - @ingroup observer_api
249
 */
250
static int index_attach_observer(struct NotifyCallback *nc)
251
0
{
252
0
  if (nc->event_type != NT_ATTACH)
253
0
    return 0;
254
0
  if (!nc->global_data)
255
0
    return -1;
256
257
0
  struct MuttWindow *win = nc->global_data;
258
0
  struct MuttWindow *dlg = dialog_find(win);
259
0
  struct IndexSharedData *shared = dlg->wdata;
260
261
0
  mutt_attachments_reset(shared->mailbox_view);
262
0
  mutt_debug(LL_DEBUG5, "attachments done\n");
263
0
  return 0;
264
0
}
265
266
/**
267
 * index_color_observer - Notification that a Color has changed - Implements ::observer_t - @ingroup observer_api
268
 */
269
static int index_color_observer(struct NotifyCallback *nc)
270
0
{
271
0
  if (nc->event_type != NT_COLOR)
272
0
    return 0;
273
0
  if (!nc->global_data || !nc->event_data)
274
0
    return -1;
275
276
0
  struct EventColor *ev_c = nc->event_data;
277
278
0
  const int cid = ev_c->cid;
279
280
  // MT_COLOR_MAX is sent on `uncolor *`
281
0
  if (!((cid == MT_COLOR_INDEX) || (cid == MT_COLOR_INDEX_AUTHOR) ||
282
0
        (cid == MT_COLOR_INDEX_COLLAPSED) || (cid == MT_COLOR_INDEX_DATE) ||
283
0
        (cid == MT_COLOR_INDEX_FLAGS) || (cid == MT_COLOR_INDEX_LABEL) ||
284
0
        (cid == MT_COLOR_INDEX_NUMBER) || (cid == MT_COLOR_INDEX_SIZE) ||
285
0
        (cid == MT_COLOR_INDEX_SUBJECT) || (cid == MT_COLOR_INDEX_TAG) ||
286
0
        (cid == MT_COLOR_INDEX_TAGS) || (cid == MT_COLOR_MAX) ||
287
0
        (cid == MT_COLOR_NORMAL) || (cid == MT_COLOR_TREE)))
288
0
  {
289
    // The changes aren't relevant to the index menu
290
0
    return 0;
291
0
  }
292
293
0
  struct MuttWindow *win = nc->global_data;
294
0
  struct MuttWindow *dlg = dialog_find(win);
295
0
  struct IndexSharedData *shared = dlg->wdata;
296
297
0
  struct Mailbox *m = shared->mailbox;
298
0
  if (!m)
299
0
    return 0;
300
301
  // Force re-caching of index colours
302
0
  for (int i = 0; i < m->msg_count; i++)
303
0
  {
304
0
    struct Email *e = m->emails[i];
305
0
    if (!e)
306
0
      break;
307
0
    e->attr_color = NULL;
308
0
  }
309
310
0
  struct MuttWindow *panel_index = window_find_child(dlg, WT_INDEX);
311
0
  struct IndexPrivateData *priv = panel_index->wdata;
312
0
  struct Menu *menu = priv->menu;
313
0
  menu->redraw = MENU_REDRAW_FULL;
314
0
  win->actions |= WA_REPAINT;
315
0
  mutt_debug(LL_DEBUG5, "color done, request MENU_REDRAW_FULL\n");
316
317
0
  return 0;
318
0
}
319
320
/**
321
 * index_config_observer - Notification that a Config Variable has changed - Implements ::observer_t - @ingroup observer_api
322
 */
323
static int index_config_observer(struct NotifyCallback *nc)
324
0
{
325
0
  if (nc->event_type != NT_CONFIG)
326
0
    return 0;
327
0
  if (!nc->global_data || !nc->event_data)
328
0
    return -1;
329
330
0
  struct EventConfig *ev_c = nc->event_data;
331
0
  if (!ev_c->name || !ev_c->he)
332
0
    return 0;
333
334
0
  struct MuttWindow *win = nc->global_data;
335
336
0
  const struct ConfigDef *cdef = ev_c->he->data;
337
0
  ConfigRedrawFlags flags = cdef->type & R_REDRAW_MASK;
338
339
0
  if (flags & R_RESORT_SUB)
340
0
    OptSortSubthreads = true;
341
0
  if (flags & R_RESORT)
342
0
    OptNeedResort = true;
343
0
  if (flags & R_RESORT_INIT)
344
0
    OptResortInit = true;
345
0
  if (!(flags & R_INDEX))
346
0
    return 0;
347
348
0
  if (mutt_str_equal(ev_c->name, "reply_regex"))
349
0
  {
350
0
    struct MuttWindow *dlg = dialog_find(win);
351
0
    struct IndexSharedData *shared = dlg->wdata;
352
0
    config_reply_regex(shared->mailbox_view);
353
0
    mutt_debug(LL_DEBUG5, "config done\n");
354
0
  }
355
0
  else if (mutt_str_equal(ev_c->name, "sort"))
356
0
  {
357
0
    config_sort(ev_c->sub);
358
0
    mutt_debug(LL_DEBUG5, "config done\n");
359
0
  }
360
0
  else if (mutt_str_equal(ev_c->name, "use_threads"))
361
0
  {
362
0
    config_use_threads(ev_c->sub);
363
0
    mutt_debug(LL_DEBUG5, "config done\n");
364
0
  }
365
366
0
  menu_queue_redraw(win->wdata, MENU_REDRAW_INDEX);
367
0
  return 0;
368
0
}
369
370
/**
371
 * index_global_observer - Notification that a Global event occurred - Implements ::observer_t - @ingroup observer_api
372
 */
373
static int index_global_observer(struct NotifyCallback *nc)
374
0
{
375
0
  if (nc->event_type != NT_GLOBAL)
376
0
    return 0;
377
0
  if (!nc->global_data)
378
0
    return -1;
379
0
  if (nc->event_subtype != NT_GLOBAL_COMMAND)
380
0
    return 0;
381
382
0
  struct MuttWindow *win = nc->global_data;
383
0
  struct MuttWindow *dlg = dialog_find(win);
384
0
  if (!dlg)
385
0
    return 0;
386
387
0
  struct IndexSharedData *shared = dlg->wdata;
388
0
  mutt_check_rescore(shared->mailbox);
389
390
0
  return 0;
391
0
}
392
393
/**
394
 * index_index_observer - Notification that the Index has changed - Implements ::observer_t - @ingroup observer_api
395
 */
396
static int index_index_observer(struct NotifyCallback *nc)
397
0
{
398
0
  if (!nc->global_data)
399
0
    return -1;
400
401
0
  struct MuttWindow *win = nc->global_data;
402
0
  win->actions |= WA_RECALC;
403
404
0
  struct Menu *menu = win->wdata;
405
0
  menu_queue_redraw(menu, MENU_REDRAW_INDEX);
406
0
  mutt_debug(LL_DEBUG5, "index done, request WA_RECALC\n");
407
408
0
  struct IndexPrivateData *priv = menu->mdata;
409
0
  struct IndexSharedData *shared = priv->shared;
410
0
  if (shared && shared->mailbox)
411
0
    menu->max = shared->mailbox->vcount;
412
0
  else
413
0
    menu->max = 0;
414
415
0
  return 0;
416
0
}
417
418
/**
419
 * index_menu_observer - Notification that the Menu has changed - Implements ::observer_t - @ingroup observer_api
420
 */
421
static int index_menu_observer(struct NotifyCallback *nc)
422
0
{
423
0
  if (nc->event_type != NT_MENU)
424
0
    return 0;
425
0
  if (!nc->global_data)
426
0
    return -1;
427
428
0
  struct MuttWindow *win = nc->global_data;
429
0
  struct MuttWindow *dlg = dialog_find(win);
430
0
  struct IndexSharedData *shared = dlg->wdata;
431
0
  struct Menu *menu = win->wdata;
432
433
0
  const int index = menu_get_index(menu);
434
0
  struct Email *e = mutt_get_virt_email(shared->mailbox, index);
435
0
  index_shared_data_set_email(shared, e);
436
437
0
  return 0;
438
0
}
439
440
/**
441
 * index_score_observer - Notification that a 'score' command has occurred - Implements ::observer_t - @ingroup observer_api
442
 */
443
static int index_score_observer(struct NotifyCallback *nc)
444
0
{
445
0
  if (nc->event_type != NT_SCORE)
446
0
    return 0;
447
0
  if (!nc->global_data)
448
0
    return -1;
449
450
0
  struct MuttWindow *win = nc->global_data;
451
0
  struct MuttWindow *dlg = dialog_find(win);
452
0
  struct IndexSharedData *shared = dlg->wdata;
453
454
0
  struct Mailbox *m = shared->mailbox;
455
0
  if (!m)
456
0
    return 0;
457
458
0
  for (int i = 0; i < m->msg_count; i++)
459
0
  {
460
0
    struct Email *e = m->emails[i];
461
0
    if (!e)
462
0
      break;
463
464
0
    mutt_score_message(m, e, true);
465
0
    e->attr_color = NULL; // Force recalc of colour
466
0
  }
467
468
0
  mutt_debug(LL_DEBUG5, "score done\n");
469
0
  return 0;
470
0
}
471
472
/**
473
 * index_subjrx_observer - Notification that a 'subjectrx' command has occurred - Implements ::observer_t - @ingroup observer_api
474
 */
475
static int index_subjrx_observer(struct NotifyCallback *nc)
476
0
{
477
0
  if (nc->event_type != NT_SUBJRX)
478
0
    return 0;
479
0
  if (!nc->global_data)
480
0
    return -1;
481
482
0
  struct MuttWindow *win = nc->global_data;
483
0
  struct MuttWindow *dlg = dialog_find(win);
484
0
  struct IndexSharedData *shared = dlg->wdata;
485
486
0
  subjrx_clear_mods(shared->mailbox_view);
487
0
  mutt_debug(LL_DEBUG5, "subjectrx done\n");
488
0
  return 0;
489
0
}
490
491
/**
492
 * index_window_observer - Notification that a Window has changed - Implements ::observer_t - @ingroup observer_api
493
 */
494
static int index_window_observer(struct NotifyCallback *nc)
495
0
{
496
0
  if (nc->event_type != NT_WINDOW)
497
0
    return 0;
498
0
  if (!nc->global_data || !nc->event_data)
499
0
    return -1;
500
501
0
  struct MuttWindow *win = nc->global_data;
502
0
  struct Menu *menu = win->wdata;
503
0
  if (nc->event_subtype != NT_WINDOW_DELETE)
504
0
  {
505
0
    if (nc->event_subtype != NT_WINDOW_FOCUS)
506
0
      menu_queue_redraw(menu, MENU_REDRAW_FULL | MENU_REDRAW_INDEX);
507
0
    return 0;
508
0
  }
509
510
0
  struct EventWindow *ev_w = nc->event_data;
511
0
  if (ev_w->win != win)
512
0
    return 0;
513
514
0
  struct IndexPrivateData *priv = menu->mdata;
515
516
0
  notify_observer_remove(NeoMutt->notify, index_altern_observer, win);
517
0
  notify_observer_remove(NeoMutt->notify, index_attach_observer, win);
518
0
  notify_observer_remove(NeoMutt->notify, index_color_observer, win);
519
0
  notify_observer_remove(NeoMutt->notify, index_config_observer, win);
520
0
  notify_observer_remove(NeoMutt->notify, index_global_observer, win);
521
0
  notify_observer_remove(priv->shared->notify, index_index_observer, win);
522
0
  notify_observer_remove(menu->notify, index_menu_observer, win);
523
0
  notify_observer_remove(NeoMutt->notify, index_score_observer, win);
524
0
  notify_observer_remove(NeoMutt->notify, index_subjrx_observer, win);
525
0
  notify_observer_remove(win->notify, index_window_observer, win);
526
527
0
  mutt_debug(LL_DEBUG5, "window delete done\n");
528
0
  return 0;
529
0
}
530
531
/**
532
 * index_recalc - Recalculate the Index display - Implements MuttWindow::recalc() - @ingroup window_recalc
533
 */
534
static int index_recalc(struct MuttWindow *win)
535
0
{
536
0
  win->actions |= WA_REPAINT;
537
0
  mutt_debug(LL_DEBUG5, "recalc done, request WA_REPAINT\n");
538
0
  return 0;
539
0
}
540
541
/**
542
 * index_repaint - Repaint the Index display - Implements MuttWindow::repaint() - @ingroup window_repaint
543
 */
544
static int index_repaint(struct MuttWindow *win)
545
0
{
546
0
  struct Menu *menu = win->wdata;
547
548
0
  if (menu->redraw & MENU_REDRAW_FULL)
549
0
    menu_redraw_full(menu);
550
551
  // If we don't have the focus, then this is a mini-Index ($pager_index_lines)
552
0
  if (!window_is_focused(menu->win))
553
0
  {
554
0
    int indicator = menu->page_len / 3;
555
556
    /* some fudge to work out whereabouts the indicator should go */
557
0
    const int index = menu_get_index(menu);
558
0
    if ((index - indicator) < 0)
559
0
      menu->top = 0;
560
0
    else if ((menu->max - index) < (menu->page_len - indicator))
561
0
      menu->top = menu->max - menu->page_len;
562
0
    else
563
0
      menu->top = index - indicator;
564
0
  }
565
0
  menu_adjust(menu);
566
0
  menu->redraw = MENU_REDRAW_INDEX;
567
568
0
  struct IndexPrivateData *priv = menu->mdata;
569
0
  struct IndexSharedData *shared = priv->shared;
570
0
  struct Mailbox *m = shared->mailbox;
571
0
  const int index = menu_get_index(menu);
572
0
  if (m && m->emails && (index < m->vcount))
573
0
  {
574
0
    if (menu->redraw & MENU_REDRAW_INDEX)
575
0
    {
576
0
      menu_redraw_index(menu);
577
0
    }
578
0
    else if (menu->redraw & MENU_REDRAW_MOTION)
579
0
    {
580
0
      menu_redraw_motion(menu);
581
0
    }
582
0
    else if (menu->redraw & MENU_REDRAW_CURRENT)
583
0
    {
584
0
      menu_redraw_current(menu);
585
0
    }
586
0
  }
587
588
0
  menu->redraw = MENU_REDRAW_NO_FLAGS;
589
0
  mutt_debug(LL_DEBUG5, "repaint done\n");
590
0
  return 0;
591
0
}
592
593
/**
594
 * index_window_new - Create a new Index Window (list of Emails)
595
 * @param priv Private Index data
596
 * @retval ptr New Window
597
 */
598
struct MuttWindow *index_window_new(struct IndexPrivateData *priv)
599
0
{
600
0
  struct MuttWindow *win = menu_window_new(MENU_INDEX, NeoMutt->sub);
601
0
  win->recalc = index_recalc;
602
0
  win->repaint = index_repaint;
603
604
0
  struct Menu *menu = win->wdata;
605
0
  menu->mdata = priv;
606
0
  menu->mdata_free = NULL; // Menu doesn't own the data
607
0
  priv->menu = menu;
608
609
0
  notify_observer_add(NeoMutt->notify, NT_ALTERN, index_altern_observer, win);
610
0
  notify_observer_add(NeoMutt->notify, NT_ATTACH, index_attach_observer, win);
611
0
  notify_observer_add(NeoMutt->notify, NT_COLOR, index_color_observer, win);
612
0
  notify_observer_add(NeoMutt->notify, NT_CONFIG, index_config_observer, win);
613
0
  notify_observer_add(NeoMutt->notify, NT_GLOBAL, index_global_observer, win);
614
0
  notify_observer_add(priv->shared->notify, NT_ALL, index_index_observer, win);
615
0
  notify_observer_add(menu->notify, NT_MENU, index_menu_observer, win);
616
0
  notify_observer_add(NeoMutt->notify, NT_SCORE, index_score_observer, win);
617
0
  notify_observer_add(NeoMutt->notify, NT_SUBJRX, index_subjrx_observer, win);
618
0
  notify_observer_add(win->notify, NT_WINDOW, index_window_observer, win);
619
620
0
  return win;
621
0
}
622
623
/**
624
 * get_current_mailbox_view - Get the current Mailbox view
625
 * @retval ptr Current Mailbox view
626
 *
627
 * Search for the last (most recent) dialog that has an Index.
628
 * Then return the Mailbox from its shared data.
629
 */
630
struct MailboxView *get_current_mailbox_view(void)
631
0
{
632
0
  if (!AllDialogsWindow)
633
0
    return NULL;
634
635
0
  struct MuttWindow *np = NULL;
636
0
  TAILQ_FOREACH_REVERSE(np, &AllDialogsWindow->children, MuttWindowList, entries)
637
0
  {
638
0
    struct MuttWindow *win = window_find_child(np, WT_DLG_INDEX);
639
0
    if (win)
640
0
    {
641
0
      struct IndexSharedData *shared = win->wdata;
642
0
      return shared->mailbox_view;
643
0
    }
644
645
0
    win = window_find_child(np, WT_DLG_POSTPONE);
646
0
    if (win)
647
0
    {
648
0
      return postponed_get_mailbox_view(win);
649
0
    }
650
0
  }
651
652
0
  return NULL;
653
0
}
654
655
/**
656
 * get_current_mailbox - Get the current Mailbox
657
 * @retval ptr Current Mailbox
658
 *
659
 * Search for the last (most recent) dialog that has an Index.
660
 * Then return the Mailbox from its shared data.
661
 */
662
struct Mailbox *get_current_mailbox(void)
663
0
{
664
0
  struct MailboxView *mv = get_current_mailbox_view();
665
0
  if (mv)
666
0
    return mv->mailbox;
667
668
0
  return NULL;
669
0
}
670
671
/**
672
 * get_current_menu - Get the current Menu
673
 * @retval ptr Current Menu
674
 *
675
 * Search for the last (most recent) dialog that has an Index.
676
 * Then return the Menu from its private data.
677
 */
678
struct Menu *get_current_menu(void)
679
0
{
680
0
  if (!AllDialogsWindow)
681
0
    return NULL;
682
683
0
  struct MuttWindow *np = NULL;
684
0
  TAILQ_FOREACH_REVERSE(np, &AllDialogsWindow->children, MuttWindowList, entries)
685
0
  {
686
0
    struct MuttWindow *dlg = window_find_child(np, WT_DLG_INDEX);
687
0
    if (dlg)
688
0
    {
689
0
      struct MuttWindow *panel_index = window_find_child(dlg, WT_INDEX);
690
0
      struct IndexPrivateData *priv = panel_index->wdata;
691
0
      return priv->menu;
692
0
    }
693
0
  }
694
695
0
  return NULL;
696
0
}