[tor-commits] [arm/master] Binding handlers for the view submenu

atagar at torproject.org atagar at torproject.org
Sun Jun 12 03:56:47 UTC 2011


commit 3cc2ecf82d693a782c11d5a4f6fb41e94e7b5d05
Author: Damian Johnson <atagar at torproject.org>
Date:   Sat Jun 11 20:55:34 2011 -0700

    Binding handlers for the view submenu
    
    Submenu consisting of page and color selection.
---
 src/cli/configPanel.py           |    2 +-
 src/cli/connections/connPanel.py |    2 +-
 src/cli/controller.py            |   48 +++++++++++++++++++++++++++----------
 src/cli/descriptorPopup.py       |    2 +-
 src/cli/menu/actions.py          |   44 ++++++++++++++++++++++++++++------
 src/cli/menu/item.py             |   46 ++++++++++++++++++++++++++++++++++++
 src/cli/popups.py                |    2 +-
 src/cli/torrcPanel.py            |    2 +-
 src/util/uiTools.py              |   36 ++++++++++++++++++++++++++--
 9 files changed, 155 insertions(+), 29 deletions(-)

diff --git a/src/cli/configPanel.py b/src/cli/configPanel.py
index c44d295..c2e290d 100644
--- a/src/cli/configPanel.py
+++ b/src/cli/configPanel.py
@@ -172,7 +172,7 @@ class ConfigPanel(panel.Panel):
   """
   
   def __init__(self, stdscr, configType, config=None):
-    panel.Panel.__init__(self, stdscr, "configState", 0)
+    panel.Panel.__init__(self, stdscr, "configuration", 0)
     
     self.sortOrdering = DEFAULT_SORT_ORDER
     self._config = dict(DEFAULT_CONFIG)
diff --git a/src/cli/connections/connPanel.py b/src/cli/connections/connPanel.py
index 45750c3..8b86c65 100644
--- a/src/cli/connections/connPanel.py
+++ b/src/cli/connections/connPanel.py
@@ -31,7 +31,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
   """
   
   def __init__(self, stdscr, config=None):
-    panel.Panel.__init__(self, stdscr, "conn", 0)
+    panel.Panel.__init__(self, stdscr, "connections", 0)
     threading.Thread.__init__(self)
     self.setDaemon(True)
     
diff --git a/src/cli/controller.py b/src/cli/controller.py
index faf9248..cf17da3 100644
--- a/src/cli/controller.py
+++ b/src/cli/controller.py
@@ -160,6 +160,14 @@ class Controller:
     
     return self._screen
   
+  def getPageCount(self):
+    """
+    Provides the number of pages the interface has. This may be zero if all
+    page panels have been disabled.
+    """
+    
+    return len(self._pagePanels)
+  
   def getPage(self):
     """
     Provides the number belonging to this page. Page numbers start at zero.
@@ -167,23 +175,35 @@ class Controller:
     
     return self._page
   
+  def setPage(self, pageNumber):
+    """
+    Sets the selected page, raising a ValueError if the page number is invalid.
+    
+    Arguments:
+      pageNumber - page number to be selected
+    """
+    
+    if pageNumber < 0 or pageNumber >= self.getPageCount():
+      raise ValueError("Invalid page number: %i" % pageNumber)
+    
+    if pageNumber != self._page:
+      self._page = pageNumber
+      self._forceRedraw = True
+      self.setMsg()
+  
   def nextPage(self):
     """
     Increments the page number.
     """
     
-    self._page = (self._page + 1) % len(self._pagePanels)
-    self._forceRedraw = True
-    self.setMsg()
+    self.setPage((self._page + 1) % len(self._pagePanels))
   
   def prevPage(self):
     """
     Decrements the page number.
     """
     
-    self._page = (self._page - 1) % len(self._pagePanels)
-    self._forceRedraw = True
-    self.setMsg()
+    self.setPage((self._page - 1) % len(self._pagePanels))
   
   def isPaused(self):
     """
@@ -227,20 +247,22 @@ class Controller:
     
     return list(self._stickyPanels)
   
-  def getDisplayPanels(self, includeSticky = True):
+  def getDisplayPanels(self, pageNumber = None, includeSticky = True):
     """
-    Provides all panels belonging to the current page and sticky content above
-    it. This is ordered they way they are presented (top to bottom) on the
-    page.
+    Provides all panels belonging to a page and sticky content above it. This
+    is ordered they way they are presented (top to bottom) on the page.
     
     Arguments:
+      pageNumber    - page number of the panels to be returned, the current
+                      page if None
       includeSticky - includes sticky panels in the results if true
     """
     
+    returnPage = self._page if pageNumber == None else pageNumber
+    
     if includeSticky:
