commit 46a1023c71f395c62a37a30c13e14db7b851640a Author: Kamran Riaz Khan krkhan@inspirated.com Date: Tue May 31 02:31:23 2011 +0500
Added support for multiple menu levels.
Once the menus have "dropped-down", handling of subsequent "levels" followed the same logical steps. Therefore *SecondLevel() methods have been replaced by *NLevel() equivalents.
If a parent item is selected using any of the selection keys, its children are brought up instead of doing nothing. --- src/cli/menu.py | 192 +++++++++++++++++++++++++++++++++++--------------- src/util/menuItem.py | 6 +-- 2 files changed, 136 insertions(+), 62 deletions(-)
diff --git a/src/cli/menu.py b/src/cli/menu.py index 69538e0..8ce97c4 100644 --- a/src/cli/menu.py +++ b/src/cli/menu.py @@ -8,7 +8,7 @@ import cli.controller import popups from util import log, panel, uiTools, menuItem
-TOPLEVEL, SECONDLEVEL = range(2) +PARENTLEVEL, TOPLEVEL = (-1, 0)
class Menu(): """Displays a popup menu and sends keys to appropriate panels""" @@ -22,23 +22,61 @@ class Menu(): menuItem.MenuItem(label="Clear" , callback=self._callbackDefault), menuItem.MenuItem(label="Save" , callback=self._callbackDefault), menuItem.MenuItem(label="Filter" , callback=self._callbackDefault), - menuItem.MenuItem(label="Duplicates" , callback=self._callbackDefault),)), + menuItem.MenuItem(label="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" , callback=self._callbackDefault), - menuItem.MenuItem(label="Size" , callback=self._callbackDefault), - menuItem.MenuItem(label="Update Interval" , callback=self._callbackDefault), - menuItem.MenuItem(label="Bounds" , callback=self._callbackDefault),)), + menuItem.MenuItem(label="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="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="Connections" , children=( - menuItem.MenuItem(label="Identity" , callback=self._callbackDefault), - menuItem.MenuItem(label="Resolver" , callback=self._callbackDefault), + 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="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" , callback=self._callbackDefault), + menuItem.MenuItem(label="Comments" , children=( + menuItem.MenuItem(label="Hidden" , callback=self._callbackDefault), + menuItem.MenuItem(label="Visible" , callback=self._callbackDefault), + )), menuItem.MenuItem(label="Reload" , callback=self._callbackDefault), menuItem.MenuItem(label="Reset Tor" , callback=self._callbackDefault),)) )) @@ -67,15 +105,12 @@ class Menu(): key = control.getScreen().getch()
if key == curses.KEY_RIGHT: - if len(self._selection) == 1: - self._moveTopLevelRight(width) + self._moveTopLevelRight(width) elif key == curses.KEY_LEFT: - if len(self._selection) == 1: - self._moveTopLevelLeft(width) + self._moveTopLevelLeft(width) elif key == curses.KEY_DOWN: - if len(self._selection) == 1: - self._dropSecondLevel() - break + self._showNLevel() + break elif uiTools.isSelectionKey(key): self._handleEvent() break @@ -87,6 +122,21 @@ class Menu(): 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()]
@@ -119,6 +169,33 @@ class Menu():
return printable if printable else parent.getChildrenCount()
+ 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)
@@ -165,12 +242,17 @@ class Menu(): popup.addch(top, left, curses.ACS_VLINE) left = left + 1
- def _dropSecondLevel(self): + def _showNLevel(self): self._first.append(0) self._selection.append(0)
- labelwidth = self._calculateSecondLevelWidths() - printable = self._calculateSecondLevelHeights() + parent = self._getCurrentItem(level=PARENTLEVEL) + + if parent.isLeaf(): + return + + labelwidth = self._calculateNLevelWidths(level=PARENTLEVEL) + printable = self._calculateNLevelHeights(level=PARENTLEVEL)
toplabelwidth, _ = self._calculateTopLevelWidths() left = (toplabelwidth + 2) * self._selection[TOPLEVEL] @@ -183,7 +265,7 @@ class Menu(): popup.win.erase() popup.win.box()
- self._drawSecondLevel(popup, width, height) + self._drawNLevel(popup, width, height)
popup.win.refresh()
@@ -191,9 +273,12 @@ class Menu(): key = control.getScreen().getch()
if key == curses.KEY_DOWN: - self._moveSecondLevelDown(height) + self._moveNLevelDown(height) elif key == curses.KEY_UP: - self._moveSecondLevelUp(height) + self._moveNLevelUp(height) + elif key == curses.KEY_RIGHT: + self._showNLevel() + break elif uiTools.isSelectionKey(key): self._handleEvent() self._first.pop() @@ -203,64 +288,57 @@ class Menu(): finally: popups.finalize()
- def _drawSecondLevel(self, popup, width, height): - printable = self._calculateSecondLevelHeights() - parent = self._getCurrentTopLevelItem() - children = parent.getChildren()[self._first[SECONDLEVEL]:self._first[SECONDLEVEL] + printable] - - toplabelwidth, _ = self._calculateTopLevelWidths(width) + 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[SECONDLEVEL] else curses.A_NORMAL + labelformat = curses.A_STANDOUT if index == self._selection[PARENTLEVEL] else curses.A_NORMAL
popup.addstr(top, left, item.getLabel(), labelformat) top = top + 1
- def _moveSecondLevelDown(self, height): - printable = self._calculateSecondLevelHeights() - parent = self._getCurrentTopLevelItem() + def _moveNLevelDown(self, height): + printable = self._calculateNLevelHeights(level=PARENTLEVEL) + parent = self._getCurrentItem(level=PARENTLEVEL)
- if self._selection[SECONDLEVEL] < printable - 1: - self._selection[SECONDLEVEL] = self._selection[SECONDLEVEL] + 1 + if self._selection[PARENTLEVEL] < printable - 1: + self._selection[PARENTLEVEL] = self._selection[PARENTLEVEL] + 1 else: - self._selection[SECONDLEVEL] = 0 + self._selection[PARENTLEVEL] = 0 if printable < parent.getChildrenCount(): - self._first[SECONDLEVEL] = (self._first[SECONDLEVEL] + printable) % parent.getChildrenCount() + self._first[PARENTLEVEL] = (self._first[PARENTLEVEL] + printable) % parent.getChildrenCount()
- if self._first[SECONDLEVEL] + self._selection[SECONDLEVEL] == parent.getChildrenCount(): - self._first[SECONDLEVEL] = 0 - self._selection[SECONDLEVEL] = 0 + if self._first[PARENTLEVEL] + self._selection[PARENTLEVEL] == parent.getChildrenCount(): + self._first[PARENTLEVEL] = 0 + self._selection[PARENTLEVEL] = 0
- def _moveSecondLevelUp(self, height): + def _moveNLevelUp(self, height): printable = self._calculateSecondLevelHeights() - parent = self._getCurrentTopLevelItem() + parent = self._getCurrentItem(level=PARENTLEVEL)
- if self._selection[SECONDLEVEL] > 0: - self._selection[SECONDLEVEL] = self._selection[SECONDLEVEL] - 1 + if self._selection[PARENTLEVEL] > 0: + self._selection[PARENTLEVEL] = self._selection[PARENTLEVEL] - 1 else: - if self._first[SECONDLEVEL] == 0: - self._first[SECONDLEVEL] = ((parent.getChildrenCount() / printable) * printable) % parent.getChildrenCount() + if self._first[PARENTLEVEL] == 0: + self._first[PARENTLEVEL] = ((parent.getChildrenCount() / printable) * printable) % parent.getChildrenCount() else: - self._first[SECONDLEVEL] = abs(self._first[SECONDLEVEL] - printable) % parent.getChildrenCount() - self._selection[SECONDLEVEL] = parent.getChildrenCount() - self._first[SECONDLEVEL] - 1 + self._first[PARENTLEVEL] = abs(self._first[PARENTLEVEL] - printable) % parent.getChildrenCount() + self._selection[PARENTLEVEL] = parent.getChildrenCount() - self._first[PARENTLEVEL] - 1
- if self._selection[SECONDLEVEL] > printable: - self._selection[SECONDLEVEL] = printable - 1 + if self._selection[PARENTLEVEL] > printable: + self._selection[PARENTLEVEL] = printable - 1
def _handleEvent(self): - item = self._rootItem - sums = [sum(values) for values in zip(self._first, self._selection)] - - for index in sums: - if item.isParent(): - item = item.getChildren()[index] - else: - break + item = self._getCurrentItem()
if item.isLeaf(): item.select() + else: + self._showNLevel()
def _callbackDefault(self, item): log.log(log.NOTICE, "%s selected" % item.getLabel()) diff --git a/src/util/menuItem.py b/src/util/menuItem.py index 4800a0f..3dbf922 100644 --- a/src/util/menuItem.py +++ b/src/util/menuItem.py @@ -5,11 +5,10 @@ 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=[], parent=None): + def __init__(self, label=None, callback=None, children=[]): self._label = label self._callback = callback self._children = children - self._parent = parent
def getLabel(self): return self._label @@ -23,9 +22,6 @@ class MenuItem(): def getChildren(self): return self._children
- def getParent(self): - return self._parent - def getChildrenCount(self): return len(self._children)
tor-commits@lists.torproject.org