about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAbdo Roig-Maranges <abdo.roig@gmail.com>2013-02-10 12:19:22 +0100
committerAbdo Roig-Maranges <abdo.roig@gmail.com>2013-02-12 12:08:44 +0100
commitaa4d6a7073165294f1e251df7d6310d20d449e81 (patch)
tree36d726ad31e0681b9fb0672e63a0333ff8417f93
parent94bf56dd024b4fa33b5ae4c80f5091fec0995d1c (diff)
downloadranger-aa4d6a7073165294f1e251df7d6310d20d449e81.tar.gz
Displays version control data on browser column
  * I have restructured BrowserColumn._draw_directory. Hopefully it is easier
    mantain now. Simplified horizontal space computations, etc.

  * Displays two symbols on the right of every entry, showing local status, and
    status regarding the remote vcs repo.

  * Added the necessary context to be able to change colors for individual
    symbols from the colorscheme.
-rw-r--r--ranger/colorschemes/default.py35
-rw-r--r--ranger/gui/context.py6
-rw-r--r--ranger/gui/widgets/__init__.py17
-rw-r--r--ranger/gui/widgets/browsercolumn.py202
4 files changed, 180 insertions, 80 deletions
diff --git a/ranger/colorschemes/default.py b/ranger/colorschemes/default.py
index 58c79962..6f6040ba 100644
--- a/ranger/colorschemes/default.py
+++ b/ranger/colorschemes/default.py
@@ -94,6 +94,13 @@ class Default(ColorScheme):
                     fg = red
             if context.loaded:
                 bg = self.progress_bar_color
+            if context.vcsinfo:
+                fg = blue
+                attr &= ~bold
+            if context.vcscommit:
+                fg = yellow
+                attr &= ~bold
+
 
         if context.text:
             if context.highlight:
@@ -112,4 +119,32 @@ class Default(ColorScheme):
                 else:
                     bg = self.progress_bar_color
 
+
+        if context.vcsfile and not context.selected:
+            attr &= ~bold
+            if context.vcsconflict:
+                fg = magenta
+            elif context.vcschanged:
+                fg = red
+            elif context.vcsunknown:
+                fg = red
+            elif context.vcsstaged:
+                fg = green
+            elif context.vcssync:
+                fg = green
+            elif context.vcsignored:
+                fg = default
+
+        elif context.vcsremote and not context.selected:
+            attr &= ~bold
+            if context.vcssync:
+                fg = green
+            elif context.vcsbehind:
+                fg = red
+            elif context.vcsahead:
+                fg = blue
+            elif context.vcsdiverged:
+                fg = magenta
+
+
         return fg, bg, attr
diff --git a/ranger/gui/context.py b/ranger/gui/context.py
index c9e8104e..2ea23571 100644
--- a/ranger/gui/context.py
+++ b/ranger/gui/context.py
@@ -15,7 +15,11 @@ CONTEXT_KEYS = ['reset', 'error', 'badinfo',
         'help_markup', # COMPAT
         'seperator', 'key', 'special', 'border', # COMPAT
         'title', 'text', 'highlight', 'bars', 'quotes', 'tab', 'loaded',
-        'keybuffer']
+        'keybuffer',
+                'infostring',
+                'vcsfile', 'vcsremote', 'vcsinfo', 'vcscommit',
+                'vcsconflict', 'vcschanged', 'vcsunknown', 'vcsignored',
+                'vcsstaged', 'vcssync', 'vcsbehind', 'vcsahead', 'vcsdiverged']
 
 class Context(object):
     def __init__(self, keys):
diff --git a/ranger/gui/widgets/__init__.py b/ranger/gui/widgets/__init__.py
index 2a930b6c..3d0899bb 100644
--- a/ranger/gui/widgets/__init__.py
+++ b/ranger/gui/widgets/__init__.py
@@ -1,3 +1,5 @@
+# -*- coding: utf-8 -*-
+
 from ranger.gui.displayable import Displayable
 
 class Widget(Displayable):
@@ -5,3 +7,18 @@ class Widget(Displayable):
     The Widget class defines no methods and only exists for
     classification of widgets.
     """
+    vcsfilestatus_symb = {'conflict':  ('X', ["vcsconflict"]),
+                  'untracked': ('+', ["vcschanged"]),
+                  'deleted':   ('-', ["vcschanged"]),
+                  'changed':   ('*', ["vcschanged"]),
+                  'staged':    ('*', ["vcsstaged"]),
+                  'ignored':   ('·', ["vcsignored"]),
+                  'sync':      ('√', ["vcssync"]),
+                  'none':      (' ', []),
+                  'unknown':   ('?', ["vcsunknown"])}
+
+    vcsremotestatus_symb = {'none':     (' ',  []),
+                'sync':     ('=', ["vcssync"]),
+                'behind':   ('<', ["vcsbehind"]),
+                'ahead':    ('>', ["vcsahead"]),
+                'diverged': ('Y', ["vcsdiverged"])}
diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py
index a6653070..38a7fdcb 100644
--- a/ranger/gui/widgets/browsercolumn.py
+++ b/ranger/gui/widgets/browsercolumn.py
@@ -1,4 +1,4 @@
-# -*- encoding: utf8 -*-
+# -*- coding: utf-8 -*-
 # Copyright (C) 2009, 2010, 2011  Roman Zimbelmann <romanz@lavabit.com>
 # This software is distributed under the terms of the GNU GPL version 3.
 
@@ -12,9 +12,12 @@ from .pager import Pager
 from ranger.fsobject import BAD_INFO
 from ranger.ext.widestring import WideString
 
+from ranger.gui.color import *
+
 class BrowserColumn(Pager):
     main_column = False
     display_infostring = False
+    display_vcsstate   = True
     scroll_begin = 0
     target = None
     last_redraw_time = -1
@@ -91,7 +94,7 @@ class BrowserColumn(Pager):
         """
         Executes a list of "commands" which can be easily cached.
 
