[tor-commits] [arm/master] Added drop-down second level menus.

atagar at torproject.org atagar at torproject.org
Sat Jun 4 21:12:30 UTC 2011


commit fbd07955c5376457a5a9ecc4e778dd4a9f97deb9
Author: Kamran Riaz Khan <krkhan at inspirated.com>
Date:   Mon May 30 01:33:22 2011 +0500

    Added drop-down second level menus.
    
    Second level menus can now be brought down using the arrow key.
    Popup init is modified to support top and left gaps, setLeft()
    is added to Panel for left padding. Constructor arguments for Panel
    are left unchanged so that existing panel-using code isn't broken.
---
 src/cli/menu.py   |  154 +++++++++++++++++++++++++++++++++++++++++++++++++----
 src/cli/popups.py |    9 +++-
 src/util/panel.py |   18 ++++++-
 3 files changed, 168 insertions(+), 13 deletions(-)

diff --git a/src/cli/menu.py b/src/cli/menu.py
index 71b56a4..69538e0 100644
--- a/src/cli/menu.py
+++ b/src/cli/menu.py
@@ -8,19 +8,40 @@ import cli.controller
 import popups
 from util import log, panel, uiTools, menuItem
 
-TOPLEVEL = 0
+TOPLEVEL, SECONDLEVEL = range(2)
 
 class Menu():
   """Displays a popup menu and sends keys to appropriate panels"""
 
   def __init__(self, item=None):
     DEFAULT_ROOT = menuItem.MenuItem(label="Root", children=(
-      menuItem.MenuItem(label="File"          , callback=self._callbackDefault),
-      menuItem.MenuItem(label="Logs"          , callback=self._callbackDefault),
-      menuItem.MenuItem(label="View"          , callback=self._callbackDefault),
-      menuItem.MenuItem(label="Graph"         , callback=self._callbackDefault),
-      menuItem.MenuItem(label="Connections"   , callback=self._callbackDefault),
-      menuItem.MenuItem(label="Configuration" , callback=self._callbackDefault)))
+      menuItem.MenuItem(label="File"          , children=(
+        menuItem.MenuItem(label="Exit"                , callback=self._callbackDefault),)),
+      menuItem.MenuItem(label="Logs"          , children=(
+        menuItem.MenuItem(label="Events"              , callback=self._callbackDefault),
+        menuItem.MenuItem(label="Clear"               , callback=self._callbackDefault),
+        menuItem.MenuItem(label="Save"                , callback=self._callbackDefault),
+        menuItem.MenuItem(label="Filter"              , callback=self._callbackDefault),
+        menuItem.MenuItem(label="Duplicates"          , callback=self._callbackDefault),)),
+      menuItem.MenuItem(label="View"          , children=(
+        menuItem.MenuItem(label="Graph"               , callback=self._callbackDefault),
+        menuItem.MenuItem(label="Connections"         , callback=self._callbackDefault),
+        menuItem.MenuItem(label="Configuration"       , callback=self._callbackDefault),
+        menuItem.MenuItem(label="Configuration File"  , callback=self._callbackDefault),)),
+      menuItem.MenuItem(label="Graph"         , children=(
+        menuItem.MenuItem(label="Stats"               , callback=self._callbackDefault),
+        menuItem.MenuItem(label="Size"                , callback=self._callbackDefault),
+        menuItem.MenuItem(label="Update Interval"     , callback=self._callbackDefault),
+        menuItem.MenuItem(label="Bounds"              , callback=self._callbackDefault),)),
+      menuItem.MenuItem(label="Connections"   , children=(
+        menuItem.MenuItem(label="Identity"            , callback=self._callbackDefault),
+        menuItem.MenuItem(label="Resolver"            , callback=self._callbackDefault),
+        menuItem.MenuItem(label="Sort Order"          , callback=self._callbackDefault),)),
+      menuItem.MenuItem(label="Configuration" , children=(
+        menuItem.MenuItem(label="Comments"            , callback=self._callbackDefault),
+        menuItem.MenuItem(label="Reload"              , callback=self._callbackDefault),
+        menuItem.MenuItem(label="Reset Tor"           , callback=self._callbackDefault),))
+      ))
 
     self._first = [0]
     self._selection = [0]
@@ -51,6 +72,10 @@ class Menu():
           elif key == curses.KEY_LEFT:
             if len(self._selection) == 1:
               self._moveTopLevelLeft(width)
+          elif key == curses.KEY_DOWN:
+            if len(self._selection) == 1:
+              self._dropSecondLevel()
+              break
           elif uiTools.isSelectionKey(key):
             self._handleEvent()
             break
