]> granicus.if.org Git - neomutt/commitdiff
sensible browser patch
authorPierre-Elliott Bécue <becue@crans.org>
Fri, 2 Sep 2016 23:52:21 +0000 (01:52 +0200)
committerPierre-Elliott Bécue <becue@crans.org>
Tue, 6 Sep 2016 12:33:08 +0000 (14:33 +0200)
 In the browser, when you go to a folder's parent, the folder you were in
 should be selected.  This patch works for IMAP folders.

browser.c
curs_main.c
doc/manual.xml.head
imap/imap.h
imap/util.c
menu.c
mutt_menu.h
muttlib.c
protos.h

index a83852de870f74fa80b8ccebd89f0db808a83912..6d4fc51b150bd95a8f52211da1c10227936af5fe 100644 (file)
--- a/browser.c
+++ b/browser.c
@@ -76,6 +76,7 @@ typedef struct folder_t
   int num;
 } FOLDER;
 
+static char OldLastDir[_POSIX_PATH_MAX] = "";
 static char LastDir[_POSIX_PATH_MAX] = "";
 static char LastDirBackup[_POSIX_PATH_MAX] = "";
 
@@ -894,17 +895,43 @@ static void init_menu (struct browser_state *state, MUTTMENU *menu, char *title,
   else
 #endif
   if (buffy)
+  {
+    menu->is_mailbox_list = 1;
     snprintf (title, titlelen, _("Mailboxes [%d]"), mutt_buffy_check (0));
+  }
   else
   {
+    menu->is_mailbox_list = 0;
     strfcpy (path, LastDir, sizeof (path));
     mutt_pretty_mailbox (path, sizeof (path));
+
+    if (mutt_strncmp (LastDir, OldLastDir, mutt_strlen (LastDir)) == 0)
+    {
+      char TargetDir[_POSIX_PATH_MAX] = "";
 #ifdef USE_IMAP
-  if (state->imap_browse && option (OPTIMAPLSUB))
-    snprintf (title, titlelen, _("Subscribed [%s], File mask: %s"),
-             path, NONULL (Mask.pattern));
-  else
+      if (state->imap_browse)
+      {
+        strfcpy (TargetDir, OldLastDir, sizeof (TargetDir));
+        imap_clean_path (TargetDir, sizeof (TargetDir));
+      }
+      else
 #endif
+        strfcpy (TargetDir,
+                strrchr (OldLastDir, '/') + 1,
+                sizeof (TargetDir));
+
+      /* If we get here, it means that LastDir is the parent directory of
+       * OldLastDir.  I.e., we're returning from a subdirectory, and we want
+       * to position the cursor on the directory we're returning from. */
+      int i;
+      for (i = 0; i < state->entrylen; i++)
+      {
+        if (mutt_strcmp (state->entry[i].name, TargetDir) == 0)
+        {
+          menu->current = i;
+        }
+      }
+    }
     snprintf (title, titlelen, _("Directory [%s], File mask: %s"),
              path, NONULL(Mask.pattern));
   }
@@ -927,6 +954,18 @@ static int file_tag (MUTTMENU *menu, int n, int m)
   return ff->tagged - ot;
 }
 
