mutt stable branch with some hacks
at master 1052 lines 27 kB view raw
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}