1 /**
2  * @file
3  * Window management
4  *
5  * @authors
6  * Copyright (C) 2018-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 gui_window Window management
25  *
26  * Window management
27  */
28 
29 #include "config.h"
30 #include <stdarg.h>
31 #include <string.h>
32 #include "mutt/lib.h"
33 #include "config/lib.h"
34 #include "mutt_window.h"
35 #include "curs_lib.h"
36 #include "mutt_curses.h"
37 #include "options.h"
38 #include "reflow.h"
39 #include "rootwin.h"
40 #ifdef USE_DEBUG_WINDOW
41 #include "debug/lib.h"
42 #endif
43 
44 /// Lookups for Window Names
45 static const struct Mapping WindowNames[] = {
46   // clang-format off
47   { "WT_ALL_DIALOGS",     WT_ALL_DIALOGS },
48   { "WT_CONTAINER",       WT_CONTAINER },
49   { "WT_CUSTOM",          WT_CUSTOM },
50   { "WT_DLG_ALIAS",       WT_DLG_ALIAS },
51   { "WT_DLG_ATTACH",      WT_DLG_ATTACH },
52   { "WT_DLG_AUTOCRYPT",   WT_DLG_AUTOCRYPT },
53   { "WT_DLG_BROWSER",     WT_DLG_BROWSER },
54   { "WT_DLG_CERTIFICATE", WT_DLG_CERTIFICATE },
55   { "WT_DLG_COMPOSE",     WT_DLG_COMPOSE },
56   { "WT_DLG_CRYPT_GPGME", WT_DLG_CRYPT_GPGME },
57   { "WT_DLG_DO_PAGER",    WT_DLG_DO_PAGER },
58   { "WT_DLG_HISTORY",     WT_DLG_HISTORY },
59   { "WT_DLG_INDEX",       WT_DLG_INDEX },
60   { "WT_DLG_PGP",         WT_DLG_PGP },
61   { "WT_DLG_POSTPONE",    WT_DLG_POSTPONE },
62   { "WT_DLG_QUERY",       WT_DLG_QUERY },
63   { "WT_DLG_REMAILER",    WT_DLG_REMAILER },
64   { "WT_DLG_SMIME",       WT_DLG_SMIME },
65   { "WT_HELP_BAR",        WT_HELP_BAR },
66   { "WT_INDEX",           WT_INDEX },
67   { "WT_MENU",            WT_MENU },
68   { "WT_MESSAGE",         WT_MESSAGE },
69   { "WT_PAGER",           WT_PAGER },
70   { "WT_ROOT",            WT_ROOT },
71   { "WT_SIDEBAR",         WT_SIDEBAR },
72   { "WT_STATUS_BAR",      WT_STATUS_BAR },
73   { NULL, 0 },
74   // clang-format on
75 };
76 
77 /**
78  * window_was_visible - Was the Window visible?
79  * @param win Window
80  * @retval true The Window was visible
81  *
82  * Using the `WindowState old`, check if a Window used to be visible.
83  * For a Window to be visible, *it* must have been visible and its parent and
84  * grandparent, etc.
85  */
window_was_visible(struct MuttWindow * win)86 static bool window_was_visible(struct MuttWindow *win)
87 {
88   if (!win)
89     return false;
90 
91   for (; win; win = win->parent)
92   {
93     if (!win->old.visible)
94       return false;
95   }
96 
97   return true;
98 }
99 
100 /**
101  * window_notify - Notify observers of changes to a Window
102  * @param win Window
103  */
window_notify(struct MuttWindow * win)104 static void window_notify(struct MuttWindow *win)
105 {
106   if (!win->notify)
107     return;
108 
109   const struct WindowState *old = &win->old;
110   const struct WindowState *state = &win->state;
111   WindowNotifyFlags flags = WN_NO_FLAGS;
112 
113   const bool was_visible = window_was_visible(win);
114   const bool is_visible = mutt_window_is_visible(win);
115   if (was_visible != is_visible)
116     flags |= is_visible ? WN_VISIBLE : WN_HIDDEN;
117 
118   if ((state->row_offset != old->row_offset) || (state->col_offset != old->col_offset))
119     flags |= WN_MOVED;
120 
121   if (state->rows > old->rows)
122     flags |= WN_TALLER;
123   else if (state->rows < old->rows)
124     flags |= WN_SHORTER;
125 
126   if (state->cols > old->cols)
127     flags |= WN_WIDER;
128   else if (state->cols < old->cols)
129     flags |= WN_NARROWER;
130 
131   if (flags == WN_NO_FLAGS)
132     return;
133 
134   mutt_debug(LL_NOTIFY, "NT_WINDOW_STATE: %s, %p\n", mutt_window_win_name(win), win);
135   struct EventWindow ev_w = { win, flags };
136   notify_send(win->notify, NT_WINDOW, NT_WINDOW_STATE, &ev_w);
137 }
138 
139 /**
140  * window_notify_all - Notify observers of changes to a Window and its children
141  * @param win Window
142  */
window_notify_all(struct MuttWindow * win)143 void window_notify_all(struct MuttWindow *win)
144 {
145   if (!win)
146     win = RootWindow;
147 
148   window_notify(win);
149 
150   struct MuttWindow *np = NULL;
151   TAILQ_FOREACH(np, &win->children, entries)
152   {
153     window_notify_all(np);
154   }
155   win->old = win->state;
156 }
157 
158 /**
159  * window_set_visible - Set a Window visible or hidden
160  * @param win     Window
161  * @param visible If true, make Window visible, otherwise hidden
162  */
window_set_visible(struct MuttWindow * win,bool visible)163 void window_set_visible(struct MuttWindow *win, bool visible)
164 {
165   if (!win)
166     win = RootWindow;
167 
168   win->state.visible = visible;
169 }
170 
171 /**
172  * mutt_window_new - Create a new Window
173  * @param type   Window type, e.g. #WT_ROOT
174  * @param orient Window orientation, e.g. #MUTT_WIN_ORIENT_VERTICAL
175  * @param size   Window size, e.g. #MUTT_WIN_SIZE_MAXIMISE
176  * @param cols   Initial number of columns to allocate, can be #MUTT_WIN_SIZE_UNLIMITED
177  * @param rows   Initial number of rows to allocate, can be #MUTT_WIN_SIZE_UNLIMITED
178  * @retval ptr New Window
179  */
mutt_window_new(enum WindowType type,enum MuttWindowOrientation orient,enum MuttWindowSize size,int cols,int rows)180 struct MuttWindow *mutt_window_new(enum WindowType type, enum MuttWindowOrientation orient,
181                                    enum MuttWindowSize size, int cols, int rows)
182 {
183   struct MuttWindow *win = mutt_mem_calloc(1, sizeof(struct MuttWindow));
184 
185   win->type = type;
186   win->orient = orient;
187   win->size = size;
188   win->req_rows = rows;
189   win->req_cols = cols;
190   win->state.visible = true;
191   win->notify = notify_new();
192   TAILQ_INIT(&win->children);
193   return win;
194 }
195 
196 /**
197  * mutt_window_free - Free a Window and its children
198  * @param ptr Window to free
199  */
mutt_window_free(struct MuttWindow ** ptr)200 void mutt_window_free(struct MuttWindow **ptr)
201 {
202   if (!ptr || !*ptr)
203     return;
204 
205   struct MuttWindow *win = *ptr;
206 
207   if (win->parent && (win->parent->focus == win))
208     win->parent->focus = NULL;
209 
210   mutt_debug(LL_NOTIFY, "NT_WINDOW_DELETE: %s, %p\n", mutt_window_win_name(win), win);
211   struct EventWindow ev_w = { win, WN_NO_FLAGS };
212   notify_send(win->notify, NT_WINDOW, NT_WINDOW_DELETE, &ev_w);
213 
214   mutt_winlist_free(&win->children);
215 
216   if (win->wdata_free && win->wdata)
217     win->wdata_free(win, &win->wdata); // Custom function to free private data
218 
219   notify_free(&win->notify);
220 
221   FREE(ptr);
222 }
223 
224 /**
225  * mutt_window_clearline - Clear a row of a Window
226  * @param win Window
227  * @param row Row to clear
228  */
mutt_window_clearline(struct MuttWindow * win,int row)229 void mutt_window_clearline(struct MuttWindow *win, int row)
230 {
231   mutt_window_move(win, 0, row);
232   mutt_window_clrtoeol(win);
233 }
234 
235 /**
236  * mutt_window_clrtoeol - Clear to the end of the line
237  * @param win Window
238  *
239  * @note Assumes the cursor has already been positioned within the window.
240  */
mutt_window_clrtoeol(struct MuttWindow * win)241 void mutt_window_clrtoeol(struct MuttWindow *win)
242 {
243   if (!win || !stdscr)
244     return;
245 
246   if ((win->state.col_offset + win->state.cols) == COLS)
247     clrtoeol();
248   else
249   {
250     int row = 0;
251     int col = 0;
252     getyx(stdscr, row, col);
253     int curcol = col;
254     while (curcol < (win->state.col_offset + win->state.cols))
255     {
256       addch(' ');
257       curcol++;
258     }
259     move(row, col);
260   }
261 }
262 
263 /**
264  * mutt_window_get_coords - Get the cursor position in the Window
265  * @param[in]  win Window
266  * @param[out] col Column in Window
267  * @param[out] row Row in Window
268  *
269  * Assumes the current position is inside the window.  Otherwise it will
270  * happily return negative or values outside the window boundaries
271  */
mutt_window_get_coords(struct MuttWindow * win,int * col,int * row)272 void mutt_window_get_coords(struct MuttWindow *win, int *col, int *row)
273 {
274   int x = 0;
275   int y = 0;
276 
277   getyx(stdscr, y, x);
278   if (col)
279     *col = x - win->state.col_offset;
280   if (row)
281     *row = y - win->state.row_offset;
282 }
283 
284 /**
285  * mutt_window_move - Move the cursor in a Window
286  * @param win Window
287  * @param col Column to move to
288  * @param row Row to move to
289  * @retval OK  Success
290  * @retval ERR Error
291  */
mutt_window_move(struct MuttWindow * win,int col,int row)292 int mutt_window_move(struct MuttWindow *win, int col, int row)
293 {
294   return move(win->state.row_offset + row, win->state.col_offset + col);
295 }
296 
297 /**
298  * mutt_window_mvaddstr - Move the cursor and write a fixed string to a Window
299  * @param win Window to write to
300  * @param col Column to move to
301  * @param row Row to move to
302  * @param str String to write
303  * @retval OK  Success
304  * @retval ERR Error
305  */
mutt_window_mvaddstr(struct MuttWindow * win,int col,int row,const char * str)306 int mutt_window_mvaddstr(struct MuttWindow *win, int col, int row, const char *str)
307 {
308   return mvaddstr(win->state.row_offset + row, win->state.col_offset + col, str);
309 }
310 
311 /**
312  * mutt_window_mvprintw - Move the cursor and write a formatted string to a Window
313  * @param win Window to write to
314  * @param col Column to move to
315  * @param row Row to move to
316  * @param fmt printf format string
317  * @param ... printf arguments
318  * @retval num Success, characters written
319  * @retval ERR Error, move failed
320  */
mutt_window_mvprintw(struct MuttWindow * win,int col,int row,const char * fmt,...)321 int mutt_window_mvprintw(struct MuttWindow *win, int col, int row, const char *fmt, ...)
322 {
323   int rc = mutt_window_move(win, col, row);
324   if (rc == ERR)
325     return rc;
326 
327   va_list ap;
328   va_start(ap, fmt);
329   rc = vw_printw(stdscr, fmt, ap);
330   va_end(ap);
331 
332   return rc;
333 }
334 
335 /**
336  * mutt_window_reflow - Resize a Window and its children
337  * @param win Window to resize
338  */
mutt_window_reflow(struct MuttWindow * win)339 void mutt_window_reflow(struct MuttWindow *win)
340 {
341   if (OptNoCurses)
342     return;
343 
344   if (!win)
345     win = RootWindow;
346 
347   mutt_debug(LL_DEBUG2, "entering\n");
348   window_reflow(win);
349   window_notify_all(win);
350 
351 #ifdef USE_DEBUG_WINDOW
352   debug_win_dump();
353 #endif
354 }
355 
356 /**
357  * mutt_window_wrap_cols - Calculate the wrap column for a given screen width
358  * @param width Screen width
359  * @param wrap  Wrap config
360  * @retval num Column that text should be wrapped at
361  *
362  * The wrap variable can be negative, meaning there should be a right margin.
363  */
mutt_window_wrap_cols(int width,short wrap)364 int mutt_window_wrap_cols(int width, short wrap)
365 {
366   if (wrap < 0)
367     return (width > -wrap) ? (width + wrap) : width;
368   if (wrap)
369     return (wrap < width) ? wrap : width;
370   return width;
371 }
372 
373 /**
374  * mutt_window_addch - Write one character to a Window
375  * @param win Window
376  * @param ch  Character to write
377  * @retval  0 Success
378  * @retval -1 Error
379  */
mutt_window_addch(struct MuttWindow * win,int ch)380 int mutt_window_addch(struct MuttWindow *win, int ch)
381 {
382   return addch(ch);
383 }
384 
385 /**
386  * mutt_window_addnstr - Write a partial string to a Window
387  * @param win Window
388  * @param str String
389  * @param num Maximum number of characters to write
390  * @retval  0 Success
391  * @retval -1 Error
392  */
mutt_window_addnstr(struct MuttWindow * win,const char * str,int num)393 int mutt_window_addnstr(struct MuttWindow *win, const char *str, int num)
394 {
395   if (!str)
396     return -1;
397 
398   return addnstr(str, num);
399 }
400 
401 /**
402  * mutt_window_addstr - Write a string to a Window
403  * @param win Window
404  * @param str String
405  * @retval  0 Success
406  * @retval -1 Error
407  */
mutt_window_addstr(struct MuttWindow * win,const char * str)408 int mutt_window_addstr(struct MuttWindow *win, const char *str)
409 {
410   if (!str)
411     return -1;
412 
413   return addstr(str);
414 }
415 
416 /**
417  * mutt_window_printf - Write a formatted string to a Window
418  * @param win Window
419  * @param fmt Format string
420  * @param ... Arguments
421  * @retval num Number of characters written
422  */
mutt_window_printf(struct MuttWindow * win,const char * fmt,...)423 int mutt_window_printf(struct MuttWindow *win, const char *fmt, ...)
424 {
425   va_list ap;
426   va_start(ap, fmt);
427   int rc = vw_printw(stdscr, fmt, ap);
428   va_end(ap);
429 
430   return rc;
431 }
432 
433 /**
434  * mutt_window_add_child - Add a child to Window
435  * @param parent Window to add to
436  * @param child  Window to add
437  */
mutt_window_add_child(struct MuttWindow * parent,struct MuttWindow * child)438 void mutt_window_add_child(struct MuttWindow *parent, struct MuttWindow *child)
439 {
440   if (!parent || !child)
441     return;
442 
443   TAILQ_INSERT_TAIL(&parent->children, child, entries);
444   child->parent = parent;
445 
446   notify_set_parent(child->notify, parent->notify);
447 
448   mutt_debug(LL_NOTIFY, "NT_WINDOW_NEW: %s, %p\n", mutt_window_win_name(child), child);
449   struct EventWindow ev_w = { child, WN_NO_FLAGS };
450   notify_send(child->notify, NT_WINDOW, NT_WINDOW_ADD, &ev_w);
451 }
452 
453 /**
454  * mutt_window_remove_child - Remove a child from a Window
455  * @param parent Window to remove from
456  * @param child  Window to remove
457  */
mutt_window_remove_child(struct MuttWindow * parent,struct MuttWindow * child)458 struct MuttWindow *mutt_window_remove_child(struct MuttWindow *parent, struct MuttWindow *child)
459 {
460   if (!parent || !child)
461     return NULL;
462 
463   // A notification will be sent when the Window is freed
464   TAILQ_REMOVE(&parent->children, child, entries);
465   child->parent = NULL;
466 
467   notify_set_parent(child->notify, NULL);
468 
469   return child;
470 }
471 
472 /**
473  * mutt_winlist_free - Free a tree of Windows
474  * @param head WindowList to free
475  */
mutt_winlist_free(struct MuttWindowList * head)476 void mutt_winlist_free(struct MuttWindowList *head)
477 {
478   if (!head)
479     return;
480 
481   struct MuttWindow *np = NULL;
482   struct MuttWindow *tmp = NULL;
483   TAILQ_FOREACH_SAFE(np, head, entries, tmp)
484   {
485     TAILQ_REMOVE(head, np, entries);
486     mutt_winlist_free(&np->children);
487     mutt_window_free(&np);
488   }
489 }
490 
491 /**
492  * mutt_window_is_visible - Is the Window visible?
493  * @param win Window
494  * @retval true The Window is visible
495  *
496  * For a Window to be visible, *it* must be visible and its parent and
497  * grandparent, etc.
498  */
mutt_window_is_visible(struct MuttWindow * win)499 bool mutt_window_is_visible(struct MuttWindow *win)
500 {
501   if (!win)
502     return false;
503 
504   for (; win; win = win->parent)
505   {
506     if (!win->state.visible)
507       return false;
508   }
509 
510   return true;
511 }
512 
513 /**
514  * window_find_child - Recursively find a child Window of a given type
515  * @param win  Window to start searching
516  * @param type Window type to find, e.g. #WT_STATUS_BAR
517  * @retval ptr  Matching Window
518  * @retval NULL No match
519  */
window_find_child(struct MuttWindow * win,enum WindowType type)520 struct MuttWindow *window_find_child(struct MuttWindow *win, enum WindowType type)
521 {
522   if (!win)
523     return NULL;
524   if (win->type == type)
525     return win;
526 
527   struct MuttWindow *np = NULL;
528   struct MuttWindow *match = NULL;
529   TAILQ_FOREACH(np, &win->children, entries)
530   {
531     match = window_find_child(np, type);
532     if (match)
533       return match;
534   }
535 
536   return NULL;
537 }
538 
539 /**
540  * window_find_parent - Find a (grand-)parent of a Window by type
541  * @param win  Window
542  * @param type Window type, e.g. #WT_DLG_INDEX
543  * @retval ptr Window
544  */
window_find_parent(struct MuttWindow * win,enum WindowType type)545 struct MuttWindow *window_find_parent(struct MuttWindow *win, enum WindowType type)
546 {
547   for (; win; win = win->parent)
548   {
549     if (win->type == type)
550       return win;
551   }
552 
553   return NULL;
554 }
555 
556 /**
557  * window_recalc - Recalculate a tree of Windows
558  * @param win Window to start at
559  */
window_recalc(struct MuttWindow * win)560 static void window_recalc(struct MuttWindow *win)
561 {
562   if (!win || !win->state.visible)
563     return;
564 
565   if (win->recalc && (win->actions & WA_RECALC))
566     win->recalc(win);
567   win->actions &= ~WA_RECALC;
568 
569   struct MuttWindow *np = NULL;
570   TAILQ_FOREACH(np, &win->children, entries)
571   {
572     window_recalc(np);
573   }
574 }
575 
576 /**
577  * window_repaint - Repaint a tree of Windows
578  * @param win   Window to start at
579  */
window_repaint(struct MuttWindow * win)580 static void window_repaint(struct MuttWindow *win)
581 {
582   if (!win || !win->state.visible)
583     return;
584 
585   if (win->repaint && (win->actions & WA_REPAINT))
586     win->repaint(win);
587   win->actions &= ~WA_REPAINT;
588 
589   struct MuttWindow *np = NULL;
590   TAILQ_FOREACH(np, &win->children, entries)
591   {
592     window_repaint(np);
593   }
594 }
595 
596 /**
597  * window_redraw - Reflow, recalc and repaint a tree of Windows
598  * @param win Window to start at
599  *
600  * @note If win is NULL, all windows will be redrawn
601  */
window_redraw(struct MuttWindow * win)602 void window_redraw(struct MuttWindow *win)
603 {
604   if (!win)
605     win = RootWindow;
606 
607   window_reflow(win);
608   window_notify_all(win);
609 
610   window_recalc(win);
611   window_repaint(win);
612   mutt_refresh();
613 }
614 
615 /**
616  * window_is_focused - Does the given Window have the focus?
617  * @param win Window to check
618  * @retval true Window has focus
619  */
window_is_focused(struct MuttWindow * win)620 bool window_is_focused(struct MuttWindow *win)
621 {
622   if (!win)
623     return false;
624 
625   struct MuttWindow *win_focus = window_get_focus();
626 
627   return (win_focus == win);
628 }
629 
630 /**
631  * window_get_focus - Get the currently focused Window
632  * @retval ptr Window with focus
633  */
window_get_focus(void)634 struct MuttWindow *window_get_focus(void)
635 {
636   struct MuttWindow *win = RootWindow;
637 
638   while (win && win->focus)
639     win = win->focus;
640 
641   return win;
642 }
643 
644 /**
645  * window_set_focus - Set the Window focus
646  * @param win Window to focus
647  * @retval ptr  Old focused Window
648  * @retval NULL Error, or focus not changed
649  */
window_set_focus(struct MuttWindow * win)650 struct MuttWindow *window_set_focus(struct MuttWindow *win)
651 {
652   if (!win)
653     return NULL;
654 
655   struct MuttWindow *old_focus = window_get_focus();
656 
657   struct MuttWindow *parent = win->parent;
658   struct MuttWindow *child = win;
659 
660   // Set the chain of focus, all the way to the root
661   for (; parent; child = parent, parent = parent->parent)
662     parent->focus = child;
663 
664   // Find the most focused Window
665   while (win && win->focus)
666     win = win->focus;
667 
668   if (win == old_focus)
669     return NULL;
670 
671   mutt_debug(LL_NOTIFY, "NT_WINDOW_FOCUS: %s, %p\n", mutt_window_win_name(win), win);
672   struct EventWindow ev_w = { win, WN_NO_FLAGS };
673   notify_send(win->notify, NT_WINDOW, NT_WINDOW_FOCUS, &ev_w);
674 #ifdef USE_DEBUG_WINDOW
675   debug_win_dump();
676 #endif
677   return old_focus;
678 }
679 
680 /**
681  * mutt_window_clear - Clear a Window
682  * @param win Window
683  *
684  * If the Window isn't visible, it won't be cleared.
685  */
mutt_window_clear(struct MuttWindow * win)686 void mutt_window_clear(struct MuttWindow *win)
687 {
688   if (!mutt_window_is_visible(win))
689     return;
690 
691   for (int i = 0; i < win->state.rows; i++)
692     mutt_window_clearline(win, i);
693 }
694 
695 /**
696  * mutt_window_win_name - Get the name of a Window
697  * @param win Window
698  * @retval ptr  String describing Window
699  * @retval NULL Error, or unknown
700  */
mutt_window_win_name(const struct MuttWindow * win)701 const char *mutt_window_win_name(const struct MuttWindow *win)
702 {
703   if (!win)
704     return "UNKNOWN";
705 
706   const char *name = mutt_map_get_name(win->type, WindowNames);
707   if (name)
708     return name;
709   return "UNKNOWN";
710 }
711 
712 /**
713  * window_invalidate - Mark a window as in need of repaint
714  * @param win   Window to start at
715  */
window_invalidate(struct MuttWindow * win)716 static void window_invalidate(struct MuttWindow *win)
717 {
718   if (!win)
719     return;
720 
721   win->actions |= WA_REPAINT;
722 
723   struct MuttWindow *np = NULL;
724   TAILQ_FOREACH(np, &win->children, entries)
725   {
726     window_invalidate(np);
727   }
728 }
729 
730 /**
731  * window_invalidate_all - Mark all windows as in need of repaint
732  */
window_invalidate_all(void)733 void window_invalidate_all(void)
734 {
735   window_invalidate(RootWindow);
736   clearok(stdscr, true);
737   keypad(stdscr, true);
738 }
739 
740 /**
741  * window_status_on_top - Organise windows according to config variable
742  * @param panel Window containing WT_MENU and WT_STATUS_BAR
743  * @param sub   Config Subset
744  * @retval true Window order was changed
745  *
746  * Set the positions of two Windows based on a config variable `$status_on_top`.
747  *
748  * @note The children are expected to have types: #WT_MENU, #WT_STATUS_BAR
749  */
window_status_on_top(struct MuttWindow * panel,struct ConfigSubset * sub)750 bool window_status_on_top(struct MuttWindow *panel, struct ConfigSubset *sub)
751 {
752   const bool c_status_on_top = cs_subset_bool(sub, "status_on_top");
753 
754   struct MuttWindow *win_first = TAILQ_FIRST(&panel->children);
755 
756   if ((c_status_on_top && (win_first->type == WT_STATUS_BAR)) ||
757       (!c_status_on_top && (win_first->type != WT_STATUS_BAR)))
758   {
759     return false;
760   }
761 
762   TAILQ_REMOVE(&panel->children, win_first, entries);
763   TAILQ_INSERT_TAIL(&panel->children, win_first, entries);
764 
765   mutt_window_reflow(panel);
766   mutt_debug(LL_DEBUG5, "config done, request WA_REFLOW\n");
767   return true;
768 }
769