[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] r23744: {arm} Making the config sort order configurable via the armrc (sti (in arm/trunk: . src/interface src/util)



Author: atagar
Date: 2010-11-04 17:18:58 +0000 (Thu, 04 Nov 2010)
New Revision: 23744

Modified:
   arm/trunk/armrc.sample
   arm/trunk/src/interface/configStatePanel.py
   arm/trunk/src/interface/controller.py
   arm/trunk/src/util/conf.py
Log:
Making the config sort order configurable via the armrc (still working on the sort dialog)
change: dropping the argument type from the main panel to make more room for the description
fix: making the default runlevel for config type errors NOTICE (was accidently lowered to INFO)



Modified: arm/trunk/armrc.sample
===================================================================
--- arm/trunk/armrc.sample	2010-11-03 16:51:12 UTC (rev 23743)
+++ arm/trunk/armrc.sample	2010-11-04 17:18:58 UTC (rev 23744)
@@ -58,6 +58,10 @@
 # ---------------------------
 # type
 #   0 -> tor state, 1 -> torrc, 2 -> arm state, 3 -> armrc
+# order
+#   three comma separated configuration attributes, options including:
+#   0 -> Category,  1 -> Option Name,   2 -> Value,     3 -> Arg Type,
+#   4 -> Arg Usage, 5 -> Description,   6 -> Is Default
 # colWidth.*
 #   maximum column content width
 # showScrollbars
@@ -72,6 +76,7 @@
 #   values or being setable themselves
 
 features.config.type 0
+features.config.order 0, 1, 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-03 16:51:12 UTC (rev 23743)
+++ arm/trunk/src/interface/configStatePanel.py	2010-11-04 17:18:58 UTC (rev 23744)
@@ -6,12 +6,13 @@
 import curses
 import threading
 
-from util import conf, panel, torTools, torConfig, uiTools
+from util import conf, log, panel, torTools, torConfig, uiTools
 
 DEFAULT_CONFIG = {"features.config.showPrivateOptions": False,
                   "features.config.showVirtualOptions": False,
                   "features.config.state.colWidth.option": 25,
-                  "features.config.state.colWidth.value": 15}
+                  "features.config.state.colWidth.value": 15,
+                  "log.configEntryTypeError": log.NOTICE}
 
 TOR_STATE, ARM_STATE = range(1, 3) # state to be presented
 
@@ -26,13 +27,15 @@
                   torConfig.UNKNOWN: "black"}
 
 # attributes of a ConfigEntry
