tor-commits
Threads by month
- ----- 2025 -----
- November
- October
- September
- August
- July
- 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
- 1 participants
- 214318 discussions
17 Jul '11
commit af0580268ef996be857eb8300aecf1848894b724
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Jun 11 18:53:02 2011 -0700
Moving menu generation into a dedicated file
---
src/cli/menu/__init__.py | 2 +-
src/cli/menu/actions.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++
src/cli/menu/menu.py | 69 +------------------------------------------
3 files changed, 76 insertions(+), 68 deletions(-)
diff --git a/src/cli/menu/__init__.py b/src/cli/menu/__init__.py
index e6b5f10..f6d43ec 100644
--- a/src/cli/menu/__init__.py
+++ b/src/cli/menu/__init__.py
@@ -2,5 +2,5 @@
Resources for displaying the menu.
"""
-__all__ = ["item", "menu"]
+__all__ = ["actions", "item", "menu"]
diff --git a/src/cli/menu/actions.py b/src/cli/menu/actions.py
new file mode 100644
index 0000000..f6f88ba
--- /dev/null
+++ b/src/cli/menu/actions.py
@@ -0,0 +1,73 @@
+"""
+Generates the menu for arm, binding options with their related actions.
+"""
+
+import cli.menu.item
+
+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
+
+
diff --git a/src/cli/menu/menu.py b/src/cli/menu/menu.py
index f83b76d..e0728e8 100644
--- a/src/cli/menu/menu.py
+++ b/src/cli/menu/menu.py
@@ -4,75 +4,10 @@ import curses
import cli.popups
import cli.controller
import cli.menu.item
+import cli.menu.actions
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.
@@ -139,7 +74,7 @@ def showMenu():
try:
# generates the menu and uses the initial selection of the first item in
# the file menu
- menu = makeMenu()
+ menu = cli.menu.actions.makeMenu()
cursor = MenuCursor(menu.getChildren()[0].getChildren()[0])
while not cursor.isDone():
1
0
commit 3cc2ecf82d693a782c11d5a4f6fb41e94e7b5d05
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Jun 11 20:55:34 2011 -0700
Binding handlers for the view submenu
Submenu consisting of page and color selection.
---
src/cli/configPanel.py | 2 +-
src/cli/connections/connPanel.py | 2 +-
src/cli/controller.py | 48 +++++++++++++++++++++++++++----------
src/cli/descriptorPopup.py | 2 +-
src/cli/menu/actions.py | 44 ++++++++++++++++++++++++++++------
src/cli/menu/item.py | 46 ++++++++++++++++++++++++++++++++++++
src/cli/popups.py | 2 +-
src/cli/torrcPanel.py | 2 +-
src/util/uiTools.py | 36 ++++++++++++++++++++++++++--
9 files changed, 155 insertions(+), 29 deletions(-)
diff --git a/src/cli/configPanel.py b/src/cli/configPanel.py
index c44d295..c2e290d 100644
--- a/src/cli/configPanel.py
+++ b/src/cli/configPanel.py
@@ -172,7 +172,7 @@ class ConfigPanel(panel.Panel):
"""
def __init__(self, stdscr, configType, config=None):
- panel.Panel.__init__(self, stdscr, "configState", 0)
+ panel.Panel.__init__(self, stdscr, "configuration", 0)
self.sortOrdering = DEFAULT_SORT_ORDER
self._config = dict(DEFAULT_CONFIG)
diff --git a/src/cli/connections/connPanel.py b/src/cli/connections/connPanel.py
index 45750c3..8b86c65 100644
--- a/src/cli/connections/connPanel.py
+++ b/src/cli/connections/connPanel.py
@@ -31,7 +31,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
"""
def __init__(self, stdscr, config=None):
- panel.Panel.__init__(self, stdscr, "conn", 0)
+ panel.Panel.__init__(self, stdscr, "connections", 0)
threading.Thread.__init__(self)
self.setDaemon(True)
diff --git a/src/cli/controller.py b/src/cli/controller.py
index faf9248..cf17da3 100644
--- a/src/cli/controller.py
+++ b/src/cli/controller.py
@@ -160,6 +160,14 @@ class Controller:
return self._screen
+ def getPageCount(self):
+ """
+ Provides the number of pages the interface has. This may be zero if all
+ page panels have been disabled.
+ """
+
+ return len(self._pagePanels)
+
def getPage(self):
"""
Provides the number belonging to this page. Page numbers start at zero.
@@ -167,23 +175,35 @@ class Controller:
return self._page
+ def setPage(self, pageNumber):
+ """
+ Sets the selected page, raising a ValueError if the page number is invalid.
+
+ Arguments:
+ pageNumber - page number to be selected
+ """
+
+ if pageNumber < 0 or pageNumber >= self.getPageCount():
+ raise ValueError("Invalid page number: %i" % pageNumber)
+
+ if pageNumber != self._page:
+ self._page = pageNumber
+ self._forceRedraw = True
+ self.setMsg()
+
def nextPage(self):
"""
Increments the page number.
"""
- self._page = (self._page + 1) % len(self._pagePanels)
- self._forceRedraw = True
- self.setMsg()
+ self.setPage((self._page + 1) % len(self._pagePanels))
def prevPage(self):
"""
Decrements the page number.
"""
- self._page = (self._page - 1) % len(self._pagePanels)
- self._forceRedraw = True
- self.setMsg()
+ self.setPage((self._page - 1) % len(self._pagePanels))
def isPaused(self):
"""
@@ -227,20 +247,22 @@ class Controller:
return list(self._stickyPanels)
- def getDisplayPanels(self, includeSticky = True):
+ def getDisplayPanels(self, pageNumber = None, includeSticky = True):
"""
- Provides all panels belonging to the current page and sticky content above
- it. This is ordered they way they are presented (top to bottom) on the
- page.
+ Provides all panels belonging to a page and sticky content above it. This
+ is ordered they way they are presented (top to bottom) on the page.
Arguments:
+ pageNumber - page number of the panels to be returned, the current
+ page if None
includeSticky - includes sticky panels in the results if true
"""
+ returnPage = self._page if pageNumber == None else pageNumber
+
if includeSticky:
- return self._stickyPanels + self._pagePanels[self._page]
- else:
- return list(self._pagePanels[self._page])
+ return self._stickyPanels + self._pagePanels[returnPage]
+ else: return list(self._pagePanels[returnPage])
def getDaemonPanels(self):
"""
diff --git a/src/cli/descriptorPopup.py b/src/cli/descriptorPopup.py
index f75d7e6..5b9f646 100644
--- a/src/cli/descriptorPopup.py
+++ b/src/cli/descriptorPopup.py
@@ -105,7 +105,7 @@ def showDescriptorPopup(connectionPanel):
# hides the title of the first panel on the page
control = controller.getController()
- topPanel = control.getDisplayPanels(False)[0]
+ topPanel = control.getDisplayPanels(includeSticky = False)[0]
topPanel.setTitleVisible(False)
topPanel.redraw(True)
diff --git a/src/cli/menu/actions.py b/src/cli/menu/actions.py
index 751751f..f81a162 100644
--- a/src/cli/menu/actions.py
+++ b/src/cli/menu/actions.py
@@ -7,7 +7,7 @@ import functools
import cli.controller
import cli.menu.item
-from util import torTools
+from util import torTools, uiTools
def makeMenu():
"""
@@ -16,6 +16,7 @@ def makeMenu():
baseMenu = cli.menu.item.Submenu("")
baseMenu.add(makeActionsMenu())
+ baseMenu.add(makeViewMenu())
logsMenu = cli.menu.item.Submenu("Logs")
logsMenu.add(cli.menu.item.MenuItem("Events", None))
@@ -29,13 +30,6 @@ def makeMenu():
logsMenu.add(duplicatesSubmenu)
baseMenu.add(logsMenu)
- viewMenu = cli.menu.item.Submenu("View")
- viewMenu.add(cli.menu.item.MenuItem("Graph", None))
- viewMenu.add(cli.menu.item.MenuItem("Connections", None))
- viewMenu.add(cli.menu.item.MenuItem("Configuration", None))
- viewMenu.add(cli.menu.item.MenuItem("Configuration File", None))
- baseMenu.add(viewMenu)
-
graphMenu = cli.menu.item.Submenu("Graph")
graphMenu.add(cli.menu.item.MenuItem("Stats", None))
@@ -93,3 +87,37 @@ def makeActionsMenu():
actionsMenu.add(cli.menu.item.MenuItem("Exit", control.quit))
return actionsMenu
+def makeViewMenu():
+ """
+ Submenu consisting of...
+ [X] <Page 1>
+ [ ] <Page 2>
+ [ ] etc...
+ Color (Submenu)
+ """
+
+ viewMenu = cli.menu.item.Submenu("View")
+ control = cli.controller.getController()
+
+ if control.getPageCount() > 0:
+ pageGroup = cli.menu.item.SelectionGroup(control.setPage, control.getPage())
+
+ for i in range(control.getPageCount()):
+ pagePanels = control.getDisplayPanels(pageNumber = i, includeSticky = False)
+ label = " / ".join([uiTools.camelCase(panel.getName()) for panel in pagePanels])
+
+ viewMenu.add(cli.menu.item.SelectionMenuItem(label, pageGroup, i))
+
+ if uiTools.isColorSupported():
+ colorMenu = cli.menu.item.Submenu("Color")
+ colorGroup = cli.menu.item.SelectionGroup(uiTools.setColorOverride, uiTools.getColorOverride())
+
+ colorMenu.add(cli.menu.item.SelectionMenuItem("All", colorGroup, None))
+
+ for color in uiTools.COLOR_LIST:
+ colorMenu.add(cli.menu.item.SelectionMenuItem(uiTools.camelCase(color), colorGroup, color))
+
+ viewMenu.add(colorMenu)
+
+ return viewMenu
+
diff --git a/src/cli/menu/item.py b/src/cli/menu/item.py
index 8c5d314..beaac9c 100644
--- a/src/cli/menu/item.py
+++ b/src/cli/menu/item.py
@@ -147,3 +147,49 @@ class Submenu(MenuItem):
def select(self):
return False
+class SelectionGroup():
+ """
+ Radio button groups that SelectionMenuItems can belong to.
+ """
+
+ def __init__(self, action, selectedArg):
+ self.action = action
+ self.selectedArg = selectedArg
+
+class SelectionMenuItem(MenuItem):
+ """
+ Menu item with an associated group which determines the selection. This is
+ for the common single argument getter/setter pattern.
+ """
+
+ def __init__(self, label, group, arg):
+ MenuItem.__init__(self, label, None)
+ self._group = group
+ self._arg = arg
+
+ def isSelected(self):
+ """
+ True if we're the selected item, false otherwise.
+ """
+
+ return self._arg == self._group.selectedArg
+
+ def getLabel(self):
+ """
+ Provides our label with a "[X]" prefix if selected and "[ ]" if not.
+ """
+
+ myLabel = MenuItem.getLabel(self)[1]
+ myPrefix = "[X] " if self.isSelected() else "[ ] "
+ return (myPrefix, myLabel, "")
+
+ def select(self):
+ """
+ Performs the group's setter action with our argument.
+ """
+
+ if not self.isSelected():
+ self._group.action(self._arg)
+
+ return True
+
diff --git a/src/cli/popups.py b/src/cli/popups.py
index 8061c8f..5f1eac2 100644
--- a/src/cli/popups.py
+++ b/src/cli/popups.py
@@ -280,7 +280,7 @@ def showMenu(title, options, oldSelection):
try:
# hides the title of the first panel on the page
control = cli.controller.getController()
- topPanel = control.getDisplayPanels(False)[0]
+ topPanel = control.getDisplayPanels(includeSticky = False)[0]
topPanel.setTitleVisible(False)
topPanel.redraw(True)
diff --git a/src/cli/torrcPanel.py b/src/cli/torrcPanel.py
index e14a16a..a12cc87 100644
--- a/src/cli/torrcPanel.py
+++ b/src/cli/torrcPanel.py
@@ -24,7 +24,7 @@ class TorrcPanel(panel.Panel):
"""
def __init__(self, stdscr, configType, config=None):
- panel.Panel.__init__(self, stdscr, "configFile", 0)
+ panel.Panel.__init__(self, stdscr, "torrc", 0)
self._config = dict(DEFAULT_CONFIG)
if config:
diff --git a/src/util/uiTools.py b/src/util/uiTools.py
index 5999c64..34d9210 100644
--- a/src/util/uiTools.py
+++ b/src/util/uiTools.py
@@ -18,6 +18,9 @@ COLOR_LIST = {"red": curses.COLOR_RED, "green": curses.COLOR_GREEN,
"cyan": curses.COLOR_CYAN, "magenta": curses.COLOR_MAGENTA,
"black": curses.COLOR_BLACK, "white": curses.COLOR_WHITE}
+# boolean for if we have color support enabled, None not yet determined
+COLOR_IS_SUPPORTED = None
+
# mappings for getColor() - this uses the default terminal color scheme if
# color support is unavailable
COLOR_ATTR_INITIALIZED = False
@@ -138,6 +141,14 @@ def getPrintable(line, keepNewlines = True):
line = "".join([char for char in line if (isprint(char) or (keepNewlines and char == "\n"))])
return line
+def isColorSupported():
+ """
+ True if the display supports showing color, false otherwise.
+ """
+
+ if COLOR_IS_SUPPORTED == None: _initColors()
+ return COLOR_IS_SUPPORTED
+
def getColor(color):
"""
Provides attribute corresponding to a given text color. Supported colors
@@ -261,6 +272,24 @@ def cropStr(msg, size, minWordLen = 4, minCrop = 0, endType = Ending.ELLIPSE, ge
if getRemainder: return (returnMsg, remainder)
else: return returnMsg
+def camelCase(label):
+ """
+ Converts the given string to camel case, ie:
+ >>> camelCase("I_LIKE_PEPPERJACK!")
+ 'I Like Pepperjack!'
+
+ Arguments:
+ label - input string to be converted
+ """
+
+ words = []
+ for entry in label.split("_"):
+ if len(entry) == 0: words.append("")
+ elif len(entry) == 1: words.append(entry.upper())
+ else: words.append(entry[0].upper() + entry[1:].lower())
+
+ return " ".join(words)
+
def drawBox(panel, top, left, width, height, attr=curses.A_NORMAL):
"""
Draws a box in the panel with the given bounds.
@@ -719,16 +748,17 @@ def _initColors():
calling curses.initscr().
"""
- global COLOR_ATTR_INITIALIZED
+ global COLOR_ATTR_INITIALIZED, COLOR_IS_SUPPORTED
if not COLOR_ATTR_INITIALIZED:
COLOR_ATTR_INITIALIZED = True
+ COLOR_IS_SUPPORTED = False
if not CONFIG["features.colorInterface"]: return
- try: hasColorSupport = curses.has_colors()
+ try: COLOR_IS_SUPPORTED = curses.has_colors()
except curses.error: return # initscr hasn't been called yet
# initializes color mappings if color support is available
- if hasColorSupport:
+ if COLOR_IS_SUPPORTED:
colorpair = 0
log.log(CONFIG["log.cursesColorSupport"], "Terminal color support detected and enabled")
1
0
17 Jul '11
commit 66e10cc08f300e46d8c6eb9857a664d824bf1159
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Jun 11 19:19:41 2011 -0700
Binding handlers for the actions submenu
Making a functional actions submenu which includes options for...
- Close Menu
- Pause / Unpause
- Reset Tor
- Quit
---
src/cli/controller.py | 23 +++++++++++++++++++----
src/cli/menu/actions.py | 30 ++++++++++++++++++++++++++----
src/cli/menu/item.py | 7 +++++--
3 files changed, 50 insertions(+), 10 deletions(-)
diff --git a/src/cli/controller.py b/src/cli/controller.py
index 997d9d3..faf9248 100644
--- a/src/cli/controller.py
+++ b/src/cli/controller.py
@@ -150,6 +150,7 @@ class Controller:
self._page = 0
self._isPaused = False
self._forceRedraw = False
+ self._isDone = False
self.setMsg() # initializes our control message
def getScreen(self):
@@ -312,6 +313,20 @@ class Controller:
if redraw: controlPanel.redraw(True)
else: self._forceRedraw = True
+
+ def isDone(self):
+ """
+ True if arm should be terminated, false otherwise.
+ """
+
+ return self._isDone
+
+ def quit(self):
+ """
+ Terminates arm after the input is processed.
+ """
+
+ self._isDone = True
def shutdownDaemons():
"""
@@ -477,7 +492,7 @@ def drawTorMonitor(stdscr, startTime):
menuKeys = []
- while True:
+ while not control.isDone():
displayPanels = control.getDisplayPanels()
isUnresponsive = heartbeatCheck(isUnresponsive)
@@ -529,9 +544,7 @@ def drawTorMonitor(stdscr, startTime):
quitConfirmed = confirmationKey in (ord('q'), ord('Q'))
else: quitConfirmed = True
- if quitConfirmed:
- shutdownDaemons()
- break
+ if quitConfirmed: control.quit()
elif key == ord('x') or key == ord('X'):
# provides prompt to confirm that arm should issue a sighup
msg = "This will reset Tor's internal state. Are you sure (x again to confirm)?"
@@ -547,4 +560,6 @@ def drawTorMonitor(stdscr, startTime):
for panelImpl in displayPanels:
isKeystrokeConsumed = panelImpl.handleKey(key)
if isKeystrokeConsumed: break
+
+ shutdownDaemons()
diff --git a/src/cli/menu/actions.py b/src/cli/menu/actions.py
index f6f88ba..751751f 100644
--- a/src/cli/menu/actions.py
+++ b/src/cli/menu/actions.py
@@ -2,18 +2,20 @@
Generates the menu for arm, binding options with their related actions.
"""
+import functools
+
+import cli.controller
import cli.menu.item
+from util import torTools
+
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)
+ baseMenu.add(makeActionsMenu())
logsMenu = cli.menu.item.Submenu("Logs")
logsMenu.add(cli.menu.item.MenuItem("Events", None))
@@ -70,4 +72,24 @@ def makeMenu():
return baseMenu
+def makeActionsMenu():
+ """
+ Submenu consisting of...
+ Close Menu
+ Pause / Unpause
+ Reset Tor
+ Exit
+ """
+
+ control = cli.controller.getController()
+ actionsMenu = cli.menu.item.Submenu("Actions")
+ actionsMenu.add(cli.menu.item.MenuItem("Close Menu", None))
+
+ if control.isPaused(): label, arg = "Unpause", False
+ else: label, arg = "Pause", True
+ actionsMenu.add(cli.menu.item.MenuItem(label, functools.partial(control.setPaused, arg)))
+
+ actionsMenu.add(cli.menu.item.MenuItem("Reset Tor", torTools.getConn().reload))
+ actionsMenu.add(cli.menu.item.MenuItem("Exit", control.quit))
+ return actionsMenu
diff --git a/src/cli/menu/item.py b/src/cli/menu/item.py
index bfc7ffd..8c5d314 100644
--- a/src/cli/menu/item.py
+++ b/src/cli/menu/item.py
@@ -53,8 +53,8 @@ class MenuItem():
the menu and false otherwise.
"""
- if self._callback: return self._callback()
- else: return False
+ if self._callback: self._callback()
+ return True
def next(self):
"""
@@ -143,4 +143,7 @@ class Submenu(MenuItem):
"""
return not bool(self._children)
+
+ def select(self):
+ return False
1
0
commit 62c76582c3816e58f1108ffd095ca8aae92105fb
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sun Jun 12 13:18:36 2011 -0700
Binding handlers for the log submenu
---
src/cli/controller.py | 19 ++++-
src/cli/graphing/graphPanel.py | 11 +---
src/cli/logPanel.py | 160 +++++++++++++++++++++++++++-------------
src/cli/menu/actions.py | 56 +++++++++++----
src/cli/menu/menu.py | 4 +-
5 files changed, 168 insertions(+), 82 deletions(-)
diff --git a/src/cli/controller.py b/src/cli/controller.py
index cf17da3..6158ade 100644
--- a/src/cli/controller.py
+++ b/src/cli/controller.py
@@ -288,12 +288,27 @@ class Controller:
return allPanels
- def requestRedraw(self):
+ def requestRedraw(self, immediate = False):
"""
Requests that all content is redrawn when the interface is next rendered.
+
+ Arguments:
+ immediate - redraws now if true, otherwise waits for when next normally
+ drawn
"""
- self._forceRedraw = True
+ if immediate:
+ displayPanels = self.getDisplayPanels()
+
+ occupiedContent = 0
+ for panelImpl in displayPanels:
+ panelImpl.setTop(occupiedContent)
+ occupiedContent += panelImpl.getHeight()
+
+ for panelImpl in displayPanels:
+ panelImpl.redraw(True)
+ else:
+ self._forceRedraw = True
def isRedrawRequested(self, clearFlag = False):
"""
diff --git a/src/cli/graphing/graphPanel.py b/src/cli/graphing/graphPanel.py
index 69b9121..8c3adf7 100644
--- a/src/cli/graphing/graphPanel.py
+++ b/src/cli/graphing/graphPanel.py
@@ -307,16 +307,7 @@ class GraphPanel(panel.Panel):
panel.CURSES_LOCK.acquire()
try:
while True:
- # redraws the resized panels
- displayPanels = control.getDisplayPanels()
-
- occupiedContent = 0
- for panelImpl in displayPanels:
- panelImpl.setTop(occupiedContent)
- occupiedContent += panelImpl.getHeight()
-
- for panelImpl in displayPanels:
- panelImpl.redraw(True)
+ control.requestRedraw(True)
msg = "press the down/up to resize the graph, and enter when done"
control.setMsg(msg, curses.A_BOLD, True)
diff --git a/src/cli/logPanel.py b/src/cli/logPanel.py
index 6d54ab4..ef372ab 100644
--- a/src/cli/logPanel.py
+++ b/src/cli/logPanel.py
@@ -13,6 +13,7 @@ import threading
from TorCtl import TorCtl
import popups
+import cli.controller
from version import VERSION
from util import conf, log, panel, sysTools, torTools, uiTools
@@ -668,6 +669,17 @@ class LogPanel(panel.Panel, threading.Thread):
log.log(self._config["log.logPanel.logFileWriteFailed"], "Unable to write to log file: %s" % sysTools.getFileErrorMsg(exc))
self.logFile = None
+ def setDuplicateVisability(self, isVisible):
+ """
+ Sets if duplicate log entries are collaped or expanded.
+
+ Arguments:
+ isVisible - if true all log entries are shown, otherwise they're
+ deduplicated
+ """
+
+ self.showDuplicates = isVisible
+
def registerEvent(self, event):
"""
Notes event and redraws log. If paused it's held in a temporary buffer.
@@ -728,6 +740,13 @@ class LogPanel(panel.Panel, threading.Thread):
self.redraw(True)
self.valsLock.release()
+ def getFilter(self):
+ """
+ Provides our currently selected regex filter.
+ """
+
+ return self.filterOptions[0] if self.regexFilter else None
+
def setFilter(self, logFilter):
"""
Filters log entries according to the given regular expression.
@@ -744,6 +763,90 @@ class LogPanel(panel.Panel, threading.Thread):
self.redraw(True)
self.valsLock.release()
+ def makeFilterSelection(self, selectedOption):
+ """
+ Makes the given filter selection, applying it to the log and reorganizing
+ our filter selection.
+
+ Arguments:
+ selectedOption - regex filter we've already added, None if no filter
+ should be applied
+ """
+
+ if selectedOption:
+ try:
+ self.setFilter(re.compile(selectedOption))
+
+ # move selection to top
+ self.filterOptions.remove(selectedOption)
+ self.filterOptions.insert(0, selectedOption)
+ except re.error, exc:
+ # shouldn't happen since we've already checked validity
+ msg = "Invalid regular expression ('%s': %s) - removing from listing" % (selectedOption, exc)
+ log.log(log.WARN, msg)
+ self.filterOptions.remove(selectedOption)
+ else: self.setFilter(None)
+
+ def showFilterPrompt(self):
+ """
+ Prompts the user to add a new regex filter.
+ """
+
+ cli.controller.getController().requestRedraw(True)
+ regexInput = popups.inputPrompt("Regular expression: ")
+
+ if regexInput:
+ try:
+ self.setFilter(re.compile(regexInput))
+ if regexInput in self.filterOptions: self.filterOptions.remove(regexInput)
+ self.filterOptions.insert(0, regexInput)
+ except re.error, exc:
+ popups.showMsg("Unable to compile expression: %s" % exc, 2)
+
+ def showEventSelectionPrompt(self):
+ """
+ Prompts the user to select the events being listened for.
+ """
+
+ # allow user to enter new types of events to log - unchanged if left blank
+ cli.controller.getController().requestRedraw(True)
+ popup, width, height = popups.init(11, 80)
+
+ if popup:
+ try:
+ # displays the available flags
+ popup.win.box()
+ popup.addstr(0, 0, "Event Types:", curses.A_STANDOUT)
+ eventLines = EVENT_LISTING.split("\n")
+
+ for i in range(len(eventLines)):
+ popup.addstr(i + 1, 1, eventLines[i][6:])
+
+ popup.win.refresh()
+
+ userInput = popups.inputPrompt("Events to log: ")
+ if userInput:
+ userInput = userInput.replace(' ', '') # strips spaces
+ try: self.setLoggedEvents(expandEvents(userInput))
+ except ValueError, exc:
+ popups.showMsg("Invalid flags: %s" % str(exc), 2)
+ finally: popups.finalize()
+
+ def showSnapshotPrompt(self):
+ """
+ Lets user enter a path to take a snapshot, canceling if left blank.
+ """
+
+ cli.controller.getController().requestRedraw(True)
+ pathInput = popups.inputPrompt("Path to save log snapshot: ")
+
+ if pathInput:
+ try:
+ self.saveSnapshot(pathInput)
+ popups.showMsg("Saved: %s" % pathInput, 2)
+ except IOError, exc:
+ popups.showMsg("Unable to save snapshot: %s" % sysTools.getFileErrorMsg(exc), 2)
+
def clear(self):
"""
Clears the contents of the event log.
@@ -817,66 +920,17 @@ class LogPanel(panel.Panel, threading.Thread):
self.setFilter(None)
elif selection == len(options) - 1:
# selected 'New...' option - prompt user to input regular expression
- regexInput = popups.inputPrompt("Regular expression: ")
-
- if regexInput:
- try:
- self.setFilter(re.compile(regexInput))
- if regexInput in self.filterOptions: self.filterOptions.remove(regexInput)
- self.filterOptions.insert(0, regexInput)
- except re.error, exc:
- popups.showMsg("Unable to compile expression: %s" % exc, 2)
+ self.showFilterPrompt()
elif selection != -1:
- selectedOption = self.filterOptions[selection - 1]
-
- try:
- self.setFilter(re.compile(selectedOption))
-
- # move selection to top
- self.filterOptions.remove(selectedOption)
- self.filterOptions.insert(0, selectedOption)
- except re.error, exc:
- # shouldn't happen since we've already checked validity
- msg = "Invalid regular expression ('%s': %s) - removing from listing" % (selectedOption, exc)
- log.log(log.WARN, msg)
- self.filterOptions.remove(selectedOption)
+ self.makeFilterSelection(self.filterOptions[selection - 1])
finally:
panel.CURSES_LOCK.release()
if len(self.filterOptions) > MAX_REGEX_FILTERS: del self.filterOptions[MAX_REGEX_FILTERS:]
elif key == ord('e') or key == ord('E'):
- # allow user to enter new types of events to log - unchanged if left blank
- popup, width, height = popups.init(11, 80)
-
- if popup:
- try:
- # displays the available flags
- popup.win.box()
- popup.addstr(0, 0, "Event Types:", curses.A_STANDOUT)
- eventLines = EVENT_LISTING.split("\n")
-
- for i in range(len(eventLines)):
- popup.addstr(i + 1, 1, eventLines[i][6:])
-
- popup.win.refresh()
-
- userInput = popups.inputPrompt("Events to log: ")
- if userInput:
- userInput = userInput.replace(' ', '') # strips spaces
- try: self.setLoggedEvents(expandEvents(userInput))
- except ValueError, exc:
- popups.showMsg("Invalid flags: %s" % str(exc), 2)
- finally: popups.finalize()
+ self.showEventSelectionPrompt()
elif key == ord('a') or key == ord('A'):
- # lets user enter a path to take a snapshot, canceling if left blank
- pathInput = popups.inputPrompt("Path to save log snapshot: ")
-
- if pathInput:
- try:
- self.saveSnapshot(pathInput)
- popups.showMsg("Saved: %s" % pathInput, 2)
- except IOError, exc:
- popups.showMsg("Unable to save snapshot: %s" % sysTools.getFileErrorMsg(exc), 2)
+ self.showSnapshotPrompt()
else: isKeystrokeConsumed = False
return isKeystrokeConsumed
diff --git a/src/cli/menu/actions.py b/src/cli/menu/actions.py
index 9d69cb4..c8b8da3 100644
--- a/src/cli/menu/actions.py
+++ b/src/cli/menu/actions.py
@@ -24,18 +24,8 @@ def makeMenu():
for pagePanel in control.getDisplayPanels(includeSticky = False):
if pagePanel.getName() == "graph":
baseMenu.add(makeGraphMenu(pagePanel))
-
- 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)
+ elif pagePanel.getName() == "log":
+ baseMenu.add(makeLogMenu(pagePanel))
connectionsMenu = cli.menu.item.Submenu("Connections")
connectionsMenu.add(cli.menu.item.MenuItem("Identity", None))
@@ -117,7 +107,7 @@ def makeGraphMenu(graphPanel):
[X] <Stat 1>
[ ] <Stat 2>
[ ] <Stat 2>
- Resize
+ Resize...
Interval (Submenu)
Bounds (Submenu)
@@ -138,7 +128,7 @@ def makeGraphMenu(graphPanel):
graphMenu.add(cli.menu.item.SelectionMenuItem(label, statGroup, statKey))
# resizing option
- graphMenu.add(cli.menu.item.MenuItem("Resize", graphPanel.resizeGraph))
+ graphMenu.add(cli.menu.item.MenuItem("Resize...", graphPanel.resizeGraph))
# interval submenu
intervalMenu = cli.menu.item.Submenu("Interval")
@@ -162,3 +152,41 @@ def makeGraphMenu(graphPanel):
return graphMenu
+def makeLogMenu(logPanel):
+ """
+ Submenu for the log panel, consisting of...
+ Events...
+ Snapshot...
+ Clear
+ Show / Hide Duplicates
+ Filter (Submenu)
+
+ Arguments:
+ logPanel - instance of the log panel
+ """
+
+ logMenu = cli.menu.item.Submenu("Log")
+
+ logMenu.add(cli.menu.item.MenuItem("Events...", logPanel.showEventSelectionPrompt))
+ logMenu.add(cli.menu.item.MenuItem("Snapshot...", logPanel.showSnapshotPrompt))
+ logMenu.add(cli.menu.item.MenuItem("Clear", logPanel.clear))
+
+ if logPanel.showDuplicates: label, arg = "Hide", False
+ else: label, arg = "Show", True
+ logMenu.add(cli.menu.item.MenuItem("%s Duplicates" % label, functools.partial(logPanel.setDuplicateVisability, arg)))
+
+ # filter submenu
+ filterMenu = cli.menu.item.Submenu("Filter")
+ filterGroup = cli.menu.item.SelectionGroup(logPanel.makeFilterSelection, logPanel.getFilter())
+
+ filterMenu.add(cli.menu.item.SelectionMenuItem("None", filterGroup, None))
+
+ for option in logPanel.filterOptions:
+ filterMenu.add(cli.menu.item.SelectionMenuItem(option, filterGroup, option))
+
+ filterMenu.add(cli.menu.item.MenuItem("New...", logPanel.showFilterPrompt))
+ logMenu.add(filterMenu)
+
+ return logMenu
+
+
diff --git a/src/cli/menu/menu.py b/src/cli/menu/menu.py
index e0728e8..b411a9c 100644
--- a/src/cli/menu/menu.py
+++ b/src/cli/menu/menu.py
@@ -110,9 +110,7 @@ def showMenu():
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)
+ if not cursor.isDone(): control.requestRedraw(True)
finally:
control.setMsg()
cli.popups.finalize()
1
0
commit ea0b39251a9371cd81262bc360c644ab18acf1c8
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sun Jun 12 12:11:23 2011 -0700
Binding handlers for the graph submenu
---
src/cli/graphing/graphPanel.py | 121 ++++++++++++++++++++++++++++------------
src/cli/menu/actions.py | 75 +++++++++++++++++++------
src/util/uiTools.py | 10 ++-
3 files changed, 150 insertions(+), 56 deletions(-)
diff --git a/src/cli/graphing/graphPanel.py b/src/cli/graphing/graphPanel.py
index 0207604..69b9121 100644
--- a/src/cli/graphing/graphPanel.py
+++ b/src/cli/graphing/graphPanel.py
@@ -239,6 +239,40 @@ class GraphPanel(panel.Panel):
self.stats = {} # available stats (mappings of label -> instance)
self.setPauseAttr("stats")
+ def getUpdateInterval(self):
+ """
+ Provides the rate that we update the graph at.
+ """
+
+ return self.updateInterval
+
+ def setUpdateInterval(self, updateInterval):
+ """
+ Sets the rate that we update the graph at.
+
+ Arguments:
+ updateInterval - update time enum
+ """
+
+ self.updateInterval = updateInterval
+
+ def getBoundsType(self):
+ """
+ Provides the type of graph bounds used.
+ """
+
+ return self.bounds
+
+ def setBoundsType(self, boundsType):
+ """
+ Sets the type of graph boundaries we use.
+
+ Arguments:
+ boundsType - graph bounds enum
+ """
+
+ self.bounds = boundsType
+
def getHeight(self):
"""
Provides the height requested by the currently displayed GraphStats (zero
@@ -260,44 +294,54 @@ class GraphPanel(panel.Panel):
self.graphHeight = max(MIN_GRAPH_HEIGHT, newGraphHeight)
+ def resizeGraph(self):
+ """
+ Prompts for user input to resize the graph panel. Options include...
+ down arrow - grow graph
+ up arrow - shrink graph
+ enter / space - set size
+ """
+
+ control = cli.controller.getController()
+
+ panel.CURSES_LOCK.acquire()
+ try:
+ while True:
+ # redraws the resized panels
+ displayPanels = control.getDisplayPanels()
+
+ occupiedContent = 0
+ for panelImpl in displayPanels:
+ panelImpl.setTop(occupiedContent)
+ occupiedContent += panelImpl.getHeight()
+
+ for panelImpl in displayPanels:
+ panelImpl.redraw(True)
+
+ msg = "press the down/up to resize the graph, and enter when done"
+ control.setMsg(msg, curses.A_BOLD, True)
+ curses.cbreak()
+ key = control.getScreen().getch()
+
+ if key == curses.KEY_DOWN:
+ # don't grow the graph if it's already consuming the whole display
+ # (plus an extra line for the graph/log gap)
+ maxHeight = self.parent.getmaxyx()[0] - self.top
+ currentHeight = self.getHeight()
+
+ if currentHeight < maxHeight + 1:
+ self.setGraphHeight(self.graphHeight + 1)
+ elif key == curses.KEY_UP:
+ self.setGraphHeight(self.graphHeight - 1)
+ elif uiTools.isSelectionKey(key): break
+ finally:
+ control.setMsg()
+ panel.CURSES_LOCK.release()
+
def handleKey(self, key):
isKeystrokeConsumed = True
if key == ord('r') or key == ord('R'):
- control = cli.controller.getController()
-
- panel.CURSES_LOCK.acquire()
- try:
- while True:
- msg = "press the down/up to resize the graph, and enter when done"
- control.setMsg(msg, curses.A_BOLD, True)
- curses.cbreak()
- key = control.getScreen().getch()
-
- if key == curses.KEY_DOWN:
- # don't grow the graph if it's already consuming the whole display
- # (plus an extra line for the graph/log gap)
- maxHeight = self.parent.getmaxyx()[0] - self.top
- currentHeight = self.getHeight()
-
- if currentHeight < maxHeight + 1:
- self.setGraphHeight(self.graphHeight + 1)
- elif key == curses.KEY_UP:
- self.setGraphHeight(self.graphHeight - 1)
- elif uiTools.isSelectionKey(key): break
-
- # redraws the resized panels
- displayPanels = control.getDisplayPanels()
-
- occupiedContent = 0
- for panelImpl in displayPanels:
- panelImpl.setTop(occupiedContent)
- occupiedContent += panelImpl.getHeight()
-
- for panelImpl in displayPanels:
- panelImpl.redraw(True)
- finally:
- control.setMsg()
- panel.CURSES_LOCK.release()
+ self.resizeGraph()
elif key == ord('b') or key == ord('B'):
# uses the next boundary type
self.bounds = Bounds.next(self.bounds)
@@ -447,6 +491,13 @@ class GraphPanel(panel.Panel):
stats._graphPanel = self
self.stats[label] = stats
+ def getStats(self):
+ """
+ Provides the currently selected stats label.
+ """
+
+ return self.currentDisplay
+
def setStats(self, label):
"""
Sets the currently displayed stats instance, hiding panel if None.
diff --git a/src/cli/menu/actions.py b/src/cli/menu/actions.py
index f81a162..9d69cb4 100644
--- a/src/cli/menu/actions.py
+++ b/src/cli/menu/actions.py
@@ -6,6 +6,7 @@ import functools
import cli.controller
import cli.menu.item
+import cli.graphing.graphPanel
from util import torTools, uiTools
@@ -18,6 +19,12 @@ def makeMenu():
baseMenu.add(makeActionsMenu())
baseMenu.add(makeViewMenu())
+ control = cli.controller.getController()
+
+ for pagePanel in control.getDisplayPanels(includeSticky = False):
+ if pagePanel.getName() == "graph":
+ baseMenu.add(makeGraphMenu(pagePanel))
+
logsMenu = cli.menu.item.Submenu("Logs")
logsMenu.add(cli.menu.item.MenuItem("Events", None))
logsMenu.add(cli.menu.item.MenuItem("Clear", None))
@@ -30,23 +37,6 @@ def makeMenu():
logsMenu.add(duplicatesSubmenu)
baseMenu.add(logsMenu)
- 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))
@@ -121,3 +111,54 @@ def makeViewMenu():
return viewMenu
+def makeGraphMenu(graphPanel):
+ """
+ Submenu for the graph panel, consisting of...
+ [X] <Stat 1>
+ [ ] <Stat 2>
+ [ ] <Stat 2>
+ Resize
+ Interval (Submenu)
+ Bounds (Submenu)
+
+ Arguments:
+ graphPanel - instance of the graph panel
+ """
+
+ graphMenu = cli.menu.item.Submenu("Graph")
+
+ # stats options
+ statGroup = cli.menu.item.SelectionGroup(graphPanel.setStats, graphPanel.getStats())
+ availableStats = graphPanel.stats.keys()
+ availableStats.sort()
+
+ for statKey in ["None"] + availableStats:
+ label = uiTools.camelCase(statKey, divider = " ")
+ statKey = None if statKey == "None" else statKey
+ graphMenu.add(cli.menu.item.SelectionMenuItem(label, statGroup, statKey))
+
+ # resizing option
+ graphMenu.add(cli.menu.item.MenuItem("Resize", graphPanel.resizeGraph))
+
+ # interval submenu
+ intervalMenu = cli.menu.item.Submenu("Interval")
+ intervalGroup = cli.menu.item.SelectionGroup(graphPanel.setUpdateInterval, graphPanel.getUpdateInterval())
+
+ for i in range(len(cli.graphing.graphPanel.UPDATE_INTERVALS)):
+ label = cli.graphing.graphPanel.UPDATE_INTERVALS[i][0]
+ label = uiTools.camelCase(label, divider = " ")
+ intervalMenu.add(cli.menu.item.SelectionMenuItem(label, intervalGroup, i))
+
+ graphMenu.add(intervalMenu)
+
+ # bounds submenu
+ boundsMenu = cli.menu.item.Submenu("Bounds")
+ boundsGroup = cli.menu.item.SelectionGroup(graphPanel.setBoundsType, graphPanel.getBoundsType())
+
+ for boundsType in cli.graphing.graphPanel.Bounds.values():
+ boundsMenu.add(cli.menu.item.SelectionMenuItem(boundsType, boundsGroup, boundsType))
+
+ graphMenu.add(boundsMenu)
+
+ return graphMenu
+
diff --git a/src/util/uiTools.py b/src/util/uiTools.py
index 34d9210..5204960 100644
--- a/src/util/uiTools.py
+++ b/src/util/uiTools.py
@@ -272,23 +272,25 @@ def cropStr(msg, size, minWordLen = 4, minCrop = 0, endType = Ending.ELLIPSE, ge
if getRemainder: return (returnMsg, remainder)
else: return returnMsg
-def camelCase(label):
+def camelCase(label, divider = "_", joiner = " "):
"""
Converts the given string to camel case, ie:
>>> camelCase("I_LIKE_PEPPERJACK!")
'I Like Pepperjack!'
Arguments:
- label - input string to be converted
+ label - input string to be converted
+ divider - character to be used for word breaks
+ joiner - character used to fill between word breaks
"""
words = []
- for entry in label.split("_"):
+ for entry in label.split(divider):
if len(entry) == 0: words.append("")
elif len(entry) == 1: words.append(entry.upper())
else: words.append(entry[0].upper() + entry[1:].lower())
- return " ".join(words)
+ return joiner.join(words)
def drawBox(panel, top, left, width, height, attr=curses.A_NORMAL):
"""
1
0
17 Jul '11
commit 6ae0b6e6aa1481718443f3e367a91d15a5bdaa6f
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sun Jun 12 13:48:13 2011 -0700
Binding handlers for the connections submenu
---
src/cli/connections/connPanel.py | 30 +++++++++++++++++-----
src/cli/menu/actions.py | 50 ++++++++++++++++++++++++++++++++-----
src/util/connections.py | 19 ++++++++++++++
3 files changed, 85 insertions(+), 14 deletions(-)
diff --git a/src/cli/connections/connPanel.py b/src/cli/connections/connPanel.py
index 8b86c65..9490ddc 100644
--- a/src/cli/connections/connPanel.py
+++ b/src/cli/connections/connPanel.py
@@ -6,6 +6,7 @@ import time
import curses
import threading
+import cli.controller
import cli.descriptorPopup
import cli.popups
@@ -124,6 +125,13 @@ class ConnectionPanel(panel.Panel, threading.Thread):
self._entryLines += entry.getLines()
self.valsLock.release()
+ def getListingType(self):
+ """
+ Provides the priority content we list connections by.
+ """
+
+ return self._listingType
+
def setListingType(self, listingType):
"""
Sets the priority information presented by the panel.
@@ -143,6 +151,20 @@ class ConnectionPanel(panel.Panel, threading.Thread):
self.valsLock.release()
+ def showSortDialog(self):
+ """
+ Provides the sort dialog for our connections.
+ """
+
+ # set ordering for connection options
+ cli.controller.getController().requestRedraw(True)
+ titleLabel = "Connection Ordering:"
+ options = entries.SortAttr.values()
+ oldSelection = self._sortOrdering
+ optionColors = dict([(attr, entries.SORT_COLORS[attr]) for attr in options])
+ results = cli.popups.showSortDialog(titleLabel, options, oldSelection, optionColors)
+ if results: self.setSortOrder(results)
+
def handleKey(self, key):
self.valsLock.acquire()
@@ -156,13 +178,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
self._showDetails = not self._showDetails
self.redraw(True)
elif key == ord('s') or key == ord('S'):
- # set ordering for connection options
- titleLabel = "Connection Ordering:"
- options = entries.SortAttr.values()
- oldSelection = self._sortOrdering
- optionColors = dict([(attr, entries.SORT_COLORS[attr]) for attr in options])
- results = cli.popups.showSortDialog(titleLabel, options, oldSelection, optionColors)
- if results: self.setSortOrder(results)
+ self.showSortDialog()
elif key == ord('u') or key == ord('U'):
# provides a menu to pick the connection resolver
title = "Resolver Util:"
diff --git a/src/cli/menu/actions.py b/src/cli/menu/actions.py
index c8b8da3..2827742 100644
--- a/src/cli/menu/actions.py
+++ b/src/cli/menu/actions.py
@@ -8,7 +8,7 @@ import cli.controller
import cli.menu.item
import cli.graphing.graphPanel
-from util import torTools, uiTools
+from util import connections, torTools, uiTools
def makeMenu():
"""
@@ -26,12 +26,8 @@ def makeMenu():
baseMenu.add(makeGraphMenu(pagePanel))
elif pagePanel.getName() == "log":
baseMenu.add(makeLogMenu(pagePanel))
-
- 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)
+ elif pagePanel.getName() == "connections":
+ baseMenu.add(makeConnectionsMenu(pagePanel))
configurationMenu = cli.menu.item.Submenu("Configuration")
@@ -188,5 +184,45 @@ def makeLogMenu(logPanel):
logMenu.add(filterMenu)
return logMenu
+
+def makeConnectionsMenu(connPanel):
+ """
+ Submenu for the connections panel, consisting of...
+ [X] IP Address
+ [ ] Fingerprint
+ [ ] Nickname
+ Sorting...
+ Resolver (Submenu)
+
+ Arguments:
+ connPanel - instance of the connections panel
+ """
+
+ connectionsMenu = cli.menu.item.Submenu("Connections")
+
+ # listing options
+ listingGroup = cli.menu.item.SelectionGroup(connPanel.setListingType, connPanel.getListingType())
+
+ listingOptions = cli.connections.entries.ListingType.values()
+ listingOptions.remove(cli.connections.entries.ListingType.HOSTNAME)
+
+ for option in listingOptions:
+ connectionsMenu.add(cli.menu.item.SelectionMenuItem(option, listingGroup, option))
+
+ # sorting option
+ connectionsMenu.add(cli.menu.item.MenuItem("Sorting...", connPanel.showSortDialog))
+
+ # resolver submenu
+ connResolver = connections.getResolver("tor")
+ resolverMenu = cli.menu.item.Submenu("Resolver")
+ resolverGroup = cli.menu.item.SelectionGroup(connResolver.setOverwriteResolver, connResolver.getOverwriteResolver())
+
+ resolverMenu.add(cli.menu.item.SelectionMenuItem("auto", resolverGroup, None))
+
+ for option in connections.Resolver.values():
+ resolverMenu.add(cli.menu.item.SelectionMenuItem(option, resolverGroup, option))
+
+ connectionsMenu.add(resolverMenu)
+ return connectionsMenu
diff --git a/src/util/connections.py b/src/util/connections.py
index 47aa8af..97de3ea 100644
--- a/src/util/connections.py
+++ b/src/util/connections.py
@@ -444,6 +444,25 @@ class ConnectionResolver(threading.Thread):
# avoid having stray spikes up the rate.
self._rateThresholdBroken = 0
+ def getOverwriteResolver(self):
+ """
+ Provides the resolver connection resolution is forced to use. This returns
+ None if it's dynamically determined.
+ """
+
+ return self.overwriteResolver
+
+ def setOverwriteResolver(self, overwriteResolver):
+ """
+ Sets the resolver used for connection resolution, if None then this is
+ automatically determined based on what is available.
+
+ Arguments:
+ overwriteResolver - connection resolver to be used
+ """
+
+ self.overwriteResolver = overwriteResolver
+
def run(self):
while not self._halt:
minWait = self.resolveRate if self.resolveRate else self.defaultRate
1
0
17 Jul '11
commit 93875272baf20437578a4b3e18e4b39a8a9efb9e
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sun Jun 12 14:08:31 2011 -0700
Binding handlers for the configuration submenu
---
src/cli/configPanel.py | 232 ++++++++++++++++++++++++++---------------------
src/cli/menu/actions.py | 34 +++++--
2 files changed, 153 insertions(+), 113 deletions(-)
diff --git a/src/cli/configPanel.py b/src/cli/configPanel.py
index c2e290d..4647286 100644
--- a/src/cli/configPanel.py
+++ b/src/cli/configPanel.py
@@ -6,7 +6,7 @@ and the resulting configuration files saved.
import curses
import threading
-import controller
+import cli.controller
import popups
from util import conf, enum, panel, sysTools, torConfig, torTools, uiTools
@@ -242,6 +242,17 @@ class ConfigPanel(panel.Panel):
return self.scroller.getCursorSelection(self._getConfigOptions())
+ def setFiltering(self, isFiltered):
+ """
+ Sets if configuration options are filtered or not.
+
+ Arguments:
+ isFiltered - if true then only relatively important options will be
+ shown, otherwise everything is shown
+ """
+
+ self.showAll = not isFiltered
+
def setSortOrder(self, ordering = None):
"""
Sets the configuration attributes we're sorting by and resorts the
@@ -258,6 +269,24 @@ class ConfigPanel(panel.Panel):
self.confImportantContents.sort(key=lambda i: (i.getAll(self.sortOrdering)))
self.valsLock.release()
+ def showSortDialog(self):
+ """
+ Provides the sort dialog for our configuration options.
+ """
+
+ # set ordering for config options
+ cli.controller.getController().requestRedraw(True)
+ titleLabel = "Config Option Ordering:"
+ options = [FIELD_ATTR[field][0] for field in Field.values()]
+ oldSelection = [FIELD_ATTR[field][0] for field in self.sortOrdering]
+ optionColors = dict([FIELD_ATTR[field] for field in Field.values()])
+ results = popups.showSortDialog(titleLabel, options, oldSelection, optionColors)
+
+ if results:
+ # converts labels back to enums
+ resultEnums = [getFieldFromLabel(label) for label in results]
+ self.setSortOrder(resultEnums)
+
def handleKey(self, key):
self.valsLock.acquire()
isKeystrokeConsumed = True
@@ -313,118 +342,117 @@ class ConfigPanel(panel.Panel):
self.showAll = not self.showAll
self.redraw(True)
elif key == ord('s') or key == ord('S'):
- # set ordering for config options
- titleLabel = "Config Option Ordering:"
- options = [FIELD_ATTR[field][0] for field in Field.values()]
- oldSelection = [FIELD_ATTR[field][0] for field in self.sortOrdering]
- optionColors = dict([FIELD_ATTR[field] for field in Field.values()])
- results = popups.showSortDialog(titleLabel, options, oldSelection, optionColors)
-
- if results:
- # converts labels back to enums
- resultEnums = [getFieldFromLabel(label) for label in results]
- self.setSortOrder(resultEnums)
+ self.showSortDialog()
elif key == ord('w') or key == ord('W'):
- # display a popup for saving the current configuration
- configLines = torConfig.getCustomOptions(True)
- popup, width, height = popups.init(len(configLines) + 2)
- if not popup: return
+ self.showWriteDialog()
+ else: isKeystrokeConsumed = False
+
+ self.valsLock.release()
+ return isKeystrokeConsumed
+
+ def showWriteDialog(self):
+ """
+ Provies an interface to confirm if the configuration is saved and, if so,
+ where.
+ """
+
+ # display a popup for saving the current configuration
+ cli.controller.getController().requestRedraw(True)
+ configLines = torConfig.getCustomOptions(True)
+ popup, width, height = popups.init(len(configLines) + 2)
+ if not popup: return
+
+ try:
+ # displayed options (truncating the labels if there's limited room)
+ if width >= 30: selectionOptions = ("Save", "Save As...", "Cancel")
+ else: selectionOptions = ("Save", "Save As", "X")
- try:
- # displayed options (truncating the labels if there's limited room)
- if width >= 30: selectionOptions = ("Save", "Save As...", "Cancel")
- else: selectionOptions = ("Save", "Save As", "X")
+ # checks if we can show options beside the last line of visible content
+ isOptionLineSeparate = False
+ lastIndex = min(height - 2, len(configLines) - 1)
+
+ # if we don't have room to display the selection options and room to
+ # grow then display the selection options on its own line
+ if width < (30 + len(configLines[lastIndex])):
+ popup.setHeight(height + 1)
+ popup.redraw(True) # recreates the window instance
+ newHeight, _ = popup.getPreferredSize()
- # checks if we can show options beside the last line of visible content
- isOptionLineSeparate = False
- lastIndex = min(height - 2, len(configLines) - 1)
+ if newHeight > height:
+ height = newHeight
+ isOptionLineSeparate = True
+
+ key, selection = 0, 2
+ while not uiTools.isSelectionKey(key):
+ # if the popup has been resized then recreate it (needed for the
+ # proper border height)
+ newHeight, newWidth = popup.getPreferredSize()
+ if (height, width) != (newHeight, newWidth):
+ height, width = newHeight, newWidth
+ popup.redraw(True)
- # if we don't have room to display the selection options and room to
- # grow then display the selection options on its own line
- if width < (30 + len(configLines[lastIndex])):
- popup.setHeight(height + 1)
- popup.redraw(True) # recreates the window instance
- newHeight, _ = popup.getPreferredSize()
-
- if newHeight > height:
- height = newHeight
- isOptionLineSeparate = True
+ # if there isn't room to display the popup then cancel it
+ if height <= 2:
+ selection = 2
+ break
- key, selection = 0, 2
- while not uiTools.isSelectionKey(key):
- # if the popup has been resized then recreate it (needed for the
- # proper border height)
- newHeight, newWidth = popup.getPreferredSize()
- if (height, width) != (newHeight, newWidth):
- height, width = newHeight, newWidth
- popup.redraw(True)
-
- # if there isn't room to display the popup then cancel it
- if height <= 2:
- selection = 2
- break
-
- popup.win.erase()
- popup.win.box()
- popup.addstr(0, 0, "Configuration being saved:", curses.A_STANDOUT)
+ popup.win.erase()
+ popup.win.box()
+ popup.addstr(0, 0, "Configuration being saved:", curses.A_STANDOUT)
+
+ visibleConfigLines = height - 3 if isOptionLineSeparate else height - 2
+ for i in range(visibleConfigLines):
+ line = uiTools.cropStr(configLines[i], width - 2)
- visibleConfigLines = height - 3 if isOptionLineSeparate else height - 2
- for i in range(visibleConfigLines):
- line = uiTools.cropStr(configLines[i], width - 2)
-
- if " " in line:
- option, arg = line.split(" ", 1)
- popup.addstr(i + 1, 1, option, curses.A_BOLD | uiTools.getColor("green"))
- popup.addstr(i + 1, len(option) + 2, arg, curses.A_BOLD | uiTools.getColor("cyan"))
- else:
- popup.addstr(i + 1, 1, line, curses.A_BOLD | uiTools.getColor("green"))
+ if " " in line:
+ option, arg = line.split(" ", 1)
+ popup.addstr(i + 1, 1, option, curses.A_BOLD | uiTools.getColor("green"))
+ popup.addstr(i + 1, len(option) + 2, arg, curses.A_BOLD | uiTools.getColor("cyan"))
+ else:
+ popup.addstr(i + 1, 1, line, curses.A_BOLD | uiTools.getColor("green"))
+
+ # draws selection options (drawn right to left)
+ drawX = width - 1
+ for i in range(len(selectionOptions) - 1, -1, -1):
+ optionLabel = selectionOptions[i]
+ drawX -= (len(optionLabel) + 2)
- # draws selection options (drawn right to left)
- drawX = width - 1
- for i in range(len(selectionOptions) - 1, -1, -1):
- optionLabel = selectionOptions[i]
- drawX -= (len(optionLabel) + 2)
-
- # if we've run out of room then drop the option (this will only
- # occure on tiny displays)
- if drawX < 1: break
-
- selectionFormat = curses.A_STANDOUT if i == selection else curses.A_NORMAL
- popup.addstr(height - 2, drawX, "[")
- popup.addstr(height - 2, drawX + 1, optionLabel, selectionFormat | curses.A_BOLD)
- popup.addstr(height - 2, drawX + len(optionLabel) + 1, "]")
-
- drawX -= 1 # space gap between the options
+ # if we've run out of room then drop the option (this will only
+ # occure on tiny displays)
+ if drawX < 1: break
- popup.win.refresh()
+ selectionFormat = curses.A_STANDOUT if i == selection else curses.A_NORMAL
+ popup.addstr(height - 2, drawX, "[")
+ popup.addstr(height - 2, drawX + 1, optionLabel, selectionFormat | curses.A_BOLD)
+ popup.addstr(height - 2, drawX + len(optionLabel) + 1, "]")
- key = controller.getController().getScreen().getch()
- if key == curses.KEY_LEFT: selection = max(0, selection - 1)
- elif key == curses.KEY_RIGHT: selection = min(len(selectionOptions) - 1, selection + 1)
+ drawX -= 1 # space gap between the options
- if selection in (0, 1):
- loadedTorrc, promptCanceled = torConfig.getTorrc(), False
- try: configLocation = loadedTorrc.getConfigLocation()
- except IOError: configLocation = ""
-
- if selection == 1:
- # prompts user for a configuration location
- configLocation = popups.inputPrompt("Save to (esc to cancel): ", configLocation)
- if not configLocation: promptCanceled = True
+ popup.win.refresh()
+
+ key = cli.controller.getController().getScreen().getch()
+ if key == curses.KEY_LEFT: selection = max(0, selection - 1)
+ elif key == curses.KEY_RIGHT: selection = min(len(selectionOptions) - 1, selection + 1)
+
+ if selection in (0, 1):
+ loadedTorrc, promptCanceled = torConfig.getTorrc(), False
+ try: configLocation = loadedTorrc.getConfigLocation()
+ except IOError: configLocation = ""
+
+ if selection == 1:
+ # prompts user for a configuration location
+ configLocation = popups.inputPrompt("Save to (esc to cancel): ", configLocation)
+ if not configLocation: promptCanceled = True
+
+ if not promptCanceled:
+ try:
+ torConfig.saveConf(configLocation, configLines)
+ msg = "Saved configuration to %s" % configLocation
+ except IOError, exc:
+ msg = "Unable to save configuration (%s)" % sysTools.getFileErrorMsg(exc)
- if not promptCanceled:
- try:
- torConfig.saveConf(configLocation, configLines)
- msg = "Saved configuration to %s" % configLocation
- except IOError, exc:
- msg = "Unable to save configuration (%s)" % sysTools.getFileErrorMsg(exc)
-
- popups.showMsg(msg, 2)
- finally: popups.finalize()
- else: isKeystrokeConsumed = False
-
- self.valsLock.release()
- return isKeystrokeConsumed
+ popups.showMsg(msg, 2)
+ finally: popups.finalize()
def getHelp(self):
options = []
diff --git a/src/cli/menu/actions.py b/src/cli/menu/actions.py
index 2827742..2072ead 100644
--- a/src/cli/menu/actions.py
+++ b/src/cli/menu/actions.py
@@ -28,17 +28,8 @@ def makeMenu():
baseMenu.add(makeLogMenu(pagePanel))
elif pagePanel.getName() == "connections":
baseMenu.add(makeConnectionsMenu(pagePanel))
-
- 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)
+ elif pagePanel.getName() == "configuration":
+ baseMenu.add(makeConfigurationMenu(pagePanel))
return baseMenu
@@ -226,3 +217,24 @@ def makeConnectionsMenu(connPanel):
return connectionsMenu
+def makeConfigurationMenu(configPanel):
+ """
+ Submenu for the configuration panel, consisting of...
+ Save Config...
+ Sorting...
+ Filter / Unfilter Options
+
+ Arguments:
+ configPanel - instance of the configuration panel
+ """
+
+ configMenu = cli.menu.item.Submenu("Configuration")
+ configMenu.add(cli.menu.item.MenuItem("Save Config...", configPanel.showWriteDialog))
+ configMenu.add(cli.menu.item.MenuItem("Sorting...", configPanel.showSortDialog))
+
+ if configPanel.showAll: label, arg = "Filter", True
+ else: label, arg = "Unfilter", False
+ configMenu.add(cli.menu.item.MenuItem("%s Options" % label, functools.partial(configPanel.setFiltering, arg)))
+
+ return configMenu
+
1
0
commit bf373956a64afa6126b0836329042775119a6114
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sun Jun 12 17:39:19 2011 -0700
Binding handlers for the torrc submenu
---
README | 1 +
src/cli/menu/actions.py | 26 ++++++++++++++++++
src/cli/torrcPanel.py | 65 ++++++++++++++++++++++++++++++++++-------------
3 files changed, 74 insertions(+), 18 deletions(-)
diff --git a/README b/README
index 0f26163..44fabf1 100644
--- a/README
+++ b/README
@@ -180,6 +180,7 @@ Layout:
__init__.py
menu.py - provides an interactive menu
item.py - individual items within the menu
+ actions.py - handlers for menu selections
__init__.py
controller.py - main display loop, handling input and layout
diff --git a/src/cli/menu/actions.py b/src/cli/menu/actions.py
index 2072ead..53e5d1c 100644
--- a/src/cli/menu/actions.py
+++ b/src/cli/menu/actions.py
@@ -30,6 +30,8 @@ def makeMenu():
baseMenu.add(makeConnectionsMenu(pagePanel))
elif pagePanel.getName() == "configuration":
baseMenu.add(makeConfigurationMenu(pagePanel))
+ elif pagePanel.getName() == "torrc":
+ baseMenu.add(makeTorrcMenu(pagePanel))
return baseMenu
@@ -238,3 +240,27 @@ def makeConfigurationMenu(configPanel):
return configMenu
+def makeTorrcMenu(torrcPanel):
+ """
+ Submenu for the torrc panel, consisting of...
+ Reload
+ Show / Hide Comments
+ Show / Hide Line Numbers
+
+ Arguments:
+ torrcPanel - instance of the torrc panel
+ """
+
+ torrcMenu = cli.menu.item.Submenu("Torrc")
+ torrcMenu.add(cli.menu.item.MenuItem("Reload", torrcPanel.reloadTorrc))
+
+ if torrcPanel.stripComments: label, arg = "Show", True
+ else: label, arg = "Hide", False
+ torrcMenu.add(cli.menu.item.MenuItem("%s Comments" % label, functools.partial(torrcPanel.setCommentsVisible, arg)))
+
+ if torrcPanel.showLineNum: label, arg = "Hide", False
+ else: label, arg = "Show", True
+ torrcMenu.add(cli.menu.item.MenuItem("%s Line Numbers" % label, functools.partial(torrcPanel.setLineNumberVisible, arg)))
+
+ return torrcMenu
+
diff --git a/src/cli/torrcPanel.py b/src/cli/torrcPanel.py
index a12cc87..c273bf0 100644
--- a/src/cli/torrcPanel.py
+++ b/src/cli/torrcPanel.py
@@ -6,6 +6,7 @@ import math
import curses
import threading
+import cli.controller
import popups
from util import conf, enum, panel, torConfig, torTools, uiTools
@@ -59,6 +60,49 @@ class TorrcPanel(panel.Panel):
self.redraw(True)
except: pass
+ def setCommentsVisible(self, isVisible):
+ """
+ Sets if comments and blank lines are shown or stripped.
+
+ Arguments:
+ isVisible - displayed comments and blank lines if true, strips otherwise
+ """
+
+ self.stripComments = not isVisible
+ self._lastContentHeightArgs = None
+ self.redraw(True)
+
+ def setLineNumberVisible(self, isVisible):
+ """
+ Sets if line numbers are shown or hidden.
+
+ Arguments:
+ isVisible - displays line numbers if true, hides otherwise
+ """
+
+ self.showLineNum = isVisible
+ self._lastContentHeightArgs = None
+ self.redraw(True)
+
+ def reloadTorrc(self):
+ """
+ Reloads the torrc, displaying an indicator of success or failure.
+ """
+
+ cli.controller.getController().requestRedraw(True)
+
+ try:
+ torConfig.getTorrc().load()
+ self._lastContentHeightArgs = None
+ self.redraw(True)
+ resultMsg = "torrc reloaded"
+ except IOError:
+ resultMsg = "failed to reload torrc"
+
+ self._lastContentHeightArgs = None
+ self.redraw(True)
+ popups.showMsg(resultMsg, 1)
+
def handleKey(self, key):
self.valsLock.acquire()
isKeystrokeConsumed = True
@@ -70,26 +114,11 @@ class TorrcPanel(panel.Panel):
self.scroll = newScroll
self.redraw(True)
elif key == ord('n') or key == ord('N'):
- self.showLineNum = not self.showLineNum
- self._lastContentHeightArgs = None
- self.redraw(True)
+ self.setLineNumberVisible(not self.showLineNum)
elif key == ord('s') or key == ord('S'):
- self.stripComments = not self.stripComments
- self._lastContentHeightArgs = None
- self.redraw(True)
+ self.setCommentsVisible(self.stripComments)
elif key == ord('r') or key == ord('R'):
- # reloads torrc, providing a notice if successful or not
- try:
- torConfig.getTorrc().load()
- self._lastContentHeightArgs = None
- self.redraw(True)
- resultMsg = "torrc reloaded"
- except IOError:
- resultMsg = "failed to reload torrc"
-
- self._lastContentHeightArgs = None
- self.redraw(True)
- popups.showMsg(resultMsg, 1)
+ self.reloadTorrc()
else: isKeystrokeConsumed = False
self.valsLock.release()
1
0
17 Jul '11
commit ee2589f311dee651c0c8d47705ebb5cb07c95a23
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sun Jun 12 18:30:25 2011 -0700
Dropping alternative menu implementation
The other menus are complete now to dropping Kamran's implementation.
---
src/cli/controller.py | 11 -
src/cli/menu/menu.py | 3 +
src/cli/menu_alt/__init__.py | 6 -
src/cli/menu_alt/menu.py | 439 ------------------------------------------
src/cli/menu_alt/menuItem.py | 41 ----
5 files changed, 3 insertions(+), 497 deletions(-)
diff --git a/src/cli/controller.py b/src/cli/controller.py
index 6158ade..b019f8e 100644
--- a/src/cli/controller.py
+++ b/src/cli/controller.py
@@ -8,7 +8,6 @@ import curses
import threading
import cli.menu.menu
-import cli.menu_alt.menu
import cli.popups
import cli.headerPanel
import cli.logPanel
@@ -527,8 +526,6 @@ def drawTorMonitor(stdscr, startTime):
overrideKey = None # uses this rather than waiting on user input
isUnresponsive = False # flag for heartbeat responsiveness check
- menuKeys = []
-
while not control.isDone():
displayPanels = control.getDisplayPanels()
isUnresponsive = heartbeatCheck(isUnresponsive)
@@ -563,14 +560,6 @@ def drawTorMonitor(stdscr, startTime):
control.prevPage()
elif key == ord('p') or key == ord('P'):
control.setPaused(not control.isPaused())
- elif key == ord('n') or key == ord('N'):
- menu = cli.menu_alt.menu.Menu()
- menuKeys = menu.showMenu(keys=menuKeys)
- if menuKeys != []:
- for key in (ord('n'), ord('q'), ord('x')):
- 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'):
diff --git a/src/cli/menu/menu.py b/src/cli/menu/menu.py
index b411a9c..1dbbbd0 100644
--- a/src/cli/menu/menu.py
+++ b/src/cli/menu/menu.py
@@ -1,3 +1,6 @@
+"""
+Display logic for presenting the menu.
+"""
import curses
diff --git a/src/cli/menu_alt/__init__.py b/src/cli/menu_alt/__init__.py
deleted file mode 100644
index 6d7b6b7..0000000
--- a/src/cli/menu_alt/__init__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-"""
-Resources for displaying the menu.
-"""
-
-__all__ = ["menuItem", "menu"]
-
diff --git a/src/cli/menu_alt/menu.py b/src/cli/menu_alt/menu.py
deleted file mode 100644
index 7e10366..0000000
--- a/src/cli/menu_alt/menu.py
+++ /dev/null
@@ -1,439 +0,0 @@
-"""
-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
deleted file mode 100644
index 277c78a..0000000
--- a/src/cli/menu_alt/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 2e7531e2ac0c36516f4a0b7a7169a988710e041d
Author: Kamran Riaz Khan <krkhan(a)inspirated.com>
Date: Tue Jun 14 01:17:07 2011 +0500
Updated graph widgets' names.
---
src/gui/arm.xml | 22 ++++++++++++++--------
src/starter.py | 2 +-
2 files changed, 15 insertions(+), 9 deletions(-)
diff --git a/src/gui/arm.xml b/src/gui/arm.xml
index 85f60d3..a511583 100644
--- a/src/gui/arm.xml
+++ b/src/gui/arm.xml
@@ -307,11 +307,11 @@
<object class="GtkHBox" id="hbox_graph">
<property name="visible">True</property>
<child>
- <object class="GtkVBox" id="vbox_graph_a">
+ <object class="GtkVBox" id="vbox_graph_primary">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
- <object class="GtkLabel" id="label_graph_a_top">
+ <object class="GtkLabel" id="label_graph_primary_top">
<property name="visible">True</property>
<property name="label" translatable="yes">Download (0.0 b/sec)</property>
</object>
@@ -322,15 +322,18 @@
</packing>
</child>
<child>
- <object class="GtkVBox" id="placeholder_graph_a">
+ <object class="GtkVBox" id="placeholder_graph_primary">
<property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
- <object class="GtkLabel" id="label_graph_a_bottom">
+ <object class="GtkLabel" id="label_graph_primary_bottom">
<property name="visible">True</property>
<property name="label" translatable="yes">avg: 3.5 Kb/sec, total: 2.0 MB</property>
</object>
@@ -346,11 +349,11 @@
</packing>
</child>
<child>
- <object class="GtkVBox" id="vbox_graph_b">
+ <object class="GtkVBox" id="vbox_graph_secondary">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
- <object class="GtkLabel" id="label_graph_b_top">
+ <object class="GtkLabel" id="label_graph_secondary_top">
<property name="visible">True</property>
<property name="label" translatable="yes">Upload (0.0 b/sec)</property>
</object>
@@ -361,15 +364,18 @@
</packing>
</child>
<child>
- <object class="GtkVBox" id="placeholder_graph_b">
+ <object class="GtkVBox" id="placeholder_graph_secondary">
<property name="visible">True</property>
+ <child>
+ <placeholder/>
+ </child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
- <object class="GtkLabel" id="label_graph_b_bottom">
+ <object class="GtkLabel" id="label_graph_secondary_bottom">
<property name="visible">True</property>
<property name="label" translatable="yes">avg: 3.5 Kb/sec, total: 2.0 MB</property>
</object>
diff --git a/src/starter.py b/src/starter.py
index d2ac910..50feceb 100644
--- a/src/starter.py
+++ b/src/starter.py
@@ -30,7 +30,7 @@ import util.uiTools
import TorCtl.TorCtl
import TorCtl.TorUtil
-INCLUDE_GUI = False
+INCLUDE_GUI = True
LOG_DUMP_PATH = os.path.expanduser("~/.arm/log")
DEFAULT_CONFIG = os.path.expanduser("~/.arm/armrc")
CONFIG = {"startup.controlPassword": None,
1
0