@@ -58,17 +83,42 @@ class Menu():
       finally:
         popups.finalize()
 
-  def _calculateTopLevelWidths(self, width):
+  def _getCurrentTopLevelItem(self):
+    index = self._first[TOPLEVEL] + self._selection[TOPLEVEL]
+    return self._rootItem.getChildren()[index]
+
+  def _calculateTopLevelWidths(self, width=0):
     labels = [menuItem.getLabel() for menuItem in self._rootItem.getChildren()]
 
     # width per label is set according to the longest label
     labelwidth = max(map(len, labels)) + 2
 
-    # total number of labels that can be printed in current width
+    # total number of labels that can be printed in supplied width
     printable = min(width / labelwidth - 1, self._rootItem.getChildrenCount())
 
     return (labelwidth, printable)
 
+  def _calculateSecondLevelWidths(self):
+    parent = self._getCurrentTopLevelItem()
+
+    labels = [menuItem.getLabel() for menuItem in parent.getChildren()]
+
+    labelwidth = max(map(len, labels))
+
+    return labelwidth
+
+  def _calculateSecondLevelHeights(self, height=0):
+    control = cli.controller.getController()
+    height, _ = control.getScreen().getmaxyx()
+    topSize = sum(stickyPanel.getHeight() for stickyPanel in control.getStickyPanels())
+    height = height - topSize
+
+    parent = self._getCurrentTopLevelItem()
+
+    printable = min(height - 4, parent.getChildrenCount())
+
+    return printable if printable else parent.getChildrenCount()
+
   def _moveTopLevelRight(self, width):
     _, printable = self._calculateTopLevelWidths(width)
 
@@ -90,7 +140,7 @@ class Menu():
       self._selection[TOPLEVEL] = self._selection[TOPLEVEL] - 1
     else:
       if self._first[TOPLEVEL] == 0:
-        self._first[TOPLEVEL] = (self._rootItem.getChildrenCount() / printable) * printable
+        self._first[TOPLEVEL] = ((self._rootItem.getChildrenCount() / printable) * printable) % self._rootItem.getChildrenCount()
       else:
         self._first[TOPLEVEL] = abs(self._first[TOPLEVEL] - printable) % self._rootItem.getChildrenCount()
       self._selection[TOPLEVEL] = self._rootItem.getChildrenCount() - self._first[TOPLEVEL] - 1
@@ -115,6 +165,90 @@ class Menu():
     popup.addch(top, left, curses.ACS_VLINE)
     left = left + 1
 
+  def _dropSecondLevel(self):
+    self._first.append(0)
+    self._selection.append(0)
+
+    labelwidth = self._calculateSecondLevelWidths()
+    printable = self._calculateSecondLevelHeights()
+
+    toplabelwidth, _ = self._calculateTopLevelWidths()
+    left = (toplabelwidth + 2) * self._selection[TOPLEVEL]
+
+    popup, width, height = popups.init(height=printable+2, width=labelwidth+2, top=2, left=left)
+
+    if popup.win:
+      try:
+        while True:
+          popup.win.erase()
+          popup.win.box()
+
+          self._drawSecondLevel(popup, width, height)
+
+          popup.win.refresh()
+
+          control = cli.controller.getController()
+          key = control.getScreen().getch()
+
+          if key == curses.KEY_DOWN:
+              self._moveSecondLevelDown(height)
+          elif key == curses.KEY_UP:
+              self._moveSecondLevelUp(height)
+          elif uiTools.isSelectionKey(key):
+            self._handleEvent()
+            self._first.pop()
+            self._selection.pop()
+            break
+
+      finally:
+        popups.finalize()
+
+  def _drawSecondLevel(self, popup, width, height):
+    printable = self._calculateSecondLevelHeights()
+    parent = self._getCurrentTopLevelItem()
+    children = parent.getChildren()[self._first[SECONDLEVEL]:self._first[SECONDLEVEL] + printable]
+
+    toplabelwidth, _ = self._calculateTopLevelWidths(width)
+
+    top = 1
+    left = 1
+    for (index, item) in enumerate(children):
+      labelformat = curses.A_STANDOUT if index == self._selection[SECONDLEVEL] else curses.A_NORMAL
+
+      popup.addstr(top, left, item.getLabel(), labelformat)
+      top = top + 1
+
+  def _moveSecondLevelDown(self, height):
+    printable = self._calculateSecondLevelHeights()
+    parent = self._getCurrentTopLevelItem()
+
+    if self._selection[SECONDLEVEL] < printable - 1:
+      self._selection[SECONDLEVEL] = self._selection[SECONDLEVEL] + 1
+    else:
+      self._selection[SECONDLEVEL] = 0
+      if printable < parent.getChildrenCount():
+        self._first[SECONDLEVEL] = (self._first[SECONDLEVEL] + printable) % parent.getChildrenCount()
+
+    if self._first[SECONDLEVEL] + self._selection[SECONDLEVEL] == parent.getChildrenCount():
+      self._first[SECONDLEVEL] = 0
+      self._selection[SECONDLEVEL] = 0
+
+  def _moveSecondLevelUp(self, height):
+    printable = self._calculateSecondLevelHeights()
+    parent = self._getCurrentTopLevelItem()
+
+    if self._selection[SECONDLEVEL] > 0:
+      self._selection[SECONDLEVEL] = self._selection[SECONDLEVEL] - 1
+    else:
+      if self._first[SECONDLEVEL] == 0:
+        self._first[SECONDLEVEL] = ((parent.getChildrenCount() / printable) * printable) % parent.getChildrenCount()
+      else:
+        self._first[SECONDLEVEL] = abs(self._first[SECONDLEVEL] - printable) % parent.getChildrenCount()
+      self._selection[SECONDLEVEL] = parent.getChildrenCount() - self._first[SECONDLEVEL] - 1
+
+    if self._selection[SECONDLEVEL] > printable:
+      self._selection[SECONDLEVEL] = printable - 1
+
   def _handleEvent(self):
     item = self._rootItem
     sums = [sum(values) for values in zip(self._first, self._selection)]