+/* Public function
+ *
+ * This function helps the browser to know which directory has
+ * been selected. It should be called anywhere a confirm hit is done
+ * to open a new directory/file which is a maildir/mbox.
+ */
+void mutt_browser_select_dir (char *f)
+{
+  strfcpy (OldLastDir, f, sizeof (OldLastDir));
+  mutt_get_parent_path (LastDir, OldLastDir, sizeof (LastDir));
+}
+
 void _mutt_select_file (char *f, size_t flen, int flags, char ***files, int *numfiles)
 {
   char buf[_POSIX_PATH_MAX];
@@ -1028,8 +1067,21 @@ void _mutt_select_file (char *f, size_t flen, int flags, char ***files, int *num
   {
     if (!folder)
       getcwd (LastDir, sizeof (LastDir));
-    else if (!LastDir[0])
-      strfcpy (LastDir, NONULL(Maildir), sizeof (LastDir));
+    else
+    {
+      /* We use mutt_browser_select_dir to initialize the two
+       * variables (LastDir, OldLastDir) at the appropriate
+       * values.
+       */
+      if (!LastDir[0])
+      {
+        mutt_browser_select_dir (CurrentFolder);
+      }
+      else if (mutt_strcmp (CurrentFolder, OldLastDir) != 0)
+      {
+        mutt_browser_select_dir (CurrentFolder);
+      }
+    }
     
 #ifdef USE_IMAP
     if (!buffy && mx_is_imap (LastDir))
@@ -1136,8 +1188,6 @@ void _mutt_select_file (char *f, size_t flen, int flags, char ***files, int *num
 #endif
            )
          {
-           char OldLastDir[_POSIX_PATH_MAX];
-
            /* save the old directory */
            strfcpy (OldLastDir, LastDir, sizeof (OldLastDir));
 
index 3bc2fbbdf98ec3ae7fefd1aff3bc59ad3ff18636..68da7134d023a4f75d6ebf9d9d2830d465eaa803 100644 (file)
@@ -1801,6 +1801,9 @@ int mutt_index_menu (void)
           if (!path || !*path)
             break;
           strncpy (buf, path, sizeof (buf));
+
+          /* Mark the selected dir for the mutt browser */
+          mutt_browser_select_dir (buf);
         }
 #endif
 #ifdef USE_NOTMUCH
@@ -1835,6 +1838,9 @@ int mutt_index_menu (void)
          }
          else
 #endif
+          /* Let's fill by default buf with the next mailbox containing
+           * unread mails
+           */
          mutt_buffy (buf, sizeof (buf));
 
           if (mutt_enter_fname (cp, buf, sizeof (buf), &menu->redraw, 1) == -1)
@@ -1847,6 +1853,10 @@ int mutt_index_menu (void)
             else
               break;
           }
+          
+          /* Selected directory is okay, let's save it.*/
+          mutt_browser_select_dir (buf);
+
          if (!buf[0])
          {
             mutt_window_clearline (MuttMessageWindow, 0);
index 2a2a27f5f8b48386e6d5a2145ba642c1f978be93..2af87451e07d1e59a43c56a6ba016d79da0bf67a 100644 (file)
@@ -8289,6 +8289,92 @@ for example, in a global <literal>.muttrc</literal>.
 
 </sect1>
 
+<sect1 id="sensible-browser">
+  <title>Sensible-Browser Patch</title>
+  <subtitle>Make the file browser behave</subtitle>
+
+  <sect2 id="sensible-browser-patch">
+    <title>Patch</title>
+
+    <para>
+      To check if Mutt supports <quote>sensible-browser</quote>, look for
+      <quote>patch-sensible-browser</quote> in the mutt version.
+      See: <xref linkend="mutt-patches"/>.
+    </para>
+
+    <itemizedlist>
+      <title>Dependencies:</title>
+      <listitem><para>mutt-1.7.0</para></listitem>
+    </itemizedlist>
+
+    <para>This patch is part of the <ulink url="https://github.com/neomutt/neomutt/wiki">NeoMutt Project</ulink>.</para>
+  </sect2>
+
+  <sect2 id="sensible-browser-intro">
+    <title>Introduction</title>
+
+    <para>
+      When using the file/folder browser, the selection isn't always where you would
+      expect. When you navigate to '..' (parent folder), the selection should show the
+      folder you <emphasis>used</emphasis> to be in.
+    </para>
+
+  </sect2>
+
+<!--
+  <sect2 id="sensible-browser-variables">
+    <title>Variables</title>
+    <para>None</para>
+  </sect2>
+
+  <sect2 id="sensible-browser-functions">
+    <title>Functions</title>
+    <para>None</para>
+  </sect2>
+
+  <sect2 id="sensible-browser-commands">
+    <title>Commands</title>
+    <para>None</para>
+  </sect2>
+
+  <sect2 id="sensible-browser-colors">
+    <title>Colors</title>
+    <para>None</para>
+  </sect2>
+
+  <sect2 id="sensible-browser-sort">
+    <title>Sort</title>
+    <para>None</para>
+  </sect2>
+-->
+
+  <sect2 id="sensible-browser-muttrc">
+    <title>Muttrc</title>
+    <para>None</para>
+  </sect2>
+
+  <sect2 id="sensible-browser-see-also">
+    <title>See Also</title>
+
+    <itemizedlist>
+      <listitem><para><ulink url="http://www.neomutt.org/">NeoMutt Project</ulink></para></listitem>
+    </itemizedlist>
+  </sect2>
+
+  <sect2 id="sensible-browser-known-bugs">
+    <title>Known Bugs</title>
+  </sect2>
+
+  <sect2 id="sensible-browser-credits">
+    <title>Credits</title>
+    <itemizedlist>
+    <listitem><para>Pierre-Elliott Bécue <email>becue@crans.org</email></para></listitem>
+    <listitem><para>Haakon Riiser <email>haakon.riiser@fys.uio.no</email></para></listitem>
+    <listitem><para>Richard Russon <email>rich@flatcap.org</email></para></listitem>
+    </itemizedlist>
+  </sect2>
+</sect1>
+
 </chapter>
 
 <chapter id="optionalfeatures">
index 94506ea980f2a6590bdd020265899ac9a8434bb5..584982d5fe3120ba324213f74b25531019203aaa 100644 (file)
@@ -70,5 +70,8 @@ int imap_wait_keepalive (pid_t pid);
 void imap_keepalive (void);
 
 int imap_account_match (const ACCOUNT* a1, const ACCOUNT* a2);
+void imap_get_parent (char *output, const char *mbox, size_t olen, char delim);
+void imap_get_parent_path (char *output, const char *path, size_t olen);
+void imap_clean_path (char *path, size_t plen);
 
 #endif
index 924d573649408a5359e2d23f8d113e199a3371b6..8c77c88a9e7c3c4a750df12276ed2c568ad5d261 100644 (file)
@@ -72,6 +72,109 @@ int imap_expand_path (char* path, size_t len)
   return rc;
 }
 
+/* Public function
+ *
+ * Provided an imap mbox name and a delimiter, returns the mbox parent
+ * name.
+ *
+ * Could be static with a prototype in imap_private.h, but could be useful
+ * as a public function.
+ */
+void imap_get_parent (char *output, const char *mbox, size_t olen, char delim)
+{
+  int n;
+
+  /* If both pointers are on the same memory part
+   * strfcpy is useless
+   */
+  if (mbox != output)
+    strfcpy (output, mbox, olen);
+
+  n = mutt_strlen (output);
+
+  /* Let's go backwards untill the next delimiter
+   *
+   * If output[n] is a '/', the first n-- will allow
+   * to ignore it. If it isn't, then output looks like
+   * "/aaaaa/bbbb". There is at least one "b", so we can't skip
+   * the "/" after the 'a's.
+   *
+   * If output == '/', then n-- => n == 0, so the loop ends
+   * immediately
+   */
+  for (n--; n >= 0 && output[n] != delim ; n--);
+
+  /* We stopped before the begining. There is a trailing
+   * slash.
+   */
+  if (n > 0)
+  {
+    /* Strip the trailing delimiter.  */
+    output[n] = '\0';
+  }
+  else
+  {
+    output[0] = (n == 0) ? delim : '\0';
+  }
+}
+
+/* Public function
+ * Provided an imap path, returns in output the parent directory if
+ * existent. Else returns the same path.
+ */
+void imap_get_parent_path (char *output, const char *path, size_t olen)
+{
+  IMAP_MBOX mx;
+  IMAP_DATA *idata;
+  char mbox[LONG_STRING] = "";
+
+  if (imap_parse_path (path, &mx) < 0)
+  {
+    strfcpy (output, path, olen);
+    return;
+  }
+
+  idata = imap_conn_find (&mx.account, MUTT_IMAP_CONN_NONEW);
+  if (!idata)
+  {
+    strfcpy (output, path, olen);
+    return;
+  }
+
+  /* Stores a fixed path in mbox */
+  imap_fix_path (idata, mx.mbox, mbox, sizeof (mbox));
+
+  /* Gets the parent mbox in mbox */
+  imap_get_parent (mbox, mbox, sizeof (mbox), idata->delim);
+
+  /* Returns a fully qualified IMAP url */
+  imap_qualify_path (output, olen, &mx, mbox);
+}
+
+/* Public function
+ *
+ * Cleans an IMAP path using imap_fix_path. Does it in place.
+ */
+void imap_clean_path (char *path, size_t plen)
+{
+  IMAP_MBOX mx;
+  IMAP_DATA *idata;
+  char mbox[LONG_STRING] = "";
+
+  if (imap_parse_path (path, &mx) < 0)
+    return;
+
+  idata = imap_conn_find (&mx.account, MUTT_IMAP_CONN_NONEW);
+  if (!idata)
+    return;
+
+  /* Stores a fixed path in mbox */
+  imap_fix_path (idata, mx.mbox, mbox, sizeof (mbox));
+
+  /* Returns a fully qualified IMAP url */
+  imap_qualify_path (path, plen, &mx, mbox);
+}
+
 #ifdef USE_HCACHE
 static int imap_hcache_namer (const char* path, char* dest, size_t dlen)
 {
diff --git a/menu.c b/menu.c
index 252760c4ba0b4eb033890515a6a776ce9d8a3f8d..d56c5a45d8c1e037a37ba5eb34b9e79265af774b 100644 (file)
--- a/menu.c
+++ b/menu.c
@@ -938,8 +938,21 @@ int menu_redraw (MUTTMENU *menu)
 
 int mutt_menuLoop (MUTTMENU *menu)
 {
+  static int last_position = -1;
   int i = OP_NULL;
 
+  if (menu->max && menu->is_mailbox_list)
+  {
+    if (last_position > (menu->max - 1))
+    {
+      last_position = -1;
+    }
+    else if (last_position >= 0)
+    {
+      menu->current = last_position;
+    }
+  }
+
   FOREVER
   {
     if (option (OPTMENUCALLER))
@@ -1163,6 +1176,8 @@ int mutt_menuLoop (MUTTMENU *menu)
        break;
 
       default:
+        if (menu->is_mailbox_list)
+          last_position = menu->current;
        return (i);
     }
   }
index d5c97d0170541e8ea947a1e8f381259d9421f13c..be4165d159c68d9f6ba4878ecc11cbdbfac6cc68 100644 (file)
@@ -53,6 +53,7 @@ typedef struct menu_t
   int offset;  /* row offset within the window to start the index */
   int pagelen; /* number of entries per screen */
   int tagprefix;
+  int is_mailbox_list;
   mutt_window_t *indexwin;
   mutt_window_t *statuswin;
   mutt_window_t *helpwin;
index 067be7765f6ef9a10b01e1564bd926b095e614f2..13af56b91e1484edd07292128582e48f99804673 100644 (file)
--- a/muttlib.c
+++ b/muttlib.c
@@ -2168,3 +2168,28 @@ int mutt_set_xdg_path(const XDGType type, char *buf, size_t bufsize)
   FREE (&x);
   return 0;
 }
+
+void mutt_get_parent_path (char *output, char *path, size_t olen)
+{
+#ifdef USE_IMAP
+  if (mx_is_imap (path))
+    imap_get_parent_path (output, path, olen);
+  else
+#endif
+  {
+    strfcpy (output, path, olen);
+    int n = mutt_strlen (output);
+
+    /* Remove everything untill the next slash */
+    for (n--; ((n >= 0) && (output[n] != '/')); n--);
+
+    if (n > 0)
+      output[n] = '\0';
+    else
+    {
+      output[0] = '/';
+      output[1] = '\0';
+    }
+  }
+}
+
index ec9c0a7f283097eda3acaa9ce71862fdaec2a1ea..5d02bd08726888f9070afdcfe15860113e4fe9ed 100644 (file)
--- a/protos.h
+++ b/protos.h
@@ -404,6 +404,10 @@ void mutt_set_header_color(CONTEXT *, HEADER *);
 void mutt_sleep (short);
 int mutt_save_confirm (const char  *, struct stat *);
 void mutt_randbuf(void *out, size_t len);
+
+void mutt_browser_select_dir (char *f);
+void mutt_get_parent_path (char *output, char *path, size_t olen);
+
 #define MUTT_RANDTAG_LEN (16)
 void mutt_rand_base32(void *out, size_t len);
 uint32_t mutt_rand32(void);