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