3 * GUI display the mailboxes in a side panel
6 * Copyright (C) 2004 Justin Hibbits <jrh29@po.cwru.edu>
7 * Copyright (C) 2004 Thomer M. Gil <mutt@thomer.com>
8 * Copyright (C) 2015-2016 Richard Russon <rich@flatcap.org>
9 * Copyright (C) 2016-2017 Kevin J. McCarthy <kevin@8t8.us>
12 * This program is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU General Public License as published by the Free Software
14 * Foundation, either version 2 of the License, or (at your option) any later
17 * This program is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
22 * You should have received a copy of the GNU General Public License along with
23 * this program. If not, see <http://www.gnu.org/licenses/>.
27 * @page sidebar GUI display the mailboxes in a side panel
29 * GUI display the mailboxes in a side panel
37 #include "mutt/mutt.h"
38 #include "config/lib.h"
44 #include "format_flags.h"
46 #include "mutt_curses.h"
47 #include "mutt_menu.h"
48 #include "mutt_window.h"
52 /* These Config Variables are only used in sidebar.c */
53 short C_SidebarComponentDepth; ///< Config: (sidebar) Strip leading path components from sidebar folders
54 char *C_SidebarDelimChars; ///< Config: (sidebar) Characters that separate nested folders
55 char *C_SidebarDividerChar; ///< Config: (sidebar) Character to draw between the sidebar and index
56 bool C_SidebarFolderIndent; ///< Config: (sidebar) Indent nested folders
57 char *C_SidebarFormat; ///< Config: (sidebar) printf-like format string for the sidebar panel
58 char *C_SidebarIndentString; ///< Config: (sidebar) Indent nested folders using this string
59 bool C_SidebarNewMailOnly; ///< Config: (sidebar) Only show folders with new/flagged mail
60 bool C_SidebarNonEmptyMailboxOnly; ///< Config: (sidebar) Only show folders with a non-zero number of mail
61 bool C_SidebarNextNewWrap; ///< Config: (sidebar) Wrap around when searching for the next mailbox with new mail
62 bool C_SidebarShortPath; ///< Config: (sidebar) Abbreviate the paths using the #C_Folder variable
63 short C_SidebarSortMethod; ///< Config: (sidebar) Method to sort the sidebar
65 /* Previous values for some sidebar config */
66 static short PreviousSort = SORT_ORDER; /* sidebar_sort_method */
69 * struct SbEntry - Info about folders in the sidebar
73 char box[256]; /**< formatted mailbox name */
74 struct Mailbox *mailbox; /**< Mailbox this represents */
75 bool is_hidden; /**< Don't show, e.g. $sidebar_new_mail_only */
78 static int EntryCount = 0;
79 static int EntryLen = 0;
80 static struct SbEntry **Entries = NULL;
82 static int TopIndex = -1; /**< First mailbox visible in sidebar */
83 static int OpnIndex = -1; /**< Current (open) mailbox */
84 static int HilIndex = -1; /**< Highlighted mailbox */
85 static int BotIndex = -1; /**< Last mailbox visible in sidebar */
88 * enum DivType - Source of the sidebar divider character
92 SB_DIV_USER, ///< User configured using $sidebar_divider_char
93 SB_DIV_ASCII, ///< An ASCII vertical bar (pipe)
94 SB_DIV_UTF8, ///< A unicode line-drawing character
98 * sidebar_format_str - Format a string for the sidebar - Implements ::format_t
100 * | Expando | Description
101 * |:--------|:--------------------------------------------------------
102 * | \%! | 'n!' Flagged messages
103 * | \%B | Name of the mailbox
104 * | \%D | Description of the mailbox
105 * | \%d | Number of deleted messages
106 * | \%F | Number of Flagged messages in the mailbox
107 * | \%L | Number of messages after limiting
108 * | \%n | N if mailbox has new mail, blank otherwise
109 * | \%N | Number of unread messages in the mailbox
110 * | \%S | Size of mailbox (total number of messages)
111 * | \%t | Number of tagged messages
113 static const char *sidebar_format_str(char *buf, size_t buflen, size_t col, int cols,
114 char op, const char *src, const char *prec,
115 const char *if_str, const char *else_str,
116 unsigned long data, MuttFormatFlags flags)
118 struct SbEntry *sbe = (struct SbEntry *) data;
124 buf[0] = '\0'; /* Just in case there's nothing to do */
126 struct Mailbox *m = sbe->mailbox;
130 bool c = Context && Context->mailbox &&
131 (mutt_str_strcmp(Context->mailbox->realpath, m->realpath) == 0);
133 bool optional = (flags & MUTT_FORMAT_OPTIONAL);
138 mutt_format_s(buf, buflen, prec, sbe->box);
144 snprintf(fmt, sizeof(fmt), "%%%sd", prec);
145 snprintf(buf, buflen, fmt, c ? Context->mailbox->msg_deleted : 0);
147 else if ((c && (Context->mailbox->msg_deleted == 0)) || !c)
152 if (sbe->mailbox->name)
153 mutt_format_s(buf, buflen, prec, sbe->mailbox->name);
155 mutt_format_s(buf, buflen, prec, sbe->box);
161 snprintf(fmt, sizeof(fmt), "%%%sd", prec);
162 snprintf(buf, buflen, fmt, m->msg_flagged);
164 else if (m->msg_flagged == 0)
171 snprintf(fmt, sizeof(fmt), "%%%sd", prec);
172 snprintf(buf, buflen, fmt, c ? Context->mailbox->vcount : m->msg_count);
174 else if ((c && (Context->mailbox->vcount == m->msg_count)) || !c)
181 snprintf(fmt, sizeof(fmt), "%%%sd", prec);
182 snprintf(buf, buflen, fmt, m->msg_unread);
184 else if (m->msg_unread == 0)
191 snprintf(fmt, sizeof(fmt), "%%%sc", prec);
192 snprintf(buf, buflen, fmt, m->has_new ? 'N' : ' ');
194 else if (m->has_new == false)
201 snprintf(fmt, sizeof(fmt), "%%%sd", prec);
202 snprintf(buf, buflen, fmt, m->msg_count);
204 else if (m->msg_count == 0)
211 snprintf(fmt, sizeof(fmt), "%%%sd", prec);
212 snprintf(buf, buflen, fmt, c ? Context->mailbox->msg_tagged : 0);
214 else if ((c && (Context->mailbox->msg_tagged == 0)) || !c)
219 if (m->msg_flagged == 0)
220 mutt_format_s(buf, buflen, prec, "");
221 else if (m->msg_flagged == 1)
222 mutt_format_s(buf, buflen, prec, "!");
223 else if (m->msg_flagged == 2)
224 mutt_format_s(buf, buflen, prec, "!!");
227 snprintf(fmt, sizeof(fmt), "%d!", m->msg_flagged);
228 mutt_format_s(buf, buflen, prec, fmt);
235 mutt_expando_format(buf, buflen, col, C_SidebarWidth, if_str,
236 sidebar_format_str, (unsigned long) sbe, flags);
238 else if (flags & MUTT_FORMAT_OPTIONAL)
240 mutt_expando_format(buf, buflen, col, C_SidebarWidth, else_str,
241 sidebar_format_str, (unsigned long) sbe, flags);
244 /* We return the format string, unchanged */
249 * make_sidebar_entry - Turn mailbox data into a sidebar string
250 * @param[out] buf Buffer in which to save string
251 * @param[in] buflen Buffer length
252 * @param[in] width Desired width in screen cells
253 * @param[in] box Mailbox name
254 * @param[in] sbe Mailbox object
256 * Take all the relevant mailbox data and the desired screen width and then get
257 * mutt_expando_format to do the actual work. mutt_expando_format will callback to
258 * us using sidebar_format_str() for the sidebar specific formatting characters.
260 static void make_sidebar_entry(char *buf, size_t buflen, int width,
261 const char *box, struct SbEntry *sbe)
263 if (!buf || !box || !sbe)
266 mutt_str_strfcpy(sbe->box, box, sizeof(sbe->box));
268 mutt_expando_format(buf, buflen, 0, width, NONULL(C_SidebarFormat),
269 sidebar_format_str, (unsigned long) sbe, MUTT_FORMAT_NO_FLAGS);
271 /* Force string to be exactly the right width */
272 int w = mutt_strwidth(buf);
273 int s = mutt_str_strlen(buf);
274 width = MIN(buflen, width);
277 /* Pad with spaces */
278 memset(buf + s, ' ', width - w);
279 buf[s + width - w] = '\0';
283 /* Truncate to fit */
284 size_t len = mutt_wstr_trunc(buf, buflen, width, NULL);
290 * cb_qsort_sbe - qsort callback to sort SbEntry's
291 * @param a First SbEntry to compare
292 * @param b Second SbEntry to compare
293 * @retval -1 a precedes b
294 * @retval 0 a and b are identical
295 * @retval 1 b precedes a
297 static int cb_qsort_sbe(const void *a, const void *b)
299 const struct SbEntry *sbe1 = *(struct SbEntry const *const *) a;
300 const struct SbEntry *sbe2 = *(struct SbEntry const *const *) b;
301 const struct Mailbox *m1 = sbe1->mailbox;
302 const struct Mailbox *m2 = sbe2->mailbox;
306 switch ((C_SidebarSortMethod & SORT_MASK))
309 if (m2->msg_count == m1->msg_count)
310 rc = mutt_str_strcoll(mailbox_path(m1), mailbox_path(m2));
312 rc = (m2->msg_count - m1->msg_count);
315 if (m2->msg_unread == m1->msg_unread)
316 rc = mutt_str_strcoll(mailbox_path(m1), mailbox_path(m2));
318 rc = (m2->msg_unread - m1->msg_unread);
321 rc = mutt_str_strcmp(m1->name, m2->name);
324 if (m2->msg_flagged == m1->msg_flagged)
325 rc = mutt_str_strcoll(mailbox_path(m1), mailbox_path(m2));
327 rc = (m2->msg_flagged - m1->msg_flagged);
331 rc = mutt_inbox_cmp(mailbox_path(m1), mailbox_path(m2));
333 rc = mutt_str_strcoll(mailbox_path(m1), mailbox_path(m2));
338 if (C_SidebarSortMethod & SORT_REVERSE)
345 * update_entries_visibility - Should a sidebar_entry be displayed in the sidebar
347 * For each SbEntry in the Entries array, check whether we should display it.
348 * This is determined by several criteria. If the Mailbox:
349 * * is the currently open mailbox
350 * * is the currently highlighted mailbox
351 * * has unread messages
352 * * has flagged messages
355 static void update_entries_visibility(void)
357 /* Aliases for readability */
358 const bool new_only = C_SidebarNewMailOnly;
359 const bool non_empty_only = C_SidebarNonEmptyMailboxOnly;
360 struct SbEntry *sbe = NULL;
362 /* Take the fast path if there is no need to test visibilities */
363 if (!new_only && !non_empty_only)
365 for (int i = 0; i < EntryCount; i++)
367 Entries[i]->is_hidden = false;
372 for (int i = 0; i < EntryCount; i++)
376 sbe->is_hidden = false;
378 if (Context && (mutt_str_strcmp(sbe->mailbox->realpath, Context->mailbox->realpath) == 0))
380 /* Spool directories are always visible */
384 if (mutt_list_find(&SidebarWhitelist, mailbox_path(sbe->mailbox)) ||
385 mutt_list_find(&SidebarWhitelist, sbe->mailbox->name))
387 /* Explicitly asked to be visible */
391 if (non_empty_only && (i != OpnIndex) && (sbe->mailbox->msg_count == 0))
393 sbe->is_hidden = true;
396 if (new_only && (i != OpnIndex) && (sbe->mailbox->msg_unread == 0) &&
397 (sbe->mailbox->msg_flagged == 0) && !sbe->mailbox->has_new)
399 sbe->is_hidden = true;
405 * unsort_entries - Restore Entries array order to match Mailbox list order
407 static void unsort_entries(void)
411 struct MailboxList ml = neomutt_mailboxlist_get_all(NeoMutt, MUTT_MAILBOX_ANY);
412 struct MailboxNode *np = NULL;
413 STAILQ_FOREACH(np, &ml, entries)
419 while ((j < EntryCount) && (Entries[j]->mailbox != np->mailbox))
425 struct SbEntry *tmp = Entries[i];
426 Entries[i] = Entries[j];
432 neomutt_mailboxlist_clear(&ml);
436 * sort_entries - Sort Entries array
438 * Sort the Entries array according to the current sort config
439 * option "sidebar_sort_method". This calls qsort to do the work which calls our
440 * callback function "cb_qsort_sbe".
442 * Once sorted, the prev/next links will be reconstructed.
444 static void sort_entries(void)
446 enum SortType ssm = (C_SidebarSortMethod & SORT_MASK);
448 /* These are the only sort methods we understand */
449 if ((ssm == SORT_COUNT) || (ssm == SORT_UNREAD) || (ssm == SORT_FLAGGED) || (ssm == SORT_PATH))
450 qsort(Entries, EntryCount, sizeof(*Entries), cb_qsort_sbe);
451 else if ((ssm == SORT_ORDER) && (C_SidebarSortMethod != PreviousSort))
456 * select_next - Selects the next unhidden mailbox
457 * @retval true Success
458 * @retval false Failure
460 static bool select_next(void)
462 int entry = HilIndex;
464 if (!EntryCount || (HilIndex < 0))
470 if (entry == EntryCount)
472 } while (Entries[entry]->is_hidden);
479 * select_next_new - Selects the next new mailbox
480 * @retval true Success
481 * @retval false Failure
483 * Search down the list of mail folders for one containing new mail.
485 static int select_next_new(void)
487 int entry = HilIndex;
489 if (!EntryCount || (HilIndex < 0))
495 if (entry == EntryCount)
497 if (C_SidebarNextNewWrap)
502 if (entry == HilIndex)
504 } while (!Entries[entry]->mailbox->has_new && (Entries[entry]->mailbox->msg_unread == 0));
511 * select_prev - Selects the previous unhidden mailbox
512 * @retval true Success
513 * @retval false Failure
515 static bool select_prev(void)
517 int entry = HilIndex;
519 if (!EntryCount || (HilIndex < 0))
527 } while (Entries[entry]->is_hidden);
534 * select_prev_new - Selects the previous new mailbox
535 * @retval true Success
536 * @retval false Failure
538 * Search up the list of mail folders for one containing new mail.
540 static bool select_prev_new(void)
542 int entry = HilIndex;
544 if (!EntryCount || (HilIndex < 0))
552 if (C_SidebarNextNewWrap)
553 entry = EntryCount - 1;
557 if (entry == HilIndex)
559 } while (!Entries[entry]->mailbox->has_new && (Entries[entry]->mailbox->msg_unread == 0));
566 * select_page_down - Selects the first entry in the next page of mailboxes
567 * @retval true Success
568 * @retval false Failure
570 static int select_page_down(void)
572 int orig_hil_index = HilIndex;
574 if (!EntryCount || (BotIndex < 0))
579 /* If the rest of the entries are hidden, go up to the last unhidden one */
580 if (Entries[HilIndex]->is_hidden)
583 return orig_hil_index != HilIndex;
587 * select_page_up - Selects the last entry in the previous page of mailboxes
588 * @retval true Success
589 * @retval false Failure
591 static int select_page_up(void)
593 int orig_hil_index = HilIndex;
595 if (!EntryCount || (TopIndex < 0))
600 /* If the rest of the entries are hidden, go down to the last unhidden one */
601 if (Entries[HilIndex]->is_hidden)
604 return orig_hil_index != HilIndex;
608 * prepare_sidebar - Prepare the list of SbEntry's for the sidebar display
609 * @param page_size The number of lines on a page
610 * @retval false No, don't draw the sidebar
611 * @retval true Yes, draw the sidebar
613 * Before painting the sidebar, we determine which are visible, sort
614 * them and set up our page pointers.
616 * This is a lot of work to do each refresh, but there are many things that
617 * can change outside of the sidebar that we don't hear about.
619 static bool prepare_sidebar(int page_size)
621 if (!EntryCount || (page_size <= 0))
624 const struct SbEntry *opn_entry = (OpnIndex >= 0) ? Entries[OpnIndex] : NULL;
625 const struct SbEntry *hil_entry = (HilIndex >= 0) ? Entries[HilIndex] : NULL;
627 update_entries_visibility();
630 for (int i = 0; i < EntryCount; i++)
632 if (opn_entry == Entries[i])
634 if (hil_entry == Entries[i])
638 if ((HilIndex < 0) || Entries[HilIndex]->is_hidden || (C_SidebarSortMethod != PreviousSort))
645 if (Entries[HilIndex]->is_hidden)
650 /* Set the Top and Bottom to frame the HilIndex in groups of page_size */
652 /* If C_SidebarNewMailOnly or C_SidebarNonEmptyMailboxOnly is set, some entries
653 * may be hidden so we need to scan for the framing interval */
654 if (C_SidebarNewMailOnly || C_SidebarNonEmptyMailboxOnly)
658 while (BotIndex < HilIndex)
660 TopIndex = BotIndex + 1;
661 int page_entries = 0;
662 while (page_entries < page_size)
665 if (BotIndex >= EntryCount)
667 if (!Entries[BotIndex]->is_hidden)
672 /* Otherwise we can just calculate the interval */
675 TopIndex = (HilIndex / page_size) * page_size;
676 BotIndex = TopIndex + page_size - 1;
679 if (BotIndex > (EntryCount - 1))
680 BotIndex = EntryCount - 1;
682 PreviousSort = C_SidebarSortMethod;
687 * draw_divider - Draw a line between the sidebar and the rest of neomutt
688 * @param num_rows Height of the Sidebar
689 * @param num_cols Width of the Sidebar
690 * @retval 0 Empty string
691 * @retval num Character occupies n screen columns
693 * Draw a divider using characters from the config option "sidebar_divider_char".
694 * This can be an ASCII or Unicode character.
695 * We calculate these characters' width in screen columns.
697 * If the user hasn't set $sidebar_divider_char we pick a character for them,
698 * respecting the value of $ascii_chars.
700 static int draw_divider(int num_rows, int num_cols)
702 if ((num_rows < 1) || (num_cols < 1))
706 enum DivType altchar = SB_DIV_UTF8;
708 /* Calculate the width of the delimiter in screen cells */
709 delim_len = mutt_strwidth(C_SidebarDividerChar);
712 delim_len = 1; /* Bad character */
714 else if (delim_len == 0)
716 if (C_SidebarDividerChar)
717 return 0; /* User has set empty string */
719 delim_len = 1; /* Unset variable */
723 altchar = SB_DIV_USER; /* User config */
726 if (C_AsciiChars && (altchar != SB_DIV_ASCII))
728 /* $ascii_chars overrides Unicode divider chars */
729 if (altchar == SB_DIV_UTF8)
731 altchar = SB_DIV_ASCII;
733 else if (C_SidebarDividerChar)
735 for (int i = 0; i < delim_len; i++)
737 if (C_SidebarDividerChar[i] & ~0x7F) /* high-bit is set */
739 altchar = SB_DIV_ASCII;
747 if (delim_len > num_cols)
750 mutt_curses_set_color(MT_COLOR_DIVIDER);
752 int col = C_SidebarOnRight ? 0 : (C_SidebarWidth - delim_len);
754 for (int i = 0; i < num_rows; i++)
756 mutt_window_move(MuttSidebarWindow, i, col);
761 mutt_window_addstr(NONULL(C_SidebarDividerChar));
764 mutt_window_addch('|');
767 mutt_window_addch(ACS_VLINE);
776 * fill_empty_space - Wipe the remaining Sidebar space
777 * @param first_row Window line to start (0-based)
778 * @param num_rows Number of rows to fill
779 * @param div_width Width in screen characters taken by the divider
780 * @param num_cols Number of columns to fill
782 * Write spaces over the area the sidebar isn't using.
784 static void fill_empty_space(int first_row, int num_rows, int div_width, int num_cols)
786 /* Fill the remaining rows with blank space */
787 mutt_curses_set_color(MT_COLOR_NORMAL);
789 if (!C_SidebarOnRight)
791 for (int r = 0; r < num_rows; r++)
793 mutt_window_move(MuttSidebarWindow, first_row + r, div_width);
795 for (int i = 0; i < num_cols; i++)
796 mutt_window_addch(' ');
801 * draw_sidebar - Write out a list of mailboxes, in a panel
802 * @param num_rows Height of the Sidebar
803 * @param num_cols Width of the Sidebar
804 * @param div_width Width in screen characters taken by the divider
806 * Display a list of mailboxes in a panel on the left. What's displayed will
807 * depend on our index markers: TopMailbox, OpnMailbox, HilMailbox, BotMailbox.
808 * On the first run they'll be NULL, so we display the top of NeoMutt's list.
810 * * TopMailbox - first visible mailbox
811 * * BotMailbox - last visible mailbox
812 * * OpnMailbox - mailbox shown in NeoMutt's Index Panel
813 * * HilMailbox - Unselected mailbox (the paging follows this)
815 * The entries are formatted using "sidebar_format" and may be abbreviated:
816 * "sidebar_short_path", indented: "sidebar_folder_indent",
817 * "sidebar_indent_string" and sorted: "sidebar_sort_method". Finally, they're
818 * trimmed to fit the available space.
820 static void draw_sidebar(int num_rows, int num_cols, int div_width)
822 struct SbEntry *entry = NULL;
823 struct Mailbox *m = NULL;
827 int w = MIN(num_cols, (C_SidebarWidth - div_width));
829 for (int entryidx = TopIndex; (entryidx < EntryCount) && (row < num_rows); entryidx++)
831 entry = Entries[entryidx];
832 if (entry->is_hidden)
836 if (entryidx == OpnIndex)
838 if ((ColorDefs[MT_COLOR_SB_INDICATOR] != 0))
839 mutt_curses_set_color(MT_COLOR_SB_INDICATOR);
841 mutt_curses_set_color(MT_COLOR_INDICATOR);
843 else if (entryidx == HilIndex)
844 mutt_curses_set_color(MT_COLOR_HIGHLIGHT);
846 mutt_curses_set_color(MT_COLOR_NEW);
847 else if (m->msg_flagged > 0)
848 mutt_curses_set_color(MT_COLOR_FLAGGED);
849 else if ((ColorDefs[MT_COLOR_SB_SPOOLFILE] != 0) &&
850 (mutt_str_strcmp(mailbox_path(m), C_Spoolfile) == 0))
852 mutt_curses_set_color(MT_COLOR_SB_SPOOLFILE);
856 if (ColorDefs[MT_COLOR_ORDINARY] != 0)
857 mutt_curses_set_color(MT_COLOR_ORDINARY);
859 mutt_curses_set_color(MT_COLOR_NORMAL);
863 if (C_SidebarOnRight)
866 mutt_window_move(MuttSidebarWindow, row, col);
867 if (Context && Context->mailbox && (Context->mailbox->realpath[0] != '\0') &&
868 (mutt_str_strcmp(m->realpath, Context->mailbox->realpath) == 0))
870 m->msg_unread = Context->mailbox->msg_unread;
871 m->msg_count = Context->mailbox->msg_count;
872 m->msg_flagged = Context->mailbox->msg_flagged;
875 /* compute length of C_Folder without trailing separator */
876 size_t maildirlen = mutt_str_strlen(C_Folder);
877 if (maildirlen && C_SidebarDelimChars &&
878 strchr(C_SidebarDelimChars, C_Folder[maildirlen - 1]))
881 /* check whether C_Folder is a prefix of the current folder's path */
882 bool maildir_is_prefix = false;
883 if ((mutt_buffer_len(&m->pathbuf) > maildirlen) &&
884 (mutt_str_strncmp(C_Folder, mailbox_path(m), maildirlen) == 0) &&
885 C_SidebarDelimChars && strchr(C_SidebarDelimChars, mailbox_path(m)[maildirlen]))
887 maildir_is_prefix = true;
890 /* calculate depth of current folder and generate its display name with indented spaces */
891 int sidebar_folder_depth = 0;
892 const char *sidebar_folder_name = NULL;
893 struct Buffer *short_folder_name = NULL;
894 if (C_SidebarShortPath)
896 /* disregard a trailing separator, so strlen() - 2 */
897 sidebar_folder_name = mailbox_path(m);
898 for (int i = mutt_str_strlen(sidebar_folder_name) - 2; i >= 0; i--)
900 if (C_SidebarDelimChars && strchr(C_SidebarDelimChars, sidebar_folder_name[i]))
902 sidebar_folder_name += (i + 1);
907 else if ((C_SidebarComponentDepth > 0) && C_SidebarDelimChars)
909 sidebar_folder_name = mailbox_path(m) + maildir_is_prefix * (maildirlen + 1);
910 for (int i = 0; i < C_SidebarComponentDepth; i++)
912 char *chars_after_delim = strpbrk(sidebar_folder_name, C_SidebarDelimChars);
913 if (!chars_after_delim)
916 sidebar_folder_name = chars_after_delim + 1;
920 sidebar_folder_name = mailbox_path(m) + maildir_is_prefix * (maildirlen + 1);
924 sidebar_folder_name = m->name;
926 else if (maildir_is_prefix && C_SidebarFolderIndent)
929 const char *tmp_folder_name = mailbox_path(m) + maildirlen + 1;
930 int tmplen = (int) mutt_str_strlen(tmp_folder_name) - 1;
931 for (int i = 0; i < tmplen; i++)
933 if (C_SidebarDelimChars && strchr(C_SidebarDelimChars, tmp_folder_name[i]))
935 sidebar_folder_depth++;
939 if (sidebar_folder_depth > 0)
941 if (C_SidebarShortPath)
942 tmp_folder_name += lastsep; /* basename */
943 short_folder_name = mutt_buffer_pool_get();
944 for (int i = 0; i < sidebar_folder_depth; i++)
945 mutt_buffer_addstr(short_folder_name, NONULL(C_SidebarIndentString));
946 mutt_buffer_addstr(short_folder_name, tmp_folder_name);
947 sidebar_folder_name = mutt_b2s(short_folder_name);
951 make_sidebar_entry(str, sizeof(str), w, sidebar_folder_name, entry);
952 mutt_window_printf("%s", str);
953 mutt_buffer_pool_release(&short_folder_name);
957 fill_empty_space(row, num_rows - row, div_width, w);
961 * mutt_sb_draw - Completely redraw the sidebar
963 * Completely refresh the sidebar region. First draw the divider; then, for
964 * each Mailbox, call make_sidebar_entry; finally blank out any remaining space.
966 void mutt_sb_draw(void)
968 if (!C_SidebarVisible)
971 int row = 0, col = 0;
972 mutt_window_get_coords(MuttSidebarWindow, &row, &col);
974 int num_rows = MuttSidebarWindow->rows;
975 int num_cols = MuttSidebarWindow->cols;
977 int div_width = draw_divider(num_rows, num_cols);
981 struct MailboxList ml = neomutt_mailboxlist_get_all(NeoMutt, MUTT_MAILBOX_ANY);
982 struct MailboxNode *np = NULL;
983 STAILQ_FOREACH(np, &ml, entries)
985 mutt_sb_notify_mailbox(np->mailbox, true);
987 neomutt_mailboxlist_clear(&ml);
990 if (!prepare_sidebar(num_rows))
992 fill_empty_space(0, num_rows, div_width, num_cols - div_width);
996 draw_sidebar(num_rows, num_cols, div_width);
997 mutt_window_move(MuttSidebarWindow, row, col);
1001 * mutt_sb_change_mailbox - Change the selected mailbox
1002 * @param op Operation code
1004 * Change the selected mailbox, e.g. "Next mailbox", "Previous Mailbox
1005 * with new mail". The operations are listed in opcodes.h.
1007 * If the operation is successful, HilMailbox will be set to the new mailbox.
1008 * This function only *selects* the mailbox, doesn't *open* it.
1010 * Allowed values are: OP_SIDEBAR_NEXT, OP_SIDEBAR_NEXT_NEW,
1011 * OP_SIDEBAR_PAGE_DOWN, OP_SIDEBAR_PAGE_UP, OP_SIDEBAR_PREV,
1012 * OP_SIDEBAR_PREV_NEW.
1014 void mutt_sb_change_mailbox(int op)
1016 if (!C_SidebarVisible)
1019 if (HilIndex < 0) /* It'll get reset on the next draw */
1024 case OP_SIDEBAR_NEXT:
1028 case OP_SIDEBAR_NEXT_NEW:
1029 if (!select_next_new())
1032 case OP_SIDEBAR_PAGE_DOWN:
1033 if (!select_page_down())
1036 case OP_SIDEBAR_PAGE_UP:
1037 if (!select_page_up())
1040 case OP_SIDEBAR_PREV:
1044 case OP_SIDEBAR_PREV_NEW:
1045 if (!select_prev_new())
1051 mutt_menu_set_current_redraw(REDRAW_SIDEBAR);
1055 * mutt_sb_get_highlight - Get the Mailbox that's highlighted in the sidebar
1056 * @retval ptr Mailbox
1058 struct Mailbox *mutt_sb_get_highlight(void)
1060 if (!C_SidebarVisible)
1063 if (!EntryCount || (HilIndex < 0))
1066 return Entries[HilIndex]->mailbox;
1070 * mutt_sb_set_open_mailbox - Set the 'open' Mailbox
1073 * Search through the list of mailboxes.
1074 * If a Mailbox has a matching path, set OpnMailbox to it.
1076 void mutt_sb_set_open_mailbox(struct Mailbox *m)
1083 for (int entry = 0; entry < EntryCount; entry++)
1085 if (mutt_str_strcmp(Entries[entry]->mailbox->realpath, m->realpath) == 0)
1095 * mutt_sb_notify_mailbox - The state of a Mailbox is about to change
1097 * @param created True if folder created, false if deleted
1099 * We receive a notification:
1100 * - After a new Mailbox has been created
1101 * - Before a Mailbox is deleted
1103 * Before a deletion, check that our pointers won't be invalidated.
1105 void mutt_sb_notify_mailbox(struct Mailbox *m, bool created)
1110 /* Any new/deleted mailboxes will cause a refresh. As long as
1111 * they're valid, our pointers will be updated in prepare_sidebar() */
1115 if (EntryCount >= EntryLen)
1118 mutt_mem_realloc(&Entries, EntryLen * sizeof(struct SbEntry *));
1120 Entries[EntryCount] = mutt_mem_calloc(1, sizeof(struct SbEntry));
1121 Entries[EntryCount]->mailbox = m;
1124 TopIndex = EntryCount;
1126 BotIndex = EntryCount;
1127 if ((OpnIndex < 0) && Context &&
1128 (mutt_str_strcmp(m->realpath, Context->mailbox->realpath) == 0))
1129 OpnIndex = EntryCount;
1136 for (del_index = 0; del_index < EntryCount; del_index++)
1137 if (Entries[del_index]->mailbox == m)
1139 if (del_index == EntryCount)
1141 FREE(&Entries[del_index]);
1144 if ((TopIndex > del_index) || (TopIndex == EntryCount))
1146 if (OpnIndex == del_index)
1148 else if (OpnIndex > del_index)
1150 if ((HilIndex > del_index) || (HilIndex == EntryCount))
1152 if ((BotIndex > del_index) || (BotIndex == EntryCount))
1155 for (; del_index < EntryCount; del_index++)
1156 Entries[del_index] = Entries[del_index + 1];
1159 mutt_menu_set_current_redraw(REDRAW_SIDEBAR);