-        "commands" is a list of lists.  Each element contains
+        "commands" is a list of lists.    Each element contains
         a text and an attribute.  First, the attribute will be
         set with attrset, then the text is printed.
 
@@ -136,7 +139,7 @@ class BrowserColumn(Pager):
             self.need_redraw = True
             self.old_dir = self.target
 
-        if self.target:  # don't garbage collect this directory please
+        if self.target:     # don't garbage collect this directory please
             self.target.use()
 
         if self.target and self.target.is_directory \
@@ -224,7 +227,6 @@ class BrowserColumn(Pager):
         self._set_scroll_begin()
 
         copied = [f.path for f in self.fm.copy_buffer]
-        ellipsis = self.ellipsis[self.settings.unicode_ellipsis]
 
         selected_i = self.target.pointer
         for line in range(self.hei):
@@ -245,100 +247,142 @@ class BrowserColumn(Pager):
 
             key = (self.wid, selected_i == i, drawn.marked, self.main_column,
                     drawn.path in copied, tagged_marker, drawn.infostring,
-                    self.fm.do_cut)
+                    drawn.vcsfilestatus, drawn.vcsremotestatus, self.fm.do_cut)
 
             if key in drawn.display_data:
                 self.execute_curses_batch(line, drawn.display_data[key])
                 self.color_reset()
                 continue
 
+            text = drawn.basename
+            if drawn.marked and (self.main_column or self.settings.display_tags_in_all_columns):
+                text = " " + text
+
+            # Computing predisplay data. predisplay contains a list of lists [string, colorlst]
+            # where string is a piece of string to display, and colorlst a list of contexts
+            # that we later pass to the colorscheme, to compute the curses attribute.
+            predisplay_left = []
+            predisplay_right = []
+
+            predisplay_left     = predisplay_left + self._draw_tagged_display(tagged, tagged_marker)
+            predisplay_right = predisplay_right + self._draw_vcsstring_display(drawn)
+            space = self.wid - self._total_len(predisplay_left) - self._total_len(predisplay_right)
+
+            # If not enough space
+            if space <= 2:
+                predisplay_right = []
+                predisplay_left     = []
+
+            predisplay_left = predisplay_left + self._draw_text_display(text, space)
+            space = self.wid - self._total_len(predisplay_left)  - self._total_len(predisplay_right)
+
+            predisplay_right = self._draw_infostring_display(drawn, space) + predisplay_right
+            space = self.wid - self._total_len(predisplay_left)  - self._total_len(predisplay_right)
+
+            if space > 0:
+                predisplay_left.append([' ' * space, []])
+            elif space < 0:
+                raise Exception("Error: there is not enough space to write the text. I have computed spaces wrong.")
+
+            # Computing display data. Now we compute the display_data list ready to display in
+            # curses. It is a list of lists [string, attr]
+
+            this_color = base_color + list(drawn.mimetype_tuple) + self._draw_directory_color(i, drawn, copied)
             display_data = []
             drawn.display_data[key] = display_data
 
-            if self.display_infostring and drawn.infostring \
-                    and self.settings.display_size_in_main_column:
-                infostring = str(drawn.infostring) + " "
-            else:
-                infostring = ""
+            predisplay = predisplay_left + predisplay_right
+            for txt, color in predisplay:
+                attr = self.settings.colorscheme.get_attr(*(this_color + color))
+                display_data.append([txt, attr])
 
-            this_color = base_color + list(drawn.mimetype_tuple)
-            text = drawn.basename
+            self.execute_curses_batch(line, display_data)
+            self.color_reset()
 
-            space = self.wid - len(infostring)
-            if self.main_column:
-                space -= 2
-            elif self.settings.display_tags_in_all_columns:
-                space -= 1
+    def _total_len(self, predisplay):
+        return sum([len(WideString(s)) for s, L in predisplay])
 
-            if i == selected_i:
-                this_color.append('selected')
+    def _draw_text_display(self, text, space):
+        wtext = WideString(text)
+        if len(wtext) > space:
+            wtext = wtext[:max(0, space - 1)] + self.ellipsis[self.settings.unicode_ellipsis]
 
