/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 | } |