-      return self._stickyPanels + self._pagePanels[self._page]
-    else:
-      return list(self._pagePanels[self._page])
+      return self._stickyPanels + self._pagePanels[returnPage]
+    else: return list(self._pagePanels[returnPage])
   
   def getDaemonPanels(self):
     """
diff --git a/src/cli/descriptorPopup.py b/src/cli/descriptorPopup.py
index f75d7e6..5b9f646 100644
--- a/src/cli/descriptorPopup.py
+++ b/src/cli/descriptorPopup.py
@@ -105,7 +105,7 @@ def showDescriptorPopup(connectionPanel):
   
   # hides the title of the first panel on the page
   control = controller.getController()
-  topPanel = control.getDisplayPanels(False)[0]
+  topPanel = control.getDisplayPanels(includeSticky = False)[0]
   topPanel.setTitleVisible(False)
   topPanel.redraw(True)
   
diff --git a/src/cli/menu/actions.py b/src/cli/menu/actions.py
index 751751f..f81a162 100644
--- a/src/cli/menu/actions.py
+++ b/src/cli/menu/actions.py
@@ -7,7 +7,7 @@ import functools
 import cli.controller
 import cli.menu.item
 
-from util import torTools
+from util import torTools, uiTools
 
 def makeMenu():
   """
@@ -16,6 +16,7 @@ def makeMenu():
   
   baseMenu = cli.menu.item.Submenu("")
   baseMenu.add(makeActionsMenu())
+  baseMenu.add(makeViewMenu())
   
   logsMenu = cli.menu.item.Submenu("Logs")
   logsMenu.add(cli.menu.item.MenuItem("Events", None))
@@ -29,13 +30,6 @@ def makeMenu():
   logsMenu.add(duplicatesSubmenu)
   baseMenu.add(logsMenu)
   
-  viewMenu = cli.menu.item.Submenu("View")
-  viewMenu.add(cli.menu.item.MenuItem("Graph", None))
-  viewMenu.add(cli.menu.item.MenuItem("Connections", None))
-  viewMenu.add(cli.menu.item.MenuItem("Configuration", None))
-  viewMenu.add(cli.menu.item.MenuItem("Configuration File", None))
-  baseMenu.add(viewMenu)
-  
   graphMenu = cli.menu.item.Submenu("Graph")
   graphMenu.add(cli.menu.item.MenuItem("Stats", None))
   
@@ -93,3 +87,37 @@ def makeActionsMenu():
   actionsMenu.add(cli.menu.item.MenuItem("Exit", control.quit))
   return actionsMenu
 
+def makeViewMenu():
+  """
+  Submenu consisting of...
+    [X] <Page 1>
+    [ ] <Page 2>
+    [ ] etc...
+        Color (Submenu)
+  """
+  
+  viewMenu = cli.menu.item.Submenu("View")
+  control = cli.controller.getController()
+  
+  if control.getPageCount() > 0:
+    pageGroup = cli.menu.item.SelectionGroup(control.setPage, control.getPage())
+    
+    for i in range(control.getPageCount()):
+      pagePanels = control.getDisplayPanels(pageNumber = i, includeSticky = False)
+      label = " / ".join([uiTools.camelCase(panel.getName()) for panel in pagePanels])
+      
+      viewMenu.add(cli.menu.item.SelectionMenuItem(label, pageGroup, i))
+  
+  if uiTools.isColorSupported():
+    colorMenu = cli.menu.item.Submenu("Color")
+    colorGroup = cli.menu.item.SelectionGroup(uiTools.setColorOverride, uiTools.getColorOverride())
+    
+    colorMenu.add(cli.menu.item.SelectionMenuItem("All", colorGroup, None))
+    
+    for color in uiTools.COLOR_LIST:
+      colorMenu.add(cli.menu.item.SelectionMenuItem(uiTools.camelCase(color), colorGroup, color))
+    
+    viewMenu.add(colorMenu)
+  
+  return viewMenu
+
diff --git a/src/cli/menu/item.py b/src/cli/menu/item.py
index 8c5d314..beaac9c 100644
--- a/src/cli/menu/item.py
+++ b/src/cli/menu/item.py
@@ -147,3 +147,49 @@ class Submenu(MenuItem):
   def select(self):
     return False
 
