Răsfoiți Sursa

select(): fixed crash if label of last node was the longest

Fabian Peter Hammerle 9 ani în urmă
părinte
comite
649af5111d
1 a modificat fișierele cu 57 adăugiri și 36 ștergeri
  1. 57 36
      ioex/selector.py

+ 57 - 36
ioex/selector.py

@@ -38,15 +38,21 @@ class Node(object):
         return selected_nodes
 
     def get_label(self, active_root):
-        return 'node'
+        return ''
+
+    def _clear_children(self):
+        self._children = []
 
     def get_children(self):
         return self._children
 
-    def append_child(self, child):
+    def _append_child(self, child):
         child._parent = self
         self._children.append(child)
 
+    def get_header_height(self):
+        return 0
+
     def select(self):
         self._selected = True
 
@@ -68,6 +74,9 @@ class StaticNode(Node):
         super(StaticNode, self).__init__()
         self.label = label
 
+    def append_child(self, child):
+        self._append_child(child)
+
     def get_label(self):
         return self.label
 
@@ -77,24 +86,25 @@ class SelectionPad(object):
         self._pad = curses.newpad(1, 1)
         self._pad.keypad(1)
 
-    def refresh(self, pminrow, smaxrow, smaxcol):
+    def refresh(self, pminrow, sminrow, smaxrow, smaxcol):
         # window.refresh([pminrow, pmincol, sminrow, smincol, smaxrow, smaxcol])
-        #   pminrow and pmincol specify the upper left-hand corner of the rectangle to be displayed in the pad. 
-        #   sminrow, smincol, smaxrow, and smaxcol specify the edges of the rectangle to be displayed on the screen. 
+        #   pminrow and pmincol specify the upper left-hand corner of the rectangle to be displayed in the pad.
+        #   sminrow, smincol, smaxrow, and smaxcol specify the edges of the rectangle to be displayed on the screen.
         #   The lower right-hand corner of the rectangle to be displayed in the pad is calculated from the screen coordinates, since the rectangles must be the same size.
-        self._pad.refresh(pminrow, 0, 0, 0, smaxrow, smaxcol)
+        self._pad.refresh(pminrow, 0, sminrow, 0, smaxrow, smaxcol)
 
     def addstr(self, line_index, col_index, text, attr = 0):
         text_encoded = text.encode(locale.getpreferredencoding())
         try:
             self._pad.addstr(line_index, col_index, text_encoded, attr)
         except Exception, ex:
-            raise Exception('pad(width=%d, height=%d).addstr(%d, %d, "%s", %d) failed' % (self.get_width(), self.get_height(), line_index, col_index, text_encoded, attr))
+            raise Exception('pad(width=%d, height=%d).addstr(%d, %d, %s, %d) failed'
+                    % (self.get_width(), self.get_height(), line_index, col_index, repr(text_encoded), attr))
 
     def clear(self):
         self._pad.clear()
 
-    def get_height(self):   
+    def get_height(self):
         return self._pad.getmaxyx()[0]
 
     def get_width(self):
@@ -104,7 +114,11 @@ class SelectionPad(object):
         return self._pad.getch()
 
     def resize(self, nlines, ncols):
-        self._pad.resize(nlines, ncols)
+        try:
+            self._pad.resize(nlines, ncols)
+        except Exception, ex:
+            raise Exception('pad(width=%d, height=%d).resize(%d, %d) failed'
+                    % (self.get_width(), self.get_height(), nlines, ncols))
 
     def resize_height(self, nlines):
         self.resize(nlines, self.get_width())
@@ -125,33 +139,40 @@ def select(stdscr, active_root, multiple = False):
     def get_active_node():
         return active_root.get_children()[active_index]
 
+    def get_visible_pad_height():
+        return get_screen_height() - active_root.get_header_height()
+
     def refresh():
         pad.clear()
         pad.resize_width(1)
-        pad.resize_height(active_root.child_count())
-        for child_index in range(len(active_root.get_children())):
-            child = active_root.get_children()[child_index]
-            label = child.get_label()
-            if len(label) > pad.get_width():
-                pad.resize_width(len(label))
-            pad.addstr(
-                child_index, 
-                0,
-                label, 
-                (curses.A_UNDERLINE if child_index == active_index else 0)
-                    | (curses.A_BOLD if child.selected() else 0)
-                )
-        pad.refresh(
-            max(
-                0, 
-                min(
-                    active_root.child_count() - get_screen_height(), 
-                    active_index - int(get_screen_height() / 2)
+        if len(active_root.get_children()) > 0:
+            pad.resize_height(active_root.child_count())
+            for child_index in range(len(active_root.get_children())):
+                child = active_root.get_children()[child_index]
+                label = child.get_label()
+                # if the last line is the longest addstr() fails if
+                # the width does not include one additional char.
+                if len(label) > pad.get_width():
+                    pad.resize_width(len(label) + 1)
+                pad.addstr(
+                    child_index,
+                    0,
+                    label,
+                    (curses.A_UNDERLINE if child_index == active_index else 0)
+                        | (curses.A_BOLD if child.selected() else 0)
                     )
-                ),
-            get_screen_height() - 1, 
-            stdscr.getmaxyx()[1] - 1
-            ) 
+            pad.refresh(
+                max(
+                    0,
+                    min(
+                        active_root.child_count() - get_visible_pad_height(),
+                        active_index - int(get_visible_pad_height() / 2)
+                        )
+                    ),
+                active_root.get_header_height(),
+                get_screen_height() - 1,
+                stdscr.getmaxyx()[1] - 1
+                )
 
     while True:
         refresh()
@@ -162,17 +183,17 @@ def select(stdscr, active_root, multiple = False):
         if key == curses.KEY_RESIZE:
             refresh()
         elif key in [curses.KEY_DOWN, KEY_SDOWN, ord('j'), ord('J')]:
-            if key in [KEY_SDOWN, ord('J')]:
+            if multiple and key in [KEY_SDOWN, ord('J')]:
                 get_active_node().toggle()
             active_index = min(active_root.child_count() - 1, active_index + 1)
         elif key in [curses.KEY_UP, KEY_SUP, ord('k'), ord('K')]:
-            if key in [KEY_SUP, ord('K')]:
+            if multiple and key in [KEY_SUP, ord('K')]:
                 get_active_node().toggle()
             active_index = max(0, active_index - 1)
         elif key == curses.KEY_NPAGE:
-            active_index = min(active_root.child_count() - 1, active_index + int(get_screen_height() / 2))
+            active_index = min(active_root.child_count() - 1, active_index + int(get_visible_pad_height() / 2))
         elif key == curses.KEY_PPAGE:
-            active_index = max(0, active_index - int(get_screen_height() / 2))
+            active_index = max(0, active_index - int(get_visible_pad_height() / 2))
         elif key in [ord(' ')]:
             if multiple:
                 get_active_node().toggle()