NeoMutt  2020-06-26-30-g76c339
Teaching an old dog new tricks
DOXYGEN
sidebar.c
Go to the documentation of this file.
1 
32 #include "config.h"
33 #include <stdbool.h>
34 #include <stdint.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include "mutt/lib.h"
39 #include "config/lib.h"
40 #include "email/lib.h"
41 #include "core/lib.h"
42 #include "gui/lib.h"
43 #include "sidebar.h"
44 #include "context.h"
45 #include "format_flags.h"
46 #include "globals.h"
47 #include "mutt_menu.h"
48 #include "muttlib.h"
49 #include "opcodes.h"
50 
51 /* These Config Variables are only used in sidebar.c */
66 
67 /* Previous values for some sidebar config */
68 static short PreviousSort = SORT_ORDER; /* sidebar_sort_method */
69 
73 struct SbEntry
74 {
75  char box[256];
76  struct Mailbox *mailbox;
77  bool is_hidden;
78 };
79 
80 static int EntryCount = 0;
81 static int EntryLen = 0;
82 static struct SbEntry **Entries = NULL;
83 
84 static int TopIndex = -1;
85 static int OpnIndex = -1;
86 static int HilIndex = -1;
87 static int BotIndex = -1;
88 
92 enum DivType
93 {
97 };
98 
118 static const char *sidebar_format_str(char *buf, size_t buflen, size_t col, int cols,
119  char op, const char *src, const char *prec,
120  const char *if_str, const char *else_str,
121  intptr_t data, MuttFormatFlags flags)
122 {
123  struct SbEntry *sbe = (struct SbEntry *) data;
124  char fmt[256];
125 
126  if (!sbe || !buf)
127  return src;
128 
129  buf[0] = '\0'; /* Just in case there's nothing to do */
130 
131  struct Mailbox *m = sbe->mailbox;
132  if (!m)
133  return src;
134 
135  bool c = Context && Context->mailbox &&
137 
138  bool optional = (flags & MUTT_FORMAT_OPTIONAL);
139 
140  switch (op)
141  {
142  case 'B':
143  mutt_format_s(buf, buflen, prec, sbe->box);
144  break;
145 
146  case 'd':
147  if (!optional)
148  {
149  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
150  snprintf(buf, buflen, fmt, c ? Context->mailbox->msg_deleted : 0);
151  }
152  else if ((c && (Context->mailbox->msg_deleted == 0)) || !c)
153  optional = false;
154  break;
155 
156  case 'D':
157  if (sbe->mailbox->name)
158  mutt_format_s(buf, buflen, prec, sbe->mailbox->name);
159  else
160  mutt_format_s(buf, buflen, prec, sbe->box);
161  break;
162 
163  case 'F':
164  if (!optional)
165  {
166  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
167  snprintf(buf, buflen, fmt, m->msg_flagged);
168  }
169  else if (m->msg_flagged == 0)
170  optional = false;
171  break;
172 
173  case 'L':
174  if (!optional)
175  {
176  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
177  snprintf(buf, buflen, fmt, c ? Context->mailbox->vcount : m->msg_count);
178  }
179  else if ((c && (Context->mailbox->vcount == m->msg_count)) || !c)
180  optional = false;
181  break;
182 
183  case 'N':
184  if (!optional)
185  {
186  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
187  snprintf(buf, buflen, fmt, m->msg_unread);
188  }
189  else if (m->msg_unread == 0)
190  optional = false;
191  break;
192 
193  case 'n':
194  if (!optional)
195  {
196  snprintf(fmt, sizeof(fmt), "%%%sc", prec);
197  snprintf(buf, buflen, fmt, m->has_new ? 'N' : ' ');
198  }
199  else if (m->has_new == false)
200  optional = false;
201  break;
202 
203  case 'o':
204  if (!optional)
205  {
206  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
207  snprintf(buf, buflen, fmt, m->msg_unread - m->msg_new);
208  }
209  else if ((c && (Context->mailbox->msg_unread - Context->mailbox->msg_new) == 0) || !c)
210  optional = false;
211  break;
212 
213  case 'r':
214  if (!optional)
215  {
216  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
217  snprintf(buf, buflen, fmt, m->msg_count - m->msg_unread);
218  }
219  else if ((c && (Context->mailbox->msg_count - Context->mailbox->msg_unread) == 0) || !c)
220  optional = false;
221  break;
222 
223  case 'S':
224  if (!optional)
225  {
226  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
227  snprintf(buf, buflen, fmt, m->msg_count);
228  }
229  else if (m->msg_count == 0)
230  optional = false;
231  break;
232 
233  case 't':
234  if (!optional)
235  {
236  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
237  snprintf(buf, buflen, fmt, c ? Context->mailbox->msg_tagged : 0);
238  }
239  else if ((c && (Context->mailbox->msg_tagged == 0)) || !c)
240  optional = false;
241  break;
242 
243  case 'Z':
244  if (!optional)
245  {
246  snprintf(fmt, sizeof(fmt), "%%%sd", prec);
247  snprintf(buf, buflen, fmt, m->msg_new);
248  }
249  else if ((c && (Context->mailbox->msg_new) == 0) || !c)
250  optional = false;
251  break;
252 
253  case '!':
254  if (m->msg_flagged == 0)
255  mutt_format_s(buf, buflen, prec, "");
256  else if (m->msg_flagged == 1)
257  mutt_format_s(buf, buflen, prec, "!");
258  else if (m->msg_flagged == 2)
259  mutt_format_s(buf, buflen, prec, "!!");
260  else
261  {
262  snprintf(fmt, sizeof(fmt), "%d!", m->msg_flagged);
263  mutt_format_s(buf, buflen, prec, fmt);
264  }
265  break;
266  }
267 
268  if (optional)
269  {
270  mutt_expando_format(buf, buflen, col, C_SidebarWidth, if_str,
271  sidebar_format_str, IP sbe, flags);
272  }
273  else if (flags & MUTT_FORMAT_OPTIONAL)
274  {
275  mutt_expando_format(buf, buflen, col, C_SidebarWidth, else_str,
276  sidebar_format_str, IP sbe, flags);
277  }
278 
279  /* We return the format string, unchanged */
280  return src;
281 }
282 
295 static void make_sidebar_entry(char *buf, size_t buflen, int width,
296  const char *box, struct SbEntry *sbe)
297 {
298  if (!buf)
299  return;
300 
301  if (box && sbe)
302  mutt_str_copy(sbe->box, box, sizeof(sbe->box));
303  else
304  buf[0] = '\0';
305 
306  mutt_expando_format(buf, buflen, 0, width, NONULL(C_SidebarFormat),
308 
309  /* Force string to be exactly the right width */
310  int w = mutt_strwidth(buf);
311  int s = mutt_str_len(buf);
312  width = MIN(buflen, width);
313  if (w < width)
314  {
315  /* Pad with spaces */
316  memset(buf + s, ' ', width - w);
317  buf[s + width - w] = '\0';
318  }
319  else if (w > width)
320  {
321  /* Truncate to fit */
322  size_t len = mutt_wstr_trunc(buf, buflen, width, NULL);
323  buf[len] = '\0';
324  }
325 }
326 
335 static int cb_qsort_sbe(const void *a, const void *b)
336 {
337  const struct SbEntry *sbe1 = *(struct SbEntry const *const *) a;
338  const struct SbEntry *sbe2 = *(struct SbEntry const *const *) b;
339  const struct Mailbox *m1 = sbe1->mailbox;
340  const struct Mailbox *m2 = sbe2->mailbox;
341 
342  int rc = 0;
343 
344  switch ((C_SidebarSortMethod & SORT_MASK))
345  {
346  case SORT_COUNT:
347  if (m2->msg_count == m1->msg_count)
348  rc = mutt_str_coll(mailbox_path(m1), mailbox_path(m2));
349  else
350  rc = (m2->msg_count - m1->msg_count);
351  break;
352  case SORT_UNREAD:
353  if (m2->msg_unread == m1->msg_unread)
354  rc = mutt_str_coll(mailbox_path(m1), mailbox_path(m2));
355  else
356  rc = (m2->msg_unread - m1->msg_unread);
357  break;
358  case SORT_DESC:
359  rc = mutt_str_cmp(m1->name, m2->name);
360  break;
361  case SORT_FLAGGED:
362  if (m2->msg_flagged == m1->msg_flagged)
363  rc = mutt_str_coll(mailbox_path(m1), mailbox_path(m2));
364  else
365  rc = (m2->msg_flagged - m1->msg_flagged);
366  break;
367  case SORT_PATH:
368  {
370  if (rc == 0)
371  rc = mutt_str_coll(mailbox_path(m1), mailbox_path(m2));
372  break;
373  }
374  }
375 
377  rc = -rc;
378 
379  return rc;
380 }
381 
393 static void update_entries_visibility(void)
394 {
395  /* Aliases for readability */
396  const bool new_only = C_SidebarNewMailOnly;
397  const bool non_empty_only = C_SidebarNonEmptyMailboxOnly;
398  struct SbEntry *sbe = NULL;
399 
400  /* Take the fast path if there is no need to test visibilities */
401  if (!new_only && !non_empty_only)
402  {
403  for (int i = 0; i < EntryCount; i++)
404  {
405  Entries[i]->is_hidden = false;
406  }
407  return;
408  }
409 
410  for (int i = 0; i < EntryCount; i++)
411  {
412  sbe = Entries[i];
413 
414  sbe->is_hidden = false;
415 
417  {
418  /* Spool directories are always visible */
419  continue;
420  }
421 
422  if (mutt_list_find(&SidebarWhitelist, mailbox_path(sbe->mailbox)) ||
423  mutt_list_find(&SidebarWhitelist, sbe->mailbox->name))
424  {
425  /* Explicitly asked to be visible */
426  continue;
427  }
428 
429  if (non_empty_only && (i != OpnIndex) && (sbe->mailbox->msg_count == 0))
430  {
431  sbe->is_hidden = true;
432  }
433 
434  if (new_only && (i != OpnIndex) && (sbe->mailbox->msg_unread == 0) &&
435  (sbe->mailbox->msg_flagged == 0) && !sbe->mailbox->has_new)
436  {
437  sbe->is_hidden = true;
438  }
439  }
440 }
441 
445 static void unsort_entries(void)
446 {
447  int i = 0;
448 
449  struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
451  struct MailboxNode *np = NULL;
452  STAILQ_FOREACH(np, &ml, entries)
453  {
454  if (i >= EntryCount)
455  break;
456 
457  int j = i;
458  while ((j < EntryCount) && (Entries[j]->mailbox != np->mailbox))
459  j++;
460  if (j < EntryCount)
461  {
462  if (j != i)
463  {
464  struct SbEntry *tmp = Entries[i];
465  Entries[i] = Entries[j];
466  Entries[j] = tmp;
467  }
468  i++;
469  }
470  }
472 }
473 
483 static void sort_entries(void)
484 {
485  enum SortType ssm = (C_SidebarSortMethod & SORT_MASK);
486 
487  /* These are the only sort methods we understand */
488  if ((ssm == SORT_COUNT) || (ssm == SORT_UNREAD) || (ssm == SORT_FLAGGED) || (ssm == SORT_PATH))
489  qsort(Entries, EntryCount, sizeof(*Entries), cb_qsort_sbe);
490  else if ((ssm == SORT_ORDER) && (C_SidebarSortMethod != PreviousSort))
491  unsort_entries();
492 }
493 
499 static bool select_next(void)
500 {
501  if ((EntryCount == 0) || (HilIndex < 0))
502  return false;
503 
504  int entry = HilIndex;
505 
506  do
507  {
508  entry++;
509  if (entry == EntryCount)
510  return false;
511  } while (Entries[entry]->is_hidden);
512 
513  HilIndex = entry;
514  return true;
515 }
516 
524 static bool select_next_new(void)
525 {
526  if ((EntryCount == 0) || (HilIndex < 0))
527  return false;
528 
529  int entry = HilIndex;
530 
531  do
532  {
533  entry++;
534  if (entry == EntryCount)
535  {
537  entry = 0;
538  else
539  return false;
540  }
541  if (entry == HilIndex)
542  return false;
543  } while (!Entries[entry]->mailbox->has_new && (Entries[entry]->mailbox->msg_unread == 0));
544 
545  HilIndex = entry;
546  return true;
547 }
548 
554 static bool select_prev(void)
555 {
556  if ((EntryCount == 0) || (HilIndex < 0))
557  return false;
558 
559  int entry = HilIndex;
560 
561  do
562  {
563  entry--;
564  if (entry < 0)
565  return false;
566  } while (Entries[entry]->is_hidden);
567 
568  HilIndex = entry;
569  return true;
570 }
571 
579 static bool select_prev_new(void)
580 {
581  if ((EntryCount == 0) || (HilIndex < 0))
582  return false;
583 
584  int entry = HilIndex;
585 
586  do
587  {
588  entry--;
589  if (entry < 0)
590  {
592  entry = EntryCount - 1;
593  else
594  return false;
595  }
596  if (entry == HilIndex)
597  return false;
598  } while (!Entries[entry]->mailbox->has_new && (Entries[entry]->mailbox->msg_unread == 0));
599 
600  HilIndex = entry;
601  return true;
602 }
603 
609 static bool select_page_down(void)
610 {
611  if ((EntryCount == 0) || (BotIndex < 0))
612  return false;
613 
614  int orig_hil_index = HilIndex;
615 
616  HilIndex = BotIndex;
617  select_next();
618  /* If the rest of the entries are hidden, go up to the last unhidden one */
619  if (Entries[HilIndex]->is_hidden)
620  select_prev();
621 
622  return (orig_hil_index != HilIndex);
623 }
624 
630 static bool select_page_up(void)
631 {
632  if ((EntryCount == 0) || (TopIndex < 0))
633  return false;
634 
635  int orig_hil_index = HilIndex;
636 
637  HilIndex = TopIndex;
638  select_prev();
639  /* If the rest of the entries are hidden, go down to the last unhidden one */
640  if (Entries[HilIndex]->is_hidden)
641  select_next();
642 
643  return (orig_hil_index != HilIndex);
644 }
645 
651 static bool select_first(void)
652 {
653  if ((EntryCount == 0) || (HilIndex < 0))
654  return false;
655 
656  int orig_hil_index = HilIndex;
657 
658  HilIndex = 0;
659  if (Entries[HilIndex]->is_hidden)
660  if (!select_next())
661  HilIndex = orig_hil_index;
662 
663  return (orig_hil_index != HilIndex);
664 }
665 
671 static bool select_last(void)
672 {
673  if ((EntryCount == 0) || (HilIndex < 0))
674  return false;
675 
676  int orig_hil_index = HilIndex;
677 
679  if (!select_prev())
680  HilIndex = orig_hil_index;
681 
682  return (orig_hil_index != HilIndex);
683 }
684 
697 static bool prepare_sidebar(int page_size)
698 {
699  if ((EntryCount == 0) || (page_size <= 0))
700  return false;
701 
702  const struct SbEntry *opn_entry = (OpnIndex >= 0) ? Entries[OpnIndex] : NULL;
703  const struct SbEntry *hil_entry = (HilIndex >= 0) ? Entries[HilIndex] : NULL;
704 
706  sort_entries();
707 
708  for (int i = 0; i < EntryCount; i++)
709  {
710  if (opn_entry == Entries[i])
711  OpnIndex = i;
712  if (hil_entry == Entries[i])
713  HilIndex = i;
714  }
715 
716  if ((HilIndex < 0) || Entries[HilIndex]->is_hidden || (C_SidebarSortMethod != PreviousSort))
717  {
718  if (OpnIndex >= 0)
719  HilIndex = OpnIndex;
720  else
721  {
722  HilIndex = 0;
723  if (Entries[HilIndex]->is_hidden)
724  select_next();
725  }
726  }
727 
728  /* Set the Top and Bottom to frame the HilIndex in groups of page_size */
729 
730  /* If C_SidebarNewMailOnly or C_SidebarNonEmptyMailboxOnly is set, some entries
731  * may be hidden so we need to scan for the framing interval */
733  {
734  TopIndex = -1;
735  BotIndex = -1;
736  while (BotIndex < HilIndex)
737  {
738  TopIndex = BotIndex + 1;
739  int page_entries = 0;
740  while (page_entries < page_size)
741  {
742  BotIndex++;
743  if (BotIndex >= EntryCount)
744  break;
745  if (!Entries[BotIndex]->is_hidden)
746  page_entries++;
747  }
748  }
749  }
750  /* Otherwise we can just calculate the interval */
751  else
752  {
753  TopIndex = (HilIndex / page_size) * page_size;
754  BotIndex = TopIndex + page_size - 1;
755  }
756 
757  if (BotIndex > (EntryCount - 1))
758  BotIndex = EntryCount - 1;
759 
761  return true;
762 }
763 
779 static int draw_divider(struct MuttWindow *win, int num_rows, int num_cols)
780 {
781  if ((num_rows < 1) || (num_cols < 1))
782  return 0;
783 
784  int delim_len;
785  enum DivType altchar = SB_DIV_UTF8;
786 
787  /* Calculate the width of the delimiter in screen cells */
788  delim_len = mutt_strwidth(C_SidebarDividerChar);
789  if (delim_len < 0)
790  {
791  delim_len = 1; /* Bad character */
792  }
793  else if (delim_len == 0)
794  {
796  return 0; /* User has set empty string */
797 
798  delim_len = 1; /* Unset variable */
799  }
800  else
801  {
802  altchar = SB_DIV_USER; /* User config */
803  }
804 
805  if (C_AsciiChars && (altchar != SB_DIV_ASCII))
806  {
807  /* $ascii_chars overrides Unicode divider chars */
808  if (altchar == SB_DIV_UTF8)
809  {
810  altchar = SB_DIV_ASCII;
811  }
812  else if (C_SidebarDividerChar)
813  {
814  for (int i = 0; i < delim_len; i++)
815  {
816  if (C_SidebarDividerChar[i] & ~0x7F) /* high-bit is set */
817  {
818  altchar = SB_DIV_ASCII;
819  delim_len = 1;
820  break;
821  }
822  }
823  }
824  }
825 
826  if (delim_len > num_cols)
827  return 0;
828 
830 
831  int col = C_SidebarOnRight ? 0 : (C_SidebarWidth - delim_len);
832 
833  for (int i = 0; i < num_rows; i++)
834  {
835  mutt_window_move(win, col, i);
836 
837  switch (altchar)
838  {
839  case SB_DIV_USER:
841  break;
842  case SB_DIV_ASCII:
843  mutt_window_addch('|');
844  break;
845  case SB_DIV_UTF8:
846  mutt_window_addch(ACS_VLINE);
847  break;
848  }
849  }
850 
851  return delim_len;
852 }
853 
864 static void fill_empty_space(struct MuttWindow *win, int first_row,
865  int num_rows, int div_width, int num_cols)
866 {
867  /* Fill the remaining rows with blank space */
869 
870  if (!C_SidebarOnRight)
871  div_width = 0;
872  for (int r = 0; r < num_rows; r++)
873  {
874  mutt_window_move(win, div_width, first_row + r);
875 
876  for (int i = 0; i < num_cols; i++)
877  mutt_window_addch(' ');
878  }
879 }
880 
887 static int imap_is_prefix(const char *folder, const char *mbox)
888 {
889  int plen = 0;
890 
891  struct Url *url_m = url_parse(mbox);
892  struct Url *url_f = url_parse(folder);
893  if (!url_m || !url_f)
894  goto done;
895 
896  if (!mutt_istr_equal(url_m->host, url_f->host))
897  goto done;
898 
899  if (url_m->user && url_f->user && !mutt_istr_equal(url_m->user, url_f->user))
900  goto done;
901 
902  size_t mlen = mutt_str_len(url_m->path);
903  size_t flen = mutt_str_len(url_f->path);
904  if (flen > mlen)
905  goto done;
906 
907  if (!mutt_strn_equal(url_m->path, url_f->path, flen))
908  goto done;
909 
910  plen = strlen(mbox) - mlen + flen;
911 
912 done:
913  url_free(&url_m);
914  url_free(&url_f);
915 
916  return plen;
917 }
918 
926 static const char *abbrev_folder(const char *mbox, const char *folder, enum MailboxType type)
927 {
928  if (!mbox || !folder)
929  return NULL;
930 
931  if (type == MUTT_IMAP)
932  {
933  int prefix = imap_is_prefix(folder, mbox);
934  if (prefix == 0)
935  return NULL;
936  return mbox + prefix;
937  }
938 
939  if (!C_SidebarDelimChars)
940  return NULL;
941 
942  size_t flen = mutt_str_len(folder);
943  if (flen == 0)
944  return NULL;
945  if (strchr(C_SidebarDelimChars, folder[flen - 1])) // folder ends with a delimiter
946  flen--;
947 
948  size_t mlen = mutt_str_len(mbox);
949  if (mlen <= flen)
950  return NULL;
951 
952  if (!mutt_strn_equal(folder, mbox, flen))
953  return NULL;
954 
955  // After the match, check that mbox has a delimiter
956  if (!strchr(C_SidebarDelimChars, mbox[flen]))
957  return NULL;
958 
959  return mbox + flen + 1;
960 }
961 
974 static const char *abbrev_url(const char *mbox, enum MailboxType type)
975 {
976  /* This is large enough to skip `notmuch://`,
977  * but not so large that it will go past the host part. */
978  const int scheme_len = 10;
979 
980  size_t len = mutt_str_len(mbox);
981  if ((len < scheme_len) || ((type != MUTT_NNTP) && (type != MUTT_IMAP) &&
982  (type != MUTT_NOTMUCH) && (type != MUTT_POP)))
983  {
984  return mbox;
985  }
986 
987  const char split = (type == MUTT_NOTMUCH) ? '?' : '/';
988 
989  // Skip over the scheme, e.g. `imaps://`, `notmuch://`
990  const char *last = strchr(mbox + scheme_len, split);
991  if (last)
992  mbox = last + 1;
993  return mbox;
994 }
995 
1002 static int calc_path_depth(const char *mbox, const char *delims, const char **last_part)
1003 {
1004  if (!mbox || !delims || !last_part)
1005  return 0;
1006 
1007  int depth = 0;
1008  const char *match = NULL;
1009  while ((match = strpbrk(mbox, delims)))
1010  {
1011  depth++;
1012  mbox = match + 1;
1013  }
1014 
1015  *last_part = mbox;
1016  return depth;
1017 }
1018 
1040 static void draw_sidebar(struct MuttWindow *win, int num_rows, int num_cols, int div_width)
1041 {
1042  struct SbEntry *entry = NULL;
1043  struct Mailbox *m = NULL;
1044  if (TopIndex < 0)
1045  return;
1046 
1047  int w = MIN(num_cols, (C_SidebarWidth - div_width));
1048  int row = 0;
1049  const char *display = NULL;
1050  struct Buffer result = mutt_buffer_make(256);
1051  for (int entryidx = TopIndex; (entryidx < EntryCount) && (row < num_rows); entryidx++)
1052  {
1053  entry = Entries[entryidx];
1054  if (entry->is_hidden)
1055  continue;
1056  m = entry->mailbox;
1057 
1058  if (entryidx == OpnIndex)
1059  {
1060  if ((Colors->defs[MT_COLOR_SIDEBAR_INDICATOR] != 0))
1062  else
1064  }
1065  else if (entryidx == HilIndex)
1067  else if (m->has_new)
1069  else if (m->msg_unread > 0)
1071  else if (m->msg_flagged > 0)
1073  else if ((Colors->defs[MT_COLOR_SIDEBAR_SPOOLFILE] != 0) &&
1075  {
1077  }
1078  else
1079  {
1082  else
1084  }
1085 
1086  int col = 0;
1087  if (C_SidebarOnRight)
1088  col = div_width;
1089 
1090  mutt_window_move(win, col, row);
1091  if (Context && Context->mailbox && (Context->mailbox->realpath[0] != '\0') &&
1093  {
1097  }
1098 
1099  const char *full_path = mailbox_path(m);
1100  display = m->name;
1101  if (!display)
1102  display = full_path;
1103 
1104  const char *abbr = m->name;
1105  if (!abbr)
1106  abbr = abbrev_folder(display, C_Folder, m->type);
1107  if (!abbr)
1108  abbr = abbrev_url(display, m->type);
1109 
1110  // Use the abbreviation if we have one. The full path is not preferable.
1111  if (abbr)
1112  display = abbr;
1113 
1114  const char *last_part = abbr;
1115  int depth = calc_path_depth(abbr, C_SidebarDelimChars, &last_part);
1116 
1117  // At this point, we don't have an abbreviation so let's keep track
1118  // before using short path.
1119  bool no_abbr = !mutt_strn_equal(display, full_path, mutt_str_len(display));
1120  if (C_SidebarShortPath)
1121  {
1122  display = last_part;
1123  }
1124 
1125  mutt_buffer_reset(&result);
1126 
1127  // Don't indent if we were unable to create an abbreviation.
1128  // Otherwise, the full path will be indent, and it looks unusual.
1129  if (C_SidebarFolderIndent && no_abbr)
1130  {
1131  if (C_SidebarComponentDepth > 0)
1132  depth -= C_SidebarComponentDepth;
1133 
1134  for (int i = 0; i < depth; i++)
1136  }
1137 
1138  mutt_buffer_addstr(&result, display);
1139 
1140  char str[256];
1141  make_sidebar_entry(str, sizeof(str), w, mutt_b2s(&result), entry);
1142  mutt_window_printf("%s", str);
1143  row++;
1144  }
1145  mutt_buffer_dealloc(&result);
1146 
1147  fill_empty_space(win, row, num_rows - row, div_width, w);
1148 }
1149 
1157 void sb_draw(struct MuttWindow *win)
1158 {
1159  if (!C_SidebarVisible || !win)
1160  return;
1161 
1162  if (!mutt_window_is_visible(win))
1163  return;
1164 
1165  int col = 0, row = 0;
1166  mutt_window_get_coords(win, &col, &row);
1167 
1168  int num_rows = win->state.rows;
1169  int num_cols = win->state.cols;
1170 
1171  int div_width = draw_divider(win, num_rows, num_cols);
1172 
1173  if (!Entries)
1174  {
1175  struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
1177  struct MailboxNode *np = NULL;
1178  STAILQ_FOREACH(np, &ml, entries)
1179  {
1181  }
1183  }
1184 
1185  if (!prepare_sidebar(num_rows))
1186  {
1187  fill_empty_space(win, 0, num_rows, div_width, num_cols - div_width);
1188  return;
1189  }
1190 
1191  draw_sidebar(win, num_rows, num_cols, div_width);
1192  mutt_window_move(win, col, row);
1193 }
1194 
1210 void sb_change_mailbox(int op)
1211 {
1212  if (!C_SidebarVisible)
1213  return;
1214 
1215  if (HilIndex < 0) /* It'll get reset on the next draw */
1216  return;
1217 
1218  switch (op)
1219  {
1220  case OP_SIDEBAR_FIRST:
1221  if (!select_first())
1222  return;
1223  break;
1224  case OP_SIDEBAR_LAST:
1225  if (!select_last())
1226  return;
1227  break;
1228  case OP_SIDEBAR_NEXT:
1229  if (!select_next())
1230  return;
1231  break;
1232  case OP_SIDEBAR_NEXT_NEW:
1233  if (!select_next_new())
1234  return;
1235  break;
1236  case OP_SIDEBAR_PAGE_DOWN:
1237  if (!select_page_down())
1238  return;
1239  break;
1240  case OP_SIDEBAR_PAGE_UP:
1241  if (!select_page_up())
1242  return;
1243  break;
1244  case OP_SIDEBAR_PREV:
1245  if (!select_prev())
1246  return;
1247  break;
1248  case OP_SIDEBAR_PREV_NEW:
1249  if (!select_prev_new())
1250  return;
1251  break;
1252  default:
1253  return;
1254  }
1256 }
1257 
1263 {
1264  if (!C_SidebarVisible)
1265  return NULL;
1266 
1267  if ((EntryCount == 0) || (HilIndex < 0))
1268  return NULL;
1269 
1270  return Entries[HilIndex]->mailbox;
1271 }
1272 
1281 {
1282  OpnIndex = -1;
1283 
1284  if (!m)
1285  return;
1286 
1287  for (int entry = 0; entry < EntryCount; entry++)
1288  {
1289  if (mutt_str_equal(Entries[entry]->mailbox->realpath, m->realpath))
1290  {
1291  OpnIndex = entry;
1292  HilIndex = entry;
1293  break;
1294  }
1295  }
1296 }
1297 
1311 {
1312  if (!m)
1313  return;
1314 
1315  /* Any new/deleted mailboxes will cause a refresh. As long as
1316  * they're valid, our pointers will be updated in prepare_sidebar() */
1317 
1318  if (sbn == SBN_CREATED)
1319  {
1320  if (EntryCount >= EntryLen)
1321  {
1322  EntryLen += 10;
1323  mutt_mem_realloc(&Entries, EntryLen * sizeof(struct SbEntry *));
1324  }
1325  Entries[EntryCount] = mutt_mem_calloc(1, sizeof(struct SbEntry));
1326  Entries[EntryCount]->mailbox = m;
1327 
1328  if (TopIndex < 0)
1329  TopIndex = EntryCount;
1330  if (BotIndex < 0)
1331  BotIndex = EntryCount;
1332  if ((OpnIndex < 0) && Context &&
1334  {
1335  OpnIndex = EntryCount;
1336  }
1337 
1338  EntryCount++;
1339  }
1340  else if (sbn == SBN_DELETED)
1341  {
1342  int del_index;
1343  for (del_index = 0; del_index < EntryCount; del_index++)
1344  if (Entries[del_index]->mailbox == m)
1345  break;
1346  if (del_index == EntryCount)
1347  return;
1348  FREE(&Entries[del_index]);
1349  EntryCount--;
1350 
1351  if ((TopIndex > del_index) || (TopIndex == EntryCount))
1352  TopIndex--;
1353  if (OpnIndex == del_index)
1354  OpnIndex = -1;
1355  else if (OpnIndex > del_index)
1356  OpnIndex--;
1357  if ((HilIndex > del_index) || (HilIndex == EntryCount))
1358  HilIndex--;
1359  if ((BotIndex > del_index) || (BotIndex == EntryCount))
1360  BotIndex--;
1361 
1362  for (; del_index < EntryCount; del_index++)
1363  Entries[del_index] = Entries[del_index + 1];
1364  }
1365 
1366  // otherwise, we just need to redraw
1367 
1369 }
1370 
1377 {
1378  if (!nc->event_data || !nc->global_data)
1379  return -1;
1380  if (nc->event_type != NT_CONFIG)
1381  return 0;
1382 
1383  struct MuttWindow *win = nc->global_data;
1384  struct EventConfig *ec = nc->event_data;
1385 
1386  if (!mutt_strn_equal(ec->name, "sidebar_", 8))
1387  return 0;
1388 
1389  bool repaint = false;
1390 
1391  if (win->state.visible != C_SidebarVisible)
1392  {
1394  repaint = true;
1395  }
1396 
1397  if (win->req_cols != C_SidebarWidth)
1398  {
1399  win->req_cols = C_SidebarWidth;
1400  repaint = true;
1401  }
1402 
1403  struct MuttWindow *parent = win->parent;
1404  struct MuttWindow *first = TAILQ_FIRST(&parent->children);
1405 
1406  if ((C_SidebarOnRight && (first == win)) || (!C_SidebarOnRight && (first != win)))
1407  {
1408  // Swap the Sidebar and the Container of the Index/Pager
1409  TAILQ_REMOVE(&parent->children, first, entries);
1410  TAILQ_INSERT_TAIL(&parent->children, first, entries);
1411  repaint = true;
1412  }
1413 
1414  if (repaint)
1415  {
1416  mutt_debug(LL_NOTIFY, "repaint sidebar\n");
1419  }
1420 
1421  return 0;
1422 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:879
Convenience wrapper for the gui headers.
The "current" mailbox.
Definition: context.h:37
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox&#39;s path string.
Definition: mailbox.h:194
int mutt_str_cmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:575
enum MailboxType type
Mailbox type.
Definition: mailbox.h:105
uint8_t MuttFormatFlags
Flags for mutt_expando_format(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: format_flags.h:29
#define NONULL(x)
Definition: string2.h:37
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
int msg_count
Total number of messages.
Definition: mailbox.h:91
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
SortType
Methods for sorting.
Definition: sort2.h:48
#define TAILQ_FIRST(head)
Definition: queue.h:716
#define MIN(a, b)
Definition: memory.h:31
Data passed to a notification function.
Definition: observer.h:39
int msg_deleted
Number of deleted messages.
Definition: mailbox.h:96
int msg_unread
Number of unread messages.
Definition: mailbox.h:92
Log of notifications.
Definition: logging.h:45
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: mailbox.h:52
Structs that make up an email.
The "currently-open" mailbox.
int msg_flagged
Number of flagged messages.
Definition: mailbox.h:93
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:66
void neomutt_mailboxlist_clear(struct MailboxList *ml)
Free a Mailbox List.
Definition: neomutt.c:135
void mutt_curses_set_color(enum ColorId color)
Set the current colour for text.
Definition: mutt_curses.c:55
Mailbox with no new or flagged messages.
Definition: color.h:89
Sort by the number of flagged emails.
Definition: sort2.h:67
int * defs
Array of all fixed colours, see enum ColorId.
Definition: color.h:131
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
static int const char * fmt
Definition: acutest.h:488
char * realpath
Used for duplicate detection, context comparison, and the sidebar.
Definition: mailbox.h:84
Match any Mailbox type.
Definition: mailbox.h:45
size_t neomutt_mailboxlist_get_all(struct MailboxList *head, struct NeoMutt *n, enum MailboxType type)
Get a List of all Mailboxes.
Definition: neomutt.c:158
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
A config-change event.
Definition: subset.h:70
Sort by the folder&#39;s path.
Definition: sort2.h:68
String manipulation buffer.
Definition: buffer.h:33
Line dividing sidebar from the index/pager.
Definition: color.h:84
A division of the screen.
Definition: mutt_window.h:108
Select cursor.
Definition: color.h:86
Info about folders in the sidebar.
Definition: sidebar.c:73
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:123
bool is_hidden
Don&#39;t show, e.g. $sidebar_new_mail_only.
Definition: sidebar.c:77
Flags to control mutt_expando_format()
All user-callable functions.
int mutt_str_coll(const char *a, const char *b)
Collate two strings (compare using locale), safely.
Definition: string.c:652
Container for Accounts, Notifications.
Definition: neomutt.h:36
Mailbox with new mail.
Definition: color.h:88
Current open mailbox.
Definition: color.h:87
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: format_flags.h:30
int vcount
The number of virtual messages.
Definition: mailbox.h:102
#define REDRAW_SIDEBAR
Redraw the sidebar.
Definition: mutt_menu.h:49
Convenience wrapper for the config headers.
int mutt_window_move(struct MuttWindow *win, int col, int row)
Move the cursor in a Window.
Definition: mutt_window.c:365
Hundreds of global variables to back the user variables.
enum NotifyType event_type
Send: Event type, e.g. NT_ACCOUNT.
Definition: observer.h:41
Some miscellaneous functions.
bool has_new
Mailbox has new mail.
Definition: mailbox.h:88
struct Mailbox * mailbox
Mailbox this represents.
Definition: sidebar.c:76
char * name
A short name for the Mailbox.
Definition: mailbox.h:85
void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src, format_t *callback, intptr_t data, MuttFormatFlags flags)
Expand expandos (x) in a string.
Definition: muttlib.c:862
struct Mailbox * mailbox
Definition: context.h:50
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:55
Convenience wrapper for the core headers.
struct ListNode * mutt_list_find(const struct ListHead *h, const char *data)
Find a string in a List.
Definition: list.c:102
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
void * global_data
Data from notify_observer_add()
Definition: observer.h:44
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
Plain text.
Definition: color.h:77
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:891
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:834
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:57
#define mutt_b2s(buf)
Definition: buffer.h:41
void mutt_window_get_coords(struct MuttWindow *win, int *col, int *row)
Get the cursor position in the Window.
Definition: mutt_window.c:298
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:113
&#39;POP3&#39; Mailbox type
Definition: mailbox.h:55
char box[256]
Formatted Mailbox name.
Definition: sidebar.c:75
bool visible
Window is visible.
Definition: mutt_window.h:56
bool mutt_window_is_visible(struct MuttWindow *win)
Is the Window visible?
Definition: mutt_window.c:630
WHERE char * C_Folder
Config: Base folder for a set of mailboxes.
Definition: globals.h:119
A mailbox.
Definition: mailbox.h:81
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1338
char * user
Username.
Definition: url.h:69
short rows
Number of rows, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:58
WHERE bool C_AsciiChars
Config: Use plain ASCII characters, when drawing email threads.
Definition: globals.h:191
int mutt_inbox_cmp(const char *a, const char *b)
do two folders share the same path and one is an inbox
Definition: muttlib.c:1683
size_t mutt_wstr_trunc(const char *src, size_t maxlen, size_t maxwid, size_t *width)
Work out how to truncate a widechar string.
Definition: curs_lib.c:1288
Selected item in list.
Definition: color.h:73
GUI present the user with a selectable list.
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:802
char * host
Host.
Definition: url.h:71
int msg_tagged
How many messages are tagged?
Definition: mailbox.h:97
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
Definition: color.h:129
char * path
Path.
Definition: url.h:73
void * event_data
Data from notify_send()
Definition: observer.h:43
&#39;Notmuch&#39; (virtual) Mailbox type
Definition: mailbox.h:54
bool mutt_strn_equal(const char *a, const char *b, size_t l)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:601
Config has changed, NotifyConfig, EventConfig.
Definition: notify_type.h:36
int mutt_window_printf(const char *fmt,...)
Write a formatted string to a Window.
Definition: mutt_window.c:549
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:639
int mutt_window_addch(int ch)
Write one character to a Window.
Definition: mutt_window.c:491
MailboxType
Supported mailbox formats.
Definition: mailbox.h:43
void mutt_format_s(char *buf, size_t buflen, const char *prec, const char *s)
Format a simple string.
Definition: curs_lib.c:1223
WHERE char * C_Spoolfile
Config: Inbox.
Definition: globals.h:140
Sort by the number of unread emails.
Definition: sort2.h:66
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:724
int msg_new
Number of new messages.
Definition: mailbox.h:95
void window_set_visible(struct MuttWindow *win, bool visible)
Set a Window (and its children) visible or hidden.
Definition: mutt_window.c:108
void mutt_window_reflow(struct MuttWindow *win)
Resize a Window and its children.
Definition: mutt_window.c:432
struct MuttWindow * parent
Parent Window.
Definition: mutt_window.h:120
Sort by the folder&#39;s description.
Definition: sort2.h:70
#define IP
Definition: set.h:54
#define FREE(x)
Definition: memory.h:40
Mailbox with unread mail.
Definition: color.h:91
struct MuttWindowList children
Children Windows.
Definition: mutt_window.h:121
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:321
#define MUTT_FORMAT_OPTIONAL
Allow optional field processing.
Definition: format_flags.h:33
Sort by number of emails in a folder.
Definition: sort2.h:65
int mutt_window_addstr(const char *str)
Write a string to a Window.
Definition: mutt_window.c:521
List of Mailboxes.
Definition: mailbox.h:145
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
Convenience wrapper for the library headers.
#define SORT_REVERSE
Reverse the order of the sort.
Definition: sort2.h:86
struct MuttWindow * MuttDialogWindow
Parent of all Dialogs.
Definition: mutt_window.c:44
&#39;IMAP&#39; Mailbox type
Definition: mailbox.h:53
const char * name
Name of config item that changed.
Definition: subset.h:73
Mailbox with flagged messages.
Definition: color.h:85
#define SORT_MASK
Mask for the sort id.
Definition: sort2.h:85
$spoolfile (Spool mailbox)
Definition: color.h:90
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:147
short req_cols
Number of columns required.
Definition: mutt_window.h:110
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:234