-            if drawn.marked:
-                this_color.append('marked')
-                if self.main_column or self.settings.display_tags_in_all_columns:
-                    text = " " + text
+        return [[str(wtext), []]]
 
+    def _draw_tagged_display(self, tagged, tagged_marker):
+        tagged_display = []
+        if (self.main_column or self.settings.display_tags_in_all_columns) and self.wid > 2:
             if tagged:
-                this_color.append('tagged')
-
-            if drawn.is_directory:
-                this_color.append('directory')
+                tagged_display.append([tagged_marker, ['tag_marker']])
             else:
-                this_color.append('file')
-
-            if drawn.stat:
-                mode = drawn.stat.st_mode
-                if mode & stat.S_IXUSR:
-                    this_color.append('executable')
-                if stat.S_ISFIFO(mode):
-                    this_color.append('fifo')
-                if stat.S_ISSOCK(mode):
-                    this_color.append('socket')
-                if drawn.is_device:
-                    this_color.append('device')
-
-            if drawn.path in copied:
-                this_color.append('cut' if self.fm.do_cut else 'copied')
-
-            if drawn.is_link:
-                this_color.append('link')
-                this_color.append(drawn.exists and 'good' or 'bad')
-
-            attr = self.settings.colorscheme.get_attr(*this_color)
-
-            if (self.main_column or self.settings.display_tags_in_all_columns) \
-                    and tagged and self.wid > 2:
-                this_color.append('tag_marker')
-                tag_attr = self.settings.colorscheme.get_attr(*this_color)
-                display_data.append([tagged_marker, tag_attr])
+                tagged_display.append([" ", ['tag_marker']])
+        return tagged_display
+
+    def _draw_infostring_display(self, drawn, space):
+        infostring_display = []
+        if self.display_infostring and drawn.infostring \
+                and self.settings.display_size_in_main_column:
+            infostring = str(drawn.infostring) + " "
+            if len(infostring) <= space:
+                infostring_display.append([infostring, ['infostring']])
+        return infostring_display
+
+    def _draw_vcsstring_display(self, drawn):
+        vcsstring_display = []
+        if self.settings.vcs_aware and (drawn.vcsfilestatus or drawn.vcsremotestatus):
+            if drawn.vcsfilestatus:
+                vcsstr, vcscol = self.vcsfilestatus_symb[drawn.vcsfilestatus]
             else:
-                text = " " + text
-                space += 1
-
-            wtext = WideString(text)
-            if len(wtext) > space:
-                wtext = wtext[:max(0, space - 1)] + ellipsis
-            text = str(wtext)
-
-            display_data.append([text, attr])
-
-            padding = self.wid - len(wtext)
-            if tagged and (self.main_column or \
-                    self.settings.display_tags_in_all_columns):
-                padding -= 1
-            if infostring:
-                if len(wtext) + 1 + len(infostring) > self.wid:
-                    pass
-                else:
-                    padding -= len(infostring)
-                    padding = max(0, padding)
-                    infostring = (" " * padding) + infostring
-                    display_data.append([infostring, attr])
+                vcsstr = " "
+                vcscol = []
+            vcsstring_display.append([vcsstr, ['vcsfile'] + vcscol])
+
+            if drawn.vcsremotestatus:
+                vcsstr, vcscol = self.vcsremotestatus_symb[drawn.vcsremotestatus]
             else:
-                display_data.append([" " * max(0, padding), attr])
 
-            self.execute_curses_batch(line, display_data)
-            self.color_reset()
+                vcsstr = " "
+                vcscol = []
+            vcsstring_display.append([vcsstr, ['vcsremote'] + vcscol])
+        elif self.target.has_vcschild:
+            vcsstring_display.append(["  ", []])
+        return vcsstring_display
+
+    def _draw_directory_color(self, i, drawn, copied):
+        this_color = []
+        if i == self.target.pointer:
+            this_color.append('selected')
+
+        if drawn.marked:
+            this_color.append('marked')
+
+        if self.fm.tags and drawn.realpath in self.fm.tags:
+            this_color.append('tagged')
+
+        if drawn.is_directory:
+            this_color.append('directory')
+        else:
+            this_color.append('file')
+
+        if drawn.stat:
+            mode = drawn.stat.st_mode
+            if mode & stat.S_IXUSR:
+                this_color.append('executable')
+            if stat.S_ISFIFO(mode):
+                this_color.append('fifo')
+            if stat.S_ISSOCK(mode):
+                this_color.append('socket')
+            if drawn.is_device:
+                this_color.append('device')
+
+        if drawn.path in copied:
+            this_color.append('cut' if self.fm.do_cut else 'copied')
+
+        if drawn.is_link:
+            this_color.append('link')
+            this_color.append(drawn.exists and 'good' or 'bad')
+
+        return this_color
 
     def _get_scroll_begin(self):
         """Determines scroll_begin (the position of the first displayed file)"""