From: Richard Russon Date: Sat, 4 Jun 2016 18:31:56 +0000 (-0700) Subject: Add neomutt version of sidebar patch. (closes #3829) X-Git-Tag: neomutt-20160822~138 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=bab103348e1e649e685b8fbe3cfdf24ce65836ef;p=neomutt Add neomutt version of sidebar patch. (closes #3829) This is the patch from neomutt; branch 'devel/win-sidebar'; commit c796fa85f9cacefb69b8f7d8545fc9ba71674180 with the following changes: - move the sample muttrc and vimrc to contrib. - remove the README.sidebar. - empty out the PATCHES file. --- diff --git a/Makefile.am b/Makefile.am index 9afb6ce74..f2f67cf44 100644 --- a/Makefile.am +++ b/Makefile.am @@ -77,6 +77,12 @@ EXTRA_DIST = COPYRIGHT GPL OPS OPS.PGP OPS.CRYPT OPS.SMIME TODO UPDATING \ EXTRA_SCRIPTS = smime_keys +if BUILD_SIDEBAR +mutt_SOURCES += sidebar.c sidebar.h +endif + +EXTRA_DIST += OPS.SIDEBAR + mutt_dotlock_SOURCES = mutt_dotlock.c mutt_dotlock_LDADD = $(LIBOBJS) mutt_dotlock_DEPENDENCIES = $(LIBOBJS) @@ -129,10 +135,10 @@ smime_keys: $(srcdir)/smime_keys.pl keymap_defs.h: $(OPS) $(srcdir)/gen_defs $(srcdir)/gen_defs $(OPS) > keymap_defs.h -keymap_alldefs.h: $(srcdir)/OPS $(srcdir)/OPS.PGP $(srcdir)/OPS.MIX $(srcdir)/OPS.CRYPT $(srcdir)/OPS.SMIME $(srcdir)/gen_defs +keymap_alldefs.h: $(srcdir)/OPS $(srcdir)/OPS.SIDEBAR $(srcdir)/OPS.PGP $(srcdir)/OPS.MIX $(srcdir)/OPS.CRYPT $(srcdir)/OPS.SMIME $(srcdir)/gen_defs rm -f $@ $(srcdir)/gen_defs $(srcdir)/OPS $(srcdir)/OPS.PGP \ - $(srcdir)/OPS.MIX $(srcdir)/OPS.CRYPT $(srcdir)/OPS.SMIME \ + $(srcdir)/OPS.SIDEBAR $(srcdir)/OPS.MIX $(srcdir)/OPS.CRYPT $(srcdir)/OPS.SMIME \ > keymap_alldefs.h reldate.h: $(srcdir)/ChangeLog diff --git a/OPS.SIDEBAR b/OPS.SIDEBAR new file mode 100644 index 000000000..d09ec1f1d --- /dev/null +++ b/OPS.SIDEBAR @@ -0,0 +1,8 @@ +OP_SIDEBAR_NEXT "Move the highlight to next mailbox" +OP_SIDEBAR_NEXT_NEW "Move the highlight to next mailbox with new mail" +OP_SIDEBAR_OPEN "Open highlighted mailbox" +OP_SIDEBAR_PAGE_DOWN "Scroll the Sidebar down 1 page" +OP_SIDEBAR_PAGE_UP "Scroll the Sidebar up 1 page" +OP_SIDEBAR_PREV "Move the highlight to previous mailbox" +OP_SIDEBAR_PREV_NEW "Move the highlight to previous mailbox with new mail" +OP_SIDEBAR_TOGGLE_VISIBLE "Make the Sidebar (in)visible" diff --git a/buffy.c b/buffy.c index 733b8bf53..325a2d743 100644 --- a/buffy.c +++ b/buffy.c @@ -27,6 +27,10 @@ #include "mutt_curses.h" +#ifdef USE_SIDEBAR +#include "sidebar.h" +#endif + #ifdef USE_IMAP #include "imap.h" #endif @@ -196,9 +200,17 @@ void mutt_update_mailbox (BUFFY * b) static BUFFY *buffy_new (const char *path) { BUFFY* buffy; +#ifdef USE_SIDEBAR + char rp[PATH_MAX] = ""; + char *r = NULL; +#endif buffy = (BUFFY *) safe_calloc (1, sizeof (BUFFY)); strfcpy (buffy->path, path, sizeof (buffy->path)); +#ifdef USE_SIDEBAR + r = realpath (path, rp); + strfcpy (buffy->realpath, r ? rp : path, sizeof (buffy->realpath)); +#endif buffy->next = NULL; buffy->magic = 0; @@ -215,7 +227,10 @@ int mutt_parse_mailboxes (BUFFER *path, BUFFER *s, unsigned long data, BUFFER *e BUFFY **tmp,*tmp1; char buf[_POSIX_PATH_MAX]; struct stat sb; - char f1[PATH_MAX], f2[PATH_MAX]; + char f1[PATH_MAX]; +#ifndef USE_SIDEBAR + char f2[PATH_MAX]; +#endif char *p, *q; while (MoreArgs (s)) @@ -228,6 +243,9 @@ int mutt_parse_mailboxes (BUFFER *path, BUFFER *s, unsigned long data, BUFFER *e for (tmp = &Incoming; *tmp;) { tmp1=(*tmp)->next; +#ifdef USE_SIDEBAR + sb_notify_mailbox (*tmp, 0); +#endif buffy_free (tmp); *tmp=tmp1; } @@ -243,8 +261,13 @@ int mutt_parse_mailboxes (BUFFER *path, BUFFER *s, unsigned long data, BUFFER *e p = realpath (buf, f1); for (tmp = &Incoming; *tmp; tmp = &((*tmp)->next)) { +#ifdef USE_SIDEBAR + q = (*tmp)->realpath; + if (mutt_strcmp (p ? p : buf, q) == 0) +#else q = realpath ((*tmp)->path, f2); if (mutt_strcmp (p ? p : buf, q ? q : (*tmp)->path) == 0) +#endif { dprint(3,(debugfile,"mailbox '%s' already registered as '%s'\n", buf, (*tmp)->path)); break; @@ -256,14 +279,21 @@ int mutt_parse_mailboxes (BUFFER *path, BUFFER *s, unsigned long data, BUFFER *e if(*tmp) { tmp1=(*tmp)->next; +#ifdef USE_SIDEBAR + sb_notify_mailbox (*tmp, 0); +#endif buffy_free (tmp); *tmp=tmp1; } continue; } - if (!*tmp) + if (!*tmp) { *tmp = buffy_new (buf); +#ifdef USE_SIDEBAR + sb_notify_mailbox (*tmp, 1); +#endif + } (*tmp)->new = 0; (*tmp)->notified = 1; @@ -306,6 +336,13 @@ static int buffy_maildir_dir_hasnew(BUFFY* mailbox, const char *dir_name) return 0; } +#ifdef USE_SIDEBAR + if (option (OPTSIDEBAR) && mailbox->msg_unread > 0) { + mailbox->new = 1; + return 1; + } +#endif + if ((dirp = opendir (path)) == NULL) { mailbox->magic = 0; @@ -357,6 +394,89 @@ static int buffy_maildir_hasnew (BUFFY* mailbox) return 0; } + +#ifdef USE_SIDEBAR +/** + * buffy_maildir_update_dir - Update counts for one directory + * @mailbox: BUFFY representing a maildir mailbox + * @dir: Which directory to search + * + * Look through one directory of a maildir mailbox. The directory could + * be either "new" or "cur". + * + * Count how many new, or flagged, messages there are. + */ +static void +buffy_maildir_update_dir (BUFFY *mailbox, const char *dir) +{ + char path[_POSIX_PATH_MAX] = ""; + DIR *dirp = NULL; + struct dirent *de = NULL; + char *p = NULL; + int read; + + snprintf (path, sizeof (path), "%s/%s", mailbox->path, dir); + + dirp = opendir (path); + if (!dirp) { + mailbox->magic = 0; + return; + } + + while ((de = readdir (dirp)) != NULL) { + if (*de->d_name == '.') + continue; + + /* Matches maildir_parse_flags logic */ + read = 0; + mailbox->msg_count++; + p = strstr (de->d_name, ":2,"); + if (p) { + p += 3; + if (strchr (p, 'S')) + read = 1; + if (strchr (p, 'F')) + mailbox->msg_flagged++; + } + if (!read) { + mailbox->msg_unread++; + } + } + + closedir (dirp); +} + +/** + * buffy_maildir_update - Update messages counts for a maildir mailbox + * @mailbox: BUFFY representing a maildir mailbox + * + * Open a mailbox directories and update our record of how many new, or + * flagged, messages there are. + */ +void +buffy_maildir_update (BUFFY *mailbox) +{ + if (!option (OPTSIDEBAR)) + return; + + mailbox->msg_count = 0; + mailbox->msg_unread = 0; + mailbox->msg_flagged = 0; + + buffy_maildir_update_dir (mailbox, "new"); + if (mailbox->msg_count) { + mailbox->new = 1; + } + buffy_maildir_update_dir (mailbox, "cur"); + + mailbox->sb_last_checked = time (NULL); + + /* make sure the updates are actually put on screen */ + sb_draw(); +} + +#endif + /* returns 1 if mailbox has new mail */ static int buffy_mbox_hasnew (BUFFY* mailbox, struct stat *sb) { @@ -368,7 +488,11 @@ static int buffy_mbox_hasnew (BUFFY* mailbox, struct stat *sb) else statcheck = sb->st_mtime > sb->st_atime || (mailbox->newly_created && sb->st_ctime == sb->st_mtime && sb->st_ctime == sb->st_atime); +#ifdef USE_SIDEBAR + if ((!option (OPTSIDEBAR) && statcheck) || (option (OPTSIDEBAR) && mailbox->msg_unread > 0)) +#else if (statcheck) +#endif { if (!option(OPTMAILCHECKRECENT) || sb->st_mtime > mailbox->last_visited) { @@ -388,6 +512,40 @@ static int buffy_mbox_hasnew (BUFFY* mailbox, struct stat *sb) return rc; } +#ifdef USE_SIDEBAR +/** + * buffy_mbox_update - Update messages counts for an mbox mailbox + * @mailbox: BUFFY representing an mbox mailbox + * @sb: stat(2) infomation about the mailbox file + * + * Open a mbox file and update our record of how many new, or flagged, + * messages there are. If the mailbox hasn't changed since the last call, + * the function does nothing. + */ +void +buffy_mbox_update (BUFFY *mailbox, struct stat *sb) +{ + CONTEXT *ctx = NULL; + + if (!option (OPTSIDEBAR)) + return; + if ((mailbox->sb_last_checked > sb->st_mtime) && (mailbox->msg_count != 0)) + return; /* no check necessary */ + + ctx = mx_open_mailbox (mailbox->path, MUTT_READONLY | MUTT_QUIET | MUTT_NOSORT | MUTT_PEEK, NULL); + if (ctx) { + mailbox->msg_count = ctx->msgcount; + mailbox->msg_unread = ctx->unread; + mailbox->msg_flagged = ctx->flagged; + mailbox->sb_last_checked = time (NULL); + mx_close_mailbox (ctx, 0); + } + + /* make sure the updates are actually put on screen */ + sb_draw(); +} +#endif + int mutt_buffy_check (int force) { BUFFY *tmp; @@ -428,6 +586,9 @@ int mutt_buffy_check (int force) contex_sb.st_ino=0; } +#ifdef USE_SIDEBAR + int should_refresh = sb_should_refresh(); +#endif for (tmp = Incoming; tmp; tmp = tmp->next) { if (tmp->magic != MUTT_IMAP) @@ -461,16 +622,30 @@ int mutt_buffy_check (int force) { case MUTT_MBOX: case MUTT_MMDF: +#ifdef USE_SIDEBAR + if (should_refresh) + buffy_mbox_update (tmp, &sb); +#endif if (buffy_mbox_hasnew (tmp, &sb) > 0) BuffyCount++; break; case MUTT_MAILDIR: +#ifdef USE_SIDEBAR + if (should_refresh) + buffy_maildir_update (tmp); +#endif if (buffy_maildir_hasnew (tmp) > 0) BuffyCount++; break; case MUTT_MH: +#ifdef USE_SIDEBAR + if (sb_should_refresh()) { + mh_buffy_update (tmp); + sb_set_update_time(); + } +#endif mh_buffy(tmp); if (tmp->new) BuffyCount++; @@ -485,6 +660,10 @@ int mutt_buffy_check (int force) else if (!tmp->notified) BuffyNotify++; } +#ifdef USE_SIDEBAR + if (should_refresh) + sb_set_update_time(); +#endif BuffyDoneTime = BuffyTime; return (BuffyCount); diff --git a/buffy.h b/buffy.h index 92ac7c18d..7f2f1d542 100644 --- a/buffy.h +++ b/buffy.h @@ -16,6 +16,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#ifndef _BUFFY_H +#define _BUFFY_H + /*parameter to mutt_parse_mailboxes*/ #define MUTT_MAILBOXES 1 #define MUTT_UNMAILBOXES 2 @@ -23,13 +26,28 @@ typedef struct buffy_t { char path[_POSIX_PATH_MAX]; +#ifdef USE_SIDEBAR + char realpath[_POSIX_PATH_MAX]; +#endif off_t size; struct buffy_t *next; +#ifdef USE_SIDEBAR + struct buffy_t *prev; +#endif short new; /* mailbox has new mail */ +#ifdef USE_SIDEBAR + int msg_count; /* total number of messages */ + int msg_unread; /* number of unread messages */ + int msg_flagged; /* number of flagged messages */ + short is_hidden; /* is hidden from the sidebar */ +#endif short notified; /* user has been notified */ short magic; /* mailbox type */ short newly_created; /* mbox or mmdf just popped into existence */ time_t last_visited; /* time of last exit from this mailbox */ +#ifdef USE_SIDEBAR + time_t sb_last_checked; /* time of last buffy check from sidebar */ +#endif } BUFFY; @@ -49,3 +67,5 @@ void mutt_buffy_cleanup (const char *buf, struct stat *st); void mutt_buffy_setnotified (const char *path); void mh_buffy (BUFFY *); + +#endif /* _BUFFY_H */ diff --git a/color.c b/color.c index 6270a85f6..c33d41ebc 100644 --- a/color.c +++ b/color.c @@ -94,6 +94,14 @@ static const struct mapping_t Fields[] = { "underline", MT_COLOR_UNDERLINE }, { "index", MT_COLOR_INDEX }, { "prompt", MT_COLOR_PROMPT }, +#ifdef USE_SIDEBAR + { "sidebar_divider", MT_COLOR_DIVIDER }, + { "sidebar_flagged", MT_COLOR_FLAGGED }, + { "sidebar_highlight",MT_COLOR_HIGHLIGHT }, + { "sidebar_indicator",MT_COLOR_SB_INDICATOR }, + { "sidebar_new", MT_COLOR_NEW }, + { "sidebar_spoolfile",MT_COLOR_SB_SPOOLFILE }, +#endif { NULL, 0 } }; @@ -146,6 +154,9 @@ void ci_start_color (void) ColorDefs[MT_COLOR_INDICATOR] = A_REVERSE; ColorDefs[MT_COLOR_SEARCH] = A_REVERSE; ColorDefs[MT_COLOR_MARKERS] = A_REVERSE; +#ifdef USE_SIDEBAR + ColorDefs[MT_COLOR_HIGHLIGHT] = A_UNDERLINE; +#endif /* special meaning: toggle the relevant attribute */ ColorDefs[MT_COLOR_BOLD] = 0; ColorDefs[MT_COLOR_UNDERLINE] = 0; diff --git a/configure.ac b/configure.ac index 6096dbb3c..f8d71a199 100644 --- a/configure.ac +++ b/configure.ac @@ -175,6 +175,15 @@ if test x$have_smime != xno ; then SMIMEAUX_TARGET="smime_keys" fi +AC_ARG_ENABLE(sidebar, AC_HELP_STRING([--enable-sidebar], [Enable Sidebar support]), +[ if test x$enableval = xyes ; then + AC_DEFINE(USE_SIDEBAR,1,[ Define if you want support for the sidebar. ]) + OPS="$OPS \$(srcdir)/OPS.SIDEBAR" + need_sidebar="yes" + fi +]) +AM_CONDITIONAL(BUILD_SIDEBAR, test x$need_sidebar = xyes) + AC_ARG_WITH(mixmaster, AS_HELP_STRING([--with-mixmaster@<:@=PATH@:>@],[Include Mixmaster support]), [if test "$withval" != no then diff --git a/contrib/sample.muttrc-sidebar b/contrib/sample.muttrc-sidebar new file mode 100644 index 000000000..1b5cda7bd --- /dev/null +++ b/contrib/sample.muttrc-sidebar @@ -0,0 +1,116 @@ +# This is a complete list of sidebar-related configuration. + +# -------------------------------------------------------------------------- +# VARIABLES - shown with their default values +# -------------------------------------------------------------------------- + +# Should the Sidebar be shown? +set sidebar_visible = no + +# How wide should the Sidebar be in screen columns? +# Note: Some characters, e.g. Chinese, take up two columns each. +set sidebar_width = 20 + +# Should the mailbox paths be abbreviated? +set sidebar_short_path = no + +# When abbreviating mailbox path names, use any of these characters as path +# separators. Only the part after the last separators will be shown. +# For file folders '/' is good. For IMAP folders, often '.' is useful. +set sidebar_delim_chars = '/.' + +# If the mailbox path is abbreviated, should it be indented? +set sidebar_folder_indent = no + +# Indent mailbox paths with this string. +set sidebar_indent_string = ' ' + +# Make the Sidebar only display mailboxes that contain new, or flagged, +# mail. +set sidebar_new_mail_only = no + +# Any mailboxes that are whitelisted will always be visible, even if the +# sidebar_new_mail_only option is enabled. +sidebar_whitelist '/home/user/mailbox1' +sidebar_whitelist '/home/user/mailbox2' + +# When searching for mailboxes containing new mail, should the search wrap +# around when it reaches the end of the list? +set sidebar_next_new_wrap = no + +# The character to use as the divider between the Sidebar and the other Mutt +# panels. +# Note: Only the first character of this string is used. +set sidebar_divider_char = '|' + +# Display the Sidebar mailboxes using this format string. +set sidebar_format = '%B%?F? [%F]?%* %?N?%N/?%S' + +# Sidebar will not refresh its list of mailboxes any more frequently than +# this number of seconds. This will help reduce disk/network traffic. +set sidebar_refresh_time = 60 + +# Sort the mailboxes in the Sidebar using this method: +# count - total number of messages +# flagged - number of flagged messages +# new - number of new messages +# path - mailbox path +# unsorted - do not sort the mailboxes +set sidebar_sort_method = 'unsorted' + +# -------------------------------------------------------------------------- +# FUNCTIONS - shown with an example mapping +# -------------------------------------------------------------------------- + +# Move the highlight to the previous mailbox +bind index,pager \Cp sidebar-prev + +# Move the highlight to the next mailbox +bind index,pager \Cn sidebar-next + +# Open the highlighted mailbox +bind index,pager \Co sidebar-open + +# Move the highlight to the previous page +# This is useful if you have a LOT of mailboxes. +bind index,pager sidebar-page-up + +# Move the highlight to the next page +# This is useful if you have a LOT of mailboxes. +bind index,pager sidebar-page-down + +# Move the highlight to the previous mailbox containing new, or flagged, +# mail. +bind index,pager sidebar-prev-new + +# Move the highlight to the next mailbox containing new, or flagged, mail. +bind index,pager sidebar-next-new + +# Toggle the visibility of the Sidebar. +bind index,pager B sidebar-toggle-visible + +# -------------------------------------------------------------------------- +# COLORS - some unpleasant examples are given +# -------------------------------------------------------------------------- +# Note: All color operations are of the form: +# color OBJECT FOREGROUND BACKGROUND + +# Color of the current, open, mailbox +# Note: This is a general Mutt option which colors all selected items. +color indicator cyan black + +# Color of the highlighted, but not open, mailbox. +color sidebar_highlight black color8 + +# Color of the divider separating the Sidebar from Mutt panels +color sidebar_divider color8 black + +# Color to give mailboxes containing flagged mail +color sidebar_flagged red black + +# Color to give mailboxes containing new mail +color sidebar_new green black + +# -------------------------------------------------------------------------- + +# vim: syntax=muttrc diff --git a/contrib/sample.vimrc-sidebar b/contrib/sample.vimrc-sidebar new file mode 100644 index 000000000..c5be50da8 --- /dev/null +++ b/contrib/sample.vimrc-sidebar @@ -0,0 +1,35 @@ +" Vim syntax file for the mutt sidebar patch + +syntax keyword muttrcVarBool skipwhite contained sidebar_folder_indent nextgroup=muttrcSetBoolAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax keyword muttrcVarBool skipwhite contained sidebar_new_mail_only nextgroup=muttrcSetBoolAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax keyword muttrcVarBool skipwhite contained sidebar_next_new_wrap nextgroup=muttrcSetBoolAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax keyword muttrcVarBool skipwhite contained sidebar_short_path nextgroup=muttrcSetBoolAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax keyword muttrcVarBool skipwhite contained sidebar_visible nextgroup=muttrcSetBoolAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr + +syntax keyword muttrcVarNum skipwhite contained sidebar_refresh_time nextgroup=muttrcSetNumAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr +syntax keyword muttrcVarNum skipwhite contained sidebar_width nextgroup=muttrcSetNumAssignment,muttrcVPrefix,muttrcVarBool,muttrcVarQuad,muttrcVarNum,muttrcVarStr + +syntax keyword muttrcVarStr contained skipwhite sidebar_divider_char nextgroup=muttrcVarEqualsIdxFmt +syntax keyword muttrcVarStr contained skipwhite sidebar_delim_chars nextgroup=muttrcVarEqualsIdxFmt +syntax keyword muttrcVarStr contained skipwhite sidebar_format nextgroup=muttrcVarEqualsIdxFmt +syntax keyword muttrcVarStr contained skipwhite sidebar_indent_string nextgroup=muttrcVarEqualsIdxFmt +syntax keyword muttrcVarStr contained skipwhite sidebar_sort_method nextgroup=muttrcVarEqualsIdxFmt + +syntax keyword muttrcCommand sidebar_whitelist + +syntax match muttrcFunction contained "\" +syntax match muttrcFunction contained "\" +syntax match muttrcFunction contained "\" +syntax match muttrcFunction contained "\" +syntax match muttrcFunction contained "\" +syntax match muttrcFunction contained "\" +syntax match muttrcFunction contained "\" +syntax match muttrcFunction contained "\" + +syntax keyword muttrcColorField contained sidebar_divider +syntax keyword muttrcColorField contained sidebar_flagged +syntax keyword muttrcColorField contained sidebar_highlight +syntax keyword muttrcColorField contained sidebar_indicator +syntax keyword muttrcColorField contained sidebar_new + +" vim: syntax=vim diff --git a/curs_main.c b/curs_main.c index f5ac2458c..e138906c7 100644 --- a/curs_main.c +++ b/curs_main.c @@ -26,8 +26,13 @@ #include "mailbox.h" #include "mapping.h" #include "sort.h" +#include "buffy.h" #include "mx.h" +#ifdef USE_SIDEBAR +#include "sidebar.h" +#endif + #ifdef USE_POP #include "pop.h" #endif @@ -593,21 +598,39 @@ int mutt_index_menu (void) menu->redraw |= REDRAW_STATUS; if (do_buffy_notify) { - if (mutt_buffy_notify () && option (OPTBEEPNEW)) - beep (); + if (mutt_buffy_notify()) + { + menu->redraw |= REDRAW_STATUS; + if (option (OPTBEEPNEW)) + beep(); + } } else do_buffy_notify = 1; } +#ifdef USE_SIDEBAR + if (option (OPTSIDEBAR)) + menu->redraw |= REDRAW_SIDEBAR; +#endif + if (op != -1) mutt_curs_set (0); if (menu->redraw & REDRAW_FULL) { menu_redraw_full (menu); +#ifdef USE_SIDEBAR + sb_draw(); +#endif mutt_show_error (); } +#ifdef USE_SIDEBAR + else if (menu->redraw & REDRAW_SIDEBAR) { + sb_draw(); + menu->redraw &= ~REDRAW_SIDEBAR; + } +#endif if (menu->menu == MENU_MAIN) { @@ -631,6 +654,9 @@ int mutt_index_menu (void) menu_status_line (buf, sizeof (buf), menu, NONULL (Status)); mutt_window_move (MuttStatusWindow, 0, 0); SETCOLOR (MT_COLOR_STATUS); +#ifdef USE_SIDEBAR + sb_set_buffystats (Context); +#endif mutt_paddstr (MuttStatusWindow->cols, buf); NORMAL_COLOR; menu->redraw &= ~REDRAW_STATUS; @@ -1090,6 +1116,9 @@ int mutt_index_menu (void) break; CHECK_MSGCOUNT; +#ifdef USE_SIDEBAR + CHECK_VISIBLE; +#endif CHECK_READONLY; { int oldvcount = Context->vcount; @@ -1149,6 +1178,9 @@ int mutt_index_menu (void) menu->redraw = REDRAW_FULL; break; +#ifdef USE_SIDEBAR + case OP_SIDEBAR_OPEN: +#endif case OP_MAIN_CHANGE_FOLDER: case OP_MAIN_NEXT_UNREAD_MAILBOX: @@ -1180,6 +1212,14 @@ int mutt_index_menu (void) { mutt_buffy (buf, sizeof (buf)); +#ifdef USE_SIDEBAR + if (op == OP_SIDEBAR_OPEN) { + const char *path = sb_get_highlight(); + if (!path) + break; + strncpy (buf, path, sizeof (buf)); + } else +#endif if (mutt_enter_fname (cp, buf, sizeof (buf), &menu->redraw, 1) == -1) { if (menu->menu == MENU_PAGER) @@ -1198,6 +1238,9 @@ int mutt_index_menu (void) } mutt_expand_path (buf, sizeof (buf)); +#ifdef USE_SIDEBAR + sb_set_open_buffy (buf); +#endif if (mx_get_magic (buf) <= 0) { mutt_error (_("%s is not a mailbox."), buf); @@ -2309,6 +2352,22 @@ int mutt_index_menu (void) mutt_what_key(); break; +#ifdef USE_SIDEBAR + case OP_SIDEBAR_NEXT: + case OP_SIDEBAR_NEXT_NEW: + case OP_SIDEBAR_PAGE_DOWN: + case OP_SIDEBAR_PAGE_UP: + case OP_SIDEBAR_PREV: + case OP_SIDEBAR_PREV_NEW: + sb_change_mailbox (op); + break; + + case OP_SIDEBAR_TOGGLE_VISIBLE: + toggle_option (OPTSIDEBAR); + mutt_reflow_windows(); + menu->redraw = REDRAW_FULL; + break; +#endif default: if (menu->menu == MENU_MAIN) km_error_key (MENU_MAIN); diff --git a/doc/manual.xml.head b/doc/manual.xml.head index a3711d093..da7a659c9 100644 --- a/doc/manual.xml.head +++ b/doc/manual.xml.head @@ -405,6 +405,623 @@ attach to a message, select multiple files to attach and many more. + + Sidebar + + The Sidebar shows a list of all your mailboxes. The list can be + turned on and off, it can be themed and the list style can be + configured. + + + This part of the manual is suitable for beginners. + If you already know Mutt you could skip ahead to the main + Sidebar guide. + If you just want to get started, you could use the sample + Sidebar muttrc. + + + This version of Sidebar is based on Terry Chan's + 2015-11-11 release. + It contains many + new features, + lots of + bugfixes + and a generous helping of + new documentation which you are already reading. + + + To check if Mutt supports Sidebar, look for the string + +USE_SIDEBAR in the mutt version. + + +mutt -v + + + Let's turn on the Sidebar: + + set sidebar_visible + + You will see something like this. + A list of mailboxes on the left. + A list of emails, from the selected mailbox, on the right. + + +Fruit [1] 3/8| 1 + Jan 24 Rhys Lee (192) Yew +Animals [1] 2/6| 2 + Feb 11 Grace Hall (167) Ilama +Cars 4| 3 Feb 23 Aimee Scott (450) Nectarine +Seas 1/7| 4 ! Feb 28 Summer Jackson (264) Lemon + | 5 Mar 07 Callum Harrison (464) Raspberry + | 6 N + Mar 24 Samuel Harris (353) Tangerine + | 7 N + Sep 05 Sofia Graham (335) Cherry + | 8 N Sep 16 Ewan Brown (105) Ugli + | + | + + + This user has four mailboxes: Fruit, + Cars, Animals and + Seas. + + + The current, open, mailbox is Fruit. We can + also see information about the other mailboxes. For example: + The Animals mailbox contains, 1 flagged email, 2 + new emails out of a total of 6 emails. + + + Navigation + + The Sidebar adds some new functions + to Mutt. + + + The user pressed the c key to + <change-folder> to the + Animals mailbox. The Sidebar automatically + updated the indicator to match. + + +Fruit [1] 3/8| 1 Jan 03 Tia Gibson (362) Caiman +Animals [1] 2/6| 2 + Jan 22 Rhys Lee ( 48) Dolphin +Cars 4| 3 ! Aug 16 Ewan Brown (333) Hummingbird +Seas 1/7| 4 Sep 25 Grace Hall ( 27) Capybara + | 5 N + Nov 12 Evelyn Rogers (453) Tapir + | 6 N + Nov 16 Callum Harrison (498) Hedgehog + | + | + | + | + + + Let's map some functions: + + +bind index,pager \CP sidebar-prev # Ctrl-Shift-P - Previous Mailbox +bind index,pager \CN sidebar-next # Ctrl-Shift-N - Next Mailbox +bind index,pager \CO sidebar-open # Ctrl-Shift-O - Open Highlighted Mailbox + + + Press Ctrl-Shift-N (Next mailbox) twice will + move the Sidebar highlight to + down to the Seas mailbox. + + +Fruit [1] 3/8| 1 Jan 03 Tia Gibson (362) Caiman +Animals [1] 2/6| 2 + Jan 22 Rhys Lee ( 48) Dolphin +Cars 4| 3 ! Aug 16 Ewan Brown (333) Hummingbird +Seas 1/7| 4 Sep 25 Grace Hall ( 27) Capybara + | 5 N + Nov 12 Evelyn Rogers (453) Tapir + | 6 N + Nov 16 Callum Harrison (498) Hedgehog + | + | + | + | + + + Functions <sidebar-next> and + <sidebar-prev> move the Sidebar + highlight. + They do not change the open + mailbox. + + + Press Ctrl-Shift-O + (<sidebar-open>) + to open the highlighted mailbox. + + +Fruit [1] 3/8| 1 ! Mar 07 Finley Jones (139) Molucca Sea +Animals [1] 2/6| 2 + Mar 24 Summer Jackson ( 25) Arafura Sea +Cars 4| 3 + Feb 28 Imogen Baker (193) Pechora Sea +Seas 1/7| 4 N + Feb 23 Isla Hussain (348) Balearic Sea + | + | + | + | + | + | + + + + Features + + The Sidebar shows a list of mailboxes in a panel. + + + Everything about the Sidebar can be configured. + + + <link linkend="intro-sidebar-basics">State of the Sidebar</link> + Visibility + Width + + + <link linkend="intro-sidebar-limit">Which mailboxes are displayed</link> + Display all + Limit to mailboxes with new mail + Whitelist mailboxes to display always + + + <link linkend="sidebar-sort">The order in which mailboxes are displayed</link> + + Unsorted (order of mailboxes commands) + Sorted alphabetically + Sorted by number of new mails + + + <link linkend="intro-sidebar-colors">Color</link> + Sidebar indicators and divider + Mailboxes depending on their type + Mailboxes depending on their contents + + + <link linkend="sidebar-functions">Key bindings</link> + Hide/Unhide the Sidebar + Select previous/next mailbox + Select previous/next mailbox with new mail + Page up/down through a list of mailboxes + + + Misc + Formatting string for mailbox + Wraparound searching + Flexible mailbox abbreviations + Support for Unicode mailbox names (utf-8) + + + + Display + + Everything about the Sidebar can be configured. + + + For a quick reference: + Sidebar variables to set + Sidebar colors to apply + Sidebar sort methods + + + Sidebar Basics + + The most important variable is $sidebar_visible. + You can set this in your muttrc, or bind a key to the + function <sidebar-toggle-visible>. + + +set sidebar_visible # Make the Sidebar visible by default +bind index,pager B sidebar-toggle-visible # Use 'B' to switch the Sidebar on and off + + + Next, decide how wide you want the Sidebar to be. 25 + characters might be enough for the mailbox name and some numbers. + Remember, you can hide/show the Sidebar at the press of button. + + + Finally, you might want to change the divider character. + By default, Sidebar draws an ASCII line between it and the Index panel + If your terminal supports it, you can use a Unicode line-drawing character. + + +set sidebar_width = 25 # Plenty of space +set sidebar_divider_char = '│' # Pretty line-drawing character + + + + Sidebar Format String + + $sidebar_format allows you to customize the Sidebar display. + For an introduction, read format strings + including the section about conditionals. + + + The default value is %B%?F? [%F]?%* %?N?%N/?%S + + + Which breaks down as: + %B - Mailbox name + %?F? [%F]? - If flagged emails [%F], otherwise nothing + %* - Pad with spaces + %?N?%N/? - If new emails %N/, otherwise nothing + %S - Total number of emails + + + sidebar_format + + + + Format + Notes + Description + + + + + %B + + Name of the mailbox + + + %S + * + Size of mailbox (total number of messages) + + + %N + * + Number of New messages in the mailbox + + + %F + * + Number of Flagged messages in the mailbox + + + %! + + + !: one flagged message; + !!: two flagged messages; + n!: n flagged messages (for n > 2). + Otherwise prints nothing. + + + + %d + * ‡ + Number of deleted messages + + + %L + * ‡ + Number of messages after limiting + + + %t + * ‡ + Number of tagged messages + + + %>X + + Right justify the rest of the string and pad with X + + + %|X + + Pad to the end of the line with + X + + + %*X + + Soft-fill with character Xas pad + + + +
+ + * = Can be optionally printed if nonzero + + + ‡ = Only applicable to the current folder + + + Here are some examples. + They show the number of (F)lagged, (N)ew and (S)ize. + + + sidebar_format + + + + Format + Example + + + + + %B%?F? [%F]?%* %?N?%N/?%S + mailbox [F] N/S + + + %B%* %F:%N:%S + mailbox F:N:S + + + %B %?N?(%N)?%* %S + mailbox (N) S + + + %B%* ?F?%F/?%N + mailbox F/S + + + +
+
+ + Abbreviating Mailbox Names + + $sidebar_delim_chars tells Sidebar + how to split up mailbox paths. For local directories + use /; for IMAP folders use . + + + Example 1 + + This example works well if your mailboxes have unique names + after the last separator. + + + Add some mailboxes of diffent depths. + + +set folder="~/mail" +mailboxes =fruit/apple =fruit/banana =fruit/cherry +mailboxes =water/sea/sicily =water/sea/archipelago =water/sea/sibuyan +mailboxes =water/ocean/atlantic =water/ocean/pacific =water/ocean/arctic + + + Shorten the names: + + +set sidebar_short_path # Shorten mailbox names +set sidebar_delim_chars="/" # Delete everything up to the last / character + + + The screenshot below shows what the Sidebar would look like + before and after shortening. + + +|fruit/apple |apple +|fruit/banana |banana +|fruit/cherry |cherry +|water/sea/sicily |sicily +|water/sea/archipelago |archipelago +|water/sea/sibuyan |sibuyan +|water/ocean/atlantic |atlantic +|water/ocean/pacific |pacific +|water/ocean/arctic |arctic + + + + Example 2 + + This example works well if you have lots of mailboxes which are arranged + in a tree. + + + Add some mailboxes of diffent depths. + + +set folder="~/mail" +mailboxes =fruit +mailboxes =fruit/apple =fruit/banana =fruit/cherry +mailboxes =water +mailboxes =water/sea +mailboxes =water/sea/sicily =water/sea/archipelago =water/sea/sibuyan +mailboxes =water/ocean +mailboxes =water/ocean/atlantic =water/ocean/pacific =water/ocean/arctic + + + Shorten the names: + + +set sidebar_short_path # Shorten mailbox names +set sidebar_delim_chars="/" # Delete everything up to the last / character +set sidebar_folder_indent # Indent folders whose names we've shortened +set sidebar_indent_string=" " # Indent with two spaces + + + The screenshot below shows what the Sidebar would look like + before and after shortening. + + +|fruit |fruit +|fruit/apple | apple +|fruit/banana | banana +|fruit/cherry | cherry +|water |water +|water/sea | sea +|water/sea/sicily | sicily +|water/sea/archipelago | archipelago +|water/sea/sibuyan | sibuyan +|water/ocean | ocean +|water/ocean/atlantic | atlantic +|water/ocean/pacific | pacific +|water/ocean/arctic | arctic + + + Sometimes, it will be necessary to add mailboxes, that you + don't use, to fill in part of the tree. This will trade + vertical space for horizonal space (but it looks good). + + + + + Limiting the Number of Mailboxes + + If you have a lot of mailboxes, sometimes it can be useful to hide + the ones you aren't using. $sidebar_new_mail_only + tells Sidebar to only show mailboxes that contain new, or flagged, email. + + + If you want some mailboxes to be always visible, then use the + sidebar_whitelist command. It takes a list of + mailboxes as parameters. + + +set sidebar_new_mail_only # Only mailboxes with new/flagged email +sidebar_whitelist fruit fruit/apple # Always display these two mailboxes + + +
+ + Colors + + Here is a sample color scheme: + + +color sidebar_indicator default color17 # Dark blue background +color sidebar_highlight white color238 # Grey background +color sidebar_spoolfile yellow default # Yellow +color sidebar_new green default # Green +color sidebar_flagged red default # Red +color sidebar_divider color8 default # Dark grey + + + There is a priority order when coloring Sidebar mailboxes. + e.g. If a mailbox has new mail it will have the + sidebar_new color, even if it also contains + flagged mails. + + + Sidebar Color Priority + + + + Priority + Color + Description + + + + + Highest + sidebar_indicator + Mailbox is open + + + + sidebar_highlight + Mailbox is highlighed + + + + sidebar_spoolfile + Mailbox is the spoolfile (receives incoming mail) + + + + sidebar_new + Mailbox contains new mail + + + + sidebar_flagged + Mailbox contains flagged mail + + + Lowest + (None) + Mailbox does not match above + + + +
+
+ + Bug-fixes + + If you haven't used Sidebar before, you can ignore this section. + + + These bugs have been fixed since the previous Sidebar release: 2015-11-11. + + + Fix bug when starting in compose mode + Fix bug with empty sidebar_divider_char string + Fix bug with header wrapping + Correctly handle utf8 character sequences + Fix a bug in mh_buffy_update + Fix refresh -- time overflowed short + Protect against empty format strings + Limit Sidebar width to COLS + Handle unmailboxes * safely + Refresh Sidebar after timeout + + + + Config Changes + + If you haven't used Sidebar before, you can ignore this section. + + + Some of the Sidebar config has been changed to make its meaning clearer. + These changes have been made since the previous Sidebar release: 2015-11-11. + + + Config Changes + + + + Old Name + New Name + + + + + $sidebar_delim + $sidebar_divider_char + + + $sidebar_folderindent + $sidebar_folder_indent + + + $sidebar_indentstr + $sidebar_indent_string + + + $sidebar_newmail_only + $sidebar_new_mail_only + + + $sidebar_refresh + $sidebar_refresh_time + + + $sidebar_shortpath + $sidebar_short_path + + + $sidebar_sort + $sidebar_sort_method + + + <sidebar-scroll-down> + <sidebar-page-down> + + + <sidebar-scroll-up> + <sidebar-page-up> + + + +
+
+
+ Help @@ -8092,6 +8709,469 @@ please have a look at the mixmaster documentation. + + Sidebar Patch + Overview of mailboxes + + + Patch + + + To check if Mutt supports Sidebar, look for + +USE_SIDEBAR in the mutt version. + See: . + + + + Dependencies: + mutt-1.5.24 + + + This patch is part of the NeoMutt Project. + + + + Introduction + + + The Sidebar shows a list of all your mailboxes. The list can be + turned on and off, it can be themed and the list style can be + configured. + + + + This part of the manual is a reference guide. + If you want a simple introduction with examples see the + Sidebar Howto. + If you just want to get started, you could use the sample + Sidebar muttrc. + + + + This version of Sidebar is based on Terry Chan's + 2015-11-11 release. + It contains many + new features, + lots of + bugfixes. + + + + + Variables + + + Sidebar Variables + + + + Name + Type + Default + + + + + sidebar_delim_chars + string + /. + + + sidebar_divider_char + string + | + + + sidebar_folder_indent + boolean + no + + + sidebar_format + string + %B%?F? [%F]?%* %?N?%N/?%S + + + sidebar_indent_string + string +    (two spaces) + + + sidebar_new_mail_only + boolean + no + + + sidebar_next_new_wrap + boolean + no + + + sidebar_refresh_time + number + 60 + + + sidebar_short_path + boolean + no + + + sidebar_sort_method + enum + SORT_ORDER + + + sidebar_visible + boolean + no + + + sidebar_whitelist + list + (empty) + + + sidebar_width + number + 20 + + + +
+
+ + + Functions + + + Sidebar adds the following functions to Mutt. + By default, none of them are bound to keys. + + + + Sidebar Functions + + + + Menus + Function + Description + + + + + index,pager + <sidebar-next> + Move the highlight to next mailbox + + + index,pager + <sidebar-next-new> + Move the highlight to next mailbox with new mail + + + index,pager + <sidebar-open> + Open highlighted mailbox + + + index,pager + <sidebar-page-down> + Scroll the Sidebar down 1 page + + + index,pager + <sidebar-page-up> + Scroll the Sidebar up 1 page + + + index,pager + <sidebar-prev> + Move the highlight to previous mailbox + + + index,pager + <sidebar-prev-new> + Move the highlight to previous mailbox with new mail + + + index,pager + <sidebar-toggle-visible> + Make the Sidebar (in)visible + + + +
+
+ + + Commands + + sidebar_whitelist + + mailbox + + + mailbox + + + + + + Colors + + + Sidebar Colors + + + + Name + Default Color + Description + + + + + sidebar_divider + default + The dividing line between the Sidebar and the Index/Pager panels + + + sidebar_flagged + default + Mailboxes containing flagged mail + + + sidebar_highlight + underline + Cursor to select a mailbox + + + sidebar_indicator + mutt indicator + The mailbox open in the Index panel + + + sidebar_new + default + Mailboxes containing new mail + + + sidebar_spoolfile + default + Mailbox that receives incoming mail + + + +
+ + If the sidebar_indicator color isn't set, then the default Mutt + indicator color will be used (the color used in the index panel). +
+ + + Sort + + + Sidebar Sort + + + + Sort + Description + + + + + alpha + Alphabetically by path + + + count + Total number of messages + + + flagged + Number of flagged messages + + + name + Alphabetically by path + + + new + Number of new messages + + + path + Alphabetically by path + + + unsorted + Do not resort the paths + + + +
+
+ + + Muttrc + +# This is a complete list of sidebar-related configuration. + +# -------------------------------------------------------------------------- +# VARIABLES - shown with their default values +# -------------------------------------------------------------------------- + +# Should the Sidebar be shown? +set sidebar_visible = no + +# How wide should the Sidebar be in screen columns? +# Note: Some characters, e.g. Chinese, take up two columns each. +set sidebar_width = 20 + +# Should the mailbox paths be abbreviated? +set sidebar_short_path = no + +# When abbreviating mailbox path names, use any of these characters as path +# separators. Only the part after the last separators will be shown. +# For file folders '/' is good. For IMAP folders, often '.' is useful. +set sidebar_delim_chars = '/.' + +# If the mailbox path is abbreviated, should it be indented? +set sidebar_folder_indent = no + +# Indent mailbox paths with this string. +set sidebar_indent_string = ' ' + +# Make the Sidebar only display mailboxes that contain new, or flagged, +# mail. +set sidebar_new_mail_only = no + +# Any mailboxes that are whitelisted will always be visible, even if the +# sidebar_new_mail_only option is enabled. +sidebar_whitelist '/home/user/mailbox1' +sidebar_whitelist '/home/user/mailbox2' + +# When searching for mailboxes containing new mail, should the search wrap +# around when it reaches the end of the list? +set sidebar_next_new_wrap = no + +# The character to use as the divider between the Sidebar and the other Mutt +# panels. +# Note: Only the first character of this string is used. +set sidebar_divider_char = '|' + +# Display the Sidebar mailboxes using this format string. +set sidebar_format = '%B%?F? [%F]?%* %?N?%N/?%S' + +# Sidebar will not refresh its list of mailboxes any more frequently than +# this number of seconds. This will help reduce disk/network traffic. +set sidebar_refresh_time = 60 + +# Sort the mailboxes in the Sidebar using this method: +# count - total number of messages +# flagged - number of flagged messages +# new - number of new messages +# path - mailbox path +# unsorted - do not sort the mailboxes +set sidebar_sort_method = 'unsorted' + +# -------------------------------------------------------------------------- +# FUNCTIONS - shown with an example mapping +# -------------------------------------------------------------------------- + +# Move the highlight to the previous mailbox +bind index,pager \Cp sidebar-prev + +# Move the highlight to the next mailbox +bind index,pager \Cn sidebar-next + +# Open the highlighted mailbox +bind index,pager \Co sidebar-open + +# Move the highlight to the previous page +# This is useful if you have a LOT of mailboxes. +bind index,pager <F3> sidebar-page-up + +# Move the highlight to the next page +# This is useful if you have a LOT of mailboxes. +bind index,pager <F4> sidebar-page-down + +# Move the highlight to the previous mailbox containing new, or flagged, +# mail. +bind index,pager <F5> sidebar-prev-new + +# Move the highlight to the next mailbox containing new, or flagged, mail. +bind index,pager <F6> sidebar-next-new + +# Toggle the visibility of the Sidebar. +bind index,pager B sidebar-toggle-visible + +# -------------------------------------------------------------------------- +# COLORS - some unpleasant examples are given +# -------------------------------------------------------------------------- +# Note: All color operations are of the form: +# color OBJECT FOREGROUND BACKGROUND + +# Color of the current, open, mailbox +# Note: This is a general Mutt option which colors all selected items. +color indicator cyan black + +# Color of the highlighted, but not open, mailbox. +color sidebar_highlight black color8 + +# Color of the divider separating the Sidebar from Mutt panels +color sidebar_divider color8 black + +# Color to give mailboxes containing flagged mail +color sidebar_flagged red black + +# Color to give mailboxes containing new mail +color sidebar_new green black + +# -------------------------------------------------------------------------- + +# vim: syntax=muttrc + + + + + See Also + + + Regular Expressions + Patterns + Color command + notmuch patch + + + + + Known Bugs + Unsorted isn't + + + + Credits + + Justin Hibbits jrh29@po.cwru.edu + Thomer M. Gil mutt@thomer.com + David Sterba dsterba@suse.cz + Evgeni Golov evgeni@debian.org + Fabian Groffen grobian@gentoo.org + Jason DeTiberus jdetiber@redhat.com + Stefan Assmann sassmann@kpanic.de + Steve Kemp steve@steve.org.uk + Terry Chan tchan@lunar-linux.org + Tyler Earnest tylere@rne.st + Richard Russon rich@flatcap.org + + +
+ @@ -9246,6 +10326,17 @@ The following are the commands understood by Mutt: + + +sidebar_whitelist + +item + + +command + + + source diff --git a/flags.c b/flags.c index 268026515..2d404f60f 100644 --- a/flags.c +++ b/flags.c @@ -25,6 +25,10 @@ #include "sort.h" #include "mx.h" +#ifdef USE_SIDEBAR +#include "sidebar.h" +#endif + void _mutt_set_flag (CONTEXT *ctx, HEADER *h, int flag, int bf, int upd_ctx) { int changed = h->changed; @@ -263,6 +267,9 @@ void _mutt_set_flag (CONTEXT *ctx, HEADER *h, int flag, int bf, int upd_ctx) */ if (h->searched && (changed != h->changed || deleted != ctx->deleted || tagged != ctx->tagged || flagged != ctx->flagged)) h->searched = 0; +#ifdef USE_SIDEBAR + sb_draw(); +#endif } void mutt_tag_set_flag (int flag, int bf) diff --git a/functions.h b/functions.h index 684b196a9..2af8827d7 100644 --- a/functions.h +++ b/functions.h @@ -168,6 +168,16 @@ const struct binding_t OpMain[] = { /* map: index */ { "decrypt-copy", OP_DECRYPT_COPY, NULL }, { "decrypt-save", OP_DECRYPT_SAVE, NULL }, +#ifdef USE_SIDEBAR + { "sidebar-next", OP_SIDEBAR_NEXT, NULL }, + { "sidebar-next-new", OP_SIDEBAR_NEXT_NEW, NULL }, + { "sidebar-open", OP_SIDEBAR_OPEN, NULL }, + { "sidebar-page-down", OP_SIDEBAR_PAGE_DOWN, NULL }, + { "sidebar-page-up", OP_SIDEBAR_PAGE_UP, NULL }, + { "sidebar-prev", OP_SIDEBAR_PREV, NULL }, + { "sidebar-prev-new", OP_SIDEBAR_PREV_NEW, NULL }, + { "sidebar-toggle-visible", OP_SIDEBAR_TOGGLE_VISIBLE, NULL }, +#endif { NULL, 0, NULL } }; @@ -272,6 +282,17 @@ const struct binding_t OpPager[] = { /* map: pager */ { "what-key", OP_WHAT_KEY, NULL }, +#ifdef USE_SIDEBAR + { "sidebar-next", OP_SIDEBAR_NEXT, NULL }, + { "sidebar-next-new", OP_SIDEBAR_NEXT_NEW, NULL }, + { "sidebar-open", OP_SIDEBAR_OPEN, NULL }, + { "sidebar-page-down", OP_SIDEBAR_PAGE_DOWN, NULL }, + { "sidebar-page-up", OP_SIDEBAR_PAGE_UP, NULL }, + { "sidebar-prev", OP_SIDEBAR_PREV, NULL }, + { "sidebar-prev-new", OP_SIDEBAR_PREV_NEW, NULL }, + { "sidebar-toggle-visible", OP_SIDEBAR_TOGGLE_VISIBLE, NULL }, +#endif + { NULL, 0, NULL } }; diff --git a/globals.h b/globals.h index abefade3d..cf8c5f0cc 100644 --- a/globals.h +++ b/globals.h @@ -118,6 +118,12 @@ WHERE short SearchContext; WHERE char *SendCharset; WHERE char *Sendmail; WHERE char *Shell; +#ifdef USE_SIDEBAR +WHERE char *SidebarDelimChars; +WHERE char *SidebarDividerChar; +WHERE char *SidebarFormat; +WHERE char *SidebarIndentString; +#endif WHERE char *Signature; WHERE char *SimpleSearch; #if USE_SMTP @@ -214,6 +220,14 @@ WHERE short ScoreThresholdDelete; WHERE short ScoreThresholdRead; WHERE short ScoreThresholdFlag; +/* This isn't excluded from the build because it's too entwined in the code. + * For now. */ +WHERE short SidebarWidth; +#ifdef USE_SIDEBAR +WHERE short SidebarRefreshTime; +WHERE LIST *SidebarWhitelist INITVAL(0); +#endif + #ifdef USE_IMAP WHERE short ImapKeepalive; WHERE short ImapPipelineDepth; diff --git a/imap/command.c b/imap/command.c index fedcfba94..f73e895dc 100644 --- a/imap/command.c +++ b/imap/command.c @@ -1016,6 +1016,14 @@ static void cmd_parse_status (IMAP_DATA* idata, char* s) opened */ status->uidnext = oldun; +#ifdef USE_SIDEBAR + /* Make the sidebar show the correct numbers */ + if (status->messages) { + inc->msg_count = status->messages; + inc->msg_unread = status->unseen; + } +#endif + FREE (&value); return; } diff --git a/imap/imap.c b/imap/imap.c index c334c441a..8a73ec9ad 100644 --- a/imap/imap.c +++ b/imap/imap.c @@ -1559,7 +1559,11 @@ int imap_buffy_check (int force) imap_munge_mbox_name (idata, munged, sizeof (munged), name); snprintf (command, sizeof (command), +#ifdef USE_SIDEBAR + "STATUS %s (UIDNEXT UIDVALIDITY UNSEEN RECENT MESSAGES)", munged); +#else "STATUS %s (UIDNEXT UIDVALIDITY UNSEEN RECENT)", munged); +#endif if (imap_exec (idata, command, IMAP_CMD_QUEUE) < 0) { diff --git a/init.c b/init.c index eed9c73b5..04d668595 100644 --- a/init.c +++ b/init.c @@ -2175,6 +2175,9 @@ static int parse_set (BUFFER *tmp, BUFFER *s, unsigned long data, BUFFER *err) case DT_SORT_AUX: map = SortAuxMethods; break; + case DT_SORT_SIDEBAR: + map = SortSidebarMethods; + break; default: map = SortMethods; break; diff --git a/init.h b/init.h index 06fb1eae0..b101582ee 100644 --- a/init.h +++ b/init.h @@ -42,11 +42,12 @@ #define DTYPE(x) ((x) & DT_MASK) /* subtypes */ -#define DT_SUBTYPE_MASK 0xf0 +#define DT_SUBTYPE_MASK 0xff0 #define DT_SORT_ALIAS 0x10 #define DT_SORT_BROWSER 0x20 #define DT_SORT_KEYS 0x40 #define DT_SORT_AUX 0x80 +#define DT_SORT_SIDEBAR 0x100 /* flags to parse_set() */ #define MUTT_SET_INV (1<<0) /* default is to invert all vars */ @@ -2666,6 +2667,146 @@ struct option_t MuttVars[] = { ** Command to use when spawning a subshell. By default, the user's login ** shell from \fC/etc/passwd\fP is used. */ +#ifdef USE_SIDEBAR + { "sidebar_divider_char", DT_STR, R_BOTH, UL &SidebarDividerChar, UL "|" }, + /* + ** .pp + ** This specifies the characters to be drawn between the sidebar (when + ** visible) and the other Mutt panels. ASCII and Unicode line-drawing + ** characters are supported. + */ + { "sidebar_delim_chars", DT_STR, R_NONE, UL &SidebarDelimChars, UL "/." }, + /* + ** .pp + ** This contains the list of characters which you would like to treat + ** as folder separators for displaying paths in the sidebar. + ** .pp + ** Local mail is often arranged in directories: `dir1/dir2/mailbox'. + ** .ts + ** set sidebar_delim_chars='/' + ** .te + ** IMAP mailboxes are often named: `folder1.folder2.mailbox'. + ** .ts + ** set sidebar_delim_chars='.' + ** .te + ** .pp + ** \fBSee also:\fP $$sidebar_short_path, $$sidebar_folder_indent, $$sidebar_indent_string. + */ + { "sidebar_folder_indent", DT_BOOL, R_BOTH, OPTSIDEBARFOLDERINDENT, 0 }, + /* + ** .pp + ** Set this to indent mailboxes in the sidebar. + ** .pp + ** \fBSee also:\fP $$sidebar_short_path, $$sidebar_indent_string, $$sidebar_delim_chars. + */ + { "sidebar_format", DT_STR, R_NONE, UL &SidebarFormat, UL "%B%?F? [%F]?%* %?N?%N/?%S" }, + /* + ** .pp + ** This variable allows you to customize the sidebar display. This string is + ** similar to $$index_format, but has its own set of \fCprintf(3)\fP-like + ** sequences: + ** .dl + ** .dt %B .dd Name of the mailbox + ** .dt %S .dd * Size of mailbox (total number of messages) + ** .dt %N .dd * Number of New messages in the mailbox + ** .dt %F .dd * Number of Flagged messages in the mailbox + ** .dt %! .dd ``!'' : one flagged message; + ** ``!!'' : two flagged messages; + ** ``n!'' : n flagged messages (for n > 2). + ** Otherwise prints nothing. + ** .dt %d .dd * @ Number of deleted messages + ** .dt %L .dd * @ Number of messages after limiting + ** .dt %t .dd * @ Number of tagged messages + ** .dt %>X .dd right justify the rest of the string and pad with ``X'' + ** .dt %|X .dd pad to the end of the line with ``X'' + ** .dt %*X .dd soft-fill with character ``X'' as pad + ** .de + ** .pp + ** * = Can be optionally printed if nonzero + ** @ = Only applicable to the current folder + */ + { "sidebar_indent_string", DT_STR, R_BOTH, UL &SidebarIndentString, UL " " }, + /* + ** .pp + ** This specifies the string that is used to indent mailboxes in the sidebar. + ** It defaults to two spaces. + ** .pp + ** \fBSee also:\fP $$sidebar_short_path, $$sidebar_folder_indent, $$sidebar_delim_chars. + */ + { "sidebar_new_mail_only", DT_BOOL, R_BOTH, OPTSIDEBARNEWMAILONLY, 0 }, + /* + ** .pp + ** When set, the sidebar will only display mailboxes containing new, or + ** flagged, mail. + ** .pp + ** \fBSee also:\fP $sidebar_whitelist. + */ + { "sidebar_next_new_wrap", DT_BOOL, R_BOTH, UL OPTSIDEBARNEXTNEWWRAP, 0 }, + /* + ** .pp + ** When set, the \fC\fP command will not stop and the end of + ** the list of mailboxes, but wrap around to the beginning. The + ** \fC\fP command is similarly affected, wrapping around to + ** the end of the list. + */ + { "sidebar_refresh_time", DT_NUM, R_BOTH, UL &SidebarRefreshTime, 60 }, + /* + ** .pp + ** Set sidebar_refresh_time to the minimum number of seconds between refreshes. + ** This will reduced network traffic. + ** .pp + ** \fBNote:\fP Set to 0 to disable refreshing. + */ + { "sidebar_short_path", DT_BOOL, R_BOTH, OPTSIDEBARSHORTPATH, 0 }, + /* + ** .pp + ** By default the sidebar will show the mailbox's path, relative to the + ** $$folder variable. Setting \fCsidebar_shortpath=yes\fP will shorten the + ** names relative to the previous name. Here's an example: + ** .dl + ** .dt \fBshortpath=no\fP .dd \fBshortpath=yes\fP .dd \fBshortpath=yes, folderindent=yes, indentstr=".."\fP + ** .dt \fCfruit\fP .dd \fCfruit\fP .dd \fCfruit\fP + ** .dt \fCfruit.apple\fP .dd \fCapple\fP .dd \fC..apple\fP + ** .dt \fCfruit.banana\fP .dd \fCbanana\fP .dd \fC..banana\fP + ** .dt \fCfruit.cherry\fP .dd \fCcherry\fP .dd \fC..cherry\fP + ** .de + ** .pp + ** \fBSee also:\fP $$sidebar_delim_chars, $$sidebar_folder_indent, $$sidebar_indent_string. + */ + { "sidebar_sort_method", DT_SORT|DT_SORT_SIDEBAR, R_NONE, UL &SidebarSortMethod, SORT_ORDER }, + /* + ** .pp + ** Specifies how to sort entries in the file browser. By default, the + ** entries are sorted alphabetically. Valid values: + ** .il + ** .dd alpha (alphabetically) + ** .dd count (all message count) + ** .dd date + ** .dd desc (description) + ** .dd new (new message count) + ** .dd size + ** .dd unsorted + ** .ie + ** .pp + ** You may optionally use the ``reverse-'' prefix to specify reverse sorting + ** order (example: ``\fCset sort_browser=reverse-date\fP''). + */ + { "sidebar_visible", DT_BOOL, R_BOTH|R_REFLOW, OPTSIDEBAR, 0 }, + /* + ** .pp + ** This specifies whether or not to show sidebar. The sidebar shows a list of + ** all your mailboxes. + ** .pp + ** \fBSee also:\fP $$sidebar_format, $$sidebar_width + */ + { "sidebar_width", DT_NUM, R_BOTH|R_REFLOW, UL &SidebarWidth, 30 }, + /* + ** .pp + ** This controls the width of the sidebar. It is measured in screen columns. + ** For example: sidebar_width=20 could display 20 ASCII characters, or 10 + ** Chinese characters. + */ +#endif { "sig_dashes", DT_BOOL, R_NONE, OPTSIGDASHES, 1 }, /* ** .pp @@ -3653,6 +3794,19 @@ const struct mapping_t SortKeyMethods[] = { { NULL, 0 } }; +const struct mapping_t SortSidebarMethods[] = { + { "alpha", SORT_PATH }, + { "count", SORT_COUNT }, + { "desc", SORT_DESC }, + { "flagged", SORT_FLAGGED }, + { "mailbox-order", SORT_ORDER }, + { "name", SORT_PATH }, + { "new", SORT_COUNT_NEW }, + { "path", SORT_PATH }, + { "unsorted", SORT_ORDER }, + { NULL, 0 } +}; + /* functions used to parse commands in a rc file */ @@ -3742,6 +3896,9 @@ const struct command_t Commands[] = { { "send-hook", mutt_parse_hook, MUTT_SENDHOOK }, { "send2-hook", mutt_parse_hook, MUTT_SEND2HOOK }, { "set", parse_set, 0 }, +#ifdef USE_SIDEBAR + { "sidebar_whitelist",parse_list, UL &SidebarWhitelist }, +#endif { "source", parse_source, 0 }, { "spam", parse_spam_list, MUTT_SPAM }, { "nospam", parse_spam_list, MUTT_NOSPAM }, diff --git a/keymap.c b/keymap.c index 8b8b972b5..301ef6537 100644 --- a/keymap.c +++ b/keymap.c @@ -457,6 +457,9 @@ int km_dokey (int menu) } #endif + /* update sidebar stats */ + mutt_buffy_check(0); + timeout (i * 1000); tmp = mutt_getch(); timeout (-1); diff --git a/mailbox.h b/mailbox.h index 322b8a90b..bdbfeac29 100644 --- a/mailbox.h +++ b/mailbox.h @@ -27,6 +27,9 @@ #define MUTT_NEWFOLDER (1<<4) /* create a new folder - same as MUTT_APPEND, but uses * safe_fopen() for mbox-style folders. */ +#ifdef USE_SIDEBAR +#define MUTT_PEEK (1<<5) /* revert atime back after taking a look (if applicable) */ +#endif /* mx_open_new_message() */ #define MUTT_ADD_FROM (1<<0) /* add a From_ line */ diff --git a/main.c b/main.c index 1cb918053..11e3cdcec 100644 --- a/main.c +++ b/main.c @@ -31,6 +31,9 @@ #include "url.h" #include "mutt_crypt.h" #include "mutt_idna.h" +#ifdef USE_SIDEBAR +#include "sidebar.h" +#endif #ifdef USE_SASL #include "mutt_sasl.h" @@ -485,6 +488,12 @@ static void show_version (void) "-USE_HCACHE " #endif +#ifdef USE_SIDEBAR + "+USE_SIDEBAR " +#else + "-USE_SIDEBAR " +#endif + ); #ifdef ISPELL @@ -558,7 +567,11 @@ init_extended_keys(); int main (int argc, char **argv) { +#ifdef USE_SIDEBAR + char folder[PATH_MAX] = ""; +#else char folder[_POSIX_PATH_MAX] = ""; +#endif char *subject = NULL; char *includeFile = NULL; char *draftFile = NULL; @@ -1189,6 +1202,15 @@ int main (int argc, char **argv) strfcpy (folder, NONULL(Spoolfile), sizeof (folder)); mutt_expand_path (folder, sizeof (folder)); +#ifdef USE_SIDEBAR + { + char tmpfolder[PATH_MAX] = ""; + strfcpy (tmpfolder, folder, sizeof (tmpfolder)); + if (!realpath (tmpfolder, folder)) + strfcpy (folder, tmpfolder, sizeof (tmpfolder)); + } +#endif + mutt_str_replace (&CurrentFolder, folder); mutt_str_replace (&LastFolder, folder); @@ -1211,6 +1233,9 @@ int main (int argc, char **argv) if((Context = mx_open_mailbox (folder, ((flags & MUTT_RO) || option (OPTREADONLY)) ? MUTT_READONLY : 0, NULL)) || !explicit_folder) { +#ifdef USE_SIDEBAR + sb_set_open_buffy (folder); +#endif mutt_index_menu (); if (Context) FREE (&Context); diff --git a/mbox.c b/mbox.c index da6921008..3d071dc46 100644 --- a/mbox.c +++ b/mbox.c @@ -100,6 +100,9 @@ int mmdf_parse_mailbox (CONTEXT *ctx) mutt_perror (ctx->path); return (-1); } +#ifdef USE_SIDEBAR + ctx->atime = sb.st_atime; +#endif ctx->mtime = sb.st_mtime; ctx->size = sb.st_size; @@ -251,6 +254,9 @@ int mbox_parse_mailbox (CONTEXT *ctx) ctx->size = sb.st_size; ctx->mtime = sb.st_mtime; +#ifdef USE_SIDEBAR + ctx->atime = sb.st_atime; +#endif #ifdef NFS_ATTRIBUTE_HACK if (sb.st_mtime > sb.st_atime) diff --git a/menu.c b/menu.c index fa5f27a74..a8be371dc 100644 --- a/menu.c +++ b/menu.c @@ -24,6 +24,9 @@ #include "mutt_curses.h" #include "mutt_menu.h" #include "mbyte.h" +#ifdef USE_SIDEBAR +#include "sidebar.h" +#endif char* SearchBuffers[MENU_MAX]; @@ -235,6 +238,9 @@ void menu_redraw_index (MUTTMENU *menu) int do_color; int attr; +#ifdef USE_SIDEBAR + sb_draw(); +#endif for (i = menu->top; i < menu->top + menu->pagelen; i++) { if (i < menu->max) diff --git a/mh.c b/mh.c index c33745a20..23b7502ae 100644 --- a/mh.c +++ b/mh.c @@ -298,6 +298,49 @@ void mh_buffy(BUFFY *b) mhs_free_sequences (&mhs); } +#ifdef USE_SIDEBAR +/** + * mh_buffy_update - Update messages counts for an mh mailbox + * @mailbox: BUFFY representing a maildir mailbox + * + * Read through an mh mailbox and count messages. Save the number of new, + * flagged messages and a timestamp for now. + */ +void +mh_buffy_update (BUFFY *mailbox) +{ + if (!mailbox) + return; + + if (!option (OPTSIDEBAR)) + return; + + struct mh_sequences mhs; + memset (&mhs, 0, sizeof (mhs)); + + if (mh_read_sequences (&mhs, mailbox->path) < 0) + return; + + mailbox->msg_count = 0; + mailbox->msg_unread = 0; + mailbox->msg_flagged = 0; + + int i; + for (i = 0; i <= mhs.max; i++) { + mailbox->msg_count++; + } + if (mhs_check (&mhs, i) & MH_SEQ_UNSEEN) { + mailbox->msg_unread++; + } + if (mhs_check (&mhs, i) & MH_SEQ_FLAGGED) { + mailbox->msg_flagged++; + } + mhs_free_sequences (&mhs); + mailbox->sb_last_checked = time (NULL); +} + +#endif + static int mh_mkstemp (CONTEXT * dest, FILE ** fp, char **tgt) { int fd; diff --git a/mutt.h b/mutt.h index f6ca62d31..cb386fb49 100644 --- a/mutt.h +++ b/mutt.h @@ -428,6 +428,13 @@ enum OPTSAVEEMPTY, OPTSAVENAME, OPTSCORE, +#ifdef USE_SIDEBAR + OPTSIDEBAR, + OPTSIDEBARFOLDERINDENT, + OPTSIDEBARNEWMAILONLY, + OPTSIDEBARNEXTNEWWRAP, + OPTSIDEBARSHORTPATH, +#endif OPTSIGDASHES, OPTSIGONTOP, OPTSORTRE, @@ -893,6 +900,9 @@ typedef struct _context { char *path; FILE *fp; +#ifdef USE_SIDEBAR + time_t atime; +#endif time_t mtime; off_t size; off_t vsize; @@ -927,6 +937,9 @@ typedef struct _context unsigned int quiet : 1; /* inhibit status messages? */ unsigned int collapsed : 1; /* are all threads collapsed? */ unsigned int closing : 1; /* mailbox is being closed */ +#ifdef USE_SIDEBAR + unsigned int peekonly : 1; /* just taking a glance, revert atime */ +#endif /* driver hooks */ void *data; /* driver specific data */ diff --git a/mutt_curses.h b/mutt_curses.h index fbdc45c24..62004491f 100644 --- a/mutt_curses.h +++ b/mutt_curses.h @@ -123,6 +123,14 @@ enum MT_COLOR_UNDERLINE, MT_COLOR_INDEX, MT_COLOR_PROMPT, +#ifdef USE_SIDEBAR + MT_COLOR_DIVIDER, + MT_COLOR_FLAGGED, + MT_COLOR_HIGHLIGHT, + MT_COLOR_NEW, + MT_COLOR_SB_INDICATOR, + MT_COLOR_SB_SPOOLFILE, +#endif MT_COLOR_MAX }; diff --git a/mutt_menu.h b/mutt_menu.h index bb738b5c7..bf5522c07 100644 --- a/mutt_menu.h +++ b/mutt_menu.h @@ -35,6 +35,9 @@ #define REDRAW_FULL (1<<5) #define REDRAW_BODY (1<<6) #define REDRAW_SIGWINCH (1<<7) +#ifdef USE_SIDEBAR +#define REDRAW_SIDEBAR (1<<8) +#endif #define MUTT_MODEFMT "-- Mutt: %s" diff --git a/mx.c b/mx.c index d15fba372..9f3a7a991 100644 --- a/mx.c +++ b/mx.c @@ -29,6 +29,9 @@ #include "copy.h" #include "keymap.h" #include "url.h" +#ifdef USE_SIDEBAR +#include "sidebar.h" +#endif #ifdef USE_IMAP #include "imap.h" @@ -613,6 +616,7 @@ static int mx_close_mailbox_append (CONTEXT *ctx) * MUTT_APPEND open mailbox for appending * MUTT_READONLY open mailbox in read-only mode * MUTT_QUIET only print error messages + * MUTT_PEEK revert atime where applicable * ctx if non-null, context struct to use */ CONTEXT *mx_open_mailbox (const char *path, int flags, CONTEXT *pctx) @@ -635,6 +639,10 @@ CONTEXT *mx_open_mailbox (const char *path, int flags, CONTEXT *pctx) ctx->quiet = 1; if (flags & MUTT_READONLY) ctx->readonly = 1; +#ifdef USE_SIDEBAR + if (flags & MUTT_PEEK) + ctx->peekonly = 1; +#endif if (flags & (MUTT_APPEND|MUTT_NEWFOLDER)) { @@ -708,8 +716,21 @@ void mx_fastclose_mailbox (CONTEXT *ctx) if(!ctx) return; +#ifdef USE_SIDEBAR + /* fix up the times so buffy won't get confused */ + struct utimbuf ut; + if (ctx->peekonly && ctx->path && (ctx->mtime > ctx->atime)) { + ut.actime = ctx->atime; + ut.modtime = ctx->mtime; + utime (ctx->path, &ut); + } +#endif + /* never announce that a mailbox we've just left has new mail. #3290 * XXX: really belongs in mx_close_mailbox, but this is a nice hook point */ +#ifdef USE_SIDEBAR + if (!ctx->peekonly) +#endif mutt_buffy_setnotified(ctx->path); if (ctx->mx_ops) @@ -722,6 +743,10 @@ void mx_fastclose_mailbox (CONTEXT *ctx) mutt_clear_threads (ctx); for (i = 0; i < ctx->msgcount; i++) mutt_free_header (&ctx->hdrs[i]); +#ifdef USE_SIDEBAR + ctx->msgcount -= ctx->deleted; + sb_set_buffystats (ctx); +#endif FREE (&ctx->hdrs); FREE (&ctx->v2r); FREE (&ctx->path); @@ -815,6 +840,12 @@ int mx_close_mailbox (CONTEXT *ctx, int *index_hint) if (!ctx->hdrs[i]->deleted && ctx->hdrs[i]->read && !(ctx->hdrs[i]->flagged && option (OPTKEEPFLAGGED))) read_msgs++; +#ifdef USE_SIDEBAR + if (ctx->hdrs[i]->deleted && !ctx->hdrs[i]->read) + ctx->unread--; + if (ctx->hdrs[i]->deleted && ctx->hdrs[i]->flagged) + ctx->flagged--; +#endif } if (read_msgs && quadoption (OPT_MOVE) != MUTT_NO) diff --git a/mx.h b/mx.h index ef8359513..6bc11b435 100644 --- a/mx.h +++ b/mx.h @@ -26,6 +26,7 @@ #define _MX_H #include "mailbox.h" +#include "buffy.h" /* supported mailbox formats */ enum @@ -52,6 +53,9 @@ int mbox_check_empty (const char *); void mbox_reset_atime (CONTEXT *, struct stat *); int mh_sync_mailbox (CONTEXT *, int *); +#ifdef USE_SIDEBAR +void mh_buffy_update (BUFFY *mailbox); +#endif int mh_check_empty (const char *); int maildir_check_empty (const char *); diff --git a/pager.c b/pager.c index 27319990c..06bed3abf 100644 --- a/pager.c +++ b/pager.c @@ -29,6 +29,9 @@ #include "pager.h" #include "attach.h" #include "mbyte.h" +#ifdef USE_SIDEBAR +#include "sidebar.h" +#endif #include "mutt_crypt.h" @@ -1668,6 +1671,9 @@ mutt_pager (const char *banner, const char *fname, int flags, pager_t *extra) /* clear() doesn't optimize screen redraws */ move (0, 0); clrtobot (); +#ifdef USE_SIDEBAR + sb_draw(); +#endif if (IsHeader (extra) && Context->vcount + 1 < PagerIndexLines) indexlen = Context->vcount + 1; @@ -2537,8 +2543,12 @@ search_next: ch = 0; } - if (option (OPTFORCEREDRAWPAGER)) + if (option (OPTFORCEREDRAWPAGER)) { redraw = REDRAW_FULL; +#ifdef USE_SIDEBAR + sb_draw(); +#endif + } unset_option (OPTFORCEREDRAWINDEX); unset_option (OPTFORCEREDRAWPAGER); break; @@ -2816,6 +2826,23 @@ search_next: mutt_what_key (); break; +#ifdef USE_SIDEBAR + case OP_SIDEBAR_NEXT: + case OP_SIDEBAR_NEXT_NEW: + case OP_SIDEBAR_PAGE_DOWN: + case OP_SIDEBAR_PAGE_UP: + case OP_SIDEBAR_PREV: + case OP_SIDEBAR_PREV_NEW: + sb_change_mailbox (ch); + break; + + case OP_SIDEBAR_TOGGLE_VISIBLE: + toggle_option (OPTSIDEBAR); + mutt_reflow_windows(); + redraw = REDRAW_FULL; + break; +#endif + default: ch = -1; break; diff --git a/sidebar.c b/sidebar.c new file mode 100644 index 000000000..6f8093d43 --- /dev/null +++ b/sidebar.c @@ -0,0 +1,987 @@ +/* Copyright (C) 2004 Justin Hibbits + * Copyright (C) 2004 Thomer M. Gil + * Copyright (C) 2015-2016 Richard Russon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include "mutt.h" +#include "buffy.h" +#include "keymap.h" +#include "mutt_curses.h" +#include "mutt_menu.h" +#include "sort.h" + +/* Previous values for some sidebar config */ +static short PreviousSort; /* sidebar_sort_method */ +static time_t LastRefresh; /* Time of last refresh */ + +/* Keep track of various BUFFYs */ +static BUFFY *TopBuffy; /* First mailbox visible in sidebar */ +static BUFFY *OpnBuffy; /* Current (open) mailbox */ +static BUFFY *HilBuffy; /* Highlighted mailbox */ +static BUFFY *BotBuffy; /* Last mailbox visible in sidebar */ +static BUFFY *Outgoing; /* Last mailbox in the linked list */ + +/** + * struct sidebar_entry - Info about folders in the sidebar + * + * Used in the mutt_FormatString callback + */ +struct sidebar_entry { + char box[SHORT_STRING]; + BUFFY *buffy; +}; + + +/** + * find_next_new - Find the next folder that contains new mail + * @wrap: Wrap around to the beginning if the end is reached + * + * Search down the list of mail folders for one containing new mail. + * + * Returns: + * BUFFY*: Success + * NULL: Failure + */ +static BUFFY * +find_next_new (int wrap) +{ + BUFFY *b = HilBuffy; + if (!b) + return NULL; + + do { + b = b->next; + if (!b && wrap) { + b = Incoming; + } + if (!b || (b == HilBuffy)) { + break; + } + if (b->msg_unread > 0) { + return b; + } + } while (b); + + return NULL; +} + +/** + * find_prev_new - Find the previous folder that contains new mail + * @wrap: Wrap around to the beginning if the end is reached + * + * Search up the list of mail folders for one containing new mail. + * + * Returns: + * BUFFY*: Success + * NULL: Failure + */ +static BUFFY * +find_prev_new (int wrap) +{ + BUFFY *b = HilBuffy; + if (!b) + return NULL; + + do { + b = b->prev; + if (!b && wrap) { + b = Outgoing; + } + if (!b || (b == HilBuffy)) { + break; + } + if (b->msg_unread > 0) { + return b; + } + } while (b); + + return NULL; +} + +/** + * cb_format_str - Create the string to show in the sidebar + * @dest: Buffer in which to save string + * @destlen: Buffer length + * @col: Starting column, UNUSED + * @op: printf-like operator, e.g. 'B' + * @src: printf-like format string + * @prefix: Field formatting string, UNUSED + * @ifstring: If condition is met, display this string + * @elsestring: Otherwise, display this string + * @data: Pointer to our sidebar_entry + * @flags: Format flags, e.g. MUTT_FORMAT_OPTIONAL + * + * cb_format_str is a callback function for mutt_FormatString. It understands + * five operators. '%B' : Mailbox name, '%F' : Number of flagged messages, + * '%N' : Number of new messages, '%S' : Size (total number of messages), + * '%!' : Icon denoting number of flagged messages. + * + * Returns: src (unchanged) + */ +static const char * +cb_format_str (char *dest, size_t destlen, size_t col, int cols, char op, + const char *src, const char *prefix, const char *ifstring, + const char *elsestring, unsigned long data, format_flag flags) +{ + struct sidebar_entry *sbe = (struct sidebar_entry *) data; + unsigned int optional; + char fmt[SHORT_STRING], buf[SHORT_STRING]; + + if (!sbe || !dest) + return src; + + dest[0] = 0; /* Just in case there's nothing to do */ + + BUFFY *b = sbe->buffy; + if (!b) + return src; + + int c = Context && (mutt_strcmp (Context->path, b->path) == 0); + + optional = flags & MUTT_FORMAT_OPTIONAL; + + switch (op) { + case 'B': + mutt_format_s (dest, destlen, prefix, sbe->box); + break; + + case 'd': + if (!optional) { + snprintf (fmt, sizeof (fmt), "%%%sd", prefix); + snprintf (dest, destlen, fmt, c ? Context->deleted : 0); + } else if ((c && Context->deleted == 0) || !c) { + optional = 0; + } + break; + + case 'F': + if (!optional) { + snprintf (fmt, sizeof (fmt), "%%%sd", prefix); + snprintf (dest, destlen, fmt, b->msg_flagged); + } else if (b->msg_flagged == 0) { + optional = 0; + } + break; + + case 'L': + if (!optional) { + snprintf (fmt, sizeof (fmt), "%%%sd", prefix); + snprintf (dest, destlen, fmt, c ? Context->vcount : b->msg_count); + } else if ((c && Context->vcount == b->msg_count) || !c) { + optional = 0; + } + break; + + case 'N': + if (!optional) { + snprintf (fmt, sizeof (fmt), "%%%sd", prefix); + snprintf (dest, destlen, fmt, b->msg_unread); + } else if (b->msg_unread == 0) { + optional = 0; + } + break; + + case 'S': + if (!optional) { + snprintf (fmt, sizeof (fmt), "%%%sd", prefix); + snprintf (dest, destlen, fmt, b->msg_count); + } else if (b->msg_count == 0) { + optional = 0; + } + break; + + case 't': + if (!optional) { + snprintf (fmt, sizeof (fmt), "%%%sd", prefix); + snprintf (dest, destlen, fmt, c ? Context->tagged : 0); + } else if ((c && Context->tagged == 0) || !c) { + optional = 0; + } + break; + + case '!': + if (b->msg_flagged == 0) { + mutt_format_s (dest, destlen, prefix, ""); + } else if (b->msg_flagged == 1) { + mutt_format_s (dest, destlen, prefix, "!"); + } else if (b->msg_flagged == 2) { + mutt_format_s (dest, destlen, prefix, "!!"); + } else { + snprintf (buf, sizeof (buf), "%d!", b->msg_flagged); + mutt_format_s (dest, destlen, prefix, buf); + } + break; + } + + if (optional) + mutt_FormatString (dest, destlen, col, SidebarWidth, ifstring, cb_format_str, (unsigned long) sbe, flags); + else if (flags & MUTT_FORMAT_OPTIONAL) + mutt_FormatString (dest, destlen, col, SidebarWidth, elsestring, cb_format_str, (unsigned long) sbe, flags); + + /* We return the format string, unchanged */ + return src; +} + +/** + * make_sidebar_entry - Turn mailbox data into a sidebar string + * @buf: Buffer in which to save string + * @buflen: Buffer length + * @width: Desired width in screen cells + * @box: Mailbox name + * @b: Mailbox object + * + * Take all the relevant mailbox data and the desired screen width and then get + * mutt_FormatString to do the actual work. mutt_FormatString will callback to + * us using cb_format_str() for the sidebar specific formatting characters. + */ +static void +make_sidebar_entry (char *buf, unsigned int buflen, int width, char *box, + BUFFY *b) +{ + struct sidebar_entry sbe; + + if (!buf || !box || !b) + return; + + sbe.buffy = b; + strncpy (sbe.box, box, sizeof (sbe.box) - 1); + + int box_len = strlen (box); + sbe.box[box_len] = '\0'; + + mutt_FormatString (buf, buflen, 0, width, NONULL(SidebarFormat), cb_format_str, (unsigned long) &sbe, 0); + + /* Force string to be exactly the right width */ + int w = mutt_strwidth (buf); + int s = strlen (buf); + if (w < width) { + /* Pad with spaces */ + memset (buf + s, ' ', width - w); + buf[s + width - w] = 0; + } else if (w > width) { + /* Truncate to fit */ + int len = mutt_wstr_trunc (buf, buflen, width, NULL); + buf[len] = 0; + } +} + +/** + * cb_qsort_buffy - qsort callback to sort BUFFYs + * @a: First BUFFY to compare + * @b: Second BUFFY to compare + * + * Compare the paths of two BUFFYs taking the locale into account. + * + * Returns: + * -1: a precedes b + * 0: a and b are identical + * 1: b precedes a + */ +static int +cb_qsort_buffy (const void *a, const void *b) +{ + const BUFFY *b1 = *(const BUFFY **) a; + const BUFFY *b2 = *(const BUFFY **) b; + + /* Special case -- move hidden BUFFYs to the end */ + if (b1->is_hidden != b2->is_hidden) { + if (b1->is_hidden) + return 1; + else + return -1; + } + + int result = 0; + + switch ((SidebarSortMethod & SORT_MASK)) { + case SORT_COUNT: + result = (b2->msg_count - b1->msg_count); + break; + case SORT_COUNT_NEW: + result = (b2->msg_unread - b1->msg_unread); + break; + case SORT_FLAGGED: + result = (b2->msg_flagged - b1->msg_flagged); + break; + case SORT_PATH: + result = mutt_strcasecmp (b1->path, b2->path); + break; + } + + if (SidebarSortMethod & SORT_REVERSE) + result = -result; + + return result; +} + +/** + * buffy_going - Prevent our pointers becoming invalid + * @b: BUFFY about to be deleted + * + * If we receive a delete-notification for a BUFFY, we need to change any + * pointers we have to reference a different BUFFY, or set them to NULL. + * + * We don't update the prev/next pointers, they'll be fixed on the next + * call to prepare_sidebar(). + * + * Returns: + * A valid alternative BUFFY, or NULL + */ +static BUFFY * +buffy_going (const BUFFY *b) +{ + if (!b) + return NULL; + + if (b->prev) { + b->prev->next = NULL; + } + + if (b->next) { + b->next->prev = NULL; + return b->next; + } + + return b->prev; +} + +/** + * update_buffy_visibility - Should a BUFFY be displayed in the sidebar + * @arr: array of BUFFYs + * @arr_len: number of BUFFYs in array + * + * For each BUFFY in the array, check whether we should display it. + * This is determined by several criteria. If the BUFFY: + * is the currently open mailbox + * is the currently highlighted mailbox + * has unread messages + * has flagged messages + * is whitelisted + */ +static void +update_buffy_visibility (BUFFY **arr, int arr_len) +{ + if (!arr) + return; + + short new_only = option (OPTSIDEBARNEWMAILONLY); + + BUFFY *b; + int i; + for (i = 0; i < arr_len; i++) { + b = arr[i]; + + b->is_hidden = 0; + + if (!new_only) + continue; + + if ((b == OpnBuffy) || (b->msg_unread > 0) || + (b == HilBuffy) || (b->msg_flagged > 0)) { + continue; + } + + if (Context && (strcmp (b->path, Context->path) == 0)) { + /* Spool directory */ + continue; + } + + if (mutt_find_list (SidebarWhitelist, b->path)) { + /* Explicitly asked to be visible */ + continue; + } + + b->is_hidden = 1; + } +} + +/** + * sort_buffy_array - Sort an array of BUFFY pointers + * @arr: array of BUFFYs + * @arr_len: number of BUFFYs in array + * + * Sort an array of BUFFY pointers according to the current sort config + * option "sidebar_sort_method". This calls qsort to do the work which calls our + * callback function "cb_qsort_buffy". + * + * Once sorted, the prev/next links will be reconstructed. + */ +static void +sort_buffy_array (BUFFY **arr, int arr_len) +{ + if (!arr) + return; + + /* These are the only sort methods we understand */ + short ssm = (SidebarSortMethod & SORT_MASK); + if ((ssm == SORT_COUNT) || + (ssm == SORT_COUNT_NEW) || + (ssm == SORT_DESC) || + (ssm == SORT_FLAGGED) || + (ssm == SORT_PATH)) { + qsort (arr, arr_len, sizeof (*arr), cb_qsort_buffy); + } + + int i; + for (i = 0; i < (arr_len - 1); i++) { + arr[i]->next = arr[i + 1]; + } + arr[arr_len - 1]->next = NULL; + + for (i = 1; i < arr_len; i++) { + arr[i]->prev = arr[i - 1]; + } + arr[0]->prev = NULL; +} + +/** + * prepare_sidebar - Prepare the list of BUFFYs for the sidebar display + * @page_size: The number of lines on a page + * + * Before painting the sidebar, we count the BUFFYs, determine which are + * visible, sort them and set up our page pointers. + * + * This is a lot of work to do each refresh, but there are many things that + * can change outside of the sidebar that we don't hear about. + * + * Returns: + * 0: No, don't draw the sidebar + * 1: Yes, draw the sidebar + */ +static int +prepare_sidebar (int page_size) +{ + BUFFY *b = Incoming; + if (!b) + return 0; + + int count = 0; + for (; b; b = b->next) + count++; + + BUFFY **arr = safe_malloc (count * sizeof (*arr)); + if (!arr) + return 0; + + int i = 0; + for (b = Incoming; b; b = b->next, i++) { + arr[i] = b; + } + + update_buffy_visibility (arr, count); + sort_buffy_array (arr, count); + + Incoming = arr[0]; + + int top_index = 0; + int opn_index = -1; + int hil_index = -1; + int bot_index = -1; + + for (i = 0; i < count; i++) { + if (OpnBuffy == arr[i]) + opn_index = i; + if (HilBuffy == arr[i]) + hil_index = i; + } + + if (!HilBuffy || (SidebarSortMethod != PreviousSort)) { + if (OpnBuffy) { + HilBuffy = OpnBuffy; + hil_index = opn_index; + } else { + HilBuffy = arr[0]; + hil_index = 0; + } + } + if (TopBuffy) { + top_index = (hil_index / page_size) * page_size; + } else { + top_index = hil_index; + } + TopBuffy = arr[top_index]; + + bot_index = top_index + page_size - 1; + if (bot_index > (count - 1)) { + bot_index = count - 1; + } + BotBuffy = arr[bot_index]; + + Outgoing = arr[count - 1]; + + PreviousSort = SidebarSortMethod; + free (arr); + return 1; +} + +/** + * draw_divider - Draw a line between the sidebar and the rest of mutt + * @num_rows: Height of the Sidebar + * @num_cols: Width of the Sidebar + * + * Draw a divider using characters from the config option "sidebar_divider_char". + * This can be an ASCII or Unicode character. First we calculate this + * characters' width in screen columns, then subtract that from the config + * option "sidebar_width". + * + * Returns: + * -1: Error: bad character, etc + * 0: Error: 0 width character + * n: Success: character occupies n screen columns + */ +static int +draw_divider (int num_rows, int num_cols) +{ + /* Calculate the width of the delimiter in screen cells */ + int delim_len = mutt_strwidth (SidebarDividerChar); + + if (delim_len < 1) + return delim_len; + + if (delim_len > num_cols) + return 0; + + SETCOLOR(MT_COLOR_DIVIDER); + + int i; + for (i = 0; i < num_rows; i++) { + mutt_window_move (MuttSidebarWindow, i, SidebarWidth - delim_len); //RAR 0 for rhs + addstr (NONULL(SidebarDividerChar)); + } + + return delim_len; +} + +/** + * fill_empty_space - Wipe the remaining Sidebar space + * @first_row: Window line to start (0-based) + * @num_rows: Number of rows to fill + * @width: Width of the Sidebar (minus the divider) + * + * Write spaces over the area the sidebar isn't using. + */ +static void +fill_empty_space (int first_row, int num_rows, int width) +{ + /* Fill the remaining rows with blank space */ + SETCOLOR(MT_COLOR_NORMAL); + + int r; + for (r = 0; r < num_rows; r++) { + mutt_window_move (MuttSidebarWindow, first_row + r, 0); //RAR rhs + int i; + for (i = 0; i < width; i++) + addch (' '); + } +} + +/** + * draw_sidebar - Write out a list of mailboxes, on the left + * @num_rows: Height of the Sidebar + * @num_cols: Width of the Sidebar + * @div_width: Width in screen characters taken by the divider + * + * Display a list of mailboxes in a panel on the left. What's displayed will + * depend on our index markers: TopBuffy, OpnBuffy, HilBuffy, BotBuffy. + * On the first run they'll be NULL, so we display the top of Mutt's list + * (Incoming). + * + * TopBuffy - first visible mailbox + * BotBuffy - last visible mailbox + * OpnBuffy - mailbox shown in Mutt's Index Panel + * HilBuffy - Unselected mailbox (the paging follows this) + * + * The entries are formatted using "sidebar_format" and may be abbreviated: + * "sidebar_short_path", indented: "sidebar_folder_indent", + * "sidebar_indent_string" and sorted: "sidebar_sort_method". Finally, they're + * trimmed to fit the available space. + */ +static void +draw_sidebar (int num_rows, int num_cols, int div_width) +{ + BUFFY *b = TopBuffy; + if (!b) + return; + + int w = MIN(num_cols, (SidebarWidth - div_width)); + int row = 0; + for (b = TopBuffy; b && (row < num_rows); b = b->next) { + if (b->is_hidden) { + continue; + } + + if (b == OpnBuffy) { + if ((ColorDefs[MT_COLOR_SB_INDICATOR] != 0)) { + SETCOLOR(MT_COLOR_SB_INDICATOR); + } else { + SETCOLOR(MT_COLOR_INDICATOR); + } + } else if (b == HilBuffy) { + SETCOLOR(MT_COLOR_HIGHLIGHT); + } else if ((ColorDefs[MT_COLOR_SB_SPOOLFILE] != 0) && + (mutt_strcmp (b->path, Spoolfile) == 0)) { + SETCOLOR(MT_COLOR_SB_SPOOLFILE); + } else if (b->msg_unread > 0) { + SETCOLOR(MT_COLOR_NEW); + } else if (b->msg_flagged > 0) { + SETCOLOR(MT_COLOR_FLAGGED); + } else { + SETCOLOR(MT_COLOR_NORMAL); + } + + mutt_window_move (MuttSidebarWindow, row, 0); + if (Context && Context->path && + (!strcmp (b->path, Context->path)|| + !strcmp (b->realpath, Context->path))) { + b->msg_unread = Context->unread; + b->msg_count = Context->msgcount; + b->msg_flagged = Context->flagged; + } + + /* compute length of Maildir without trailing separator */ + size_t maildirlen = strlen (Maildir); + if (SidebarDelimChars && strchr (SidebarDelimChars, Maildir[maildirlen - 1])) { + maildirlen--; + } + + /* check whether Maildir is a prefix of the current folder's path */ + short maildir_is_prefix = 0; + if ((strlen (b->path) > maildirlen) && (strncmp (Maildir, b->path, maildirlen) == 0)) { + maildir_is_prefix = 1; + } + /* calculate depth of current folder and generate its display name with indented spaces */ + int sidebar_folder_depth = 0; + char *sidebar_folder_name; + int i; + if (option (OPTSIDEBARSHORTPATH)) { + /* disregard a trailing separator, so strlen() - 2 */ + sidebar_folder_name = b->path; + for (i = strlen (sidebar_folder_name) - 2; i >= 0; i--) { + if (SidebarDelimChars && + strchr (SidebarDelimChars, sidebar_folder_name[i])) { + sidebar_folder_name += (i + 1); + break; + } + } + } else { + sidebar_folder_name = b->path + maildir_is_prefix * (maildirlen + 1); + } + if (maildir_is_prefix && option (OPTSIDEBARFOLDERINDENT)) { + const char *tmp_folder_name; + int lastsep = 0; + tmp_folder_name = b->path + maildirlen + 1; + int tmplen = (int) strlen (tmp_folder_name) - 1; + for (i = 0; i < tmplen; i++) { + if (SidebarDelimChars && strchr (SidebarDelimChars, tmp_folder_name[i])) { + sidebar_folder_depth++; + lastsep = i + 1; + } + } + if (sidebar_folder_depth > 0) { + if (option (OPTSIDEBARSHORTPATH)) { + tmp_folder_name += lastsep; /* basename */ + } + sidebar_folder_name = malloc (strlen (tmp_folder_name) + sidebar_folder_depth*strlen (NONULL(SidebarIndentString)) + 1); + sidebar_folder_name[0]=0; + for (i=0; i < sidebar_folder_depth; i++) + strncat (sidebar_folder_name, NONULL(SidebarIndentString), strlen (NONULL(SidebarIndentString))); + strncat (sidebar_folder_name, tmp_folder_name, strlen (tmp_folder_name)); + } + } + char str[SHORT_STRING]; + make_sidebar_entry (str, sizeof (str), w, sidebar_folder_name, b); + printw ("%s", str); + if (sidebar_folder_depth > 0) + free (sidebar_folder_name); + row++; + } + + fill_empty_space (row, num_rows - row, w); +} + + +/** + * sb_draw - Completely redraw the sidebar + * + * Completely refresh the sidebar region. First draw the divider; then, for + * each BUFFY, call make_sidebar_entry; finally blank out any remaining space. + */ +void +sb_draw (void) +{ + if (!option (OPTSIDEBAR)) + return; + + int num_rows = MuttSidebarWindow->rows; + int num_cols = MuttSidebarWindow->cols; + + int div_width = draw_divider (num_rows, num_cols); + if (div_width < 0) + return; + + if (!Incoming) { + fill_empty_space (0, num_rows, SidebarWidth - div_width); + return; + } + + if (!prepare_sidebar (num_rows)) + return; + + draw_sidebar (num_rows, num_cols, div_width); +} + +/** + * sb_should_refresh - Check if the sidebar is due to be refreshed + * + * The "sidebar_refresh_time" config option allows the user to limit the frequency + * with which the sidebar is refreshed. + * + * Returns: + * 1 Yes, refresh is due + * 0 No, refresh happened recently + */ +int +sb_should_refresh (void) +{ + if (!option (OPTSIDEBAR)) + return 0; + + if (SidebarRefreshTime == 0) + return 0; + + time_t diff = (time (NULL) - LastRefresh); + + return (diff >= SidebarRefreshTime); +} + +/** + * sb_change_mailbox - Change the selected mailbox + * @op: Operation code + * + * Change the selected mailbox, e.g. "Next mailbox", "Previous Mailbox + * with new mail". The operations are listed OPS.SIDEBAR which is built + * into an enum in keymap_defs.h. + * + * If the operation is successful, HilBuffy will be set to the new mailbox. + * This function only *selects* the mailbox, doesn't *open* it. + * + * Allowed values are: OP_SIDEBAR_NEXT, OP_SIDEBAR_NEXT_NEW, + * OP_SIDEBAR_PAGE_DOWN, OP_SIDEBAR_PAGE_UP, OP_SIDEBAR_PREV, + * OP_SIDEBAR_PREV_NEW. + */ +void +sb_change_mailbox (int op) +{ + if (!option (OPTSIDEBAR)) + return; + + BUFFY *b; + if (!HilBuffy) /* It'll get reset on the next draw */ + return; + + switch (op) { + case OP_SIDEBAR_NEXT: + if (!HilBuffy->next) + return; + if (HilBuffy->next->is_hidden) + return; + HilBuffy = HilBuffy->next; + break; + case OP_SIDEBAR_NEXT_NEW: + b = find_next_new (option (OPTSIDEBARNEXTNEWWRAP)); + if (!b) { + return; + } else { + HilBuffy = b; + } + break; + case OP_SIDEBAR_PAGE_DOWN: + HilBuffy = BotBuffy; + if (HilBuffy->next) { + HilBuffy = HilBuffy->next; + } + break; + case OP_SIDEBAR_PAGE_UP: + HilBuffy = TopBuffy; + if (HilBuffy != Incoming) { + HilBuffy = HilBuffy->prev; + } + break; + case OP_SIDEBAR_PREV: + if (!HilBuffy->prev) + return; + if (HilBuffy->prev->is_hidden) /* Can't happen, we've sorted the hidden to the end */ + return; + HilBuffy = HilBuffy->prev; + break; + case OP_SIDEBAR_PREV_NEW: + b = find_prev_new (option (OPTSIDEBARNEXTNEWWRAP)); + if (!b) { + return; + } else { + HilBuffy = b; + } + break; + default: + return; + } + sb_draw(); +} + +/** + * sb_set_buffystats - Update the BUFFY's message counts from the CONTEXT + * @ctx: A mailbox CONTEXT + * + * Given a mailbox CONTEXT, find a matching mailbox BUFFY and copy the message + * counts into it. + */ +void +sb_set_buffystats (const CONTEXT *ctx) +{ + /* Even if the sidebar's hidden, + * we should take note of the new data. */ + BUFFY *b = Incoming; + if (!ctx || !b) + return; + + for (; b; b = b->next) { + if (!strcmp (b->path, ctx->path) || + !strcmp (b->realpath, ctx->path)) { + b->msg_unread = ctx->unread; + b->msg_count = ctx->msgcount; + b->msg_flagged = ctx->flagged; + break; + } + } +} + +/** + * sb_get_highlight - Get the BUFFY that's highlighted in the sidebar + * + * Get the path of the mailbox that's highlighted in the sidebar. + * + * Returns: + * Mailbox path + */ +const char * +sb_get_highlight (void) +{ + if (!option (OPTSIDEBAR)) + return NULL; + + if (!HilBuffy) + return NULL; + + return HilBuffy->path; +} + +/** + * sb_set_open_buffy - Set the OpnBuffy based on a mailbox path + * @path: Mailbox path + * + * Search through the list of mailboxes. If a BUFFY has a matching path, set + * OpnBuffy to it. + */ +BUFFY * +sb_set_open_buffy (const char *path) +{ + /* Even if the sidebar is hidden */ + + BUFFY *b = Incoming; + + if (!path || !b) + return NULL; + + OpnBuffy = NULL; + + for (; b; b = b->next) { + if (!strcmp (b->path, path) || + !strcmp (b->realpath, path)) { + OpnBuffy = b; + HilBuffy = b; + break; + } + } + + return OpnBuffy; +} + +/** + * sb_set_update_time - Note the time that the sidebar was updated + * + * Update the timestamp representing the last sidebar update. If the user + * configures "sidebar_refresh_time", this will help to reduce traffic. + */ +void +sb_set_update_time (void) +{ + /* XXX - should this be public? */ + + LastRefresh = time (NULL); +} + +/** + * sb_notify_mailbox - The state of a BUFFY is about to change + * + * We receive a notification: + * After a new BUFFY has been created + * Before a BUFFY is deleted + * + * Before a deletion, check that our pointers won't be invalidated. + */ +void +sb_notify_mailbox (BUFFY *b, int created) +{ + if (!b) + return; + + /* Any new/deleted mailboxes will cause a refresh. As long as + * they're valid, our pointers will be updated in prepare_sidebar() */ + + if (created) { + if (!TopBuffy) + TopBuffy = b; + if (!HilBuffy) + HilBuffy = b; + if (!BotBuffy) + BotBuffy = b; + if (!Outgoing) + Outgoing = b; + if (!OpnBuffy && Context) { + /* This might happen if the user "unmailboxes *", then + * "mailboxes" our current mailbox back again */ + if (mutt_strcmp (b->path, Context->path) == 0) { + OpnBuffy = b; + } + } + } else { + if (TopBuffy == b) + TopBuffy = buffy_going (TopBuffy); + if (OpnBuffy == b) + OpnBuffy = buffy_going (OpnBuffy); + if (HilBuffy == b) + HilBuffy = buffy_going (HilBuffy); + if (BotBuffy == b) + BotBuffy = buffy_going (BotBuffy); + if (Outgoing == b) + Outgoing = buffy_going (Outgoing); + } +} diff --git a/sidebar.h b/sidebar.h new file mode 100644 index 000000000..d56b02a23 --- /dev/null +++ b/sidebar.h @@ -0,0 +1,36 @@ +/* Copyright (C) 2004 Justin Hibbits + * Copyright (C) 2004 Thomer M. Gil + * Copyright (C) 2015-2016 Richard Russon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +#ifndef SIDEBAR_H +#define SIDEBAR_H + +#include "mutt.h" +#include "buffy.h" + +void sb_change_mailbox (int op); +void sb_draw (void); +const char * sb_get_highlight (void); +void sb_init (void); +void sb_notify_mailbox (BUFFY *b, int created); +void sb_set_buffystats (const CONTEXT *ctx); +BUFFY * sb_set_open_buffy (const char *path); +void sb_set_update_time (void); +int sb_should_refresh (void); + +#endif /* SIDEBAR_H */ diff --git a/sort.h b/sort.h index f2832b21e..f24491b95 100644 --- a/sort.h +++ b/sort.h @@ -31,6 +31,12 @@ #define SORT_KEYID 12 #define SORT_TRUST 13 #define SORT_SPAM 14 +#define SORT_COUNT 15 +#define SORT_COUNT_NEW 16 +#define SORT_DESC 17 +#define SORT_FLAGGED 18 +#define SORT_PATH 19 + /* dgc: Sort & SortAux are shorts, so I'm bumping these bitflags up from * bits 4 & 5 to bits 8 & 9 to make room for more sort keys in the future. */ #define SORT_MASK 0xff @@ -50,6 +56,7 @@ WHERE short BrowserSort INITVAL (SORT_SUBJECT); WHERE short Sort INITVAL (SORT_DATE); WHERE short SortAux INITVAL (SORT_DATE); /* auxiliary sorting method */ WHERE short SortAlias INITVAL (SORT_ALIAS); +WHERE short SidebarSortMethod INITVAL (SORT_ORDER); /* FIXME: This one does not belong to here */ WHERE short PgpSortKeys INITVAL (SORT_ADDRESS);