"Fossies" - the Fresh Open Source Software Archive

Member "pysize-0.2/pysize/ui/curses/ui_curses.py" (11 Mar 2007, 7499 Bytes) of package /linux/privat/old/pysize-0.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "ui_curses.py" see the Fossies "Dox" file reference documentation.

    1 # This program is free software; you can redistribute it and/or modify
    2 # it under the terms of the GNU General Public License as published by
    3 # the Free Software Foundation; either version 2 of the License, or
    4 # (at your option) any later version.
    5 #
    6 # This program is distributed in the hope that it will be useful,
    7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
    8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    9 # GNU Library General Public License for more details.
   10 #
   11 # You should have received a copy of the GNU General Public License
   12 # along with this program; if not, write to the Free Software
   13 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   14 #
   15 # See the COPYING file for license information.
   16 #
   17 # Copyright (c) 2006, 2007 Guillaume Chazarain <guichaz@yahoo.fr>
   18 
   19 import curses
   20 import sys
   21 import time
   22 
   23 from pysize.ui.utils import human_unit, short_string, update_progress
   24 from pysize.ui.utils import sanitize_string, UINotAvailableException
   25 from pysize.ui.char_matrix import CharMatrix, MASK, SELECTED
   26 from pysize.core.pysize_fs_tree import pysize_tree
   27 from pysize.core.compute_size import size_observable
   28 
   29 class _CursesApp(object):
   30     """The curses UI."""
   31     def __init__(self, win, options, args):
   32         self.window = win
   33         self.options = options
   34         self.max_depth = options.max_depth
   35         if options.min_size != 'auto':
   36             raise Exception, 'curses UI supports only --min-size=auto'
   37         self._set_paths(args)
   38         self._init_curses()
   39         self.last_star = 0
   40         self.last_star_time = time.time()
   41         size_observable.add_observer(self.draw_star)
   42 
   43     def draw_star(self):
   44         progress = update_progress()
   45         if progress:
   46             self.status_win.insch(0, self.width - 1, progress, curses.A_REVERSE)
   47             self.status_win.refresh()
   48 
   49     def _init_curses(self):
   50         curses.use_default_colors()
   51         curses.start_color()
   52         curses.curs_set(0)
   53 
   54         def dots(*positions):
   55             return sum(map(lambda n: 1<<n, positions))
   56 
   57         self.MATRIX_TO_CURSES = {
   58             dots(): ' ',
   59             dots(3, 4, 5): curses.ACS_HLINE,
   60             dots(1, 4, 7): curses.ACS_VLINE,
   61             dots(1, 3, 4, 5, 7): curses.ACS_PLUS,
   62             dots(4, 5, 7): curses.ACS_ULCORNER,
   63             dots(3, 4, 7): curses.ACS_URCORNER,
   64             dots(1, 4, 5): curses.ACS_LLCORNER,
   65             dots(1, 3, 4): curses.ACS_LRCORNER,
   66             dots(3, 4, 5, 7): curses.ACS_TTEE,
   67             dots(1, 3, 4, 5): curses.ACS_BTEE,
   68             dots(1, 4, 5, 7): curses.ACS_LTEE,
   69             dots(1, 3, 4, 7): curses.ACS_RTEE
   70         }
   71 
   72         try:
   73             self.status_win = curses.newwin(0, 0)
   74             self.panel = curses.newwin(0, 0)
   75             self._resize()
   76         except curses.error:
   77             print 'Cannot create windows'
   78             sys.exit(1)
   79 
   80     def _resize(self):
   81         """Called when the terminal size changes."""
   82         self.height, self.width = self.window.getmaxyx()
   83         self.height -= 1 # Status bar
   84         self.need_tree = True
   85         self.status_win.resize(1, self.width)
   86         self.status_win.mvwin(self.height, 0)
   87         self.panel.resize(self.height, self.width)
   88         self._set_paths(self.paths)
   89         self._refresh()
   90 
   91     def _redraw(self):
   92         self.window.refresh()
   93         if self.need_tree:
   94             # -1 accounts for the last horizontal line
   95             self.tree = pysize_tree(self.paths, self.max_depth,
   96                                     2.0 / (self.height - 1), self.options)
   97             self.need_tree = False
   98         if not self.tree.root:
   99             return
  100         self.panel.erase()
  101 
  102         self._draw_matrix(CharMatrix(self.width, self.height, self.tree,
  103                                      self.selected_node))
  104 
  105         self.panel.refresh()
  106         self.status_win.erase()
  107         self.status_win.hline(0, 0, ord(' ') | curses.A_REVERSE, self.width)
  108         status_line = sanitize_string(self.tree.root.get_name()) + ' '
  109         status_line += human_unit(self.tree.root.size)
  110         END = ' - Pysize'
  111         if len(status_line) + len(END) >= self.width:
  112             status_line = short_string(status_line, self.width - 2)
  113         else:
  114             status_line += END
  115         self.status_win.addstr(status_line, curses.A_REVERSE)
  116         self.status_win.refresh()
  117 
  118     def _refresh(self):
  119         while True:
  120             try:
  121                 self._redraw()
  122                 break
  123             except curses.error, e:
  124                 print e
  125                 time.sleep(1)
  126 
  127     def _set_paths(self, paths):
  128         self.selected_node = None
  129         self.selection = None
  130         self.paths = paths
  131         self.need_tree = True
  132 
  133     def _next_step(self):
  134         self._set_paths([self.selected_node.get_dirname()])
  135 
  136     def _select(self, function):
  137         if not self.selected_node:
  138             if self.tree.root.children:
  139                 self.selected_node = self.tree.root.children[0]
  140             else:
  141                 # Forcing browsing to the left
  142                 self._set_paths([self.tree.root.get_dirname()])
  143             return
  144         selected = function(self.selected_node)
  145         if not selected:
  146             if function == self.tree.get_first_child and \
  147                     self.selected_node.is_dir():
  148                 # Browsing to the right
  149                 self._next_step()
  150             return
  151         self.selected_node = selected
  152         if self.selected_node == self.tree.root:
  153             # Browsing to the left
  154             self._set_paths([self.tree.root.get_dirname()])
  155 
  156     def _selected(self):
  157         if self.selected_node:
  158             self._set_paths(self.selected_node.get_fullpaths())
  159 
  160     def run(self):
  161         """The main dispatcher."""
  162         key_bindings = {
  163             ord('q'): lambda:
  164                 sys.exit(0),
  165 
  166             curses.KEY_RESIZE: lambda:
  167                 self._resize(),
  168 
  169             curses.KEY_DOWN: lambda:
  170                 self._select(self.tree.get_next_sibling),
  171 
  172             curses.KEY_UP: lambda:
  173                 self._select(self.tree.get_previous_sibling),
  174 
  175             curses.KEY_LEFT: lambda:
  176                 self._select(self.tree.get_parent),
  177 
  178             curses.KEY_RIGHT: lambda:
  179                 self._select(self.tree.get_first_child),
  180 
  181             ord('\n'): lambda:
  182                 self._selected()
  183         }
  184 
  185         while True:
  186             self._refresh()
  187             c = self.window.getch()
  188             action = key_bindings.get(c, lambda: None)
  189             action()
  190 
  191     def _draw_matrix(self, matrix):
  192         for y, line in enumerate(matrix.matrix):
  193             for x, char in enumerate(line):
  194                 if isinstance(char, int):
  195                     selected = (char & SELECTED) != 0
  196                     char &= MASK
  197                     char = self.MATRIX_TO_CURSES[char]
  198                     if selected:
  199                         char |= curses.A_REVERSE
  200                     self.panel.insch(y, x, char)
  201                 else:
  202                     try:
  203                         char = char.encode('UTF-8')
  204                     except UnicodeDecodeError:
  205                         pass
  206                     self.panel.insstr(y, x, char)
  207 
  208 def _run_curses(win, options, args):
  209     args = args or ['.']
  210     app = _CursesApp(win, options, args)
  211     app.run()
  212 
  213 def run(options, args):
  214     if sys.stdin.isatty() and sys.stdout.isatty():
  215         curses.wrapper(_run_curses, options, args)
  216     else:
  217         raise UINotAvailableException