+class SelectionGroup():
+  """
+  Radio button groups that SelectionMenuItems can belong to.
+  """
+  
+  def __init__(self, action, selectedArg):
+    self.action = action
+    self.selectedArg = selectedArg
+
+class SelectionMenuItem(MenuItem):
+  """
+  Menu item with an associated group which determines the selection. This is
+  for the common single argument getter/setter pattern.
+  """
+  
+  def __init__(self, label, group, arg):
+    MenuItem.__init__(self, label, None)
+    self._group = group
+    self._arg = arg
+  
+  def isSelected(self):
+    """
+    True if we're the selected item, false otherwise.
+    """
+    
+    return self._arg == self._group.selectedArg
+  
+  def getLabel(self):
+    """
+    Provides our label with a "[X]" prefix if selected and "[ ]" if not.
+    """
+    
+    myLabel = MenuItem.getLabel(self)[1]
+    myPrefix = "[X] " if self.isSelected() else "[ ] "
+    return (myPrefix, myLabel, "")
+  
+  def select(self):
+    """
+    Performs the group's setter action with our argument.
+    """
+    
+    if not self.isSelected():
+      self._group.action(self._arg)
+    
+    return True
+
diff --git a/src/cli/popups.py b/src/cli/popups.py
index 8061c8f..5f1eac2 100644
--- a/src/cli/popups.py
+++ b/src/cli/popups.py
@@ -280,7 +280,7 @@ def showMenu(title, options, oldSelection):
   try:
     # hides the title of the first panel on the page
     control = cli.controller.getController()
-    topPanel = control.getDisplayPanels(False)[0]
+    topPanel = control.getDisplayPanels(includeSticky = False)[0]
     topPanel.setTitleVisible(False)
     topPanel.redraw(True)
     
diff --git a/src/cli/torrcPanel.py b/src/cli/torrcPanel.py
index e14a16a..a12cc87 100644
--- a/src/cli/torrcPanel.py
+++ b/src/cli/torrcPanel.py
@@ -24,7 +24,7 @@ class TorrcPanel(panel.Panel):
   """
   
   def __init__(self, stdscr, configType, config=None):
-    panel.Panel.__init__(self, stdscr, "configFile", 0)
+    panel.Panel.__init__(self, stdscr, "torrc", 0)
     
     self._config = dict(DEFAULT_CONFIG)
     if config:
diff --git a/src/util/uiTools.py b/src/util/uiTools.py
index 5999c64..34d9210 100644
--- a/src/util/uiTools.py
+++ b/src/util/uiTools.py
@@ -18,6 +18,9 @@ COLOR_LIST = {"red": curses.COLOR_RED,        "green": curses.COLOR_GREEN,
               "cyan": curses.COLOR_CYAN,      "magenta": curses.COLOR_MAGENTA,
               "black": curses.COLOR_BLACK,    "white": curses.COLOR_WHITE}
 
+# boolean for if we have color support enabled, None not yet determined
+COLOR_IS_SUPPORTED = None
+
 # mappings for getColor() - this uses the default terminal color scheme if
 # color support is unavailable
 COLOR_ATTR_INITIALIZED = False
@@ -138,6 +141,14 @@ def getPrintable(line, keepNewlines = True):
   line = "".join([char for char in line if (isprint(char) or (keepNewlines and char == "\n"))])
   return line
 
+def isColorSupported():
+  """
+  True if the display supports showing color, false otherwise.
+  """
+  
+  if COLOR_IS_SUPPORTED == None: _initColors()
+  return COLOR_IS_SUPPORTED
+
 def getColor(color):
   """
   Provides attribute corresponding to a given text color. Supported colors
@@ -261,6 +272,24 @@ def cropStr(msg, size, minWordLen = 4, minCrop = 0, endType = Ending.ELLIPSE, ge
   if getRemainder: return (returnMsg, remainder)
   else: return returnMsg
 
+def camelCase(label):
+  """
+  Converts the given string to camel case, ie:
+  >>> camelCase("I_LIKE_PEPPERJACK!")
+  'I Like Pepperjack!'
+  
+  Arguments:
+    label - input string to be converted
+  """
+  
+  words = []
+  for entry in label.split("_"):
+    if len(entry) == 0: words.append("")
+    elif len(entry) == 1: words.append(entry.upper())
+    else: words.append(entry[0].upper() + entry[1:].lower())
+  
+  return " ".join(words)
+
 def drawBox(panel, top, left, width, height, attr=curses.A_NORMAL):
   """
   Draws a box in the panel with the given bounds.
@@ -719,16 +748,17 @@ def _initColors():
   calling curses.initscr().
   """
   
-  global COLOR_ATTR_INITIALIZED
+  global COLOR_ATTR_INITIALIZED, COLOR_IS_SUPPORTED
   if not COLOR_ATTR_INITIALIZED:
     COLOR_ATTR_INITIALIZED = True
+    COLOR_IS_SUPPORTED = False
     if not CONFIG["features.colorInterface"]: return
     
-    try: hasColorSupport = curses.has_colors()
+    try: COLOR_IS_SUPPORTED = curses.has_colors()
     except curses.error: return # initscr hasn't been called yet
     
     # initializes color mappings if color support is available
-    if hasColorSupport:
+    if COLOR_IS_SUPPORTED:
       colorpair = 0
       log.log(CONFIG["log.cursesColorSupport"], "Terminal color support detected and enabled")
       



More information about the tor-commits mailing list