diff --git a/src/cli/popups.py b/src/cli/popups.py
index 7ed5302..b3eb105 100644
--- a/src/cli/popups.py
+++ b/src/cli/popups.py
@@ -8,7 +8,7 @@ import cli.controller
 
 from util import panel, uiTools
 
-def init(height = -1, width = -1):
+def init(height = -1, width = -1, top = -1, left = -1):
   """
   Preparation for displaying a popup. This creates a popup with a valid
   subwindow instance. If that's successful then the curses lock is acquired
@@ -23,8 +23,15 @@ def init(height = -1, width = -1):
   
   control = cli.controller.getController()
   topSize = sum(stickyPanel.getHeight() for stickyPanel in control.getStickyPanels())
+  leftSize = 0
+
+  if top != -1:
+    topSize = topSize + top
+  if left != -1:
+    leftSize = left
   
   popup = panel.Panel(control.getScreen(), "popup", topSize, height, width)
+  popup.setLeft(leftSize)
   popup.setVisible(True)
   
   # Redraws the popup to prepare a subwindow instance. If none is spawned then
diff --git a/src/util/panel.py b/src/util/panel.py
index a104a07..4fc2158 100644
--- a/src/util/panel.py
+++ b/src/util/panel.py
@@ -74,6 +74,7 @@ class Panel():
     self.pauseTime = -1
     
     self.top = top
+    self.left = 0
     self.height = height
     self.width = width
     
@@ -261,6 +262,18 @@ class Panel():
     """
     
     return self.height
+
+  def setLeft(self, left):
+    """
+    Changes the position where subwindows are placed within its parent.
+    
+    Arguments:
+      top - positioning of top within parent
+    """
+    
+    if self.left != left:
+      self.left = left
+      self.win = None
   
   def setHeight(self, height):
     """
@@ -303,6 +316,7 @@ class Panel():
     newHeight, newWidth = self.parent.getmaxyx()
     setHeight, setWidth = self.getHeight(), self.getWidth()
     newHeight = max(0, newHeight - self.top)
+    newWidth = max(0, newWidth - self.left)
     if setHeight != -1: newHeight = min(newHeight, setHeight)
     if setWidth != -1: newWidth = min(newWidth, setWidth)
     return (newHeight, newWidth)
@@ -569,7 +583,7 @@ class Panel():
     
     # temporary subwindow for user input
     displayWidth = self.getPreferredSize()[1]
-    inputSubwindow = self.parent.subwin(1, displayWidth - x, self.top, x)
+    inputSubwindow = self.parent.subwin(1, displayWidth - x, self.top, self.left + x)
     
     # prepopulates the initial text
     if initialText: inputSubwindow.addstr(0, 0, initialText)
@@ -682,7 +696,7 @@ class Panel():
     # would mean far more complicated code and no more selective refreshing)
     
     if recreate:
-      self.win = self.parent.subwin(newHeight, newWidth, self.top, 0)
+      self.win = self.parent.subwin(newHeight, newWidth, self.top, self.left)
       
       # note: doing this log before setting win produces an infinite loop
       msg = "recreating panel '%s' with the dimensions of %i/%i" % (self.getName(), newHeight, newWidth)





More information about the tor-commits mailing list