mutt stable branch with some hacks
1/* Copyright (C) 2004 Justin Hibbits <jrh29@po.cwru.edu>
2 * Copyright (C) 2004 Thomer M. Gil <mutt@thomer.com>
3 * Copyright (C) 2015-2016 Richard Russon <rich@flatcap.org>
4 * Copyright (C) 2016 Kevin J. McCarthy <kevin@8t8.us>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
19 */
20
21#if HAVE_CONFIG_H
22# include "config.h"
23#endif
24
25#include "mutt.h"
26#include "buffy.h"
27#include "keymap.h"
28#include "mutt_curses.h"
29#include "mutt_menu.h"
30#include "sort.h"
31
32/* Previous values for some sidebar config */
33static short PreviousSort = SORT_ORDER; /* sidebar_sort_method */
34
35/**
36 * struct sidebar_entry - Info about folders in the sidebar
37 */
38typedef struct sidebar_entry
39{
40 char box[STRING]; /* formatted mailbox name */
41 BUFFY *buffy;
42 short is_hidden;
43} SBENTRY;
44
45static int EntryCount = 0;
46static int EntryLen = 0;
47static SBENTRY **Entries = NULL;
48
49static int TopIndex = -1; /* First mailbox visible in sidebar */
50static int OpnIndex = -1; /* Current (open) mailbox */
51static int HilIndex = -1; /* Highlighted mailbox */
52static int BotIndex = -1; /* Last mailbox visible in sidebar */
53
54static int select_next (void);
55
56
57/**
58 * cb_format_str - Create the string to show in the sidebar
59 * @dest: Buffer in which to save string
60 * @destlen: Buffer length
61 * @col: Starting column, UNUSED
62 * @op: printf-like operator, e.g. 'B'
63 * @src: printf-like format string
64 * @prefix: Field formatting string, UNUSED
65 * @ifstring: If condition is met, display this string
66 * @elsestring: Otherwise, display this string
67 * @data: Pointer to our sidebar_entry
68 * @flags: Format flags, e.g. MUTT_FORMAT_OPTIONAL
69 *
70 * cb_format_str is a callback function for mutt_FormatString. It understands
71 * six operators. '%B' : Mailbox name, '%F' : Number of flagged messages,
72 * '%N' : Number of new messages, '%S' : Size (total number of messages),
73 * '%!' : Icon denoting number of flagged messages.
74 * '%n' : N if folder has new mail, blank otherwise.
75 *
76 * Returns: src (unchanged)
77 */
78static const char *cb_format_str(char *dest, size_t destlen, size_t col, int cols, char op,
79 const char *src, const char *prefix, const char *ifstring,
80 const char *elsestring, unsigned long data, format_flag flags)
81{
82 SBENTRY *sbe = (SBENTRY *) data;
83 unsigned int optional;
84 char fmt[STRING];
85
86 if (!sbe || !dest)
87 return src;
88
89 dest[0] = 0; /* Just in case there's nothing to do */
90
91 BUFFY *b = sbe->buffy;
92 if (!b)
93 return src;
94
95 int c = Context && (mutt_strcmp (Context->realpath, b->realpath) == 0);
96
97 optional = flags & MUTT_FORMAT_OPTIONAL;
98
99 switch (op)
100 {
101 case 'B':
102 mutt_format_s (dest, destlen, prefix, sbe->box);
103 break;
104
105 case 'd':
106 if (!optional)
107 {
108 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
109 snprintf (dest, destlen, fmt, c ? Context->deleted : 0);
110 }
111 else if ((c && Context->deleted == 0) || !c)
112 optional = 0;
113 break;
114
115 case 'F':
116 if (!optional)
117 {
118 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
119 snprintf (dest, destlen, fmt, b->msg_flagged);
120 }
121 else if (b->msg_flagged == 0)
122 optional = 0;
123 break;
124
125 case 'L':
126 if (!optional)
127 {
128 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
129 snprintf (dest, destlen, fmt, c ? Context->vcount : b->msg_count);
130 }
131 else if ((c && Context->vcount == b->msg_count) || !c)
132 optional = 0;
133 break;
134
135 case 'N':
136 if (!optional)
137 {
138 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
139 snprintf (dest, destlen, fmt, b->msg_unread);
140 }
141 else if (b->msg_unread == 0)
142 optional = 0;
143 break;
144
145 case 'n':
146 if (!optional)
147 {
148 snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
149 snprintf (dest, destlen, fmt, b->new ? 'N' : ' ');
150 }
151 else if (b->new == 0)
152 optional = 0;
153 break;
154
155 case 'S':
156 if (!optional)
157 {
158 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
159 snprintf (dest, destlen, fmt, b->msg_count);
160 }
161 else if (b->msg_count == 0)
162 optional = 0;
163 break;
164
165 case 't':
166 if (!optional)
167 {
168 snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
169 snprintf (dest, destlen, fmt, c ? Context->tagged : 0);
170 }
171 else if ((c && Context->tagged == 0) || !c)
172 optional = 0;
173 break;
174
175 case '!':
176 if (b->msg_flagged == 0)
177 mutt_format_s (dest, destlen, prefix, "");
178 else if (b->msg_flagged == 1)
179 mutt_format_s (dest, destlen, prefix, "!");
180 else if (b->msg_flagged == 2)
181 mutt_format_s (dest, destlen, prefix, "!!");
182 else
183 {
184 snprintf (fmt, sizeof (fmt), "%d!", b->msg_flagged);
185 mutt_format_s (dest, destlen, prefix, fmt);
186 }
187 break;
188 }
189
190 if (optional)
191 mutt_FormatString (dest, destlen, col, SidebarWidth, ifstring, cb_format_str, (unsigned long) sbe, flags);
192 else if (flags & MUTT_FORMAT_OPTIONAL)
193 mutt_FormatString (dest, destlen, col, SidebarWidth, elsestring, cb_format_str, (unsigned long) sbe, flags);
194
195 /* We return the format string, unchanged */
196 return src;
197}
198
199/**
200 * make_sidebar_entry - Turn mailbox data into a sidebar string
201 * @buf: Buffer in which to save string
202 * @buflen: Buffer length
203 * @width: Desired width in screen cells
204 * @box: Mailbox name
205 * @b: Mailbox object
206 *
207 * Take all the relevant mailbox data and the desired screen width and then get
208 * mutt_FormatString to do the actual work. mutt_FormatString will callback to
209 * us using cb_format_str() for the sidebar specific formatting characters.
210 */
211static void make_sidebar_entry (char *buf, unsigned int buflen, int width, char *box,
212 SBENTRY *sbe)
213{
214 if (!buf || !box || !sbe)
215 return;
216
217 strfcpy (sbe->box, box, sizeof (sbe->box));
218
219 mutt_FormatString (buf, buflen, 0, width, NONULL(SidebarFormat), cb_format_str, (unsigned long) sbe, 0);
220
221 /* Force string to be exactly the right width */
222 int w = mutt_strwidth (buf);
223 int s = mutt_strlen (buf);
224 width = MIN(buflen, width);
225 if (w < width)
226 {
227 /* Pad with spaces */
228 memset (buf + s, ' ', width - w);
229 buf[s + width - w] = 0;
230 }
231 else if (w > width)
232 {
233 /* Truncate to fit */
234 int len = mutt_wstr_trunc (buf, buflen, width, NULL);
235 buf[len] = 0;
236 }
237}
238
239/**
240 * cb_qsort_sbe - qsort callback to sort SBENTRYs
241 * @a: First SBENTRY to compare
242 * @b: Second SBENTRY to compare
243 *
244 * Returns:
245 * -1: a precedes b
246 * 0: a and b are identical
247 * 1: b precedes a
248 */
249static int cb_qsort_sbe (const void *a, const void *b)
250{
251 const SBENTRY *sbe1 = *(const SBENTRY **) a;
252 const SBENTRY *sbe2 = *(const SBENTRY **) b;
253 BUFFY *b1 = sbe1->buffy;
254 BUFFY *b2 = sbe2->buffy;
255
256 int result = 0;
257
258 switch ((SidebarSortMethod & SORT_MASK))
259 {
260 case SORT_COUNT:
261 result = (b2->msg_count - b1->msg_count);
262 break;
263 case SORT_COUNT_NEW:
264 result = (b2->msg_unread - b1->msg_unread);
265 break;
266 case SORT_FLAGGED:
267 result = (b2->msg_flagged - b1->msg_flagged);
268 break;
269 case SORT_PATH:
270 result = mutt_strcasecmp (b1->path, b2->path);
271 break;
272 }
273
274 if (SidebarSortMethod & SORT_REVERSE)
275 result = -result;
276
277 return result;
278}
279
280/**
281 * update_entries_visibility - Should a sidebar_entry be displayed in the sidebar
282 *
283 * For each SBENTRY in the Entries array, check whether we should display it.
284 * This is determined by several criteria. If the BUFFY:
285 * is the currently open mailbox
286 * is the currently highlighted mailbox
287 * has unread messages
288 * has flagged messages
289 * is whitelisted
290 */
291static void update_entries_visibility (void)
292{
293 short new_only = option (OPTSIDEBARNEWMAILONLY);
294 SBENTRY *sbe;
295 int i;
296
297 for (i = 0; i < EntryCount; i++)
298 {
299 sbe = Entries[i];
300
301 sbe->is_hidden = 0;
302
303 if (!new_only)
304 continue;
305
306 if ((i == OpnIndex) || (sbe->buffy->msg_unread > 0) || sbe->buffy->new ||
307 (sbe->buffy->msg_flagged > 0))
308 continue;
309
310 if (Context && (mutt_strcmp (sbe->buffy->realpath, Context->realpath) == 0))
311 /* Spool directory */
312 continue;
313
314 if (mutt_find_list (SidebarWhitelist, sbe->buffy->path))
315 /* Explicitly asked to be visible */
316 continue;
317
318 sbe->is_hidden = 1;
319 }
320}
321
322/**
323 * unsort_entries - Restore Entries array order to match Buffy list order
324 */
325static void unsort_entries (void)
326{
327 BUFFY *cur = Incoming;
328 int i = 0, j;
329 SBENTRY *tmp;
330
331 while (cur && (i < EntryCount))
332 {
333 j = i;
334 while ((j < EntryCount) &&
335 (Entries[j]->buffy != cur))
336 j++;
337 if (j < EntryCount)
338 {
339 if (j != i)
340 {
341 tmp = Entries[i];
342 Entries[i] = Entries[j];
343 Entries[j] = tmp;
344 }
345 i++;
346 }
347 cur = cur->next;
348 }
349}
350
351/**
352 * sort_entries - Sort Entries array.
353 *
354 * Sort the Entries array according to the current sort config
355 * option "sidebar_sort_method". This calls qsort to do the work which calls our
356 * callback function "cb_qsort_sbe".
357 *
358 * Once sorted, the prev/next links will be reconstructed.
359 */
360static void sort_entries (void)
361{
362 short ssm = (SidebarSortMethod & SORT_MASK);
363
364 /* These are the only sort methods we understand */
365 if ((ssm == SORT_COUNT) ||
366 (ssm == SORT_COUNT_NEW) ||
367 (ssm == SORT_FLAGGED) ||
368 (ssm == SORT_PATH))
369 qsort (Entries, EntryCount, sizeof (*Entries), cb_qsort_sbe);
370 else if ((ssm == SORT_ORDER) &&
371 (SidebarSortMethod != PreviousSort))
372 unsort_entries ();
373}
374
375/**
376 * prepare_sidebar - Prepare the list of SBENTRYs for the sidebar display
377 * @page_size: The number of lines on a page
378 *
379 * Before painting the sidebar, we determine which are visible, sort
380 * them and set up our page pointers.
381 *
382 * This is a lot of work to do each refresh, but there are many things that
383 * can change outside of the sidebar that we don't hear about.
384 *
385 * Returns:
386 * 0: No, don't draw the sidebar
387 * 1: Yes, draw the sidebar
388 */
389static int prepare_sidebar (int page_size)
390{
391 int i;
392 SBENTRY *opn_entry = NULL, *hil_entry = NULL;
393 int page_entries;
394
395 if (!EntryCount || (page_size <= 0))
396 return 0;
397
398 if (OpnIndex >= 0)
399 opn_entry = Entries[OpnIndex];
400 if (HilIndex >= 0)
401 hil_entry = Entries[HilIndex];
402
403 update_entries_visibility ();
404 sort_entries ();
405
406 for (i = 0; i < EntryCount; i++)
407 {
408 if (opn_entry == Entries[i])
409 OpnIndex = i;
410 if (hil_entry == Entries[i])
411 HilIndex = i;
412 }
413
414 if ((HilIndex < 0) || Entries[HilIndex]->is_hidden ||
415 (SidebarSortMethod != PreviousSort))
416 {
417 if (OpnIndex >= 0)
418 HilIndex = OpnIndex;
419 else
420 {
421 HilIndex = 0;
422 if (Entries[HilIndex]->is_hidden)
423 select_next ();
424 }
425 }
426
427 /* Set the Top and Bottom to frame the HilIndex in groups of page_size */
428
429 /* If OPTSIDEBARNEMAILONLY is set, some entries may be hidden so we
430 * need to scan for the framing interval */
431 if (option (OPTSIDEBARNEWMAILONLY))
432 {
433 TopIndex = BotIndex = -1;
434 while (BotIndex < HilIndex)
435 {
436 TopIndex = BotIndex + 1;
437 page_entries = 0;
438 while (page_entries < page_size)
439 {
440 BotIndex++;
441 if (BotIndex >= EntryCount)
442 break;
443 if (! Entries[BotIndex]->is_hidden)
444 page_entries++;
445 }
446 }
447 }
448 /* Otherwise we can just calculate the interval */
449 else
450 {
451 TopIndex = (HilIndex / page_size) * page_size;
452 BotIndex = TopIndex + page_size - 1;
453 }
454
455 if (BotIndex > (EntryCount - 1))
456 BotIndex = EntryCount - 1;
457
458 PreviousSort = SidebarSortMethod;
459 return 1;
460}
461
462/**
463 * draw_divider - Draw a line between the sidebar and the rest of mutt
464 * @num_rows: Height of the Sidebar
465 * @num_cols: Width of the Sidebar
466 *
467 * Draw a divider using characters from the config option "sidebar_divider_char".
468 * This can be an ASCII or Unicode character. First we calculate this
469 * characters' width in screen columns, then subtract that from the config
470 * option "sidebar_width".
471 *
472 * Returns:
473 * -1: Error: bad character, etc
474 * 0: Error: 0 width character
475 * n: Success: character occupies n screen columns
476 */
477static int draw_divider (int num_rows, int num_cols)
478{
479 /* Calculate the width of the delimiter in screen cells */
480 int delim_len = mutt_strwidth (SidebarDividerChar);
481
482 if (delim_len < 1)
483 return delim_len;
484
485 if (delim_len > num_cols)
486 return 0;
487
488 SETCOLOR(MT_COLOR_DIVIDER);
489
490 int i;
491 for (i = 0; i < num_rows; i++)
492 {
493 mutt_window_move (MuttSidebarWindow, i, SidebarWidth - delim_len); //RAR 0 for rhs
494 addstr (NONULL(SidebarDividerChar));
495 }
496
497 return delim_len;
498}
499
500/**
501 * fill_empty_space - Wipe the remaining Sidebar space
502 * @first_row: Window line to start (0-based)
503 * @num_rows: Number of rows to fill
504 * @width: Width of the Sidebar (minus the divider)
505 *
506 * Write spaces over the area the sidebar isn't using.
507 */
508static void fill_empty_space (int first_row, int num_rows, int width)
509{
510 /* Fill the remaining rows with blank space */
511 SETCOLOR(MT_COLOR_NORMAL);
512
513 int r;
514 for (r = 0; r < num_rows; r++)
515 {
516 mutt_window_move (MuttSidebarWindow, first_row + r, 0); //RAR rhs
517 int i;
518 for (i = 0; i < width; i++)
519 addch (' ');
520 }
521}
522
523/**
524 * draw_sidebar - Write out a list of mailboxes, on the left
525 * @num_rows: Height of the Sidebar
526 * @num_cols: Width of the Sidebar
527 * @div_width: Width in screen characters taken by the divider
528 *
529 * Display a list of mailboxes in a panel on the left. What's displayed will
530 * depend on our index markers: TopBuffy, OpnBuffy, HilBuffy, BotBuffy.
531 * On the first run they'll be NULL, so we display the top of Mutt's list
532 * (Incoming).
533 *
534 * TopBuffy - first visible mailbox
535 * BotBuffy - last visible mailbox
536 * OpnBuffy - mailbox shown in Mutt's Index Panel
537 * HilBuffy - Unselected mailbox (the paging follows this)
538 *
539 * The entries are formatted using "sidebar_format" and may be abbreviated:
540 * "sidebar_short_path", indented: "sidebar_folder_indent",
541 * "sidebar_indent_string" and sorted: "sidebar_sort_method". Finally, they're
542 * trimmed to fit the available space.
543 */
544static void draw_sidebar (int num_rows, int num_cols, int div_width)
545{
546 int entryidx;
547 SBENTRY *entry;
548 BUFFY *b;
549 if (TopIndex < 0)
550 return;
551
552 int w = MIN(num_cols, (SidebarWidth - div_width));
553 int row = 0;
554 for (entryidx = TopIndex; (entryidx < EntryCount) && (row < num_rows); entryidx++)
555 {
556 entry = Entries[entryidx];
557 if (entry->is_hidden)
558 continue;
559 b = entry->buffy;
560
561 if (entryidx == OpnIndex)
562 {
563 if ((ColorDefs[MT_COLOR_SB_INDICATOR] != 0))
564 SETCOLOR(MT_COLOR_SB_INDICATOR);
565 else
566 SETCOLOR(MT_COLOR_INDICATOR);
567 }
568 else if (entryidx == HilIndex)
569 SETCOLOR(MT_COLOR_HIGHLIGHT);
570 else if ((b->msg_unread > 0) || (b->new))
571 SETCOLOR(MT_COLOR_NEW);
572 else if (b->msg_flagged > 0)
573 SETCOLOR(MT_COLOR_FLAGGED);
574 else if ((ColorDefs[MT_COLOR_SB_SPOOLFILE] != 0) &&
575 (mutt_strcmp (b->path, Spoolfile) == 0))
576 SETCOLOR(MT_COLOR_SB_SPOOLFILE);
577 else
578 SETCOLOR(MT_COLOR_NORMAL);
579
580 mutt_window_move (MuttSidebarWindow, row, 0);
581 if (Context && Context->realpath &&
582 !mutt_strcmp (b->realpath, Context->realpath))
583 {
584 b->msg_unread = Context->unread;
585 b->msg_count = Context->msgcount;
586 b->msg_flagged = Context->flagged;
587 }
588
589 /* compute length of Maildir without trailing separator */
590 size_t maildirlen = mutt_strlen (Maildir);
591 if (maildirlen &&
592 SidebarDelimChars &&
593 strchr (SidebarDelimChars, Maildir[maildirlen - 1]))
594 maildirlen--;
595
596 /* check whether Maildir is a prefix of the current folder's path */
597 short maildir_is_prefix = 0;
598 if ((mutt_strlen (b->path) > maildirlen) &&
599 (mutt_strncmp (Maildir, b->path, maildirlen) == 0) &&
600 SidebarDelimChars &&
601 strchr (SidebarDelimChars, b->path[maildirlen]))
602 maildir_is_prefix = 1;
603
604 /* calculate depth of current folder and generate its display name with indented spaces */
605 int sidebar_folder_depth = 0;
606 char *sidebar_folder_name;
607 int i;
608 if (option (OPTSIDEBARSHORTPATH))
609 {
610 /* disregard a trailing separator, so strlen() - 2 */
611 sidebar_folder_name = b->path;
612 for (i = mutt_strlen (sidebar_folder_name) - 2; i >= 0; i--)
613 {
614 if (SidebarDelimChars &&
615 strchr (SidebarDelimChars, sidebar_folder_name[i]))
616 {
617 sidebar_folder_name += (i + 1);
618 break;
619 }
620 }
621 }
622 else
623 sidebar_folder_name = b->path + maildir_is_prefix * (maildirlen + 1);
624
625 if (maildir_is_prefix && option (OPTSIDEBARFOLDERINDENT))
626 {
627 const char *tmp_folder_name;
628 int lastsep = 0;
629 tmp_folder_name = b->path + maildirlen + 1;
630 int tmplen = (int) mutt_strlen (tmp_folder_name) - 1;
631 for (i = 0; i < tmplen; i++)
632 {
633 if (SidebarDelimChars && strchr (SidebarDelimChars, tmp_folder_name[i]))
634 {
635 sidebar_folder_depth++;
636 lastsep = i + 1;
637 }
638 }
639 if (sidebar_folder_depth > 0)
640 {
641 if (option (OPTSIDEBARSHORTPATH))
642 tmp_folder_name += lastsep; /* basename */
643 int sfn_len = mutt_strlen (tmp_folder_name) +
644 sidebar_folder_depth*mutt_strlen (SidebarIndentString) + 1;
645 sidebar_folder_name = safe_malloc (sfn_len);
646 sidebar_folder_name[0]=0;
647 for (i=0; i < sidebar_folder_depth; i++)
648 safe_strcat (sidebar_folder_name, sfn_len, NONULL(SidebarIndentString));
649 safe_strcat (sidebar_folder_name, sfn_len, tmp_folder_name);
650 }
651 }
652 char str[STRING];
653 make_sidebar_entry (str, sizeof (str), w, sidebar_folder_name, entry);
654 printw ("%s", str);
655 if (sidebar_folder_depth > 0)
656 FREE (&sidebar_folder_name);
657 row++;
658 }
659
660 fill_empty_space (row, num_rows - row, w);
661}
662
663
664/**
665 * mutt_sb_draw - Completely redraw the sidebar
666 *
667 * Completely refresh the sidebar region. First draw the divider; then, for
668 * each BUFFY, call make_sidebar_entry; finally blank out any remaining space.
669 */
670void mutt_sb_draw (void)
671{
672 if (!option (OPTSIDEBAR))
673 return;
674
675 int num_rows = MuttSidebarWindow->rows;
676 int num_cols = MuttSidebarWindow->cols;
677
678 int div_width = draw_divider (num_rows, num_cols);
679 if (div_width < 0)
680 return;
681
682 if (!Incoming)
683 {
684 fill_empty_space (0, num_rows, SidebarWidth - div_width);
685 return;
686 }
687
688 if (!prepare_sidebar (num_rows))
689 return;
690
691 draw_sidebar (num_rows, num_cols, div_width);
692}
693
694/**
695 * select_next - Selects the next unhidden mailbox
696 *
697 * Returns:
698 * 1: Success
699 * 0: Failure
700 */
701static int select_next (void)
702{
703 int entry = HilIndex;
704
705 if (!EntryCount || HilIndex < 0)
706 return 0;
707
708 do
709 {
710 entry++;
711 if (entry == EntryCount)
712 return 0;
713 } while (Entries[entry]->is_hidden);
714
715 HilIndex = entry;
716 return 1;
717}
718
719/**
720 * select_next_new - Selects the next new mailbox
721 *
722 * Search down the list of mail folders for one containing new mail.
723 *
724 * Returns:
725 * 1: Success
726 * 0: Failure
727 */
728static int select_next_new (void)
729{
730 int entry = HilIndex;
731
732 if (!EntryCount || HilIndex < 0)
733 return 0;
734
735 do
736 {
737 entry++;
738 if (entry == EntryCount)
739 {
740 if (option (OPTSIDEBARNEXTNEWWRAP))
741 entry = 0;
742 else
743 return 0;
744 }
745 if (entry == HilIndex)
746 return 0;
747 } while (!Entries[entry]->buffy->new &&
748 !Entries[entry]->buffy->msg_unread);
749
750 HilIndex = entry;
751 return 1;
752}
753
754/**
755 * select_prev - Selects the previous unhidden mailbox
756 *
757 * Returns:
758 * 1: Success
759 * 0: Failure
760 */
761static int select_prev (void)
762{
763 int entry = HilIndex;
764
765 if (!EntryCount || HilIndex < 0)
766 return 0;
767
768 do
769 {
770 entry--;
771 if (entry < 0)
772 return 0;
773 } while (Entries[entry]->is_hidden);
774
775 HilIndex = entry;
776 return 1;
777}
778
779/**
780 * select_prev_new - Selects the previous new mailbox
781 *
782 * Search up the list of mail folders for one containing new mail.
783 *
784 * Returns:
785 * 1: Success
786 * 0: Failure
787 */
788static int select_prev_new (void)
789{
790 int entry = HilIndex;
791
792 if (!EntryCount || HilIndex < 0)
793 return 0;
794
795 do
796 {
797 entry--;
798 if (entry < 0)
799 {
800 if (option (OPTSIDEBARNEXTNEWWRAP))
801 entry = EntryCount - 1;
802 else
803 return 0;
804 }
805 if (entry == HilIndex)
806 return 0;
807 } while (!Entries[entry]->buffy->new &&
808 !Entries[entry]->buffy->msg_unread);
809
810 HilIndex = entry;
811 return 1;
812}
813
814/**
815 * select_page_down - Selects the first entry in the next page of mailboxes
816 *
817 * Returns:
818 * 1: Success
819 * 0: Failure
820 */
821static int select_page_down (void)
822{
823 int orig_hil_index = HilIndex;
824
825 if (!EntryCount || BotIndex < 0)
826 return 0;
827
828 HilIndex = BotIndex;
829 select_next ();
830 /* If the rest of the entries are hidden, go up to the last unhidden one */
831 if (Entries[HilIndex]->is_hidden)
832 select_prev ();
833
834 return (orig_hil_index != HilIndex);
835}
836
837/**
838 * select_page_up - Selects the last entry in the previous page of mailboxes
839 *
840 * Returns:
841 * 1: Success
842 * 0: Failure
843 */
844static int select_page_up (void)
845{
846 int orig_hil_index = HilIndex;
847
848 if (!EntryCount || TopIndex < 0)
849 return 0;
850
851 HilIndex = TopIndex;
852 select_prev ();
853 /* If the rest of the entries are hidden, go down to the last unhidden one */
854 if (Entries[HilIndex]->is_hidden)
855 select_next ();
856
857 return (orig_hil_index != HilIndex);
858}
859
860/**
861 * mutt_sb_change_mailbox - Change the selected mailbox
862 * @op: Operation code
863 *
864 * Change the selected mailbox, e.g. "Next mailbox", "Previous Mailbox
865 * with new mail". The operations are listed OPS.SIDEBAR which is built
866 * into an enum in keymap_defs.h.
867 *
868 * If the operation is successful, HilBuffy will be set to the new mailbox.
869 * This function only *selects* the mailbox, doesn't *open* it.
870 *
871 * Allowed values are: OP_SIDEBAR_NEXT, OP_SIDEBAR_NEXT_NEW,
872 * OP_SIDEBAR_PAGE_DOWN, OP_SIDEBAR_PAGE_UP, OP_SIDEBAR_PREV,
873 * OP_SIDEBAR_PREV_NEW.
874 */
875void mutt_sb_change_mailbox (int op)
876{
877 if (!option (OPTSIDEBAR))
878 return;
879
880 if (HilIndex < 0) /* It'll get reset on the next draw */
881 return;
882
883 switch (op)
884 {
885 case OP_SIDEBAR_NEXT:
886 if (! select_next ())
887 return;
888 break;
889 case OP_SIDEBAR_NEXT_NEW:
890 if (! select_next_new ())
891 return;
892 break;
893 case OP_SIDEBAR_PAGE_DOWN:
894 if (! select_page_down ())
895 return;
896 break;
897 case OP_SIDEBAR_PAGE_UP:
898 if (! select_page_up ())
899 return;
900 break;
901 case OP_SIDEBAR_PREV:
902 if (! select_prev ())
903 return;
904 break;
905 case OP_SIDEBAR_PREV_NEW:
906 if (! select_prev_new ())
907 return;
908 break;
909 default:
910 return;
911 }
912 SidebarNeedsRedraw = 1;
913}
914
915/**
916 * mutt_sb_set_buffystats - Update the BUFFY's message counts from the CONTEXT
917 * @ctx: A mailbox CONTEXT
918 *
919 * Given a mailbox CONTEXT, find a matching mailbox BUFFY and copy the message
920 * counts into it.
921 */
922void mutt_sb_set_buffystats (const CONTEXT *ctx)
923{
924 /* Even if the sidebar's hidden,
925 * we should take note of the new data. */
926 BUFFY *b = Incoming;
927 if (!ctx || !b)
928 return;
929
930 for (; b; b = b->next)
931 {
932 if (!mutt_strcmp (b->realpath, ctx->realpath))
933 {
934 b->msg_unread = ctx->unread;
935 b->msg_count = ctx->msgcount;
936 b->msg_flagged = ctx->flagged;
937 break;
938 }
939 }
940}
941
942/**
943 * mutt_sb_get_highlight - Get the BUFFY that's highlighted in the sidebar
944 *
945 * Get the path of the mailbox that's highlighted in the sidebar.
946 *
947 * Returns:
948 * Mailbox path
949 */
950const char *mutt_sb_get_highlight (void)
951{
952 if (!option (OPTSIDEBAR))
953 return NULL;
954
955 if (!EntryCount || HilIndex < 0)
956 return NULL;
957
958 return Entries[HilIndex]->buffy->path;
959}
960
961/**
962 * mutt_sb_set_open_buffy - Set the OpnBuffy based on the global Context
963 *
964 * Search through the list of mailboxes. If a BUFFY has a matching path, set
965 * OpnBuffy to it.
966 */
967void mutt_sb_set_open_buffy (void)
968{
969 int entry;
970
971 OpnIndex = -1;
972
973 if (!Context)
974 return;
975
976 for (entry = 0; entry < EntryCount; entry++)
977 {
978 if (!mutt_strcmp (Entries[entry]->buffy->realpath, Context->realpath))
979 {
980 OpnIndex = entry;
981 HilIndex = entry;
982 break;
983 }
984 }
985}
986
987/**
988 * mutt_sb_notify_mailbox - The state of a BUFFY is about to change
989 *
990 * We receive a notification:
991 * After a new BUFFY has been created
992 * Before a BUFFY is deleted
993 *
994 * Before a deletion, check that our pointers won't be invalidated.
995 */
996void mutt_sb_notify_mailbox (BUFFY *b, int created)
997{
998 int del_index;
999
1000 if (!b)
1001 return;
1002
1003 /* Any new/deleted mailboxes will cause a refresh. As long as
1004 * they're valid, our pointers will be updated in prepare_sidebar() */
1005
1006 if (created)
1007 {
1008 if (EntryCount >= EntryLen)
1009 {
1010 EntryLen += 10;
1011 safe_realloc (&Entries, EntryLen * sizeof (SBENTRY *));
1012 }
1013 Entries[EntryCount] = safe_calloc (1, sizeof(SBENTRY));
1014 Entries[EntryCount]->buffy = b;
1015
1016 if (TopIndex < 0)
1017 TopIndex = EntryCount;
1018 if (BotIndex < 0)
1019 BotIndex = EntryCount;
1020 if ((OpnIndex < 0) && Context &&
1021 (mutt_strcmp (b->realpath, Context->realpath) == 0))
1022 OpnIndex = EntryCount;
1023
1024 EntryCount++;
1025 }
1026 else
1027 {
1028 for (del_index = 0; del_index < EntryCount; del_index++)
1029 if (Entries[del_index]->buffy == b)
1030 break;
1031 if (del_index == EntryCount)
1032 return;
1033 FREE (&Entries[del_index]);
1034 EntryCount--;
1035
1036 if (TopIndex > del_index || TopIndex == EntryCount)
1037 TopIndex--;
1038 if (OpnIndex == del_index)
1039 OpnIndex = -1;
1040 else if (OpnIndex > del_index)
1041 OpnIndex--;
1042 if (HilIndex > del_index || HilIndex == EntryCount)
1043 HilIndex--;
1044 if (BotIndex > del_index || BotIndex == EntryCount)
1045 BotIndex--;
1046
1047 for (; del_index < EntryCount; del_index++)
1048 Entries[del_index] = Entries[del_index + 1];
1049 }
1050
1051 SidebarNeedsRedraw = 1;
1052}