[tor-commits] [arm/master] Initial commit for the menu code. Currently displays a top-bar in a

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


commit 5fd9b5f74a86eb7e641366ae1c0a1a47404cf873
Author: Kamran Riaz Khan <krkhan at inspirated.com>
Date:   Fri May 27 18:01:25 2011 +0500

    Initial commit for the menu code. Currently displays a top-bar in a
    popup and dispatches the menu entry to the callback function, which
    shall later on be used to determine the panel/key combination for
    taking specified action.
---
 src/cli/controller.py |   18 +++++++-
 src/cli/menu.py       |   99 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 114 insertions(+), 3 deletions(-)

diff --git a/src/cli/controller.py b/src/cli/controller.py
index e94f7f6..c506302 100644
--- a/src/cli/controller.py
+++ b/src/cli/controller.py
@@ -17,6 +17,7 @@ import cli.graphing.bandwidthStats
 import cli.graphing.connStats
 import cli.graphing.resourceStats
 import cli.connections.connPanel
+import cli.menu
 
 from util import connections, conf, enum, log, panel, sysTools, torConfig, torTools
 
@@ -76,8 +77,11 @@ def initController(stdscr, startTime):
   # fourth page: torrc
   pagePanels.append([cli.torrcPanel.TorrcPanel(stdscr, cli.torrcPanel.Config.TORRC, config)])
   
+  # top menu
+  menu = cli.menu.Menu()
+  
   # initializes the controller
-  ARM_CONTROLLER = Controller(stdscr, stickyPanels, pagePanels)
+  ARM_CONTROLLER = Controller(stdscr, stickyPanels, pagePanels, menu)
   
   # additional configuration for the graph panel
   graphPanel = ARM_CONTROLLER.getPanel("graph")
@@ -131,7 +135,7 @@ class Controller:
   Tracks the global state of the interface
   """
   
-  def __init__(self, stdscr, stickyPanels, pagePanels):
+  def __init__(self, stdscr, stickyPanels, pagePanels, menu):
     """
     Creates a new controller instance. Panel lists are ordered as they appear,
     top to bottom on the page.
@@ -140,11 +144,13 @@ class Controller:
       stdscr       - curses window
       stickyPanels - panels shown at the top of each page
       pagePanels   - list of pages, each being a list of the panels on it
+      menu         - popup drop-down menu
     """
     
     self._screen = stdscr
     self._stickyPanels = stickyPanels
     self._pagePanels = pagePanels
+    self._menu = menu
     self._page = 0
     self._isPaused = False
     self._forceRedraw = False
@@ -182,6 +188,9 @@ class Controller:
     self._forceRedraw = True
     self.setMsg()
   
+  def getMenu(self):
+    return self._menu
+  
   def isPaused(self):
     """
     True if the interface is paused, false otherwise.
@@ -299,7 +308,7 @@ class Controller:
       
       if attr == None:
         if not self._isPaused:
-          msg = "page %i / %i - q: quit, p: pause, h: page help" % (self._page + 1, len(self._pagePanels))
+          msg = "page %i / %i - m: menu, q: quit, p: pause, h: page help" % (self._page + 1, len(self._pagePanels))
           attr = curses.A_NORMAL
         else:
           msg = "Paused"
@@ -507,6 +516,9 @@ def drawTorMonitor(stdscr, startTime):
       control.prevPage()
     elif key == ord('p') or key == ord('P'):
       control.setPaused(not control.isPaused())
+    elif key == ord('m') or key == ord('M'):
+      menu = control.getMenu()
+      menu.draw()
     elif key == ord('q') or key == ord('Q'):
       # provides prompt to confirm that arm should exit
       if CONFIG["features.confirmQuit"]:
diff --git a/src/cli/menu.py b/src/cli/menu.py
new file mode 100644
index 0000000..315d349
--- /dev/null
+++ b/src/cli/menu.py
@@ -0,0 +1,99 @@
+"""
+A drop-down menu for sending actions to panels.
+"""
+
+import curses
+from collections import namedtuple
+from operator import attrgetter
+
+import cli.controller
+import popups
+from util import log, panel, uiTools
+
+LeafEntry = namedtuple('LeafEntry', ['title', 'callback'])
+ParentEntry = namedtuple('ParentEntry', ['title', 'children'])
+
+class Menu():
+  """Displays a popup menu and sends keys to appropriate panels"""
+
+  def __init__(self, entry=None):
+    DEFAULT_ROOT = ParentEntry(title="Root", children=(
+      LeafEntry(title="File"          , callback=self._callbackDefault),
+      LeafEntry(title="Logs"          , callback=self._callbackDefault),
+      LeafEntry(title="View"          , callback=self._callbackDefault),
+      LeafEntry(title="Graph"         , callback=self._callbackDefault),
+      LeafEntry(title="Connections"   , callback=self._callbackDefault),
+      LeafEntry(title="Configuration" , callback=self._callbackDefault)))
+
+    self._selection = [0]
+    self._rootEntry = (entry and isinstance(entry, ParentEntry)) and entry or DEFAULT_ROOT
+
+  def draw(self):
+    popup, width, height = popups.init(height=3)
+    if popup:
+      try:
+        popup.win.box()
+
+        while True:
+          self._drawTopLevel(popup, width, height)
+
+          popup.win.refresh()
+
+          control = cli.controller.getController()
+          key = control.getScreen().getch()
+
+          if key == curses.KEY_RIGHT:
+            if len(self._selection) == 1:
+              # selection is on top menu
+              self._selection[0] = (self._selection[0] + 1) % len(self._rootEntry.children)
+          elif key == curses.KEY_LEFT:
+            if len(self._selection) == 1:
+              # selection is on top menu
+              self._selection[0] = (self._selection[0] - 1) % len(self._rootEntry.children)
+          elif uiTools.isSelectionKey(key):
+            self._handleEvent()
+            break
+
+      finally:
+        popups.finalize()
+
+  def _drawTopLevel(self, popup, width, height):
+    titles = map(attrgetter('title'), self._rootEntry.children)
+
+    # width per title is set according to the longest title
+    titlewidth = max(map(lambda title: len(title), titles)) + 2
+
+    # total number of titles that can be printed in current width
+    printable = (width - 2) / titlewidth
+
+    top = 1
+    left = 1
+    for (index, entry) in enumerate(self._rootEntry.children[:printable]):
+      titleformat = curses.A_NORMAL
+
+      if index == self._selection[0]:
+        titleformat = curses.A_STANDOUT
+
+      popup.win.addch(top, left, curses.ACS_VLINE)
+      left = left + 1
+      popup.win.addstr(top, left, entry.title.center(titlewidth), titleformat)
+      left = left + titlewidth
+
+    popup.win.addch(top, left, curses.ACS_VLINE)
+    left = left + 1
+
+  def _handleEvent(self):
+    entry = self._rootEntry
+
+    for index in self._selection:
+      if isinstance(entry, ParentEntry):
+        entry = entry.children[index]
+      else:
+        break
+
+    if isinstance(entry, LeafEntry):
+      entry.callback(entry)
+
+  def _callbackDefault(self, entry):
+    log.log(log.NOTICE, "%s selected" % entry.title)
+





More information about the tor-commits mailing list