[or-cvs] r23794: {arm} Detail panel for the currently selected configuration option (in arm/trunk: . src/interface src/util)

Damian Johnson atagar1 at gmail.com
Sun Nov 14 03:28:42 UTC 2010


Author: atagar
Date: 2010-11-14 03:28:42 +0000 (Sun, 14 Nov 2010)
New Revision: 23794

Modified:
   arm/trunk/armrc.sample
   arm/trunk/src/interface/configStatePanel.py
   arm/trunk/src/interface/logPanel.py
   arm/trunk/src/util/connections.py
   arm/trunk/src/util/torConfig.py
   arm/trunk/src/util/uiTools.py
Log:
Detail panel for the currently selected configuration option.



Modified: arm/trunk/armrc.sample
===================================================================
--- arm/trunk/armrc.sample	2010-11-14 00:22:06 UTC (rev 23793)
+++ arm/trunk/armrc.sample	2010-11-14 03:28:42 UTC (rev 23794)
@@ -62,11 +62,14 @@
 #   three comma separated configuration attributes, options including:
 #   0 -> Category,  1 -> Option Name,   2 -> Value,     3 -> Arg Type,
 #   4 -> Arg Usage, 5 -> Description,   6 -> Man Entry, 7 -> Is Default
+# selectionDetails.height
+#   rows of data for the panel showing details on the current selection, this
+#   is disabled entirely if zero
 # colWidth.*
 #   maximum column content width
-# showScrollbars
+# file.showScrollbars
 #   displays scrollbars when the torrc content is longer than the display
-# maxLinesPerEntry
+# file.maxLinesPerEntry
 #   max number of lines to display for a single entry in the torrc
 # showPrivateOptions
 #   tor provides config options of the form "__<option>" that can be dangerous
@@ -77,6 +80,7 @@
 
 features.config.type 0
 features.config.order 0, 6, 7
+features.config.selectionDetails.height 6
 features.config.state.colWidth.option 25
 features.config.state.colWidth.value 15
 features.config.file.showScrollbars true

Modified: arm/trunk/src/interface/configStatePanel.py
===================================================================
--- arm/trunk/src/interface/configStatePanel.py	2010-11-14 00:22:06 UTC (rev 23793)
+++ arm/trunk/src/interface/configStatePanel.py	2010-11-14 03:28:42 UTC (rev 23794)
@@ -8,7 +8,8 @@
 
 from util import conf, panel, torTools, torConfig, uiTools
 
