mutt stable branch with some hacks
at jcs 1173 lines 30 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-2017 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, const 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_UNREAD: 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 (mutt_b2s (b1->pathbuf), mutt_b2s (b2->pathbuf)); 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, mutt_b2s (sbe->buffy->pathbuf))) 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_UNREAD) || 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 * calculate_depth - Calculate depth of path based on SidebarDelimChars. 525 * 526 * If lastpath is not NULL, common_depth is also calculated. These 527 * are used for indentation and short_path calculation. 528 */ 529static void calculate_depth (const char *path, const char *lastpath, 530 int *depth, int *common_depth) 531{ 532 int i, has_trailing_delim = 0; 533 534 *depth = *common_depth = 0; 535 if (!SidebarDelimChars || !path) 536 return; 537 538 for (i = 0; path[i]; i++) 539 { 540 if (strchr (SidebarDelimChars, path[i])) 541 { 542 (*depth)++; 543 544 /* /a/b/c and /a/b/c/ both are a depth of 3. 545 * Only count the final '\0' if the last character wasn't a separator. 546 */ 547 if (!path[i+1]) 548 has_trailing_delim = 1; 549 } 550 551 if (lastpath) 552 { 553 /* path /a/b/c/d 554 * lastpath /a/b 555 * lastpath /a/ 556 * lastpath /a 557 * ^ 558 */ 559 if (strchr (SidebarDelimChars, path[i]) && 560 (strchr (SidebarDelimChars, lastpath[i]) || !lastpath[i])) 561 { 562 (*common_depth)++; 563 if (!lastpath[i]) 564 lastpath = NULL; 565 } 566 567 /* path /abc 568 * lastpath /ad 569 * lastpath /a/ 570 * lastpath /a 571 * ^ 572 */ 573 else if (!lastpath[i] || path[i] != lastpath[i]) 574 lastpath = NULL; 575 } 576 } 577 578 if (!has_trailing_delim) 579 { 580 (*depth)++; 581 582 /* path /a 583 * lastpath /a/b/c 584 * lastpath /a/ 585 * lastpath /a 586 * ^ 587 */ 588 if (lastpath && 589 (strchr (SidebarDelimChars, lastpath[i]) || !lastpath[i])) 590 (*common_depth)++; 591 } 592} 593 594#define SIDEBAR_MAX_INDENT 32 595 596/** 597 * draw_sidebar - Write out a list of mailboxes, on the left 598 * @num_rows: Height of the Sidebar 599 * @num_cols: Width of the Sidebar 600 * @div_width: Width in screen characters taken by the divider 601 * 602 * Display a list of mailboxes in a panel on the left. What's displayed will 603 * depend on our index markers: TopBuffy, OpnBuffy, HilBuffy, BotBuffy. 604 * On the first run they'll be NULL, so we display the top of Mutt's list 605 * (Incoming). 606 * 607 * TopBuffy - first visible mailbox 608 * BotBuffy - last visible mailbox 609 * OpnBuffy - mailbox shown in Mutt's Index Panel 610 * HilBuffy - Unselected mailbox (the paging follows this) 611 * 612 * The entries are formatted using "sidebar_format" and may be abbreviated: 613 * "sidebar_short_path", indented: "sidebar_folder_indent", 614 * "sidebar_indent_string" and sorted: "sidebar_sort_method". Finally, they're 615 * trimmed to fit the available space. 616 */ 617static void draw_sidebar (int num_rows, int num_cols, int div_width) 618{ 619 int entryidx; 620 SBENTRY *entry; 621 BUFFY *b; 622 int maildir_is_prefix; 623 int indent_width = -1; 624 int indent_depths[SIDEBAR_MAX_INDENT]; 625 const char *sidebar_folder_name; 626 BUFFER *pretty_folder_name, *last_folder_name, *indent_folder_name; 627 628 if (TopIndex < 0) 629 return; 630 631 pretty_folder_name = mutt_buffer_pool_get (); 632 last_folder_name = mutt_buffer_pool_get (); 633 indent_folder_name = mutt_buffer_pool_get (); 634 635 int w = MIN(num_cols, (SidebarWidth - div_width)); 636 int row = 0; 637 for (entryidx = TopIndex; (entryidx < EntryCount) && (row < num_rows); entryidx++) 638 { 639 entry = Entries[entryidx]; 640 if (entry->is_hidden) 641 continue; 642 b = entry->buffy; 643 644 if (entryidx == OpnIndex) 645 { 646 if ((ColorDefs[MT_COLOR_SB_INDICATOR] != 0)) 647 SETCOLOR(MT_COLOR_SB_INDICATOR); 648 else 649 SETCOLOR(MT_COLOR_INDICATOR); 650 } 651 else if (entryidx == HilIndex) 652 SETCOLOR(MT_COLOR_HIGHLIGHT); 653 else if ((b->msg_unread > 0) || (b->new)) 654 SETCOLOR(MT_COLOR_NEW); 655 else if (b->msg_flagged > 0) 656 SETCOLOR(MT_COLOR_FLAGGED); 657 else if ((ColorDefs[MT_COLOR_SB_SPOOLFILE] != 0) && 658 (mutt_strcmp (mutt_b2s (b->pathbuf), Spoolfile) == 0)) 659 SETCOLOR(MT_COLOR_SB_SPOOLFILE); 660 else 661 SETCOLOR(MT_COLOR_NORMAL); 662 663 mutt_window_move (MuttSidebarWindow, row, 0); 664 if (Context && Context->realpath && 665 !mutt_strcmp (b->realpath, Context->realpath)) 666 { 667 b->msg_unread = Context->unread; 668 b->msg_count = Context->msgcount; 669 b->msg_flagged = Context->flagged; 670 } 671 672 maildir_is_prefix = 0; 673 if (option (OPTSIDEBARUSEMBSHORTCUTS)) 674 { 675 mutt_buffer_strcpy (pretty_folder_name, mutt_b2s (b->pathbuf)); 676 mutt_buffer_pretty_mailbox (pretty_folder_name); 677 sidebar_folder_name = mutt_b2s (pretty_folder_name); 678 if (sidebar_folder_name[0] == '=') 679 maildir_is_prefix = 1; 680 } 681 else 682 { 683 /* compute length of Maildir without trailing separator */ 684 size_t maildirlen = mutt_strlen (Maildir); 685 if (maildirlen && 686 SidebarDelimChars && 687 strchr (SidebarDelimChars, Maildir[maildirlen - 1])) 688 maildirlen--; 689 690 /* check whether Maildir is a prefix of the current folder's path */ 691 if ((mutt_buffer_len (b->pathbuf) > maildirlen) && 692 (mutt_strncmp (Maildir, mutt_b2s (b->pathbuf), maildirlen) == 0) && 693 SidebarDelimChars && 694 strchr (SidebarDelimChars, mutt_b2s (b->pathbuf)[maildirlen])) 695 { 696 sidebar_folder_name = mutt_b2s (b->pathbuf) + (maildirlen + 1); 697 maildir_is_prefix = 1; 698 } 699 else 700 sidebar_folder_name = mutt_b2s (b->pathbuf); 701 } 702 703 if (SidebarDelimChars) 704 { 705 int parent_depth = 0; 706 int i; 707 708 if (option (OPTSIDEBARSHORTPATH) || option (OPTSIDEBARFOLDERINDENT)) 709 { 710 int depth = 0, common_depth = 0; 711 712 calculate_depth (sidebar_folder_name, mutt_b2s (last_folder_name), 713 &depth, &common_depth); 714 715 if (option(OPTSIDEBARRELSPINDENT)) 716 { 717 mutt_buffer_strcpy (last_folder_name, sidebar_folder_name); 718 719 if (indent_width < SIDEBAR_MAX_INDENT) 720 indent_width++; 721 722 /* indent_depths[] hold the path depths at each level of indentation. 723 * Indent based off the longest path that we share in common. 724 * 725 * The 'indent_depths[] >= depth' test below is for a corner case: 726 * 727 * path depth common_depth indent_width 728 * /a 2 0 0 729 * /a/b 3 2 1 730 * /a/b/ 3 3 1 731 * 732 * Because the common_depth of /a/b/ matches the depth of 733 * /a/b, we need the additional test to continue popping the 734 * indent_depths[] stack. 735 */ 736 while (indent_width && 737 ((indent_depths[indent_width - 1] > common_depth) || 738 (indent_depths[indent_width - 1] >= depth))) 739 indent_width--; 740 741 if (indent_width < SIDEBAR_MAX_INDENT) 742 indent_depths[indent_width] = depth; 743 if (indent_width) 744 parent_depth = indent_depths[indent_width - 1]; 745 } 746 else 747 { 748 parent_depth = depth - 1; 749 indent_width = maildir_is_prefix ? depth - 1 : 0; 750 } 751 } 752 753 if (option (OPTSIDEBARSHORTPATH) && (parent_depth > 0)) 754 { 755 for (i = 0; parent_depth && sidebar_folder_name[i]; i++) 756 if (strchr (SidebarDelimChars, sidebar_folder_name[i])) 757 parent_depth--; 758 sidebar_folder_name += i; 759 } 760 761 if (option (OPTSIDEBARFOLDERINDENT) && (indent_width > 0)) 762 { 763 mutt_buffer_clear (indent_folder_name); 764 for (i = 0; i < indent_width; i++) 765 mutt_buffer_addstr (indent_folder_name, NONULL(SidebarIndentString)); 766 mutt_buffer_addstr (indent_folder_name, sidebar_folder_name); 767 sidebar_folder_name = mutt_b2s (indent_folder_name); 768 } 769 } 770 771 char str[STRING]; 772 make_sidebar_entry (str, sizeof (str), w, sidebar_folder_name, entry); 773 printw ("%s", str); 774 row++; 775 } 776 777 mutt_buffer_pool_release (&pretty_folder_name); 778 mutt_buffer_pool_release (&last_folder_name); 779 mutt_buffer_pool_release (&indent_folder_name); 780 781 fill_empty_space (row, num_rows - row, w); 782} 783 784 785/** 786 * mutt_sb_draw - Completely redraw the sidebar 787 * 788 * Completely refresh the sidebar region. First draw the divider; then, for 789 * each BUFFY, call make_sidebar_entry; finally blank out any remaining space. 790 */ 791void mutt_sb_draw (void) 792{ 793 if (!option (OPTSIDEBAR)) 794 return; 795 796 int num_rows = MuttSidebarWindow->rows; 797 int num_cols = MuttSidebarWindow->cols; 798 799 int div_width = draw_divider (num_rows, num_cols); 800 if (div_width < 0) 801 return; 802 803 if (!Incoming) 804 { 805 fill_empty_space (0, num_rows, SidebarWidth - div_width); 806 return; 807 } 808 809 if (!prepare_sidebar (num_rows)) 810 return; 811 812 draw_sidebar (num_rows, num_cols, div_width); 813} 814 815/** 816 * select_next - Selects the next unhidden mailbox 817 * 818 * Returns: 819 * 1: Success 820 * 0: Failure 821 */ 822static int select_next (void) 823{ 824 int entry = HilIndex; 825 826 if (!EntryCount || HilIndex < 0) 827 return 0; 828 829 do 830 { 831 entry++; 832 if (entry == EntryCount) 833 return 0; 834 } while (Entries[entry]->is_hidden); 835 836 HilIndex = entry; 837 return 1; 838} 839 840/** 841 * select_next_new - Selects the next new mailbox 842 * 843 * Search down the list of mail folders for one containing new mail. 844 * 845 * Returns: 846 * 1: Success 847 * 0: Failure 848 */ 849static int select_next_new (void) 850{ 851 int entry = HilIndex; 852 853 if (!EntryCount || HilIndex < 0) 854 return 0; 855 856 do 857 { 858 entry++; 859 if (entry == EntryCount) 860 { 861 if (option (OPTSIDEBARNEXTNEWWRAP)) 862 entry = 0; 863 else 864 return 0; 865 } 866 if (entry == HilIndex) 867 return 0; 868 } while (!Entries[entry]->buffy->new && 869 !Entries[entry]->buffy->msg_unread); 870 871 HilIndex = entry; 872 return 1; 873} 874 875/** 876 * select_prev - Selects the previous unhidden mailbox 877 * 878 * Returns: 879 * 1: Success 880 * 0: Failure 881 */ 882static int select_prev (void) 883{ 884 int entry = HilIndex; 885 886 if (!EntryCount || HilIndex < 0) 887 return 0; 888 889 do 890 { 891 entry--; 892 if (entry < 0) 893 return 0; 894 } while (Entries[entry]->is_hidden); 895 896 HilIndex = entry; 897 return 1; 898} 899 900/** 901 * select_prev_new - Selects the previous new mailbox 902 * 903 * Search up the list of mail folders for one containing new mail. 904 * 905 * Returns: 906 * 1: Success 907 * 0: Failure 908 */ 909static int select_prev_new (void) 910{ 911 int entry = HilIndex; 912 913 if (!EntryCount || HilIndex < 0) 914 return 0; 915 916 do 917 { 918 entry--; 919 if (entry < 0) 920 { 921 if (option (OPTSIDEBARNEXTNEWWRAP)) 922 entry = EntryCount - 1; 923 else 924 return 0; 925 } 926 if (entry == HilIndex) 927 return 0; 928 } while (!Entries[entry]->buffy->new && 929 !Entries[entry]->buffy->msg_unread); 930 931 HilIndex = entry; 932 return 1; 933} 934 935/** 936 * select_page_down - Selects the first entry in the next page of mailboxes 937 * 938 * Returns: 939 * 1: Success 940 * 0: Failure 941 */ 942static int select_page_down (void) 943{ 944 int orig_hil_index = HilIndex; 945 946 if (!EntryCount || BotIndex < 0) 947 return 0; 948 949 HilIndex = BotIndex; 950 select_next (); 951 /* If the rest of the entries are hidden, go up to the last unhidden one */ 952 if (Entries[HilIndex]->is_hidden) 953 select_prev (); 954 955 return (orig_hil_index != HilIndex); 956} 957 958/** 959 * select_page_up - Selects the last entry in the previous page of mailboxes 960 * 961 * Returns: 962 * 1: Success 963 * 0: Failure 964 */ 965static int select_page_up (void) 966{ 967 int orig_hil_index = HilIndex; 968 969 if (!EntryCount || TopIndex < 0) 970 return 0; 971 972 HilIndex = TopIndex; 973 select_prev (); 974 /* If the rest of the entries are hidden, go down to the last unhidden one */ 975 if (Entries[HilIndex]->is_hidden) 976 select_next (); 977 978 return (orig_hil_index != HilIndex); 979} 980 981/** 982 * mutt_sb_change_mailbox - Change the selected mailbox 983 * @op: Operation code 984 * 985 * Change the selected mailbox, e.g. "Next mailbox", "Previous Mailbox 986 * with new mail". The operations are listed OPS.SIDEBAR which is built 987 * into an enum in keymap_defs.h. 988 * 989 * If the operation is successful, HilBuffy will be set to the new mailbox. 990 * This function only *selects* the mailbox, doesn't *open* it. 991 * 992 * Allowed values are: OP_SIDEBAR_NEXT, OP_SIDEBAR_NEXT_NEW, 993 * OP_SIDEBAR_PAGE_DOWN, OP_SIDEBAR_PAGE_UP, OP_SIDEBAR_PREV, 994 * OP_SIDEBAR_PREV_NEW. 995 */ 996void mutt_sb_change_mailbox (int op) 997{ 998 if (!option (OPTSIDEBAR)) 999 return; 1000 1001 if (HilIndex < 0) /* It'll get reset on the next draw */ 1002 return; 1003 1004 switch (op) 1005 { 1006 case OP_SIDEBAR_NEXT: 1007 if (! select_next ()) 1008 return; 1009 break; 1010 case OP_SIDEBAR_NEXT_NEW: 1011 if (! select_next_new ()) 1012 return; 1013 break; 1014 case OP_SIDEBAR_PAGE_DOWN: 1015 if (! select_page_down ()) 1016 return; 1017 break; 1018 case OP_SIDEBAR_PAGE_UP: 1019 if (! select_page_up ()) 1020 return; 1021 break; 1022 case OP_SIDEBAR_PREV: 1023 if (! select_prev ()) 1024 return; 1025 break; 1026 case OP_SIDEBAR_PREV_NEW: 1027 if (! select_prev_new ()) 1028 return; 1029 break; 1030 default: 1031 return; 1032 } 1033 mutt_set_current_menu_redraw (REDRAW_SIDEBAR); 1034} 1035 1036/** 1037 * mutt_sb_set_buffystats - Update the BUFFY's message counts from the CONTEXT 1038 * @ctx: A mailbox CONTEXT 1039 * 1040 * Given a mailbox CONTEXT, find a matching mailbox BUFFY and copy the message 1041 * counts into it. 1042 */ 1043void mutt_sb_set_buffystats (const CONTEXT *ctx) 1044{ 1045 /* Even if the sidebar's hidden, 1046 * we should take note of the new data. */ 1047 BUFFY *b = Incoming; 1048 if (!ctx || !b) 1049 return; 1050 1051 for (; b; b = b->next) 1052 { 1053 if (!mutt_strcmp (b->realpath, ctx->realpath)) 1054 { 1055 b->msg_unread = ctx->unread; 1056 b->msg_count = ctx->msgcount; 1057 b->msg_flagged = ctx->flagged; 1058 break; 1059 } 1060 } 1061} 1062 1063/** 1064 * mutt_sb_get_highlight - Get the BUFFY that's highlighted in the sidebar 1065 * 1066 * Get the path of the mailbox that's highlighted in the sidebar. 1067 * 1068 * Returns: 1069 * Mailbox path 1070 */ 1071const char *mutt_sb_get_highlight (void) 1072{ 1073 if (!option (OPTSIDEBAR)) 1074 return NULL; 1075 1076 if (!EntryCount || HilIndex < 0) 1077 return NULL; 1078 1079 return mutt_b2s (Entries[HilIndex]->buffy->pathbuf); 1080} 1081 1082/** 1083 * mutt_sb_set_open_buffy - Set the OpnBuffy based on the global Context 1084 * 1085 * Search through the list of mailboxes. If a BUFFY has a matching path, set 1086 * OpnBuffy to it. 1087 */ 1088void mutt_sb_set_open_buffy (void) 1089{ 1090 int entry; 1091 1092 OpnIndex = -1; 1093 1094 if (!Context) 1095 return; 1096 1097 for (entry = 0; entry < EntryCount; entry++) 1098 { 1099 if (!mutt_strcmp (Entries[entry]->buffy->realpath, Context->realpath)) 1100 { 1101 OpnIndex = entry; 1102 HilIndex = entry; 1103 break; 1104 } 1105 } 1106} 1107 1108/** 1109 * mutt_sb_notify_mailbox - The state of a BUFFY is about to change 1110 * 1111 * We receive a notification: 1112 * After a new BUFFY has been created 1113 * Before a BUFFY is deleted 1114 * 1115 * Before a deletion, check that our pointers won't be invalidated. 1116 */ 1117void mutt_sb_notify_mailbox (BUFFY *b, int created) 1118{ 1119 int del_index; 1120 1121 if (!b) 1122 return; 1123 1124 /* Any new/deleted mailboxes will cause a refresh. As long as 1125 * they're valid, our pointers will be updated in prepare_sidebar() */ 1126 1127 if (created) 1128 { 1129 if (EntryCount >= EntryLen) 1130 { 1131 EntryLen += 10; 1132 safe_realloc (&Entries, EntryLen * sizeof (SBENTRY *)); 1133 } 1134 Entries[EntryCount] = safe_calloc (1, sizeof(SBENTRY)); 1135 Entries[EntryCount]->buffy = b; 1136 1137 if (TopIndex < 0) 1138 TopIndex = EntryCount; 1139 if (BotIndex < 0) 1140 BotIndex = EntryCount; 1141 if ((OpnIndex < 0) && Context && 1142 (mutt_strcmp (b->realpath, Context->realpath) == 0)) 1143 OpnIndex = EntryCount; 1144 1145 EntryCount++; 1146 } 1147 else 1148 { 1149 for (del_index = 0; del_index < EntryCount; del_index++) 1150 if (Entries[del_index]->buffy == b) 1151 break; 1152 if (del_index == EntryCount) 1153 return; 1154 FREE (&Entries[del_index]); 1155 EntryCount--; 1156 1157 if (TopIndex > del_index || TopIndex == EntryCount) 1158 TopIndex--; 1159 if (OpnIndex == del_index) 1160 OpnIndex = -1; 1161 else if (OpnIndex > del_index) 1162 OpnIndex--; 1163 if (HilIndex > del_index || HilIndex == EntryCount) 1164 HilIndex--; 1165 if (BotIndex > del_index || BotIndex == EntryCount) 1166 BotIndex--; 1167 1168 for (; del_index < EntryCount; del_index++) 1169 Entries[del_index] = Entries[del_index + 1]; 1170 } 1171 1172 mutt_set_current_menu_redraw (REDRAW_SIDEBAR); 1173}