tor-commits
Threads by month
- ----- 2025 -----
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
July 2011
- 16 participants
- 868 discussions

17 Jul '11
commit 2a6916e1fbcc6fc6a60563e969d0ddc448ece1fd
Author: Kamran Riaz Khan <krkhan(a)inspirated.com>
Date: Tue May 31 14:02:49 2011 +0500
Mapped menu items to respective actions.
There are three kinds of menu items:
* The ones which are directly mapped to a panel action via
Menu._callbackPressKey()
* "State-ful" menu items which are enabled/disabled depending on their
panel's states. These are taken care of via the
Menu._callbackGetEnabled() and Menu._callbackSet() methods. For
example, the "Duplicates" submenu in "Logs" changes its state
depending on log.showDuplicates.
* View menu items, which change controller state. Supported via
Menu._callbackView()
Launching a panel action switches view to that panel, e.g., if we're
on configuration panel and graph bounds are changed the controller
is traversed through pages until one with graph panel is found.
Otherwise, an error is logged.
File->Exit and Configuration->Reset Tor items are unimplemented as of
yet. Possible approaches include factoring out a handleKey() in
controller or duplicating the same code in callbacks.
---
src/cli/menu.py | 198 +++++++++++++++++++++++++++++++++++---------------
src/util/menuItem.py | 11 +++-
2 files changed, 149 insertions(+), 60 deletions(-)
diff --git a/src/cli/menu.py b/src/cli/menu.py
index 5a51797..9aadf8d 100644
--- a/src/cli/menu.py
+++ b/src/cli/menu.py
@@ -6,6 +6,8 @@ import curses
import cli.controller
import popups
+
+from cli.graphing.graphPanel import Bounds as GraphBounds
from util import log, panel, uiTools, menuItem
PARENTLEVEL, TOPLEVEL = (-1, 0)
@@ -15,70 +17,78 @@ class Menu():
def __init__(self, item=None):
DEFAULT_ROOT = menuItem.MenuItem(label="Root", children=(
- 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" , children=(
- menuItem.MenuItem(label="Hidden" , callback=self._callbackDefault),
- menuItem.MenuItem(label="Visible" , 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" , children=(
- menuItem.MenuItem(label="Bandwidth" , callback=self._callbackDefault),
- menuItem.MenuItem(label="Connections" , callback=self._callbackDefault),
- menuItem.MenuItem(label="Resources" , callback=self._callbackDefault),
- )),
- menuItem.MenuItem(label="Size" , children=(
- menuItem.MenuItem(label="Increase" , callback=self._callbackDefault),
- menuItem.MenuItem(label="Decrease" , callback=self._callbackDefault),
- )),
- menuItem.MenuItem(label="Update Interval" , children=(
- menuItem.MenuItem(label="Each second" , callback=self._callbackDefault),
- menuItem.MenuItem(label="5 seconds" , callback=self._callbackDefault),
- menuItem.MenuItem(label="30 seconds" , callback=self._callbackDefault),
- menuItem.MenuItem(label="1 minute" , callback=self._callbackDefault),
- menuItem.MenuItem(label="30 minutes" , callback=self._callbackDefault),
- menuItem.MenuItem(label="Hourly" , callback=self._callbackDefault),
- menuItem.MenuItem(label="Daily" , 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=lambda item: self._callbackPressKey('log', ord('e'))),
+ menuItem.MenuItem(label="Clear",
+ callback=lambda item: self._callbackPressKey('log', ord('c'))),
+ menuItem.MenuItem(label="Save",
+ callback=lambda item: self._callbackPressKey('log', ord('a'))),
+ menuItem.MenuItem(label="Filter",
+ callback=lambda item: self._callbackPressKey('log', ord('f'))),
+ menuItem.MenuItem(label="Duplicates", children=(
+ menuItem.MenuItem(label="Hidden",
+ callback=lambda item: self._callbackSet('log', 'showDuplicates', False, ord('u')),
+ enabled=lambda: self._getItemEnabled('log', 'showDuplicates', False)),
+ menuItem.MenuItem(label="Visible",
+ callback=lambda item: self._callbackSet('log', 'showDuplicates', True, ord('u')),
+ enabled=lambda: self._getItemEnabled('log', 'showDuplicates', True)),
+ )))),
+ menuItem.MenuItem(label="View", children=(
+ menuItem.MenuItem(label="Graph",
+ callback=lambda item: self._callbackView('graph')),
+ menuItem.MenuItem(label="Connections",
+ callback=lambda item: self._callbackView('conn')),
+ menuItem.MenuItem(label="Configuration",
+ callback=lambda item: self._callbackView('configState')),
+ menuItem.MenuItem(label="Configuration File",
+ callback=lambda item: self._callbackView('configFile')),)),
+ menuItem.MenuItem(label="Graph", children=(
+ menuItem.MenuItem(label="Stats",
+ callback=lambda item: self._callbackPressKey('graph', ord('s'))),
+ menuItem.MenuItem(label="Size", children=(
+ menuItem.MenuItem(label="Increase",
+ callback=lambda item: self._callbackPressKey('graph', ord('m'))),
+ menuItem.MenuItem(label="Decrease",
+ callback=lambda item: self._callbackPressKey('graph', ord('n'))),
)),
- menuItem.MenuItem(label="Bounds" , children=(
- menuItem.MenuItem(label="Local Max" , callback=self._callbackDefault),
- menuItem.MenuItem(label="Global Max" , callback=self._callbackDefault),
- menuItem.MenuItem(label="Tight" , callback=self._callbackDefault),
+ menuItem.MenuItem(label="Update Interval",
+ callback=lambda item: self._callbackPressKey('graph', ord('i'))),
+ menuItem.MenuItem(label="Bounds", children=(
+ menuItem.MenuItem(label="Local Max",
+ callback=lambda item: self._callbackSet('graph', 'bounds', GraphBounds.LOCAL_MAX, ord('b')),
+ enabled=lambda: self._getItemEnabled('graph', 'bounds', GraphBounds.LOCAL_MAX)),
+ menuItem.MenuItem(label="Global Max",
+ callback=lambda item: self._callbackSet('graph', 'bounds', GraphBounds.GLOBAL_MAX, ord('b')),
+ enabled=lambda: self._getItemEnabled('graph', 'bounds', GraphBounds.GLOBAL_MAX)),
+ menuItem.MenuItem(label="Tight",
+ callback=lambda item: self._callbackSet('graph', 'bounds', GraphBounds.TIGHT, ord('b')),
+ enabled=lambda: self._getItemEnabled('graph', 'bounds', GraphBounds.TIGHT)),
)),)),
- menuItem.MenuItem(label="Connections" , children=(
- menuItem.MenuItem(label="Identity" , children=(
- menuItem.MenuItem(label="IP" , callback=self._callbackDefault),
- menuItem.MenuItem(label="Fingerprints" , callback=self._callbackDefault),
- menuItem.MenuItem(label="Nicknames" , callback=self._callbackDefault),
+ menuItem.MenuItem(label="Connections", children=(
+ menuItem.MenuItem(label="Identity",
+ callback=lambda item: self._callbackPressKey('conn', ord('l'))),
+ menuItem.MenuItem(label="Resolver",
+ callback=lambda item: self._callbackPressKey('conn', ord('u'))),
+ menuItem.MenuItem(label="Sort Order",
+ callback=lambda item: self._callbackPressKey('conn', ord('s'))),
)),
- menuItem.MenuItem(label="Resolver" , children=(
- menuItem.MenuItem(label="auto" , callback=self._callbackDefault),
- menuItem.MenuItem(label="proc" , callback=self._callbackDefault),
- menuItem.MenuItem(label="netstat" , callback=self._callbackDefault),
- menuItem.MenuItem(label="ss" , callback=self._callbackDefault),
- menuItem.MenuItem(label="lsof" , callback=self._callbackDefault),
- menuItem.MenuItem(label="sockstat" , callback=self._callbackDefault),
- menuItem.MenuItem(label="sockstat (bsd)" , callback=self._callbackDefault),
- menuItem.MenuItem(label="procstat (bsd)" , callback=self._callbackDefault),
- )),
- menuItem.MenuItem(label="Sort Order" , callback=self._callbackDefault),)),
menuItem.MenuItem(label="Configuration" , children=(
- menuItem.MenuItem(label="Comments" , children=(
- menuItem.MenuItem(label="Hidden" , callback=self._callbackDefault),
- menuItem.MenuItem(label="Visible" , callback=self._callbackDefault),
+ menuItem.MenuItem(label="Comments", children=(
+ menuItem.MenuItem(label="Hidden",
+ callback=lambda item: self._callbackSet('configFile', 'stripComments', True, ord('s')),
+ enabled=lambda: self._getItemEnabled('configFile', 'stripComments', True)),
+ menuItem.MenuItem(label="Visible",
+ callback=lambda item: self._callbackSet('configFile', 'stripComments', False, ord('s')),
+ enabled=lambda: self._getItemEnabled('configFile', 'stripComments', False)),
)),
- menuItem.MenuItem(label="Reload" , callback=self._callbackDefault),
- menuItem.MenuItem(label="Reset Tor" , callback=self._callbackDefault),))
+ menuItem.MenuItem(label="Reload",
+ callback=lambda item: self._callbackPressKey('configFile', ord('r'))),
+ menuItem.MenuItem(label="Reset Tor",
+ callback=self._callbackDefault),))
))
self._first = [0]
@@ -111,6 +121,8 @@ class Menu():
elif key == curses.KEY_DOWN:
self._showNLevel()
break
+ elif key == 27:
+ break
elif uiTools.isSelectionKey(key):
self._handleEvent()
break
@@ -238,6 +250,9 @@ class Menu():
popup, width, height = popups.init(height=printable+2, width=labelwidth+2, top=2, left=left)
+ while self._getCurrentItem().isEnabled() == False:
+ self._moveNLevelDown(height)
+
if popup.win:
try:
while True:
@@ -258,6 +273,10 @@ class Menu():
elif key == curses.KEY_RIGHT:
self._showNLevel()
break
+ elif key == 27:
+ self._first.pop()
+ self._selection.pop()
+ break
elif uiTools.isSelectionKey(key):
self._handleEvent()
self._first.pop()
@@ -277,6 +296,9 @@ class Menu():
for (index, item) in enumerate(children):
labelformat = curses.A_STANDOUT if index == self._selection[PARENTLEVEL] else curses.A_NORMAL
+ if not item.isEnabled():
+ labelformat = labelformat | uiTools.getColor('yellow')
+
popup.addstr(top, left, item.getLabel(), labelformat)
top = top + 1
@@ -295,6 +317,9 @@ class Menu():
self._first[PARENTLEVEL] = 0
self._selection[PARENTLEVEL] = 0
+ while self._getCurrentItem().isEnabled() == False:
+ self._moveNLevelDown(height)
+
def _moveNLevelUp(self, height):
printable = self._calculateNLevelHeights(level=PARENTLEVEL)
parent = self._getCurrentItem(level=PARENTLEVEL)
@@ -311,6 +336,9 @@ class Menu():
if self._selection[PARENTLEVEL] > printable:
self._selection[PARENTLEVEL] = printable - 1
+ while self._getCurrentItem().isEnabled() == False:
+ self._moveNLevelUp(height)
+
def _handleEvent(self):
item = self._getCurrentItem()
@@ -322,3 +350,55 @@ class Menu():
def _callbackDefault(self, item):
log.log(log.NOTICE, "%s selected" % item.getLabel())
+ def _callbackView(self, panelname):
+ control = cli.controller.getController()
+
+ start = control.getPage()
+ panels = control.getDisplayPanels(includeSticky=False)
+ panelnames = [panel.getName() for panel in panels]
+ while not panelname in panelnames:
+ control.nextPage()
+ panels = control.getDisplayPanels(includeSticky=False)
+ panelnames = [panel.getName() for panel in panels]
+
+ if control.getPage() == start:
+ log.log(log.ERR, "Panel %s not found" % panelname)
+ break
+
+ def _getItemEnabled(self, panel, attr, value):
+ control = cli.controller.getController()
+ if control:
+ panel = control.getPanel(panel)
+
+ if panel:
+ return getattr(panel, attr, None) != value
+
+ return False
+
+ def _callbackSet(self, panel, attr, value, key=None):
+ self._callbackView(panel)
+
+ control = cli.controller.getController()
+ panel = control.getPanel(panel)
+
+ panelattr = getattr(panel, attr, None)
+
+ if panelattr != None:
+ if hasattr(panelattr, '__call__'):
+ panelattr(value)
+ elif panelattr != value and key != None:
+ start = panelattr
+ while panelattr != value:
+ panel.handleKey(key)
+ panelattr = getattr(panel, attr, None)
+ if panelattr == start:
+ log.log(log.ERR, "Could not set %s.%s" % (panel, attr))
+ break
+
+ def _callbackPressKey(self, panel, key):
+ self._callbackView(panel)
+
+ control = cli.controller.getController()
+ panel = control.getPanel(panel)
+ panel.handleKey(key)
+
diff --git a/src/util/menuItem.py b/src/util/menuItem.py
index 3dbf922..6868db6 100644
--- a/src/util/menuItem.py
+++ b/src/util/menuItem.py
@@ -5,10 +5,11 @@ Menu Item class, used by the drop-down menus
class MenuItem():
"""Contains title, callback handler and possible children"""
- def __init__(self, label=None, callback=None, children=[]):
+ def __init__(self, label=None, callback=None, children=[], enabled=None):
self._label = label
self._callback = callback
self._children = children
+ self._enabled = enabled
def getLabel(self):
return self._label
@@ -19,6 +20,14 @@ class MenuItem():
def isParent(self):
return self._children != []
+ def isEnabled(self):
+ if self._enabled == None:
+ return True
+ elif hasattr(self._enabled, '__call__'):
+ return self._enabled()
+ else:
+ return self._enabled
+
def getChildren(self):
return self._children
1
0
commit 69de93464909606a930451a626fbe54fc50ca387
Author: Kamran Riaz Khan <krkhan(a)inspirated.com>
Date: Wed Jun 1 16:13:26 2011 +0500
Minor readability improvements.
---
src/cli/menu.py | 29 +++++++++++++++++------------
1 files changed, 17 insertions(+), 12 deletions(-)
diff --git a/src/cli/menu.py b/src/cli/menu.py
index 9aadf8d..da45309 100644
--- a/src/cli/menu.py
+++ b/src/cli/menu.py
@@ -119,7 +119,7 @@ class Menu():
elif key == curses.KEY_LEFT:
self._moveTopLevelLeft(width)
elif key == curses.KEY_DOWN:
- self._showNLevel()
+ self._cascadeNLevel()
break
elif key == 27:
break
@@ -130,6 +130,14 @@ class Menu():
finally:
popups.finalize()
+ def _appendLevel(self):
+ self._first.append(0)
+ self._selection.append(0)
+
+ def _removeLevel(self):
+ self._first.pop()
+ self._selection.pop()
+
def _getCurrentTopLevelItem(self):
index = self._first[TOPLEVEL] + self._selection[TOPLEVEL]
return self._rootItem.getChildren()[index]
@@ -233,15 +241,14 @@ class Menu():
popup.addch(top, left, curses.ACS_VLINE)
left = left + 1
- def _showNLevel(self):
- self._first.append(0)
- self._selection.append(0)
-
- parent = self._getCurrentItem(level=PARENTLEVEL)
+ def _cascadeNLevel(self):
+ parent = self._getCurrentItem()
if parent.isLeaf():
return
+ self._appendLevel()
+
labelwidth = self._calculateNLevelWidths(level=PARENTLEVEL)
printable = self._calculateNLevelHeights(level=PARENTLEVEL)
@@ -271,16 +278,14 @@ class Menu():
elif key == curses.KEY_UP:
self._moveNLevelUp(height)
elif key == curses.KEY_RIGHT:
- self._showNLevel()
+ self._cascadeNLevel()
break
elif key == 27:
- self._first.pop()
- self._selection.pop()
+ self._removeLevel()
break
elif uiTools.isSelectionKey(key):
self._handleEvent()
- self._first.pop()
- self._selection.pop()
+ self._removeLevel()
break
finally:
@@ -345,7 +350,7 @@ class Menu():
if item.isLeaf():
item.select()
else:
- self._showNLevel()
+ self._cascadeNLevel()
def _callbackDefault(self, item):
log.log(log.NOTICE, "%s selected" % item.getLabel())
1
0

[arm/release] Next top-level item is dropped down in case right/left arrow keys
by atagar@torproject.org 17 Jul '11
by atagar@torproject.org 17 Jul '11
17 Jul '11
commit 5ffd3b0506cc6acb618456ae276dd0238c1e3936
Author: Kamran Riaz Khan <krkhan(a)inspirated.com>
Date: Wed Jun 1 17:06:27 2011 +0500
Next top-level item is dropped down in case right/left arrow keys
are pressed on leaf submenus.
In order to let the controller redraw before next top-level item is
dropped down, "menu keys" are calculated and returned back to the
controller. The controller overrides its key so that menu is redrawn,
redraws all other panels and sends back the "menu keys" to the new menu
instance. This maintains the illusion of top-level drop-down moving
right/left while screen is cleared and redrawn once in between.
---
src/cli/controller.py | 6 +++++-
src/cli/menu.py | 36 +++++++++++++++++++++++++++++++-----
2 files changed, 36 insertions(+), 6 deletions(-)
diff --git a/src/cli/controller.py b/src/cli/controller.py
index 25c3051..d073312 100644
--- a/src/cli/controller.py
+++ b/src/cli/controller.py
@@ -477,6 +477,8 @@ def drawTorMonitor(stdscr, startTime):
# main draw loop
overrideKey = None # uses this rather than waiting on user input
isUnresponsive = False # flag for heartbeat responsiveness check
+
+ menuKeys = []
while True:
displayPanels = control.getDisplayPanels()
@@ -514,7 +516,9 @@ def drawTorMonitor(stdscr, startTime):
control.setPaused(not control.isPaused())
elif key == ord('m') or key == ord('M'):
menu = cli.menu.Menu()
- menu.showMenu()
+ menuKeys = menu.showMenu(keys=menuKeys)
+ if menuKeys != []:
+ overrideKey = ord('m')
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
index da45309..29b6f82 100644
--- a/src/cli/menu.py
+++ b/src/cli/menu.py
@@ -99,7 +99,10 @@ class Menu():
else:
self._rootItem = DEFAULT_ROOT
- def showMenu(self):
+ def showMenu(self, keys=[]):
+ keys.reverse()
+ returnkeys = []
+
popup, width, height = popups.init(height=3)
if popup:
try:
@@ -112,14 +115,18 @@ class Menu():
popup.win.refresh()
control = cli.controller.getController()
- key = control.getScreen().getch()
+
+ if keys == []:
+ key = control.getScreen().getch()
+ else:
+ key = keys.pop()
if key == curses.KEY_RIGHT:
self._moveTopLevelRight(width)
elif key == curses.KEY_LEFT:
self._moveTopLevelLeft(width)
elif key == curses.KEY_DOWN:
- self._cascadeNLevel()
+ cascaded, returnkeys = self._cascadeNLevel()
break
elif key == 27:
break
@@ -130,6 +137,8 @@ class Menu():
finally:
popups.finalize()
+ return returnkeys
+
def _appendLevel(self):
self._first.append(0)
self._selection.append(0)
@@ -245,7 +254,7 @@ class Menu():
parent = self._getCurrentItem()
if parent.isLeaf():
- return
+ return (False, [])
self._appendLevel()
@@ -261,6 +270,7 @@ class Menu():
self._moveNLevelDown(height)
if popup.win:
+ returnkeys = []
try:
while True:
popup.win.erase()
@@ -278,7 +288,19 @@ class Menu():
elif key == curses.KEY_UP:
self._moveNLevelUp(height)
elif key == curses.KEY_RIGHT:
- self._cascadeNLevel()
+ cascaded, returnkeys = self._cascadeNLevel()
+ if cascaded == False:
+ index = self._first[TOPLEVEL] + self._selection[TOPLEVEL] + 1
+ for i in range(index):
+ returnkeys.append(curses.KEY_RIGHT)
+ returnkeys.append(curses.KEY_DOWN)
+ break
+ elif key == curses.KEY_LEFT:
+ index = self._first[TOPLEVEL] + self._selection[TOPLEVEL] - 1
+ index = index % self._rootItem.getChildrenCount()
+ for i in range(index):
+ returnkeys.append(curses.KEY_RIGHT)
+ returnkeys.append(curses.KEY_DOWN)
break
elif key == 27:
self._removeLevel()
@@ -291,6 +313,10 @@ class Menu():
finally:
popups.finalize()
+ return (True, returnkeys)
+
+ return (False, [])
+
def _drawNLevel(self, popup, width, height):
printable = self._calculateNLevelHeights(level=PARENTLEVEL)
parent = self._getCurrentItem(level=PARENTLEVEL)
1
0

[arm/release] Controller page isn't changed in case the required panel isn't currently
by atagar@torproject.org 17 Jul '11
by atagar@torproject.org 17 Jul '11
17 Jul '11
commit a2937170af8f17ff048cccc1093776f174467352
Author: Kamran Riaz Khan <krkhan(a)inspirated.com>
Date: Wed Jun 1 17:12:35 2011 +0500
Controller page isn't changed in case the required panel isn't currently
being displayed.
---
src/cli/menu.py | 4 ----
1 files changed, 0 insertions(+), 4 deletions(-)
diff --git a/src/cli/menu.py b/src/cli/menu.py
index 29b6f82..beceb01 100644
--- a/src/cli/menu.py
+++ b/src/cli/menu.py
@@ -407,8 +407,6 @@ class Menu():
return False
def _callbackSet(self, panel, attr, value, key=None):
- self._callbackView(panel)
-
control = cli.controller.getController()
panel = control.getPanel(panel)
@@ -427,8 +425,6 @@ class Menu():
break
def _callbackPressKey(self, panel, key):
- self._callbackView(panel)
-
control = cli.controller.getController()
panel = control.getPanel(panel)
panel.handleKey(key)
1
0

[arm/release] Added support for File->Exit and Configuation->Reset Tor.
by atagar@torproject.org 17 Jul '11
by atagar@torproject.org 17 Jul '11
17 Jul '11
commit 62f118867dca5848d798bc2747a87b517bf81db9
Author: Kamran Riaz Khan <krkhan(a)inspirated.com>
Date: Thu Jun 2 15:28:07 2011 +0500
Added support for File->Exit and Configuation->Reset Tor.
Event handlers can return a key which shall be carried all the way
back to the controller which can use it for the next override in case
it's recognized. Exit and Reset Tor return 'q' and 'x' keys
respectively.
cli.__all__ is updated to include menu module.
---
src/cli/__init__.py | 2 +-
src/cli/controller.py | 5 ++++-
src/cli/menu.py | 15 +++++++++++----
src/util/menuItem.py | 2 +-
4 files changed, 17 insertions(+), 7 deletions(-)
diff --git a/src/cli/__init__.py b/src/cli/__init__.py
index 1564f68..81fabeb 100644
--- a/src/cli/__init__.py
+++ b/src/cli/__init__.py
@@ -2,5 +2,5 @@
Panels, popups, and handlers comprising the arm user interface.
"""
-__all__ = ["configPanel", "controller", "descriptorPopup", "headerPanel", "logPanel", "popups", "torrcPanel"]
+__all__ = ["configPanel", "controller", "descriptorPopup", "headerPanel", "logPanel", "menu", "popups", "torrcPanel"]
diff --git a/src/cli/controller.py b/src/cli/controller.py
index d073312..bb258c3 100644
--- a/src/cli/controller.py
+++ b/src/cli/controller.py
@@ -518,7 +518,10 @@ def drawTorMonitor(stdscr, startTime):
menu = cli.menu.Menu()
menuKeys = menu.showMenu(keys=menuKeys)
if menuKeys != []:
- overrideKey = ord('m')
+ for key in (ord('m'), ord('q'), ord('x')):
+ if key in menuKeys:
+ menuKeys.remove(key)
+ overrideKey = key
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
index beceb01..47e8d09 100644
--- a/src/cli/menu.py
+++ b/src/cli/menu.py
@@ -19,7 +19,7 @@ class Menu():
DEFAULT_ROOT = menuItem.MenuItem(label="Root", children=(
menuItem.MenuItem(label="File", children=(
menuItem.MenuItem(label="Exit",
- callback=self._callbackDefault),)),
+ callback=lambda item: self._callbackReturnKey(ord('q'))),)),
menuItem.MenuItem(label="Logs", children=(
menuItem.MenuItem(label="Events",
callback=lambda item: self._callbackPressKey('log', ord('e'))),
@@ -88,7 +88,7 @@ class Menu():
menuItem.MenuItem(label="Reload",
callback=lambda item: self._callbackPressKey('configFile', ord('r'))),
menuItem.MenuItem(label="Reset Tor",
- callback=self._callbackDefault),))
+ callback=lambda item: self._callbackReturnKey(ord('x'))),))
))
self._first = [0]
@@ -291,6 +291,7 @@ class Menu():
cascaded, returnkeys = self._cascadeNLevel()
if cascaded == False:
index = self._first[TOPLEVEL] + self._selection[TOPLEVEL] + 1
+ returnkeys.append(ord('m'))
for i in range(index):
returnkeys.append(curses.KEY_RIGHT)
returnkeys.append(curses.KEY_DOWN)
@@ -298,6 +299,7 @@ class Menu():
elif key == curses.KEY_LEFT:
index = self._first[TOPLEVEL] + self._selection[TOPLEVEL] - 1
index = index % self._rootItem.getChildrenCount()
+ returnkeys.append(ord('m'))
for i in range(index):
returnkeys.append(curses.KEY_RIGHT)
returnkeys.append(curses.KEY_DOWN)
@@ -306,7 +308,9 @@ class Menu():
self._removeLevel()
break
elif uiTools.isSelectionKey(key):
- self._handleEvent()
+ returnkey = self._handleEvent()
+ if returnkey:
+ returnkeys.append(returnkey)
self._removeLevel()
break
@@ -374,7 +378,7 @@ class Menu():
item = self._getCurrentItem()
if item.isLeaf():
- item.select()
+ return item.select()
else:
self._cascadeNLevel()
@@ -429,3 +433,6 @@ class Menu():
panel = control.getPanel(panel)
panel.handleKey(key)
+ def _callbackReturnKey(self, key):
+ return key
+
diff --git a/src/util/menuItem.py b/src/util/menuItem.py
index 6868db6..b48c433 100644
--- a/src/util/menuItem.py
+++ b/src/util/menuItem.py
@@ -35,5 +35,5 @@ class MenuItem():
return len(self._children)
def select(self):
- self._callback(self)
+ return self._callback(self)
1
0

17 Jul '11
commit b5c7dca851bb7f1e13c90bd667136398fa89ce01
Author: Damian Johnson <atagar(a)torproject.org>
Date: Tue May 31 20:50:03 2011 -0700
Minor api, formatting, and documentation changes
Revising some of the merged changes for the menus.
---
src/cli/controller.py | 10 +++-------
src/cli/popups.py | 15 +++++----------
src/util/menuItem.py | 22 ++++++++++++----------
src/util/panel.py | 25 +++++++++++++++++--------
4 files changed, 37 insertions(+), 35 deletions(-)
diff --git a/src/cli/controller.py b/src/cli/controller.py
index bb258c3..fcbaed5 100644
--- a/src/cli/controller.py
+++ b/src/cli/controller.py
@@ -7,6 +7,7 @@ import time
import curses
import threading
+import cli.menu
import cli.popups
import cli.headerPanel
import cli.logPanel
@@ -17,7 +18,6 @@ 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
@@ -77,9 +77,6 @@ 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)
@@ -110,7 +107,7 @@ class LabelPanel(panel.Panel):
"""
def __init__(self, stdscr):
- panel.Panel.__init__(self, stdscr, "msg", 0, 1)
+ panel.Panel.__init__(self, stdscr, "msg", 0, height=1)
self.msgText = ""
self.msgAttr = curses.A_NORMAL
@@ -144,7 +141,6 @@ 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
@@ -304,7 +300,7 @@ class Controller:
if attr == None:
if not self._isPaused:
- msg = "page %i / %i - m: menu, q: quit, p: pause, h: page help" % (self._page + 1, len(self._pagePanels))
+ msg = "page %i / %i - m: menu, p: pause, h: page help, q: quit" % (self._page + 1, len(self._pagePanels))
attr = curses.A_NORMAL
else:
msg = "Paused"
diff --git a/src/cli/popups.py b/src/cli/popups.py
index b3eb105..0e50aa1 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, top = -1, left = -1):
+def init(height = -1, width = -1, top = 0, left = 0):
"""
Preparation for displaying a popup. This creates a popup with a valid
subwindow instance. If that's successful then the curses lock is acquired
@@ -19,19 +19,14 @@ def init(height = -1, width = -1, top = -1, left = -1):
Arguments:
height - maximum height of the popup
width - maximum width of the popup
+ top - top position, relative to the sticky content
+ left - left position from the screen
"""
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
+ stickyHeight = sum(stickyPanel.getHeight() for stickyPanel in control.getStickyPanels())
- popup = panel.Panel(control.getScreen(), "popup", topSize, height, width)
- popup.setLeft(leftSize)
+ popup = panel.Panel(control.getScreen(), "popup", top + stickyHeight, left, height, width)
popup.setVisible(True)
# Redraws the popup to prepare a subwindow instance. If none is spawned then
diff --git a/src/util/menuItem.py b/src/util/menuItem.py
index b48c433..277c78a 100644
--- a/src/util/menuItem.py
+++ b/src/util/menuItem.py
@@ -1,25 +1,27 @@
"""
-Menu Item class, used by the drop-down menus
+Menu Item class, used by the drop-down menus.
"""
class MenuItem():
- """Contains title, callback handler and possible children"""
-
+ """
+ Contains title, callback handler and possible children.
+ """
+
def __init__(self, label=None, callback=None, children=[], enabled=None):
self._label = label
self._callback = callback
self._children = children
self._enabled = enabled
-
+
def getLabel(self):
return self._label
-
+
def isLeaf(self):
return self._children == []
-
+
def isParent(self):
return self._children != []
-
+
def isEnabled(self):
if self._enabled == None:
return True
@@ -27,13 +29,13 @@ class MenuItem():
return self._enabled()
else:
return self._enabled
-
+
def getChildren(self):
return self._children
-
+
def getChildrenCount(self):
return len(self._children)
-
+
def select(self):
return self._callback(self)
diff --git a/src/util/panel.py b/src/util/panel.py
index 4fc2158..93b44c2 100644
--- a/src/util/panel.py
+++ b/src/util/panel.py
@@ -43,7 +43,7 @@ class Panel():
redraw().
"""
- def __init__(self, parent, name, top, height=-1, width=-1):
+ def __init__(self, parent, name, top, left=0, height=-1, width=-1):
"""
Creates a durable wrapper for a curses subwindow in the given parent.
@@ -51,6 +51,7 @@ class Panel():
parent - parent curses window
name - identifier for the panel
top - positioning of top within parent
+ left - positioning of the left edge within the parent
height - maximum height of panel (uses all available space if -1)
width - maximum width of panel (uses all available space if -1)
"""
@@ -74,7 +75,7 @@ class Panel():
self.pauseTime = -1
self.top = top
- self.left = 0
+ self.left = left
self.height = height
self.width = width
@@ -256,25 +257,33 @@ class Panel():
self.top = top
self.win = None
- def getHeight(self):
+ def getLeft(self):
"""
- Provides the height used for subwindows (-1 if it isn't limited).
+ Provides the left position where this subwindow is placed within its
+ parent.
"""
- return self.height
-
+ return self.left
+
def setLeft(self, left):
"""
- Changes the position where subwindows are placed within its parent.
+ Changes the left position where this subwindow is placed within its parent.
Arguments:
- top - positioning of top within parent
+ left - positioning of top within parent
"""
if self.left != left:
self.left = left
self.win = None
+ def getHeight(self):
+ """
+ Provides the height used for subwindows (-1 if it isn't limited).
+ """
+
+ return self.height
+
def setHeight(self, height):
"""
Changes the height used for subwindows. This uses all available space if -1.
1
0
commit ccd77c1e77c30265a6d36732697854cf6afc069b
Merge: 32c6f17 62f1188
Author: Damian Johnson <atagar(a)torproject.org>
Date: Thu Jun 2 08:59:45 2011 -0700
Merging krkhan's menus work
src/cli/__init__.py | 2 +-
src/cli/controller.py | 17 ++-
src/cli/menu.py | 438 +++++++++++++++++++++++++++++++++++++++++++++++++
src/cli/popups.py | 9 +-
src/util/menuItem.py | 39 +++++
src/util/panel.py | 18 ++-
6 files changed, 518 insertions(+), 5 deletions(-)
1
0
commit f56e2b85d5dd133c40955e9438f4109c31cd9f30
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Jun 4 20:20:16 2011 -0700
Replacing list comprehension with map
Couple minor refactoring changes from a code review comment of Robert's. I'm
actually not sure why the first was working since it was missing the
brackets...
---
src/cli/popups.py | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/cli/popups.py b/src/cli/popups.py
index b90f95d..52e5376 100644
--- a/src/cli/popups.py
+++ b/src/cli/popups.py
@@ -26,7 +26,7 @@ def init(height = -1, width = -1, top = 0, left = 0, belowStatic = True):
control = cli.controller.getController()
if belowStatic:
- stickyHeight = sum(stickyPanel.getHeight() for stickyPanel in control.getStickyPanels())
+ stickyHeight = sum(map(getHeight, control.getStickyPanels()))
else: stickyHeight = 0
popup = panel.Panel(control.getScreen(), "popup", top + stickyHeight, left, height, width)
@@ -272,7 +272,7 @@ def showMenu(title, options, oldSelection):
selection without a carrot if -1)
"""
- maxWidth = max([len(label) for label in options]) + 9
+ maxWidth = max(map(len, options)) + 9
popup, _, _ = init(len(options) + 2, maxWidth)
if not popup: return
key, selection = 0, oldSelection if oldSelection != -1 else 0
1
0

17 Jul '11
commit 856226d684220b746b44c2f44a1c3d3d03f8384b
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Jun 4 14:05:23 2011 -0700
Moving Kamran's menu to avoid namespace conflicts
My 'src/cli/menu' module was conflicting with his 'src/cli/menu.py' class so
I've moved his implementation to 'src/cli/menu_alt'.
---
src/cli/__init__.py | 2 +-
src/cli/controller.py | 6 +-
src/cli/menu.py | 438 -----------------------------------------
src/cli/menu_alt/__init__.py | 6 +
src/cli/menu_alt/menu.py | 439 ++++++++++++++++++++++++++++++++++++++++++
src/cli/menu_alt/menuItem.py | 41 ++++
src/util/menuItem.py | 41 ----
7 files changed, 490 insertions(+), 483 deletions(-)
diff --git a/src/cli/__init__.py b/src/cli/__init__.py
index 81fabeb..1564f68 100644
--- a/src/cli/__init__.py
+++ b/src/cli/__init__.py
@@ -2,5 +2,5 @@
Panels, popups, and handlers comprising the arm user interface.
"""
-__all__ = ["configPanel", "controller", "descriptorPopup", "headerPanel", "logPanel", "menu", "popups", "torrcPanel"]
+__all__ = ["configPanel", "controller", "descriptorPopup", "headerPanel", "logPanel", "popups", "torrcPanel"]
diff --git a/src/cli/controller.py b/src/cli/controller.py
index fac43a4..997d9d3 100644
--- a/src/cli/controller.py
+++ b/src/cli/controller.py
@@ -7,8 +7,8 @@ import time
import curses
import threading
-import cli.menu
import cli.menu.menu
+import cli.menu_alt.menu
import cli.popups
import cli.headerPanel
import cli.logPanel
@@ -512,10 +512,10 @@ def drawTorMonitor(stdscr, startTime):
elif key == ord('p') or key == ord('P'):
control.setPaused(not control.isPaused())
elif key == ord('n') or key == ord('N'):
- menu = cli.menu.Menu()
+ menu = cli.menu_alt.menu.Menu()
menuKeys = menu.showMenu(keys=menuKeys)
if menuKeys != []:
- for key in (ord('m'), ord('q'), ord('x')):
+ for key in (ord('n'), ord('q'), ord('x')):
if key in menuKeys:
menuKeys.remove(key)
overrideKey = key
diff --git a/src/cli/menu.py b/src/cli/menu.py
deleted file mode 100644
index 47e8d09..0000000
--- a/src/cli/menu.py
+++ /dev/null
@@ -1,438 +0,0 @@
-"""
-A drop-down menu for sending actions to panels.
-"""
-
-import curses
-
-import cli.controller
-import popups
-
-from cli.graphing.graphPanel import Bounds as GraphBounds
-from util import log, panel, uiTools, menuItem
-
-PARENTLEVEL, TOPLEVEL = (-1, 0)
-
-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", children=(
- menuItem.MenuItem(label="Exit",
- callback=lambda item: self._callbackReturnKey(ord('q'))),)),
- menuItem.MenuItem(label="Logs", children=(
- menuItem.MenuItem(label="Events",
- callback=lambda item: self._callbackPressKey('log', ord('e'))),
- menuItem.MenuItem(label="Clear",
- callback=lambda item: self._callbackPressKey('log', ord('c'))),
- menuItem.MenuItem(label="Save",
- callback=lambda item: self._callbackPressKey('log', ord('a'))),
- menuItem.MenuItem(label="Filter",
- callback=lambda item: self._callbackPressKey('log', ord('f'))),
- menuItem.MenuItem(label="Duplicates", children=(
- menuItem.MenuItem(label="Hidden",
- callback=lambda item: self._callbackSet('log', 'showDuplicates', False, ord('u')),
- enabled=lambda: self._getItemEnabled('log', 'showDuplicates', False)),
- menuItem.MenuItem(label="Visible",
- callback=lambda item: self._callbackSet('log', 'showDuplicates', True, ord('u')),
- enabled=lambda: self._getItemEnabled('log', 'showDuplicates', True)),
- )))),
- menuItem.MenuItem(label="View", children=(
- menuItem.MenuItem(label="Graph",
- callback=lambda item: self._callbackView('graph')),
- menuItem.MenuItem(label="Connections",
- callback=lambda item: self._callbackView('conn')),
- menuItem.MenuItem(label="Configuration",
- callback=lambda item: self._callbackView('configState')),
- menuItem.MenuItem(label="Configuration File",
- callback=lambda item: self._callbackView('configFile')),)),
- menuItem.MenuItem(label="Graph", children=(
- menuItem.MenuItem(label="Stats",
- callback=lambda item: self._callbackPressKey('graph', ord('s'))),
- menuItem.MenuItem(label="Size", children=(
- menuItem.MenuItem(label="Increase",
- callback=lambda item: self._callbackPressKey('graph', ord('m'))),
- menuItem.MenuItem(label="Decrease",
- callback=lambda item: self._callbackPressKey('graph', ord('n'))),
- )),
- menuItem.MenuItem(label="Update Interval",
- callback=lambda item: self._callbackPressKey('graph', ord('i'))),
- menuItem.MenuItem(label="Bounds", children=(
- menuItem.MenuItem(label="Local Max",
- callback=lambda item: self._callbackSet('graph', 'bounds', GraphBounds.LOCAL_MAX, ord('b')),
- enabled=lambda: self._getItemEnabled('graph', 'bounds', GraphBounds.LOCAL_MAX)),
- menuItem.MenuItem(label="Global Max",
- callback=lambda item: self._callbackSet('graph', 'bounds', GraphBounds.GLOBAL_MAX, ord('b')),
- enabled=lambda: self._getItemEnabled('graph', 'bounds', GraphBounds.GLOBAL_MAX)),
- menuItem.MenuItem(label="Tight",
- callback=lambda item: self._callbackSet('graph', 'bounds', GraphBounds.TIGHT, ord('b')),
- enabled=lambda: self._getItemEnabled('graph', 'bounds', GraphBounds.TIGHT)),
- )),)),
- menuItem.MenuItem(label="Connections", children=(
- menuItem.MenuItem(label="Identity",
- callback=lambda item: self._callbackPressKey('conn', ord('l'))),
- menuItem.MenuItem(label="Resolver",
- callback=lambda item: self._callbackPressKey('conn', ord('u'))),
- menuItem.MenuItem(label="Sort Order",
- callback=lambda item: self._callbackPressKey('conn', ord('s'))),
- )),
- menuItem.MenuItem(label="Configuration" , children=(
- menuItem.MenuItem(label="Comments", children=(
- menuItem.MenuItem(label="Hidden",
- callback=lambda item: self._callbackSet('configFile', 'stripComments', True, ord('s')),
- enabled=lambda: self._getItemEnabled('configFile', 'stripComments', True)),
- menuItem.MenuItem(label="Visible",
- callback=lambda item: self._callbackSet('configFile', 'stripComments', False, ord('s')),
- enabled=lambda: self._getItemEnabled('configFile', 'stripComments', False)),
- )),
- menuItem.MenuItem(label="Reload",
- callback=lambda item: self._callbackPressKey('configFile', ord('r'))),
- menuItem.MenuItem(label="Reset Tor",
- callback=lambda item: self._callbackReturnKey(ord('x'))),))
- ))
-
- self._first = [0]
- self._selection = [0]
-
- if item and item.isParent():
- self._rootItem = item
- else:
- self._rootItem = DEFAULT_ROOT
-
- def showMenu(self, keys=[]):
- keys.reverse()
- returnkeys = []
-
- popup, width, height = popups.init(height=3)
- if popup:
- try:
- while True:
- popup.win.erase()
- popup.win.box()
-
- self._drawTopLevel(popup, width, height)
-
- popup.win.refresh()
-
- control = cli.controller.getController()
-
- if keys == []:
- key = control.getScreen().getch()
- else:
- key = keys.pop()
-
- if key == curses.KEY_RIGHT:
- self._moveTopLevelRight(width)
- elif key == curses.KEY_LEFT:
- self._moveTopLevelLeft(width)
- elif key == curses.KEY_DOWN:
- cascaded, returnkeys = self._cascadeNLevel()
- break
- elif key == 27:
- break
- elif uiTools.isSelectionKey(key):
- self._handleEvent()
- break
-
- finally:
- popups.finalize()
-
- return returnkeys
-
- def _appendLevel(self):
- self._first.append(0)
- self._selection.append(0)
-
- def _removeLevel(self):
- self._first.pop()
- self._selection.pop()
-
- def _getCurrentTopLevelItem(self):
- index = self._first[TOPLEVEL] + self._selection[TOPLEVEL]
- return self._rootItem.getChildren()[index]
-
- def _getCurrentItem(self, level=0):
- item = self._rootItem
- if level == 0:
- sums = [sum(values) for values in zip(self._first, self._selection)]
- else:
- sums = [sum(values) for values in zip(self._first[:level], self._selection[:level])]
-
- for index in sums:
- if item.isParent():
- item = item.getChildren()[index]
- else:
- break
-
- return item
-
- 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 supplied width
- printable = min(width / labelwidth - 1, self._rootItem.getChildrenCount())
-
- return (labelwidth, printable)
-
- def _calculateNLevelWidths(self, level=0):
- parent = self._getCurrentItem(level)
-
- if parent.isLeaf():
- return 0
-
- labels = [menuItem.getLabel() for menuItem in parent.getChildren()]
-
- labelwidth = max(map(len, labels))
-
- return labelwidth
-
- def _calculateNLevelHeights(self, height=0, level=0):
- control = cli.controller.getController()
- height, _ = control.getScreen().getmaxyx()
- topSize = sum(stickyPanel.getHeight() for stickyPanel in control.getStickyPanels())
- height = height - topSize
-
- parent = self._getCurrentItem(level)
-
- if parent.isLeaf():
- return 0
-
- printable = min(height - 4, parent.getChildrenCount())
-
- return printable if printable else parent.getChildrenCount()
-
- def _moveTopLevelRight(self, width):
- _, printable = self._calculateTopLevelWidths(width)
-
- if self._selection[TOPLEVEL] < printable - 1:
- self._selection[TOPLEVEL] = self._selection[TOPLEVEL] + 1
- else:
- self._selection[TOPLEVEL] = 0
- if printable < self._rootItem.getChildrenCount():
- self._first[TOPLEVEL] = (self._first[TOPLEVEL] + printable) % self._rootItem.getChildrenCount()
-
- if self._first[TOPLEVEL] + self._selection[TOPLEVEL] == self._rootItem.getChildrenCount():
- self._first[TOPLEVEL] = 0
- self._selection[TOPLEVEL] = 0
-
- def _moveTopLevelLeft(self, width):
- _, printable = self._calculateTopLevelWidths(width)
-
- if self._selection[TOPLEVEL] > 0:
- self._selection[TOPLEVEL] = self._selection[TOPLEVEL] - 1
- else:
- if self._first[TOPLEVEL] == 0:
- 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
-
- if self._selection[TOPLEVEL] > printable:
- self._selection[TOPLEVEL] = printable - 1
-
- def _drawTopLevel(self, popup, width, height):
- labelwidth, printable = self._calculateTopLevelWidths(width)
- children = self._rootItem.getChildren()[self._first[TOPLEVEL]:self._first[TOPLEVEL] + printable]
-
- top = 1
- left = 1
- for (index, item) in enumerate(children):
- labelformat = curses.A_STANDOUT if index == self._selection[TOPLEVEL] else curses.A_NORMAL
-
- popup.addch(top, left, curses.ACS_VLINE)
- left = left + 1
- popup.addstr(top, left, item.getLabel().center(labelwidth), labelformat)
- left = left + labelwidth
-
- popup.addch(top, left, curses.ACS_VLINE)
- left = left + 1
-
- def _cascadeNLevel(self):
- parent = self._getCurrentItem()
-
- if parent.isLeaf():
- return (False, [])
-
- self._appendLevel()
-
- labelwidth = self._calculateNLevelWidths(level=PARENTLEVEL)
- printable = self._calculateNLevelHeights(level=PARENTLEVEL)
-
- toplabelwidth, _ = self._calculateTopLevelWidths()
- left = (toplabelwidth + 2) * self._selection[TOPLEVEL]
-
- popup, width, height = popups.init(height=printable+2, width=labelwidth+2, top=2, left=left)
-
- while self._getCurrentItem().isEnabled() == False:
- self._moveNLevelDown(height)
-
- if popup.win:
- returnkeys = []
- try:
- while True:
- popup.win.erase()
- popup.win.box()
-
- self._drawNLevel(popup, width, height)
-
- popup.win.refresh()
-
- control = cli.controller.getController()
- key = control.getScreen().getch()
-
- if key == curses.KEY_DOWN:
- self._moveNLevelDown(height)
- elif key == curses.KEY_UP:
- self._moveNLevelUp(height)
- elif key == curses.KEY_RIGHT:
- cascaded, returnkeys = self._cascadeNLevel()
- if cascaded == False:
- index = self._first[TOPLEVEL] + self._selection[TOPLEVEL] + 1
- returnkeys.append(ord('m'))
- for i in range(index):
- returnkeys.append(curses.KEY_RIGHT)
- returnkeys.append(curses.KEY_DOWN)
- break
- elif key == curses.KEY_LEFT:
- index = self._first[TOPLEVEL] + self._selection[TOPLEVEL] - 1
- index = index % self._rootItem.getChildrenCount()
- returnkeys.append(ord('m'))
- for i in range(index):
- returnkeys.append(curses.KEY_RIGHT)
- returnkeys.append(curses.KEY_DOWN)
- break
- elif key == 27:
- self._removeLevel()
- break
- elif uiTools.isSelectionKey(key):
- returnkey = self._handleEvent()
- if returnkey:
- returnkeys.append(returnkey)
- self._removeLevel()
- break
-
- finally:
- popups.finalize()
-
- return (True, returnkeys)
-
- return (False, [])
-
- def _drawNLevel(self, popup, width, height):
- printable = self._calculateNLevelHeights(level=PARENTLEVEL)
- parent = self._getCurrentItem(level=PARENTLEVEL)
- children = parent.getChildren()[self._first[PARENTLEVEL]:self._first[PARENTLEVEL] + printable]
-
- top = 1
- left = 1
- for (index, item) in enumerate(children):
- labelformat = curses.A_STANDOUT if index == self._selection[PARENTLEVEL] else curses.A_NORMAL
-
- if not item.isEnabled():
- labelformat = labelformat | uiTools.getColor('yellow')
-
- popup.addstr(top, left, item.getLabel(), labelformat)
- top = top + 1
-
- def _moveNLevelDown(self, height):
- printable = self._calculateNLevelHeights(level=PARENTLEVEL)
- parent = self._getCurrentItem(level=PARENTLEVEL)
-
- if self._selection[PARENTLEVEL] < printable - 1:
- self._selection[PARENTLEVEL] = self._selection[PARENTLEVEL] + 1
- else:
- self._selection[PARENTLEVEL] = 0
- if printable < parent.getChildrenCount():
- self._first[PARENTLEVEL] = (self._first[PARENTLEVEL] + printable) % parent.getChildrenCount()
-
- if self._first[PARENTLEVEL] + self._selection[PARENTLEVEL] == parent.getChildrenCount():
- self._first[PARENTLEVEL] = 0
- self._selection[PARENTLEVEL] = 0
-
- while self._getCurrentItem().isEnabled() == False:
- self._moveNLevelDown(height)
-
- def _moveNLevelUp(self, height):
- printable = self._calculateNLevelHeights(level=PARENTLEVEL)
- parent = self._getCurrentItem(level=PARENTLEVEL)
-
- if self._selection[PARENTLEVEL] > 0:
- self._selection[PARENTLEVEL] = self._selection[PARENTLEVEL] - 1
- else:
- if self._first[PARENTLEVEL] == 0:
- self._first[PARENTLEVEL] = ((parent.getChildrenCount() / printable) * printable) % parent.getChildrenCount()
- else:
- self._first[PARENTLEVEL] = abs(self._first[PARENTLEVEL] - printable) % parent.getChildrenCount()
- self._selection[PARENTLEVEL] = parent.getChildrenCount() - self._first[PARENTLEVEL] - 1
-
- if self._selection[PARENTLEVEL] > printable:
- self._selection[PARENTLEVEL] = printable - 1
-
- while self._getCurrentItem().isEnabled() == False:
- self._moveNLevelUp(height)
-
- def _handleEvent(self):
- item = self._getCurrentItem()
-
- if item.isLeaf():
- return item.select()
- else:
- self._cascadeNLevel()
-
- def _callbackDefault(self, item):
- log.log(log.NOTICE, "%s selected" % item.getLabel())
-
- def _callbackView(self, panelname):
- control = cli.controller.getController()
-
- start = control.getPage()
- panels = control.getDisplayPanels(includeSticky=False)
- panelnames = [panel.getName() for panel in panels]
- while not panelname in panelnames:
- control.nextPage()
- panels = control.getDisplayPanels(includeSticky=False)
- panelnames = [panel.getName() for panel in panels]
-
- if control.getPage() == start:
- log.log(log.ERR, "Panel %s not found" % panelname)
- break
-
- def _getItemEnabled(self, panel, attr, value):
- control = cli.controller.getController()
- if control:
- panel = control.getPanel(panel)
-
- if panel:
- return getattr(panel, attr, None) != value
-
- return False
-
- def _callbackSet(self, panel, attr, value, key=None):
- control = cli.controller.getController()
- panel = control.getPanel(panel)
-
- panelattr = getattr(panel, attr, None)
-
- if panelattr != None:
- if hasattr(panelattr, '__call__'):
- panelattr(value)
- elif panelattr != value and key != None:
- start = panelattr
- while panelattr != value:
- panel.handleKey(key)
- panelattr = getattr(panel, attr, None)
- if panelattr == start:
- log.log(log.ERR, "Could not set %s.%s" % (panel, attr))
- break
-
- def _callbackPressKey(self, panel, key):
- control = cli.controller.getController()
- panel = control.getPanel(panel)
- panel.handleKey(key)
-
- def _callbackReturnKey(self, key):
- return key
-
diff --git a/src/cli/menu_alt/__init__.py b/src/cli/menu_alt/__init__.py
new file mode 100644
index 0000000..6d7b6b7
--- /dev/null
+++ b/src/cli/menu_alt/__init__.py
@@ -0,0 +1,6 @@
+"""
+Resources for displaying the menu.
+"""
+
+__all__ = ["menuItem", "menu"]
+
diff --git a/src/cli/menu_alt/menu.py b/src/cli/menu_alt/menu.py
new file mode 100644
index 0000000..7e10366
--- /dev/null
+++ b/src/cli/menu_alt/menu.py
@@ -0,0 +1,439 @@
+"""
+A drop-down menu for sending actions to panels.
+"""
+
+import curses
+
+import cli.controller
+import cli.menu_alt.menuItem as menuItem
+import cli.popups
+
+from cli.graphing.graphPanel import Bounds as GraphBounds
+from util import log, panel, uiTools
+
+PARENTLEVEL, TOPLEVEL = (-1, 0)
+
+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", children=(
+ menuItem.MenuItem(label="Exit",
+ callback=lambda item: self._callbackReturnKey(ord('q'))),)),
+ menuItem.MenuItem(label="Logs", children=(
+ menuItem.MenuItem(label="Events",
+ callback=lambda item: self._callbackPressKey('log', ord('e'))),
+ menuItem.MenuItem(label="Clear",
+ callback=lambda item: self._callbackPressKey('log', ord('c'))),
+ menuItem.MenuItem(label="Save",
+ callback=lambda item: self._callbackPressKey('log', ord('a'))),
+ menuItem.MenuItem(label="Filter",
+ callback=lambda item: self._callbackPressKey('log', ord('f'))),
+ menuItem.MenuItem(label="Duplicates", children=(
+ menuItem.MenuItem(label="Hidden",
+ callback=lambda item: self._callbackSet('log', 'showDuplicates', False, ord('u')),
+ enabled=lambda: self._getItemEnabled('log', 'showDuplicates', False)),
+ menuItem.MenuItem(label="Visible",
+ callback=lambda item: self._callbackSet('log', 'showDuplicates', True, ord('u')),
+ enabled=lambda: self._getItemEnabled('log', 'showDuplicates', True)),
+ )))),
+ menuItem.MenuItem(label="View", children=(
+ menuItem.MenuItem(label="Graph",
+ callback=lambda item: self._callbackView('graph')),
+ menuItem.MenuItem(label="Connections",
+ callback=lambda item: self._callbackView('conn')),
+ menuItem.MenuItem(label="Configuration",
+ callback=lambda item: self._callbackView('configState')),
+ menuItem.MenuItem(label="Configuration File",
+ callback=lambda item: self._callbackView('configFile')),)),
+ menuItem.MenuItem(label="Graph", children=(
+ menuItem.MenuItem(label="Stats",
+ callback=lambda item: self._callbackPressKey('graph', ord('s'))),
+ menuItem.MenuItem(label="Size", children=(
+ menuItem.MenuItem(label="Increase",
+ callback=lambda item: self._callbackPressKey('graph', ord('m'))),
+ menuItem.MenuItem(label="Decrease",
+ callback=lambda item: self._callbackPressKey('graph', ord('n'))),
+ )),
+ menuItem.MenuItem(label="Update Interval",
+ callback=lambda item: self._callbackPressKey('graph', ord('i'))),
+ menuItem.MenuItem(label="Bounds", children=(
+ menuItem.MenuItem(label="Local Max",
+ callback=lambda item: self._callbackSet('graph', 'bounds', GraphBounds.LOCAL_MAX, ord('b')),
+ enabled=lambda: self._getItemEnabled('graph', 'bounds', GraphBounds.LOCAL_MAX)),
+ menuItem.MenuItem(label="Global Max",
+ callback=lambda item: self._callbackSet('graph', 'bounds', GraphBounds.GLOBAL_MAX, ord('b')),
+ enabled=lambda: self._getItemEnabled('graph', 'bounds', GraphBounds.GLOBAL_MAX)),
+ menuItem.MenuItem(label="Tight",
+ callback=lambda item: self._callbackSet('graph', 'bounds', GraphBounds.TIGHT, ord('b')),
+ enabled=lambda: self._getItemEnabled('graph', 'bounds', GraphBounds.TIGHT)),
+ )),)),
+ menuItem.MenuItem(label="Connections", children=(
+ menuItem.MenuItem(label="Identity",
+ callback=lambda item: self._callbackPressKey('conn', ord('l'))),
+ menuItem.MenuItem(label="Resolver",
+ callback=lambda item: self._callbackPressKey('conn', ord('u'))),
+ menuItem.MenuItem(label="Sort Order",
+ callback=lambda item: self._callbackPressKey('conn', ord('s'))),
+ )),
+ menuItem.MenuItem(label="Configuration" , children=(
+ menuItem.MenuItem(label="Comments", children=(
+ menuItem.MenuItem(label="Hidden",
+ callback=lambda item: self._callbackSet('configFile', 'stripComments', True, ord('s')),
+ enabled=lambda: self._getItemEnabled('configFile', 'stripComments', True)),
+ menuItem.MenuItem(label="Visible",
+ callback=lambda item: self._callbackSet('configFile', 'stripComments', False, ord('s')),
+ enabled=lambda: self._getItemEnabled('configFile', 'stripComments', False)),
+ )),
+ menuItem.MenuItem(label="Reload",
+ callback=lambda item: self._callbackPressKey('configFile', ord('r'))),
+ menuItem.MenuItem(label="Reset Tor",
+ callback=lambda item: self._callbackReturnKey(ord('x'))),))
+ ))
+
+ self._first = [0]
+ self._selection = [0]
+
+ if item and item.isParent():
+ self._rootItem = item
+ else:
+ self._rootItem = DEFAULT_ROOT
+
+ def showMenu(self, keys=[]):
+ keys.reverse()
+ returnkeys = []
+
+ popup, width, height = cli.popups.init(height=3)
+ if popup:
+ try:
+ while True:
+ popup.win.erase()
+ popup.win.box()
+
+ self._drawTopLevel(popup, width, height)
+
+ popup.win.refresh()
+
+ control = cli.controller.getController()
+
+ if keys == []:
+ key = control.getScreen().getch()
+ else:
+ key = keys.pop()
+
+ if key == curses.KEY_RIGHT:
+ self._moveTopLevelRight(width)
+ elif key == curses.KEY_LEFT:
+ self._moveTopLevelLeft(width)
+ elif key == curses.KEY_DOWN:
+ cascaded, returnkeys = self._cascadeNLevel()
+ break
+ elif key == 27:
+ break
+ elif uiTools.isSelectionKey(key):
+ self._handleEvent()
+ break
+
+ finally:
+ cli.popups.finalize()
+
+ return returnkeys
+
+ def _appendLevel(self):
+ self._first.append(0)
+ self._selection.append(0)
+
+ def _removeLevel(self):
+ self._first.pop()
+ self._selection.pop()
+
+ def _getCurrentTopLevelItem(self):
+ index = self._first[TOPLEVEL] + self._selection[TOPLEVEL]
+ return self._rootItem.getChildren()[index]
+
+ def _getCurrentItem(self, level=0):
+ item = self._rootItem
+ if level == 0:
+ sums = [sum(values) for values in zip(self._first, self._selection)]
+ else:
+ sums = [sum(values) for values in zip(self._first[:level], self._selection[:level])]
+
+ for index in sums:
+ if item.isParent():
+ item = item.getChildren()[index]
+ else:
+ break
+
+ return item
+
+ 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 supplied width
+ printable = min(width / labelwidth - 1, self._rootItem.getChildrenCount())
+
+ return (labelwidth, printable)
+
+ def _calculateNLevelWidths(self, level=0):
+ parent = self._getCurrentItem(level)
+
+ if parent.isLeaf():
+ return 0
+
+ labels = [menuItem.getLabel() for menuItem in parent.getChildren()]
+
+ labelwidth = max(map(len, labels))
+
+ return labelwidth
+
+ def _calculateNLevelHeights(self, height=0, level=0):
+ control = cli.controller.getController()
+ height, _ = control.getScreen().getmaxyx()
+ topSize = sum(stickyPanel.getHeight() for stickyPanel in control.getStickyPanels())
+ height = height - topSize
+
+ parent = self._getCurrentItem(level)
+
+ if parent.isLeaf():
+ return 0
+
+ printable = min(height - 4, parent.getChildrenCount())
+
+ return printable if printable else parent.getChildrenCount()
+
+ def _moveTopLevelRight(self, width):
+ _, printable = self._calculateTopLevelWidths(width)
+
+ if self._selection[TOPLEVEL] < printable - 1:
+ self._selection[TOPLEVEL] = self._selection[TOPLEVEL] + 1
+ else:
+ self._selection[TOPLEVEL] = 0
+ if printable < self._rootItem.getChildrenCount():
+ self._first[TOPLEVEL] = (self._first[TOPLEVEL] + printable) % self._rootItem.getChildrenCount()
+
+ if self._first[TOPLEVEL] + self._selection[TOPLEVEL] == self._rootItem.getChildrenCount():
+ self._first[TOPLEVEL] = 0
+ self._selection[TOPLEVEL] = 0
+
+ def _moveTopLevelLeft(self, width):
+ _, printable = self._calculateTopLevelWidths(width)
+
+ if self._selection[TOPLEVEL] > 0:
+ self._selection[TOPLEVEL] = self._selection[TOPLEVEL] - 1
+ else:
+ if self._first[TOPLEVEL] == 0:
+ 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
+
+ if self._selection[TOPLEVEL] > printable:
+ self._selection[TOPLEVEL] = printable - 1
+
+ def _drawTopLevel(self, popup, width, height):
+ labelwidth, printable = self._calculateTopLevelWidths(width)
+ children = self._rootItem.getChildren()[self._first[TOPLEVEL]:self._first[TOPLEVEL] + printable]
+
+ top = 1
+ left = 1
+ for (index, item) in enumerate(children):
+ labelformat = curses.A_STANDOUT if index == self._selection[TOPLEVEL] else curses.A_NORMAL
+
+ popup.addch(top, left, curses.ACS_VLINE)
+ left = left + 1
+ popup.addstr(top, left, item.getLabel().center(labelwidth), labelformat)
+ left = left + labelwidth
+
+ popup.addch(top, left, curses.ACS_VLINE)
+ left = left + 1
+
+ def _cascadeNLevel(self):
+ parent = self._getCurrentItem()
+
+ if parent.isLeaf():
+ return (False, [])
+
+ self._appendLevel()
+
+ labelwidth = self._calculateNLevelWidths(level=PARENTLEVEL)
+ printable = self._calculateNLevelHeights(level=PARENTLEVEL)
+
+ toplabelwidth, _ = self._calculateTopLevelWidths()
+ left = (toplabelwidth + 2) * self._selection[TOPLEVEL]
+
+ popup, width, height = cli.popups.init(height=printable+2, width=labelwidth+2, top=2, left=left)
+
+ while self._getCurrentItem().isEnabled() == False:
+ self._moveNLevelDown(height)
+
+ if popup.win:
+ returnkeys = []
+ try:
+ while True:
+ popup.win.erase()
+ popup.win.box()
+
+ self._drawNLevel(popup, width, height)
+
+ popup.win.refresh()
+
+ control = cli.controller.getController()
+ key = control.getScreen().getch()
+
+ if key == curses.KEY_DOWN:
+ self._moveNLevelDown(height)
+ elif key == curses.KEY_UP:
+ self._moveNLevelUp(height)
+ elif key == curses.KEY_RIGHT:
+ cascaded, returnkeys = self._cascadeNLevel()
+ if cascaded == False:
+ index = self._first[TOPLEVEL] + self._selection[TOPLEVEL] + 1
+ returnkeys.append(ord('n'))
+ for i in range(index):
+ returnkeys.append(curses.KEY_RIGHT)
+ returnkeys.append(curses.KEY_DOWN)
+ break
+ elif key == curses.KEY_LEFT:
+ index = self._first[TOPLEVEL] + self._selection[TOPLEVEL] - 1
+ index = index % self._rootItem.getChildrenCount()
+ returnkeys.append(ord('n'))
+ for i in range(index):
+ returnkeys.append(curses.KEY_RIGHT)
+ returnkeys.append(curses.KEY_DOWN)
+ break
+ elif key == 27:
+ self._removeLevel()
+ break
+ elif uiTools.isSelectionKey(key):
+ returnkey = self._handleEvent()
+ if returnkey:
+ returnkeys.append(returnkey)
+ self._removeLevel()
+ break
+
+ finally:
+ cli.popups.finalize()
+
+ return (True, returnkeys)
+
+ return (False, [])
+
+ def _drawNLevel(self, popup, width, height):
+ printable = self._calculateNLevelHeights(level=PARENTLEVEL)
+ parent = self._getCurrentItem(level=PARENTLEVEL)
+ children = parent.getChildren()[self._first[PARENTLEVEL]:self._first[PARENTLEVEL] + printable]
+
+ top = 1
+ left = 1
+ for (index, item) in enumerate(children):
+ labelformat = curses.A_STANDOUT if index == self._selection[PARENTLEVEL] else curses.A_NORMAL
+
+ if not item.isEnabled():
+ labelformat = labelformat | uiTools.getColor('yellow')
+
+ popup.addstr(top, left, item.getLabel(), labelformat)
+ top = top + 1
+
+ def _moveNLevelDown(self, height):
+ printable = self._calculateNLevelHeights(level=PARENTLEVEL)
+ parent = self._getCurrentItem(level=PARENTLEVEL)
+
+ if self._selection[PARENTLEVEL] < printable - 1:
+ self._selection[PARENTLEVEL] = self._selection[PARENTLEVEL] + 1
+ else:
+ self._selection[PARENTLEVEL] = 0
+ if printable < parent.getChildrenCount():
+ self._first[PARENTLEVEL] = (self._first[PARENTLEVEL] + printable) % parent.getChildrenCount()
+
+ if self._first[PARENTLEVEL] + self._selection[PARENTLEVEL] == parent.getChildrenCount():
+ self._first[PARENTLEVEL] = 0
+ self._selection[PARENTLEVEL] = 0
+
+ while self._getCurrentItem().isEnabled() == False:
+ self._moveNLevelDown(height)
+
+ def _moveNLevelUp(self, height):
+ printable = self._calculateNLevelHeights(level=PARENTLEVEL)
+ parent = self._getCurrentItem(level=PARENTLEVEL)
+
+ if self._selection[PARENTLEVEL] > 0:
+ self._selection[PARENTLEVEL] = self._selection[PARENTLEVEL] - 1
+ else:
+ if self._first[PARENTLEVEL] == 0:
+ self._first[PARENTLEVEL] = ((parent.getChildrenCount() / printable) * printable) % parent.getChildrenCount()
+ else:
+ self._first[PARENTLEVEL] = abs(self._first[PARENTLEVEL] - printable) % parent.getChildrenCount()
+ self._selection[PARENTLEVEL] = parent.getChildrenCount() - self._first[PARENTLEVEL] - 1
+
+ if self._selection[PARENTLEVEL] > printable:
+ self._selection[PARENTLEVEL] = printable - 1
+
+ while self._getCurrentItem().isEnabled() == False:
+ self._moveNLevelUp(height)
+
+ def _handleEvent(self):
+ item = self._getCurrentItem()
+
+ if item.isLeaf():
+ return item.select()
+ else:
+ self._cascadeNLevel()
+
+ def _callbackDefault(self, item):
+ log.log(log.NOTICE, "%s selected" % item.getLabel())
+
+ def _callbackView(self, panelname):
+ control = cli.controller.getController()
+
+ start = control.getPage()
+ panels = control.getDisplayPanels(includeSticky=False)
+ panelnames = [panel.getName() for panel in panels]
+ while not panelname in panelnames:
+ control.nextPage()
+ panels = control.getDisplayPanels(includeSticky=False)
+ panelnames = [panel.getName() for panel in panels]
+
+ if control.getPage() == start:
+ log.log(log.ERR, "Panel %s not found" % panelname)
+ break
+
+ def _getItemEnabled(self, panel, attr, value):
+ control = cli.controller.getController()
+ if control:
+ panel = control.getPanel(panel)
+
+ if panel:
+ return getattr(panel, attr, None) != value
+
+ return False
+
+ def _callbackSet(self, panel, attr, value, key=None):
+ control = cli.controller.getController()
+ panel = control.getPanel(panel)
+
+ panelattr = getattr(panel, attr, None)
+
+ if panelattr != None:
+ if hasattr(panelattr, '__call__'):
+ panelattr(value)
+ elif panelattr != value and key != None:
+ start = panelattr
+ while panelattr != value:
+ panel.handleKey(key)
+ panelattr = getattr(panel, attr, None)
+ if panelattr == start:
+ log.log(log.ERR, "Could not set %s.%s" % (panel, attr))
+ break
+
+ def _callbackPressKey(self, panel, key):
+ control = cli.controller.getController()
+ panel = control.getPanel(panel)
+ panel.handleKey(key)
+
+ def _callbackReturnKey(self, key):
+ return key
+
diff --git a/src/cli/menu_alt/menuItem.py b/src/cli/menu_alt/menuItem.py
new file mode 100644
index 0000000..277c78a
--- /dev/null
+++ b/src/cli/menu_alt/menuItem.py
@@ -0,0 +1,41 @@
+"""
+Menu Item class, used by the drop-down menus.
+"""
+
+class MenuItem():
+ """
+ Contains title, callback handler and possible children.
+ """
+
+ def __init__(self, label=None, callback=None, children=[], enabled=None):
+ self._label = label
+ self._callback = callback
+ self._children = children
+ self._enabled = enabled
+
+ def getLabel(self):
+ return self._label
+
+ def isLeaf(self):
+ return self._children == []
+
+ def isParent(self):
+ return self._children != []
+
+ def isEnabled(self):
+ if self._enabled == None:
+ return True
+ elif hasattr(self._enabled, '__call__'):
+ return self._enabled()
+ else:
+ return self._enabled
+
+ def getChildren(self):
+ return self._children
+
+ def getChildrenCount(self):
+ return len(self._children)
+
+ def select(self):
+ return self._callback(self)
+
diff --git a/src/util/menuItem.py b/src/util/menuItem.py
deleted file mode 100644
index 277c78a..0000000
--- a/src/util/menuItem.py
+++ /dev/null
@@ -1,41 +0,0 @@
-"""
-Menu Item class, used by the drop-down menus.
-"""
-
-class MenuItem():
- """
- Contains title, callback handler and possible children.
- """
-
- def __init__(self, label=None, callback=None, children=[], enabled=None):
- self._label = label
- self._callback = callback
- self._children = children
- self._enabled = enabled
-
- def getLabel(self):
- return self._label
-
- def isLeaf(self):
- return self._children == []
-
- def isParent(self):
- return self._children != []
-
- def isEnabled(self):
- if self._enabled == None:
- return True
- elif hasattr(self._enabled, '__call__'):
- return self._enabled()
- else:
- return self._enabled
-
- def getChildren(self):
- return self._children
-
- def getChildrenCount(self):
- return len(self._children)
-
- def select(self):
- return self._callback(self)
-
1
0
commit c1aecf68b13bf2eb12a104b98dab9dde6a2a46f0
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Jun 4 13:52:09 2011 -0700
Alternative menu implementation
This is a smaller and (I think) simpler implementation of a menu. Its model
and presentation logic are done so the menu can be opened and used, but
handlers haven't been attached yet so nothing happens when a selection is
made.
---
README | 5 +
src/cli/controller.py | 5 +-
src/cli/menu/__init__.py | 6 ++
src/cli/menu/item.py | 146 ++++++++++++++++++++++++++++++
src/cli/menu/menu.py | 222 ++++++++++++++++++++++++++++++++++++++++++++++
src/cli/popups.py | 15 ++--
src/util/panel.py | 2 +-
7 files changed, 393 insertions(+), 8 deletions(-)
diff --git a/README b/README
index b20ec1c..0f26163 100644
--- a/README
+++ b/README
@@ -176,6 +176,11 @@ Layout:
connEntry.py - individual connections to or from the system
entries.py - common parent for connPanel display entries
+ menu/
+ __init__.py
+ menu.py - provides an interactive menu
+ item.py - individual items within the menu
+
__init__.py
controller.py - main display loop, handling input and layout
headerPanel.py - top of all pages, providing general information
diff --git a/src/cli/controller.py b/src/cli/controller.py
index fcbaed5..fac43a4 100644
--- a/src/cli/controller.py
+++ b/src/cli/controller.py
@@ -8,6 +8,7 @@ import curses
import threading
import cli.menu
+import cli.menu.menu
import cli.popups
import cli.headerPanel
import cli.logPanel
@@ -510,7 +511,7 @@ 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'):
+ elif key == ord('n') or key == ord('N'):
menu = cli.menu.Menu()
menuKeys = menu.showMenu(keys=menuKeys)
if menuKeys != []:
@@ -518,6 +519,8 @@ def drawTorMonitor(stdscr, startTime):
if key in menuKeys:
menuKeys.remove(key)
overrideKey = key
+ elif key == ord('m') or key == ord('M'):
+ cli.menu.menu.showMenu()
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/__init__.py b/src/cli/menu/__init__.py
new file mode 100644
index 0000000..e6b5f10
--- /dev/null
+++ b/src/cli/menu/__init__.py
@@ -0,0 +1,6 @@
+"""
+Resources for displaying the menu.
+"""
+
+__all__ = ["item", "menu"]
+
diff --git a/src/cli/menu/item.py b/src/cli/menu/item.py
new file mode 100644
index 0000000..bfc7ffd
--- /dev/null
+++ b/src/cli/menu/item.py
@@ -0,0 +1,146 @@
+"""
+Menu item, representing an option in the drop-down menu.
+"""
+
+class MenuItem():
+ """
+ Option in a drop-down menu.
+ """
+
+ def __init__(self, label, callback):
+ self._label = label
+ self._callback = callback
+ self._parent = None
+
+ def getLabel(self):
+ """
+ Provides a tuple of three strings representing the prefix, label, and
+ suffix for this item.
+ """
+
+ return ("", self._label, "")
+
+ def getParent(self):
+ """
+ Provides the Submenu we're contained within.
+ """
+
+ return self._parent
+
+ def getHierarchy(self):
+ """
+ Provides a list with all of our parents, up to the root.
+ """
+
+ myHierarchy = [self]
+ while myHierarchy[-1].getParent():
+ myHierarchy.append(myHierarchy[-1].getParent())
+
+ myHierarchy.reverse()
+ return myHierarchy
+
+ def getRoot(self):
+ """
+ Provides the base submenu we belong to.
+ """
+
+ if self._parent: return self._parent.getRoot()
+ else: return self
+
+ def select(self):
+ """
+ Performs the callback for the menu item, returning true if we should close
+ the menu and false otherwise.
+ """
+
+ if self._callback: return self._callback()
+ else: return False
+
+ def next(self):
+ """
+ Provides the next option for the submenu we're in, raising a ValueError
+ if we don't have a parent.
+ """
+
+ return self._getSibling(1)
+
+ def prev(self):
+ """
+ Provides the previous option for the submenu we're in, raising a ValueError
+ if we don't have a parent.
+ """
+
+ return self._getSibling(-1)
+
+ def _getSibling(self, offset):
+ """
+ Provides our sibling with a given index offset from us, raising a
+ ValueError if we don't have a parent.
+
+ Arguments:
+ offset - index offset for the sibling to be returned
+ """
+
+ if self._parent:
+ mySiblings = self._parent.getChildren()
+
+ try:
+ myIndex = mySiblings.index(self)
+ return mySiblings[(myIndex + offset) % len(mySiblings)]
+ except ValueError:
+ # We expect a bidirectional references between submenus and their
+ # children. If we don't have this then our menu's screwed up.
+
+ msg = "The '%s' submenu doesn't contain '%s' (children: '%s')" % (self, self._parent, "', '".join(mySiblings))
+ raise ValueError(msg)
+ else: raise ValueError("Menu option '%s' doesn't have a parent" % self)
+
+ def __str__(self):
+ return self._label
+
+class Submenu(MenuItem):
+ """
+ Menu item that lists other menu options.
+ """
+
+ def __init__(self, label):
+ MenuItem.__init__(self, label, None)
+ self._children = []
+
+ def getLabel(self):
+ """
+ Provides our label with a ">" suffix to indicate that we have suboptions.
+ """
+
+ myLabel = MenuItem.getLabel(self)[1]
+ return ("", myLabel, " >")
+
+ def add(self, menuItem):
+ """
+ Adds the given menu item to our listing. This raises a ValueError if the
+ item already has a parent.
+
+ Arguments:
+ menuItem - menu option to be added
+ """
+
+ if menuItem.getParent():
+ raise ValueError("Menu option '%s' already has a parent" % menuItem)
+ else:
+ menuItem._parent = self
+ self._children.append(menuItem)
+
+ def getChildren(self):
+ """
+ Provides the menu and submenus we contain.
+ """
+
+ return list(self._children)
+
+ def isEmpty(self):
+ """
+ True if we have no children, false otherwise.
+ """
+
+ return not bool(self._children)
+
diff --git a/src/cli/menu/menu.py b/src/cli/menu/menu.py
new file mode 100644
index 0000000..02f87ee
--- /dev/null
+++ b/src/cli/menu/menu.py
@@ -0,0 +1,222 @@
+
+import curses
+
+import cli.popups
+import cli.controller
+import cli.menu.item
+
+from util import uiTools
+
+def makeMenu():
+ """
+ Constructs the base menu and all of its contents.
+ """
+
+ baseMenu = cli.menu.item.Submenu("")
+
+ fileMenu = cli.menu.item.Submenu("File")
+ fileMenu.add(cli.menu.item.MenuItem("Exit", None))
+ baseMenu.add(fileMenu)
+
+ logsMenu = cli.menu.item.Submenu("Logs")
+ logsMenu.add(cli.menu.item.MenuItem("Events", None))
+ logsMenu.add(cli.menu.item.MenuItem("Clear", None))
+ logsMenu.add(cli.menu.item.MenuItem("Save", None))
+ logsMenu.add(cli.menu.item.MenuItem("Filter", None))
+
+ duplicatesSubmenu = cli.menu.item.Submenu("Duplicates")
+ duplicatesSubmenu.add(cli.menu.item.MenuItem("Hidden", None))
+ duplicatesSubmenu.add(cli.menu.item.MenuItem("Visible", None))
+ 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))
+
+ sizeSubmenu = cli.menu.item.Submenu("Size")
+ sizeSubmenu.add(cli.menu.item.MenuItem("Increase", None))
+ sizeSubmenu.add(cli.menu.item.MenuItem("Decrease", None))
+ graphMenu.add(sizeSubmenu)
+
+ graphMenu.add(cli.menu.item.MenuItem("Update Interval", None))
+
+ boundsSubmenu = cli.menu.item.Submenu("Bounds")
+ boundsSubmenu.add(cli.menu.item.MenuItem("Local Max", None))
+ boundsSubmenu.add(cli.menu.item.MenuItem("Global Max", None))
+ boundsSubmenu.add(cli.menu.item.MenuItem("Tight", None))
+ graphMenu.add(boundsSubmenu)
+ baseMenu.add(graphMenu)
+
+ connectionsMenu = cli.menu.item.Submenu("Connections")
+ connectionsMenu.add(cli.menu.item.MenuItem("Identity", None))
+ connectionsMenu.add(cli.menu.item.MenuItem("Resolver", None))
+ connectionsMenu.add(cli.menu.item.MenuItem("Sort Order", None))
+ baseMenu.add(connectionsMenu)
+
+ configurationMenu = cli.menu.item.Submenu("Configuration")
+
+ commentsSubmenu = cli.menu.item.Submenu("Comments")
+ commentsSubmenu.add(cli.menu.item.MenuItem("Hidden", None))
+ commentsSubmenu.add(cli.menu.item.MenuItem("Visible", None))
+ configurationMenu.add(commentsSubmenu)
+
+ configurationMenu.add(cli.menu.item.MenuItem("Reload", None))
+ configurationMenu.add(cli.menu.item.MenuItem("Reset Tor", None))
+ baseMenu.add(configurationMenu)
+
+ return baseMenu
+
+class MenuCursor:
+ """
+ Tracks selection and key handling in the menu.
+ """
+
+ def __init__(self, initialSelection):
+ self._selection = initialSelection
+ self._isDone = False
+
+ def isDone(self):
+ """
+ Provides true if a selection has indicated that we should close the menu.
+ False otherwise.
+ """
+
+ return self._isDone
+
+ def getSelection(self):
+ """
+ Provides the currently selected menu item.
+ """
+
+ return self._selection
+
+ def handleKey(self, key):
+ isSelectionSubmenu = isinstance(self._selection, cli.menu.item.Submenu)
+ selectionHierarchy = self._selection.getHierarchy()
+
+ if uiTools.isSelectionKey(key):
+ if isSelectionSubmenu:
+ if not self._selection.isEmpty():
+ self._selection = self._selection.getChildren()[0]
+ else: self._isDone = self._selection.select()
+ elif key == curses.KEY_UP:
+ self._selection = self._selection.prev()
+ elif key == curses.KEY_DOWN:
+ self._selection = self._selection.next()
+ elif key == curses.KEY_LEFT:
+ if len(selectionHierarchy) <= 3:
+ # shift to the previous main submenu
+ prevSubmenu = selectionHierarchy[1].prev()
+ self._selection = prevSubmenu.getChildren()[0]
+ else:
+ # go up a submenu level
+ self._selection = self._selection.getParent()
+ elif key == curses.KEY_RIGHT:
+ if isSelectionSubmenu:
+ # open submenu (same as making a selection)
+ if not self._selection.isEmpty():
+ self._selection = self._selection.getChildren()[0]
+ else:
+ # shift to the next main submenu
+ nextSubmenu = selectionHierarchy[1].next()
+ self._selection = nextSubmenu.getChildren()[0]
+ elif key in (27, ord('m'), ord('M')):
+ # close menu
+ self._isDone = True
+
+def showMenu():
+ popup, _, _ = cli.popups.init(1, belowStatic = False)
+ if not popup: return
+ control = cli.controller.getController()
+
+ try:
+ # generates the menu and uses the initial selection of the first item in
+ # the file menu
+ menu = makeMenu()
+ cursor = MenuCursor(menu.getChildren()[0].getChildren()[0])
+
+ while not cursor.isDone():
+ # sets the background color
+ popup.win.clear()
+ popup.win.bkgd(' ', curses.A_STANDOUT | uiTools.getColor("red"))
+ selectionHierarchy = cursor.getSelection().getHierarchy()
+
+ # renders the menu bar, noting where the open submenu is positioned
+ drawLeft, selectionLeft = 0, 0
+
+ for topLevelItem in menu.getChildren():
+ drawFormat = curses.A_BOLD
+ if topLevelItem == selectionHierarchy[1]:
+ drawFormat |= curses.A_UNDERLINE
+ selectionLeft = drawLeft
+
+ drawLabel = " %s " % topLevelItem.getLabel()[1]
+ popup.addstr(0, drawLeft, drawLabel, drawFormat)
+ popup.addch(0, drawLeft + len(drawLabel), curses.ACS_VLINE)
+
+ drawLeft += len(drawLabel) + 1
+
+ # recursively shows opened submenus
+ _drawSubmenu(cursor, 1, 1, selectionLeft)
+
+ popup.win.refresh()
+
+ key = control.getScreen().getch()
+ cursor.handleKey(key)
+
+ # redraws the rest of the interface if we're rendering on it again
+ if not cursor.isDone():
+ for panelImpl in control.getDisplayPanels():
+ panelImpl.redraw(True)
+ finally: cli.popups.finalize()
+
+def _drawSubmenu(cursor, level, top, left):
+ selectionHierarchy = cursor.getSelection().getHierarchy()
+
+ # checks if there's nothing to display
+ if len(selectionHierarchy) < level + 2: return
+
+ # fetches the submenu and selection we're displaying
+ submenu = selectionHierarchy[level]
+ selection = selectionHierarchy[level + 1]
+
+ # gets the size of the prefix, middle, and suffix columns
+ allLabelSets = [entry.getLabel() for entry in submenu.getChildren()]
+ prefixColSize = max([len(entry[0]) for entry in allLabelSets])
+ middleColSize = max([len(entry[1]) for entry in allLabelSets])
+ suffixColSize = max([len(entry[2]) for entry in allLabelSets])
+
+ # formatted string so we can display aligned menu entries
+ labelFormat = " %%-%is%%-%is%%-%is " % (prefixColSize, middleColSize, suffixColSize)
+ menuWidth = len(labelFormat % ("", "", ""))
+
+ popup, _, _ = cli.popups.init(len(submenu.getChildren()), menuWidth, top, left, belowStatic = False)
+ if not popup: return
+
+ try:
+ # sets the background color
+ popup.win.bkgd(' ', curses.A_STANDOUT | uiTools.getColor("red"))
+
+ drawTop, selectionTop = 0, 0
+ for menuItem in submenu.getChildren():
+ if menuItem == selection:
+ drawFormat = curses.A_BOLD | uiTools.getColor("white")
+ selectionTop = drawTop
+ else: drawFormat = curses.A_NORMAL
+
+ popup.addstr(drawTop, 0, labelFormat % menuItem.getLabel(), drawFormat)
+ drawTop += 1
+
+ popup.win.refresh()
+
+ # shows the next submenu
+ _drawSubmenu(cursor, level + 1, top + selectionTop, left + menuWidth)
+ finally: cli.popups.finalize()
+
diff --git a/src/cli/popups.py b/src/cli/popups.py
index 0e50aa1..b90f95d 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, top = 0, left = 0):
+def init(height = -1, width = -1, top = 0, left = 0, belowStatic = True):
"""
Preparation for displaying a popup. This creates a popup with a valid
subwindow instance. If that's successful then the curses lock is acquired
@@ -17,14 +17,17 @@ def init(height = -1, width = -1, top = 0, left = 0):
Otherwise this leaves curses unlocked and returns None.
Arguments:
- height - maximum height of the popup
- width - maximum width of the popup
- top - top position, relative to the sticky content
- left - left position from the screen
+ height - maximum height of the popup
+ width - maximum width of the popup
+ top - top position, relative to the sticky content
+ left - left position from the screen
+ belowStatic - positions popup below static content if true
"""
control = cli.controller.getController()
- stickyHeight = sum(stickyPanel.getHeight() for stickyPanel in control.getStickyPanels())
+ if belowStatic:
+ stickyHeight = sum(stickyPanel.getHeight() for stickyPanel in control.getStickyPanels())
+ else: stickyHeight = 0
popup = panel.Panel(control.getScreen(), "popup", top + stickyHeight, left, height, width)
popup.setVisible(True)
diff --git a/src/util/panel.py b/src/util/panel.py
index 93b44c2..6fae341 100644
--- a/src/util/panel.py
+++ b/src/util/panel.py
@@ -484,7 +484,7 @@ class Panel():
# direction) from actual content to prevent crash when shrank
if self.win and self.maxX > x and self.maxY > y:
try:
- self.win.addstr(y, x, msg[:self.maxX - x - 1], attr)
+ self.win.addstr(y, x, msg[:self.maxX - x], attr)
except:
# this might produce a _curses.error during edge cases, for instance
# when resizing with visible popups
1
0