-FIELD_CATEGORY, FIELD_OPTION, FIELD_VALUE, FIELD_TYPE, FIELD_ARG_USAGE, FIELD_DESCRIPTION, FIELD_IS_DEFAULT = range(1, 8)
-FIELD_STR = {FIELD_CATEGORY: "Category",
-             FIELD_OPTION: "Option Name",
-             FIELD_TYPE: "Arg Type",
-             FIELD_ARG_USAGE: "Arg Usage",
-             FIELD_DESCRIPTION: "Description",
-             FIELD_IS_DEFAULT: "Is Default"}
+FIELD_CATEGORY, FIELD_OPTION, FIELD_VALUE, FIELD_TYPE, FIELD_ARG_USAGE, FIELD_DESCRIPTION, FIELD_IS_DEFAULT = range(7)
+DEFAULT_SORT_ORDER = (FIELD_CATEGORY, FIELD_OPTION, FIELD_IS_DEFAULT)
+FIELD_ATTR = {FIELD_CATEGORY: ("Category", "red"),
+              FIELD_OPTION: ("Option Name", "blue"),
+              FIELD_VALUE: ("Value", "cyan"),
+              FIELD_TYPE: ("Arg Type", "green"),
+              FIELD_ARG_USAGE: ("Arg Usage", "yellow"),
+              FIELD_DESCRIPTION: ("Description", "white"),
+              FIELD_IS_DEFAULT: ("Is Default", "magenta")}
 
 class ConfigEntry():
   """
@@ -98,10 +101,35 @@
   def __init__(self, stdscr, configType, config=None):
     panel.Panel.__init__(self, stdscr, "configState", 0)
     
+    self.sortOrdering = DEFAULT_SORT_ORDER
     self._config = dict(DEFAULT_CONFIG)
-    if config: config.update(self._config, {
-      "features.config.state.colWidth.option": 5,
-      "features.config.state.colWidth.value": 5})
+    if config:
+      config.update(self._config, {
+        "features.config.state.colWidth.option": 5,
+        "features.config.state.colWidth.value": 5})
+      
+      # overrides the initial sort orderting if set
+      confSortOrder = config.get("features.config.order")
+      if confSortOrder:
+        # validates the input, setting the errorMsg if there's a problem
+        confSortOrderComp, errorMsg = confSortOrder.split(","), None
+        defaultOrderStr = ", ".join([str(i) for i in self.sortOrdering])
+        baseErrorMsg = "config entry 'features.config.order' is expected to %%s, defaulting to '%s'" % defaultOrderStr
+        
+        if len(confSortOrderComp) != 3:
+          # checks that we got three comma separated values
+          errorMsg = baseErrorMsg % "be three comma separated values"
+        else:
+          # checks that values are numeric and in the right range
+          for val in confSortOrderComp:
+            val = val.strip()
+            if not val.isdigit() or int(val) < 0 or int(val) > 6:
+              errorMsg = baseErrorMsg % "only have numeric entries ranging 0-6"
+              break
+        
+        if errorMsg: log.log(self._config["log.configEntryTypeError"], errorMsg)
+        else:
+          self.sortOrdering = [int(val.strip()) for val in confSortOrderComp]
     
     self.configType = configType
     self.confContents = []
@@ -139,21 +167,40 @@
         
         self.confContents.append(ConfigEntry(cat, confOption, confType, arg, desc, not confOption in setOptions))
       
-      self.confContents.sort(key=lambda i: (i.getAttr([FIELD_CATEGORY, FIELD_OPTION, FIELD_IS_DEFAULT])))
+      
+      self.setSortOrder() # initial sorting of the contents
     elif self.configType == ARM_STATE:
       # loaded via the conf utility
       armConf = conf.getConfig("arm")
       for key in armConf.getKeys():
         self.confContents.append(ConfigEntry("", key, ", ".join(armConf.getValue(key, [], True)), "", "", True))
-      
-      #self.confContents.sort() # TODO: make contents sortable?
   
+  def setSortOrder(self, ordering = None):
+    """
+    Sets the configuration attributes we're sorting by and resorts the
+    contents. If the ordering isn't defined then this resorts based on the
+    last set ordering.
+    """
+    
+    self.valsLock.acquire()
+    if ordering: self.sortOrdering = ordering
+    self.confContents.sort(key=lambda i: (i.getAttr(self.sortOrdering)))
+    self.valsLock.release()
+  
+  def getSortOrder(self):
+    """
+    Provides the current configuration attributes we're sorting by.
+    """
+    
+    return self.sortOrdering
+  
   def handleKey(self, key):
     self.valsLock.acquire()
     if uiTools.isScrollKey(key):
       pageHeight = self.getPreferredSize()[0] - 1
       isChanged = self.scroller.handleKey(key, self.confContents, pageHeight)
       if isChanged: self.redraw(True)
+    self.valsLock.release()
   
   def draw(self, subwindow, width, height):
     self.valsLock.acquire()
@@ -172,7 +219,7 @@
       self.addScrollBar(scrollLoc, scrollLoc + height - 1, len(self.confContents), 1)
     
     # determines the width for the columns
-    optionColWidth, valueColWidth, typeColWidth = 0, 0, 0
+    optionColWidth, valueColWidth = 0, 0
     
     # constructs a mapping of entries to their current values
     entryToValues = {}
@@ -180,11 +227,10 @@
       entryToValues[entry] = entry.get(FIELD_VALUE)
       optionColWidth = max(optionColWidth, len(entry.get(FIELD_OPTION)))
       valueColWidth = max(valueColWidth, len(entryToValues[entry]))
-      typeColWidth = max(typeColWidth, len(entry.get(FIELD_TYPE)))
     
     optionColWidth = min(self._config["features.config.state.colWidth.option"], optionColWidth)
     valueColWidth = min(self._config["features.config.state.colWidth.value"], valueColWidth)
-    descriptionColWidth = max(0, width - scrollOffset - optionColWidth - valueColWidth - typeColWidth - 3)
+    descriptionColWidth = max(0, width - scrollOffset - optionColWidth - valueColWidth - 2)
     
     for lineNum in range(scrollLoc, len(self.confContents)):
       entry = self.confContents[lineNum]
@@ -200,8 +246,8 @@
       #lineFormat = uiTools.getColor("green") if entry.isDefault else curses.A_BOLD | uiTools.getColor("yellow")
       if entry == cursorSelection: lineFormat |= curses.A_STANDOUT
       
-      lineTextLayout = "%%-%is %%-%is %%-%is %%-%is" % (optionColWidth, valueColWidth, typeColWidth, descriptionColWidth)
-      lineText = lineTextLayout % (optionLabel, valueLabel, entry.get(FIELD_TYPE), descriptionLabel)
+      lineTextLayout = "%%-%is %%-%is %%-%is" % (optionColWidth, valueColWidth, descriptionColWidth)
+      lineText = lineTextLayout % (optionLabel, valueLabel, descriptionLabel)
       self.addstr(drawLine, scrollOffset, lineText, lineFormat)
       
       if drawLine >= height: break

Modified: arm/trunk/src/interface/controller.py
===================================================================
--- arm/trunk/src/interface/controller.py	2010-11-03 16:51:12 UTC (rev 23743)
+++ arm/trunk/src/interface/controller.py	2010-11-04 17:18:58 UTC (rev 23744)
@@ -1484,6 +1484,77 @@
       if selection != -1: panels["torrc"].setConfigType(selection)
       
       selectiveRefresh(panels, page)
+    elif page == 2 and (key == ord('s') or key == ord('S')):
+      # set ordering for config options
+      panel.CURSES_LOCK.acquire()
+      try:
+        setPauseState(panels, isPaused, page, True)
+        curses.cbreak() # wait indefinitely for key presses (no timeout)
+        
+        # lists event types
+        popup = panels["popup"]
+        selections = []     # new ordering
+        cursorLoc = 0       # index of highlighted option
+        
+        # listing of inital ordering
+        prevOrdering = "<b>Current Order: "
+        for sortType in panels["torrc"].sortOrdering:
+          sortStr, colorStr = configStatePanel.FIELD_ATTR[sortType]
+          prevOrdering += "<%s>%s</%s>, " % (colorStr, sortStr, colorStr)
+        prevOrdering = prevOrdering[:-2] + "</b>"
+        
+        # Makes listing of all options
+        options = []
+        for sortType in range(7): options.append(configStatePanel.FIELD_ATTR[sortType][0])
+        options.append("Cancel")
+        
+        while len(selections) < 3:
+          popup.clear()
+          popup.win.box()
+          popup.addstr(0, 0, "Config Option Ordering:", curses.A_STANDOUT)
+          popup.addfstr(1, 2, prevOrdering)
+          
+          # provides new ordering
+          newOrdering = "<b>New Order: "
+          if selections:
+            for sortType in selections:
+              sortStr, colorStr = configStatePanel.FIELD_ATTR[sortType]
+              prevOrdering += "<%s>%s</%s>, " % (colorStr, sortStr, colorStr)
+            newOrdering = newOrdering[:-2] + "</b>"
+          else: newOrdering += "</b>"
+          popup.addfstr(2, 2, newOrdering)
+          
+          row, col, index = 4, 0, 0
+          for option in options:
+            popup.addstr(row, col * 19 + 2, option, curses.A_STANDOUT if cursorLoc == index else curses.A_NORMAL)
+            col += 1
+            index += 1
+            if col == 4: row, col = row + 1, 0
+          
+          popup.refresh()
+          
+          key = stdscr.getch()
+          if key == curses.KEY_LEFT: cursorLoc = max(0, cursorLoc - 1)
+          elif key == curses.KEY_RIGHT: cursorLoc = min(len(options) - 1, cursorLoc + 1)
+          elif key == curses.KEY_UP: cursorLoc = max(0, cursorLoc - 4)
+          elif key == curses.KEY_DOWN: cursorLoc = min(len(options) - 1, cursorLoc + 4)
+          elif key in (curses.KEY_ENTER, 10, ord(' ')):
+            # selected entry (the ord of '10' seems needed to pick up enter)
+            selection = options[cursorLoc]
+            if selection == "Cancel": break
+            else:
+              options.remove(selection)
+              cursorLoc = min(cursorLoc, len(options) - 1)
+          elif key == 27: break # esc - cancel
+          
+        if len(selections) == 3:
+          panels["torrc"].setSortOrder(selections)
+        
+        setPauseState(panels, isPaused, page)
+        curses.halfdelay(REFRESH_RATE * 10) # reset normal pausing behavior
+        panels["torrc"].redraw(True)
+      finally:
+        panel.CURSES_LOCK.release()
     elif page == 0:
       panels["log"].handleKey(key)
     elif page == 1:

Modified: arm/trunk/src/util/conf.py
===================================================================
--- arm/trunk/src/util/conf.py	2010-11-03 16:51:12 UTC (rev 23743)
+++ arm/trunk/src/util/conf.py	2010-11-04 17:18:58 UTC (rev 23744)
@@ -21,7 +21,7 @@
 
 CONFS = {}  # mapping of identifier to singleton instances of configs
 CONFIG = {"log.configEntryNotFound": None,
-          "log.configEntryTypeError": log.INFO}
+          "log.configEntryTypeError": log.NOTICE}
 
 def loadConfig(config):
   config.update(CONFIG)