-DEFAULT_CONFIG = {"features.config.showPrivateOptions": False,
+DEFAULT_CONFIG = {"features.config.selectionDetails.height": 6,
+                  "features.config.showPrivateOptions": False,
                   "features.config.showVirtualOptions": False,
                   "features.config.state.colWidth.option": 25,
                   "features.config.state.colWidth.value": 15}
@@ -113,6 +114,7 @@
     self._config = dict(DEFAULT_CONFIG)
     if config:
       config.update(self._config, {
+        "features.config.selectionDetails.height": 0,
         "features.config.state.colWidth.option": 5,
         "features.config.state.colWidth.value": 5})
       
@@ -182,6 +184,10 @@
     self.valsLock.acquire()
     if uiTools.isScrollKey(key):
       pageHeight = self.getPreferredSize()[0] - 1
+      detailPanelHeight = self._config["features.config.selectionDetails.height"]
+      if detailPanelHeight > 0 and detailPanelHeight + 2 <= pageHeight:
+        pageHeight -= (detailPanelHeight + 1)
+      
       isChanged = self.scroller.handleKey(key, self.confContents, pageHeight)
       if isChanged: self.redraw(True)
     self.valsLock.release()
@@ -190,17 +196,30 @@
     self.valsLock.acquire()
     
     # draws the top label
-    sourceLabel = "Tor" if self.configType == TOR_STATE else "Arm"
-    self.addstr(0, 0, "%s Config:" % sourceLabel, curses.A_STANDOUT)
+    titleLabel = "%s Configuration:" % ("Tor" if self.configType == TOR_STATE else "Arm")
+    self.addstr(0, 0, titleLabel, curses.A_STANDOUT)
     
-    scrollLoc = self.scroller.getScrollLoc(self.confContents, height - 1)
-    cursorSelection = self.scroller.getCursorSelection(self.confContents)
+    # panel with details for the current selection
+    detailPanelHeight = self._config["features.config.selectionDetails.height"]
+    if detailPanelHeight == 0 or detailPanelHeight + 2 >= height:
+      # no detail panel
+      detailPanelHeight = 0
+      scrollLoc = self.scroller.getScrollLoc(self.confContents, height - 1)
+      cursorSelection = self.scroller.getCursorSelection(self.confContents)
+    else:
+      # Shrink detail panel if there isn't sufficient room for the whole
+      # thing. The extra line is for the bottom border.
+      detailPanelHeight = min(height - 1, detailPanelHeight + 1)
+      scrollLoc = self.scroller.getScrollLoc(self.confContents, height - 1 - detailPanelHeight)
+      cursorSelection = self.scroller.getCursorSelection(self.confContents)
+      
+      self._drawSelectionPanel(cursorSelection, width, detailPanelHeight, titleLabel)
     
     # draws left-hand scroll bar if content's longer than the height
     scrollOffset = 0
-    if len(self.confContents) > height - 1:
+    if len(self.confContents) > height - detailPanelHeight - 1:
       scrollOffset = 3
-      self.addScrollBar(scrollLoc, scrollLoc + height - 1, len(self.confContents), 1)
+      self.addScrollBar(scrollLoc, scrollLoc + height - detailPanelHeight - 1, len(self.confContents), 1 + detailPanelHeight)
     
     # determines the width for the columns
     optionColWidth, valueColWidth = 0, 0
@@ -218,7 +237,7 @@
     
     for lineNum in range(scrollLoc, len(self.confContents)):
       entry = self.confContents[lineNum]
-      drawLine = lineNum + 1 - scrollLoc
+      drawLine = lineNum + detailPanelHeight + 1 - scrollLoc
       
       # TODO: need to cut off description at the first newline
       optionLabel = uiTools.cropStr(entry.get(FIELD_OPTION), optionColWidth)
@@ -237,4 +256,71 @@
       if drawLine >= height: break
     
     self.valsLock.release()
+  
+  def _drawSelectionPanel(self, cursorSelection, width, detailPanelHeight, titleLabel):
+    """
+    Renders a panel for the selected configuration option.
+    """
+    
+    # border (top)
+    if width >= len(titleLabel):
+      self.win.hline(0, len(titleLabel), curses.ACS_HLINE, width - len(titleLabel))
+      self.win.vline(0, width, curses.ACS_URCORNER, 1)
+    
+    # border (sides)
+    self.win.vline(1, 0, curses.ACS_VLINE, detailPanelHeight - 1)
+    self.win.vline(1, width, curses.ACS_VLINE, detailPanelHeight - 1)
+    
+    # border (bottom)
+    self.win.vline(detailPanelHeight, 0, curses.ACS_LLCORNER, 1)
+    if width >= 2: self.win.vline(detailPanelHeight, 1, curses.ACS_TTEE, 1)
+    if width >= 3: self.win.hline(detailPanelHeight, 2, curses.ACS_HLINE, width - 2)
+    self.win.vline(detailPanelHeight, width, curses.ACS_LRCORNER, 1)
+    
+    selectionFormat = curses.A_BOLD | uiTools.getColor(CATEGORY_COLOR[cursorSelection.get(FIELD_CATEGORY)])
+    
+    # first entry:
+    # <option> (<category> Option)
+    optionLabel =" (%s Option)" % torConfig.OPTION_CATEGORY_STR[cursorSelection.get(FIELD_CATEGORY)]
+    self.addstr(1, 2, cursorSelection.get(FIELD_OPTION) + optionLabel, selectionFormat)
+    
+    # second entry:
+    # Value: <value> ([default|custom], <type>, usage: <argument usage>)
+    if detailPanelHeight >= 3:
+      valueAttr = []
+      valueAttr.append("default" if cursorSelection.get(FIELD_IS_DEFAULT) else "custom")
+      valueAttr.append(cursorSelection.get(FIELD_TYPE))
+      valueAttr.append("usage: %s" % (cursorSelection.get(FIELD_ARG_USAGE)))
+      valueAttrLabel = ", ".join(valueAttr)
+      
+      valueLabelWidth = width - 12 - len(valueAttrLabel)
+      valueLabel = uiTools.cropStr(cursorSelection.get(FIELD_VALUE), valueLabelWidth)
+      
+      self.addstr(2, 2, "Value: %s (%s)" % (valueLabel, valueAttrLabel), selectionFormat)
+    
+    # remainder is filled with the man page description
+    descriptionHeight = max(0, detailPanelHeight - 3)
+    descriptionContent = "Description: " + cursorSelection.get(FIELD_DESCRIPTION)
+    
+    for i in range(descriptionHeight):
+      # checks if we're done writing the description
+      if not descriptionContent: break
+      
+      # there's a leading indent after the first line
+      if i > 0: descriptionContent = "  " + descriptionContent
+      
+      # we only want to work with content up until the next newline
+      if "\n" in descriptionContent:
+        lineContent, descriptionContent = descriptionContent.split("\n", 1)
+      else: lineContent, descriptionContent = descriptionContent, ""
+      
+      if i != descriptionHeight - 1:
+        # there's more lines to display
+        msg, remainder = uiTools.cropStr(lineContent, width - 2, 4, 4, uiTools.END_WITH_HYPHEN, True)
+        descriptionContent = remainder.strip() + descriptionContent
+      else:
+        # this is the last line, end it with an ellipse
+        msg = uiTools.cropStr(lineContent, width - 2, 4, 4)
+      
+      self.addstr(3 + i, 2, msg, selectionFormat)
 

Modified: arm/trunk/src/interface/logPanel.py
===================================================================
--- arm/trunk/src/interface/logPanel.py	2010-11-14 00:22:06 UTC (rev 23793)
+++ arm/trunk/src/interface/logPanel.py	2010-11-14 03:28:42 UTC (rev 23794)
@@ -8,7 +8,6 @@
 import os
 import curses
 import threading
-from curses.ascii import isprint
 
 from TorCtl import TorCtl
 
@@ -616,7 +615,7 @@
     if not event.type in self.loggedEvents: return
     
     # strips control characters to avoid screwing up the terminal
-    event.msg = "".join([char for char in event.msg if (isprint(char) or char == "\n")])
+    event.msg = uiTools.getPrintable(event.msg)
     
     # note event in the log file if we're saving them
     if self.logFile:

Modified: arm/trunk/src/util/connections.py
===================================================================
--- arm/trunk/src/util/connections.py	2010-11-14 00:22:06 UTC (rev 23793)
+++ arm/trunk/src/util/connections.py	2010-11-14 03:28:42 UTC (rev 23794)
@@ -147,7 +147,7 @@
   else: RESOLVERS[haltedIndex] = r
   return r
 
-if __name__ == '__main__':
+def test():
   # quick method for testing connection resolution
   userInput = raw_input("Enter query (<ss, netstat, lsof> PROCESS_NAME [PID]): ").split()
   

Modified: arm/trunk/src/util/torConfig.py
===================================================================
--- arm/trunk/src/util/torConfig.py	2010-11-14 00:22:06 UTC (rev 23793)
+++ arm/trunk/src/util/torConfig.py	2010-11-14 03:28:42 UTC (rev 23794)
@@ -159,6 +159,7 @@
       optionCount, lastOption, lastArg = 0, None, None
       lastCategory, lastDescription = GENERAL, ""
       for line in manCallResults:
+        line = uiTools.getPrintable(line)
         strippedLine = line.strip()
         
         # we have content, but an indent less than an option (ignore line)
@@ -535,7 +536,7 @@
         for lineNum in range(len(self.contents)):
           lineText = self.contents[lineNum]
           lineText = lineText.replace("\t", "   ")
-          lineText = "".join([char for char in lineText if curses.ascii.isprint(char)])
+          lineText = uiTools.getPrintable(lineText)
           self.displayableContents.append(lineText)
       
       if strip:

Modified: arm/trunk/src/util/uiTools.py
===================================================================
--- arm/trunk/src/util/uiTools.py	2010-11-14 00:22:06 UTC (rev 23793)
+++ arm/trunk/src/util/uiTools.py	2010-11-14 03:28:42 UTC (rev 23794)
@@ -8,6 +8,7 @@
 import sys
 import curses
 
+from curses.ascii import isprint
 from util import log
 
 # colors curses can handle
@@ -86,6 +87,19 @@
   
   stdscr.getch() # quit on keyboard input
 
+def getPrintable(line, keepNewlines = True):
+  """
+  Provides the line back with non-printable characters stripped.
+  
+  Arguments:
+    line          - string to be processed
+    stripNewlines - retains newlines if true, stripped otherwise
+  """
+  
+  line = line.replace('\xc2', "'")
+  line = "".join([char for char in line if (isprint(char) or (keepNewlines and char == "\n"))])
+  return line
+
 def getColor(color):
   """
   Provides attribute corresponding to a given text color. Supported colors



More information about the tor-commits mailing list