summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--doc/ranger.pod7
-rw-r--r--ranger/config/commands.py7
-rw-r--r--ranger/container/directory.py74
-rw-r--r--ranger/container/fsobject.py16
-rw-r--r--ranger/core/fm.py10
-rw-r--r--ranger/gui/widgets/browsercolumn.py4
-rw-r--r--ranger/gui/widgets/titlebar.py2
7 files changed, 72 insertions, 48 deletions
diff --git a/doc/ranger.pod b/doc/ranger.pod
index 7a6de447..c556c63c 100644
--- a/doc/ranger.pod
+++ b/doc/ranger.pod
@@ -831,6 +831,7 @@ ranger.  For your convenience, this is a list of the "public" commands including
  eval [-q] python_code
  filter [string]
  find pattern
+ flat level
  grep pattern
  linemode linemodename
  load_copy_buffer
@@ -997,6 +998,12 @@ be run immediately. (Or entered, if it's a directory.)
 
 This command is based on the I<scout> command and supports all of its options.
 
+=item flat level
+
+Flattens the directory view up to the specified level. Level -1 means infinite
+level. Level 0 means standard view without flattened directory view. Level
+values -2 and less are invalid.
+
 =item grep I<pattern>
 
 Looks for a string in all marked files or directories.
diff --git a/ranger/config/commands.py b/ranger/config/commands.py
index 49d8f213..4e82fb0f 100644
--- a/ranger/config/commands.py
+++ b/ranger/config/commands.py
@@ -1303,9 +1303,10 @@ class flat(Command):
     """
     :flat <level>
 
-    Flattens the directory view up to level specified.
+    Flattens the directory view up to the specified level.
+
         -1 fully flattened
-        0  remove flattened view
+         0 remove flattened view
     """
 
     def execute(self):
@@ -1314,6 +1315,8 @@ class flat(Command):
             level = int(level)
         except ValueError:
             level = self.quantifier
+        if level < -1:
+            self.fm.notify("Need an integer number (-1, 0, 1, ...)", bad=True)
         self.fm.thisdir.unload()
         self.fm.thisdir.flat = level
         self.fm.thisdir.load_content()
diff --git a/ranger/container/directory.py b/ranger/container/directory.py
index c331053a..7bef379d 100644
--- a/ranger/container/directory.py
+++ b/ranger/container/directory.py
@@ -20,7 +20,7 @@ from ranger.container.settings import LocalSettings
 
 def sort_by_basename(path):
     """returns path.basename (for sorting)"""
-    return path.basename
+    return path.drawn_basename
 
 def sort_by_basename_icase(path):
     """returns case-insensitive path.basename (for sorting)"""
@@ -45,6 +45,25 @@ def accept_file(fname, directory, hidden_filter, name_filter):
         return False
     return True
 
+def walklevel(some_dir, level):
+    some_dir = some_dir.rstrip(os.path.sep)
+    followlinks = True if level > 0 else False
+    assert os.path.isdir(some_dir)
+    num_sep = some_dir.count(os.path.sep)
+    for root, dirs, files in os.walk(some_dir, followlinks=followlinks):
+        yield root, dirs, files
+        num_sep_this = root.count(os.path.sep)
+        if level != -1 and num_sep + level <= num_sep_this:
+            del dirs[:]
+
+def mtimelevel(path, level):
+    mtime = os.stat(path).st_mtime
+    for dirpath, dirnames, filenames in walklevel(path, level):
+        dirlist = [os.path.join("/", dirpath, d) for d in dirnames
+                if level == -1 or dirpath.count(os.path.sep) - path.count(os.path.sep) <= level]
+        mtime = max(mtime, max([-1] + [os.stat(d).st_mtime for d in dirlist]))
+    return mtime
+
 class Directory(FileSystemObject, Accumulator, Loadable):
     is_directory = True
     enterable = False
@@ -183,7 +202,7 @@ class Directory(FileSystemObject, Accumulator, Loadable):
         self.move_to_obj(self.pointed_obj)
 
     # XXX: Check for possible race conditions
-    def load_bit_by_bit(self, flat=0):
+    def load_bit_by_bit(self):
         """An iterator that loads a part on every next() call
 
         Returns a generator which load a part of the directory
@@ -194,17 +213,7 @@ class Directory(FileSystemObject, Accumulator, Loadable):
         self.percent = 0
         self.load_if_outdated()
 
-        basename_is_rel = True if flat else False
-
-        def walklevel(some_dir, level):
-            some_dir = some_dir.rstrip(os.path.sep)
-            assert os.path.isdir(some_dir)
-            num_sep = some_dir.count(os.path.sep)
-            for root, dirs, files in os.walk(some_dir):
-                yield root, dirs, files
-                num_sep_this = root.count(os.path.sep)
-                if level != -1 and num_sep + level <= num_sep_this:
-                    del dirs[:]
+        basename_is_rel_to = self.path if self.flat else None
 
         try:
             if self.runnable:
@@ -213,17 +222,20 @@ class Directory(FileSystemObject, Accumulator, Loadable):
 
                 self.mount_path = mount_path(mypath)
 
-                if flat:
+                if self.flat:
                     filelist = []
-                    for dirpath, dirnames, filenames in walklevel(mypath, flat):
-                        filelist += [os.path.join("/", dirpath, d) for d in dirnames
-                                if dirpath.count(os.path.sep) - mypath.count(os.path.sep) == flat]
+                    for dirpath, dirnames, filenames in walklevel(mypath, self.flat):
+                        dirlist = [os.path.join("/", dirpath, d) for d in dirnames
+                                if self.flat == -1 or dirpath.count(os.path.sep) - mypath.count(os.path.sep) <= self.flat]
+                        filelist += dirlist
                         filelist += [os.path.join("/", dirpath, f) for f in filenames]
-                    filenames = [os.path.relpath(name, mypath) for name in filelist]
+                    filenames = filelist
+                    self.load_content_mtime = mtimelevel(mypath, self.flat)
                 else:
                     filelist = os.listdir(mypath)
                     filenames = [mypath + (mypath == '/' and fname or '/' + fname)
                             for fname in filelist]
+                    self.load_content_mtime = os.stat(mypath).st_mtime
 
                 if self._cumulative_size_calculated:
                     # If self.content_loaded is true, this is not the first
@@ -245,8 +257,6 @@ class Directory(FileSystemObject, Accumulator, Loadable):
 
                 yield
 
-                self.load_content_mtime = os.stat(mypath).st_mtime
-
                 marked_paths = [obj.path for obj in self.marked_items]
 
                 files = []
@@ -269,17 +279,20 @@ class Directory(FileSystemObject, Accumulator, Loadable):
                         stats = None
                         is_a_dir = False
                     if is_a_dir:
-                        try:
-                            item = self.fm.get_directory(name,
-                                    basename_is_rel=basename_is_rel)
-                            item.load_if_outdated()
-                        except:
+                        if self.flat:
                             item = Directory(name, preload=stats, path_is_abs=True,
-                                    basename_is_rel=basename_is_rel)
+                                    basename_is_rel_to=basename_is_rel_to)
                             item.load()
+                        else:
+                            try:
+                                item = self.fm.get_directory(name)
+                                item.load_if_outdated()
+                            except:
+                                item = Directory(name, preload=stats, path_is_abs=True)
+                                item.load()
                     else:
                         item = File(name, preload=stats, path_is_abs=True,
-                                    basename_is_rel=basename_is_rel)
+                                    basename_is_rel_to=basename_is_rel_to)
                         item.load()
                         disk_usage += item.size
 
@@ -350,7 +363,7 @@ class Directory(FileSystemObject, Accumulator, Loadable):
                 schedule = True   # was: self.size > 30
 
             if self.load_generator is None:
-                self.load_generator = self.load_bit_by_bit(flat=self.flat)
+                self.load_generator = self.load_bit_by_bit()
 
                 if schedule and self.fm:
                     self.fm.loader.add(self)
@@ -530,7 +543,10 @@ class Directory(FileSystemObject, Accumulator, Loadable):
             return True
 
         try:
-            real_mtime = os.stat(self.path).st_mtime
+            if self.flat:
+                real_mtime = mtimelevel(self.path, self.flat)
+            else:
+                real_mtime = os.stat(self.path).st_mtime
         except OSError:
             real_mtime = None
             return False
diff --git a/ranger/container/fsobject.py b/ranger/container/fsobject.py
index 59e23f6b..020721d8 100644
--- a/ranger/container/fsobject.py
+++ b/ranger/container/fsobject.py
@@ -83,20 +83,22 @@ class FileSystemObject(FileManagerAware, SettingsAware):
     vcs_outdated = False
     vcs_enabled = False
 
-    basename_is_rel = False
+    basename_is_rel_to = None
 
     _linemode = DEFAULT_LINEMODE
 
-    def __init__(self, path, preload=None, path_is_abs=False, basename_is_rel=False):
+    def __init__(self, path, preload=None, path_is_abs=False, basename_is_rel_to=None):
         if not path_is_abs:
             path = abspath(path)
         self.path = path
-        self.basename_is_rel = basename_is_rel
-        if not basename_is_rel:
+        self.basename_is_rel_to = basename_is_rel_to
+        if basename_is_rel_to == None:
             self.basename = basename(path)
+            self.drawn_basename = self.basename
         else:
-            self.basename = relpath(path, getcwd())
-        self.basename_lower = self.basename.lower()
+            self.basename = basename(path)
+            self.drawn_basename = relpath(path, basename_is_rel_to)
+        self.basename_lower = self.drawn_basename.lower()
         self.extension = splitext(self.basename)[1].lstrip(extsep) or None
         self.dirname = dirname(path)
         self.preload = preload
@@ -140,7 +142,7 @@ class FileSystemObject(FileManagerAware, SettingsAware):
     @lazy_property
     def basename_natural(self):
         return [c if i % 3 == 1 else (int(c) if c else 0) for i, c in \
-            enumerate(_extract_number_re.split(self.basename))]
+            enumerate(_extract_number_re.split(self.drawn_basename))]
 
     @lazy_property
     def basename_natural_lower(self):
diff --git a/ranger/core/fm.py b/ranger/core/fm.py
index 3497f20c..6bb4fd22 100644
--- a/ranger/core/fm.py
+++ b/ranger/core/fm.py
@@ -258,17 +258,13 @@ class FM(Actions, SignalDispatcher):
         """returns the path relative to rangers library directory"""
         return os.path.join(ranger.RANGERDIR, *paths)
 
-    def get_directory(self, path, basename_is_rel = False):
+    def get_directory(self, path):
         """Get the directory object at the given path"""
         path = os.path.abspath(path)
         try:
-            directory = self.directories[path]
-            if directory.basename_is_rel != basename_is_rel:
-                del self.directories[path]
-                raise KeyError
-            return directory
+            return self.directories[path]
         except KeyError:
-            obj = Directory(path, basename_is_rel = basename_is_rel)
+            obj = Directory(path)
             self.directories[path] = obj
             return obj
 
diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py
index b2159339..5eabe2c6 100644
--- a/ranger/gui/widgets/browsercolumn.py
+++ b/ranger/gui/widgets/browsercolumn.py
@@ -274,10 +274,10 @@ class BrowserColumn(Pager):
                 else:
                     text = paperinfo.title
             if use_linemode == "filename":
-                text = drawn.basename
+                text = drawn.drawn_basename
             elif use_linemode == "permissions":
                 text = "%s %s %s %s" % (drawn.get_permission_string(),
-                        drawn.user, drawn.group, drawn.basename)
+                        drawn.user, drawn.group, drawn.drawn_basename)
 
 
             if drawn.marked and (self.main_column or \
diff --git a/ranger/gui/widgets/titlebar.py b/ranger/gui/widgets/titlebar.py
index fa10a744..25edd5e4 100644
--- a/ranger/gui/widgets/titlebar.py
+++ b/ranger/gui/widgets/titlebar.py
@@ -117,7 +117,7 @@ class TitleBar(Widget):
 
         if self.fm.thisfile is not None and \
                 self.settings.show_selection_in_titlebar:
-            bar.add(self.fm.thisfile.basename, 'file')
+            bar.add(self.fm.thisfile.drawn_basename, 'file')
 
     def _get_right_part(self, bar):
         # TODO: fix that pressed keys are cut off when chaining CTRL keys