summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--doc/ranger.pod3
-rw-r--r--ranger/api/__init__.py8
-rw-r--r--ranger/config/commands.py6
-rw-r--r--ranger/container/fsobject.py10
-rw-r--r--ranger/core/actions.py4
-rw-r--r--ranger/core/linemode.py78
-rw-r--r--ranger/gui/widgets/browsercolumn.py42
7 files changed, 115 insertions, 36 deletions
diff --git a/doc/ranger.pod b/doc/ranger.pod
index 75da97b9..7b3354c4 100644
--- a/doc/ranger.pod
+++ b/doc/ranger.pod
@@ -1040,6 +1040,9 @@ Sets the linemode of all files in the current directory.  The linemode may be:
      available, fall back to the "filename" linemode if no
      metadata was found.  See :meta command.
 
+The custom linemodes may be added by subclassing the I<LinemodeBase> class.
+See the I<ranger.core.linemode> module for some examples.
+
 =item load_copy_buffer
 
 Load the copy buffer from F<~/.config/ranger/copy_buffer>.  This can be used to
diff --git a/ranger/api/__init__.py b/ranger/api/__init__.py
index a50f706f..cc0815c2 100644
--- a/ranger/api/__init__.py
+++ b/ranger/api/__init__.py
@@ -26,3 +26,11 @@ def hook_ready(fm):
     This hook is executed after the user interface is initialized.  You should
     NOT print anything to stdout anymore from here on.  Use fm.notify instead.
     """
+
+from ranger.core.linemode import LinemodeBase
+
+def register_linemode(*linemodes):
+    """Register the linemodes in a dictionary of the available linemodes."""
+    from ranger.container.fsobject import FileSystemObject
+    for linemode in linemodes:
+        FileSystemObject.linemode_dict[linemode.name] = linemode()
diff --git a/ranger/config/commands.py b/ranger/config/commands.py
index 6467f98a..60b5a26f 100644
--- a/ranger/config/commands.py
+++ b/ranger/config/commands.py
@@ -390,7 +390,7 @@ class setintag(setlocal):
 class default_linemode(Command):
     def execute(self):
         import re
-        from ranger.container.fsobject import POSSIBLE_LINEMODES
+        from ranger.core.linemode import REGISTERED_LINEMODES
 
         if len(self.args) < 2:
             self.fm.notify("Usage: default_linemode [path=<regexp> | tag=<tag(s)>] <linemode>", bad=True)
@@ -410,9 +410,9 @@ class default_linemode(Command):
 
         # Extract and validate the line mode from the command line
         linemode = self.rest(1)
-        if linemode not in POSSIBLE_LINEMODES:
+        if linemode not in REGISTERED_LINEMODES:
             self.fm.notify("Invalid linemode: %s; should be %s" %
-                    (linemode, "/".join(POSSIBLE_LINEMODES)), bad=True)
+                    (linemode, "/".join(REGISTERED_LINEMODES)), bad=True)
 
         # Add the prepared entry to the fm.default_linemodes
         entry = [method, argument, linemode]
diff --git a/ranger/container/fsobject.py b/ranger/container/fsobject.py
index 2f2b5197..f7cf7f97 100644
--- a/ranger/container/fsobject.py
+++ b/ranger/container/fsobject.py
@@ -12,14 +12,12 @@ DOCUMENT_BASENAMES = ('bugs', 'bugs', 'changelog', 'copying', 'credits',
 
 BAD_INFO = '?'
 
-POSSIBLE_LINEMODES = ("filename", "metatitle", "permissions")
-DEFAULT_LINEMODE = "filename"
-
 import re
 from grp import getgrgid
 from os import lstat, stat, getcwd
 from os.path import abspath, basename, dirname, realpath, splitext, extsep, relpath
 from pwd import getpwuid
+from ranger.core.linemode import DEFAULT_LINEMODE, REGISTERED_LINEMODES
 from ranger.core.shared import FileManagerAware, SettingsAware
 from ranger.ext.shell_escape import shell_escape
 from ranger.ext.spawn import spawn
@@ -86,6 +84,10 @@ class FileSystemObject(FileManagerAware, SettingsAware):
     basename_is_rel_to = None
 
     _linemode = DEFAULT_LINEMODE
+    linemode_dict = dict(
+        (linemode.name, linemode()) for linemode in
+        [DefaultLinemode, TitleLinemode, PermissionsLinemode]
+    )
 
     def __init__(self, path, preload=None, path_is_abs=False, basename_is_rel_to=None):
         if not path_is_abs:
@@ -112,7 +114,7 @@ class FileSystemObject(FileManagerAware, SettingsAware):
 
         # Set the line mode from fm.default_linemodes
         for method, argument, linemode in self.fm.default_linemodes:
-            if linemode in POSSIBLE_LINEMODES:
+            if linemode in linemode_dict:
                 if method == "always":
                     self._linemode = linemode
                     break
diff --git a/ranger/core/actions.py b/ranger/core/actions.py
index be7c7a00..33903859 100644
--- a/ranger/core/actions.py
+++ b/ranger/core/actions.py
@@ -27,7 +27,7 @@ from ranger.core.tab import Tab
 from ranger.container.file import File
 from ranger.core.loader import CommandLoader, CopyLoader
 from ranger.container.settings import ALLOWED_SETTINGS
-from ranger.container.fsobject import POSSIBLE_LINEMODES, DEFAULT_LINEMODE
+from ranger.core.linemode import DEFAULT_LINEMODE
 
 MACRO_FAIL = "<\x01\x01MACRO_HAS_NO_VALUE\x01\01>"
 
@@ -167,7 +167,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
         if mode == "normal":
             mode = DEFAULT_LINEMODE
 
-        if mode not in POSSIBLE_LINEMODES:
+        if mode not in self.thisfile.linemode_dict:
             self.notify("Unhandled linemode: `%s'" % mode, bad=True)
             return
 
diff --git a/ranger/core/linemode.py b/ranger/core/linemode.py
new file mode 100644
index 00000000..ee770cb0
--- /dev/null
+++ b/ranger/core/linemode.py
@@ -0,0 +1,78 @@
+# -*- coding: utf-8 -*-
+
+from abc import *
+
+DEFAULT_LINEMODE = "filename"
+
+
+class LinemodeBase(object):
+    """Supplies the file line contents for BrowserColumn.
+
+    Attributes:
+        name (required!) - Name by which the linemode is referred to by the user
+
+        uses_metadata - True if metadata should to be loaded for this linemode
+
+        required_metadata -
+            If any of these metadata fields are absent, fall back to
+            the default linemode
+    """
+    __metaclass__ = ABCMeta
+
+    uses_metadata = False
+    required_metadata = []
+
+    name = abstractproperty()
+
+    @abstractmethod
+    def filetitle(self, file, metadata):
+        """The left-aligned part of the line."""
+        raise NotImplementedError
+
+    @abstractmethod
+    def infostring(self, file, metadata):
+        """The right-aligned part of the line."""
+        raise NotImplementedError
+
+
+class DefaultLinemode(LinemodeBase):
+    name = "filename"
+
+    def filetitle(self, file, metadata):
+        return file.drawn_basename
+
+    def infostring(self, file, metadata):
+        # Should never be called for this linemode, implemented in BrowserColumn
+        raise NotImplementedError
+
+
+class TitleLinemode(LinemodeBase):
+    name = "metatitle"
+    uses_metadata = True
+    required_metadata = ["title"]
+
+    def filetitle(self, file, metadata):
+        name = metadata.title or file.basename
+        if metadata.year:
+            return "%s - %s" % (metadata.year, name)
+        else:
+            return name
+
+    def infostring(self, file, metadata):
+        if metadata.authors:
+            authorstring = metadata.authors
+            if ',' in authorstring:
+                authorstring = authorstring[0:authorstring.find(",")]
+            return authorstring
+        return ""
+
+
+class PermissionsLinemode(LinemodeBase):
+    name = "permissions"
+
+    def filetitle(self, file, metadata):
+        return "%s %s %s %s" % (file.get_permission_string(),
+                file.user, file.group, file.drawn_basename)
+
+    def infostring(self, file, metadata):
+        return ""
diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py
index 563716bb..2e9bf7a2 100644
--- a/ranger/gui/widgets/browsercolumn.py
+++ b/ranger/gui/widgets/browsercolumn.py
@@ -12,6 +12,8 @@ from . import Widget
 from .pager import Pager
 from ranger.ext.widestring import WideString
 
+from ranger.core import linemode
+
 from ranger.gui.color import *
 
 class BrowserColumn(Pager):
@@ -250,38 +252,25 @@ class BrowserColumn(Pager):
 
             # Extract linemode-related information from the drawn object
             metadata = None
-            use_linemode = drawn._linemode
-            if use_linemode == "metatitle":
+            current_linemode = drawn.linemode_dict[drawn._linemode]
+            if current_linemode.uses_metadata:
                 metadata = self.fm.metadata.get_metadata(drawn.path)
-                if not metadata.title:
-                    use_linemode = "filename"
+                if not all(getattr(metadata, tag)
+                           for tag in current_linemode.required_metadata):
+                    current_linemode = drawn.linemode_dict[linemode.DEFAULT_LINEMODE]
 
             metakey = hash(repr(sorted(metadata.items()))) if metadata else 0
             key = (self.wid, selected_i == i, drawn.marked, self.main_column,
                     drawn.path in copied, tagged_marker, drawn.infostring,
                     drawn.vcsfilestatus, drawn.vcsremotestatus, self.fm.do_cut,
-                    use_linemode, metakey)
+                    current_linemode.name, metakey)
 
             if key in drawn.display_data:
                 self.execute_curses_batch(line, drawn.display_data[key])
                 self.color_reset()
                 continue
 
-
-            # Deal with the line mode
-            text = ""
-            if use_linemode == "metatitle":
-                assert metadata.title, "Ensure that metadata.title is set!"
-                if metadata.year:
-                    text = "%s - %s" % (metadata.year, metadata.title)
-                else:
-                    text = metadata.title
-            if use_linemode == "filename":
-                text = drawn.relative_path
-            elif use_linemode == "permissions":
-                text = "%s %s %s %s" % (drawn.get_permission_string(),
-                        drawn.user, drawn.group, drawn.relative_path)
-
+            text = current_linemode.filetitle(drawn, metadata)
 
             if drawn.marked and (self.main_column or \
                     self.settings.display_tags_in_all_columns):
@@ -312,14 +301,13 @@ class BrowserColumn(Pager):
             # info string
             infostring = []
             infostringlen = 0
-            if use_linemode == "filename":
+            if current_linemode.name == "filename":
                 infostring = self._draw_infostring_display(drawn, space)
-            elif use_linemode == "metatitle":
-                if metadata.authors:
-                    authorstring = metadata.authors
-                    if ',' in authorstring:
-                        authorstring = authorstring[0:authorstring.find(",")]
-                    infostring.append([" " + authorstring + " ", ["infostring"]])
+            else:
+                infostringdata = current_linemode.infostring(drawn, metadata)
+                if infostringdata:
+                    infostring.append([" " + infostringdata + " ",
+                                       ["infostring"]])
             if infostring:
                 infostringlen = self._total_len(infostring)
                 if space - infostringlen > 2: