Exemple #1
0
    def handleKey(self, key):
        isSelectionSubmenu = isinstance(self._selection, cli.menu.item.Submenu)
        selectionHierarchy = self._selection.getHierarchy()

        if uiTools.isSelectionKey(key):
            if isSelectionSubmenu:
                if not self._selection.isEmpty():
                    self._selection = self._selection.getChildren()[0]
            else:
                self._isDone = self._selection.select()
        elif key == curses.KEY_UP:
            self._selection = self._selection.prev()
        elif key == curses.KEY_DOWN:
            self._selection = self._selection.next()
        elif key == curses.KEY_LEFT:
            if len(selectionHierarchy) <= 3:
                # shift to the previous main submenu
                prevSubmenu = selectionHierarchy[1].prev()
                self._selection = prevSubmenu.getChildren()[0]
            else:
                # go up a submenu level
                self._selection = self._selection.getParent()
        elif key == curses.KEY_RIGHT:
            if isSelectionSubmenu:
                # open submenu (same as making a selection)
                if not self._selection.isEmpty():
                    self._selection = self._selection.getChildren()[0]
            else:
                # shift to the next main submenu
                nextSubmenu = selectionHierarchy[1].next()
                self._selection = nextSubmenu.getChildren()[0]
        elif key in (27, ord('m'), ord('M')):
            # close menu
            self._isDone = True
Exemple #2
0
 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:
       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
       
       control.redraw()
   finally:
     control.setMsg()
     panel.CURSES_LOCK.release()
Exemple #3
0
 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:
       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
       
       control.redraw()
   finally:
     control.setMsg()
     panel.CURSES_LOCK.release()
Exemple #4
0
 def handleKey(self, key):
   isSelectionSubmenu = isinstance(self._selection, cli.menu.item.Submenu)
   selectionHierarchy = self._selection.getHierarchy()
   
   if uiTools.isSelectionKey(key):
     if isSelectionSubmenu:
       if not self._selection.isEmpty():
         self._selection = self._selection.getChildren()[0]
     else: self._isDone = self._selection.select()
   elif key == curses.KEY_UP:
     self._selection = self._selection.prev()
   elif key == curses.KEY_DOWN:
     self._selection = self._selection.next()
   elif key == curses.KEY_LEFT:
     if len(selectionHierarchy) <= 3:
       # shift to the previous main submenu
       prevSubmenu = selectionHierarchy[1].prev()
       self._selection = prevSubmenu.getChildren()[0]
     else:
       # go up a submenu level
       self._selection = self._selection.getParent()
   elif key == curses.KEY_RIGHT:
     if isSelectionSubmenu:
       # open submenu (same as making a selection)
       if not self._selection.isEmpty():
         self._selection = self._selection.getChildren()[0]
     else:
       # shift to the next main submenu
       nextSubmenu = selectionHierarchy[1].next()
       self._selection = nextSubmenu.getChildren()[0]
   elif key in (27, ord('m'), ord('M')):
     # close menu
     self._isDone = True
Exemple #5
0
    def handleKey(self, key):
        self.valsLock.acquire()

        isKeystrokeConsumed = True
        if uiTools.isScrollKey(key):
            pageHeight = self.getPreferredSize()[0] - 1
            if self._showDetails: pageHeight -= (DETAILS_HEIGHT + 1)
            isChanged = self._scroller.handleKey(key, self._entryLines,
                                                 pageHeight)
            if isChanged: self.redraw(True)
        elif uiTools.isSelectionKey(key):
            self._showDetails = not self._showDetails
            self.redraw(True)
        elif key == ord('s') or key == ord('S'):
            self.showSortDialog()
        elif key == ord('u') or key == ord('U'):
            # provides a menu to pick the connection resolver
            title = "Resolver Util:"
            options = ["auto"] + list(connections.Resolver)
            connResolver = connections.getResolver("tor")

            currentOverwrite = connResolver.overwriteResolver
            if currentOverwrite == None: oldSelection = 0
            else: oldSelection = options.index(currentOverwrite)

            selection = cli.popups.showMenu(title, options, oldSelection)

            # applies new setting
            if selection != -1:
                selectedOption = options[selection] if selection != 0 else None
                connResolver.overwriteResolver = selectedOption
        elif key == ord('l') or key == ord('L'):
            # provides a menu to pick the primary information we list connections by
            title = "List By:"
            options = list(entries.ListingType)

            # dropping the HOSTNAME listing type until we support displaying that content
            options.remove(cli.connections.entries.ListingType.HOSTNAME)

            oldSelection = options.index(self.getListingType())
            selection = cli.popups.showMenu(title, options, oldSelection)

            # applies new setting
            if selection != -1: self.setListingType(options[selection])
        elif key == ord('d') or key == ord('D'):
            # presents popup for raw consensus data
            descriptorPopup.showDescriptorPopup(self)
        elif (key == ord('c') or key == ord('C')) and self.isClientsAllowed():
            countPopup.showCountDialog(countPopup.CountType.CLIENT_LOCALE,
                                       self._clientLocaleUsage)
        elif (key == ord('e') or key == ord('E')) and self.isExitsAllowed():
            countPopup.showCountDialog(countPopup.CountType.EXIT_PORT,
                                       self._exitPortUsage)
        else:
            isKeystrokeConsumed = False

        self.valsLock.release()
        return isKeystrokeConsumed
Exemple #6
0
 def handleKey(self, key):
   self.valsLock.acquire()
   
   isKeystrokeConsumed = True
   if uiTools.isScrollKey(key):
     pageHeight = self.getPreferredSize()[0] - 1
     if self._showDetails: pageHeight -= (DETAILS_HEIGHT + 1)
     isChanged = self._scroller.handleKey(key, self._entryLines, pageHeight)
     if isChanged: self.redraw(True)
   elif uiTools.isSelectionKey(key):
     self._showDetails = not self._showDetails
     self.redraw(True)
   elif key == ord('s') or key == ord('S'):
     self.showSortDialog()
   elif key == ord('u') or key == ord('U'):
     # provides a menu to pick the connection resolver
     title = "Resolver Util:"
     options = ["auto"] + connections.Resolver.values()
     connResolver = connections.getResolver("tor")
     
     currentOverwrite = connResolver.overwriteResolver
     if currentOverwrite == None: oldSelection = 0
     else: oldSelection = options.index(currentOverwrite)
     
     selection = cli.popups.showMenu(title, options, oldSelection)
     
     # applies new setting
     if selection != -1:
       selectedOption = options[selection] if selection != 0 else None
       connResolver.overwriteResolver = selectedOption
   elif key == ord('l') or key == ord('L'):
     # provides a menu to pick the primary information we list connections by
     title = "List By:"
     options = entries.ListingType.values()
     
     # dropping the HOSTNAME listing type until we support displaying that content
     options.remove(cli.connections.entries.ListingType.HOSTNAME)
     
     oldSelection = options.index(self._listingType)
     selection = cli.popups.showMenu(title, options, oldSelection)
     
     # applies new setting
     if selection != -1: self.setListingType(options[selection])
   elif key == ord('d') or key == ord('D'):
     # presents popup for raw consensus data
     descriptorPopup.showDescriptorPopup(self)
   elif (key == ord('c') or key == ord('C')) and self.isClientsAllowed():
     countPopup.showCountDialog(countPopup.CountType.CLIENT_LOCALE, self._clientLocaleUsage)
   elif (key == ord('e') or key == ord('E')) and self.isExitsAllowed():
     countPopup.showCountDialog(countPopup.CountType.EXIT_PORT, self._exitPortUsage)
   else: isKeystrokeConsumed = False
   
   self.valsLock.release()
   return isKeystrokeConsumed
Exemple #7
0
def showMenu(title, options, oldSelection):
  """
  Provides menu with options laid out in a single column. User can cancel
  selection with the escape key, in which case this proives -1. Otherwise this
  returns the index of the selection.
  
  Arguments:
    title        - title displayed for the popup window
    options      - ordered listing of options to display
    oldSelection - index of the initially selected option (uses the first
                   selection without a carrot if -1)
  """
  
  maxWidth = max(map(len, options)) + 9
  popup, _, _ = init(len(options) + 2, maxWidth)
  if not popup: return
  key, selection = 0, oldSelection if oldSelection != -1 else 0
  
  try:
    # hides the title of the first panel on the page
    control = cli.controller.getController()
    topPanel = control.getDisplayPanels(includeSticky = False)[0]
    topPanel.setTitleVisible(False)
    topPanel.redraw(True)
    
    curses.cbreak()   # wait indefinitely for key presses (no timeout)
    
    while not uiTools.isSelectionKey(key):
      popup.win.erase()
      popup.win.box()
      popup.addstr(0, 0, title, curses.A_STANDOUT)
      
      for i in range(len(options)):
        label = options[i]
        format = curses.A_STANDOUT if i == selection else curses.A_NORMAL
        tab = "> " if i == oldSelection else "  "
        popup.addstr(i + 1, 2, tab)
        popup.addstr(i + 1, 4, " %s " % label, format)
      
      popup.win.refresh()
      
      key = control.getScreen().getch()
      if key == curses.KEY_UP: selection = max(0, selection - 1)
      elif key == curses.KEY_DOWN: selection = min(len(options) - 1, selection + 1)
      elif key == 27: selection, key = -1, curses.KEY_ENTER # esc - cancel
  finally:
    topPanel.setTitleVisible(True)
    finalize()
  
  return selection
 def handleKey(self, key):
   isKeystrokeConsumed = True
   if uiTools.isSelectionKey(key):
     self.prompt()
   elif uiTools.isScrollKey(key) and not self.isInputMode:
     pageHeight = self.getPreferredSize()[0] - 1
     displayLength = len(self.interpretor.getDisplayContents(PROMPT_LINE))
     newScroll = uiTools.getScrollPosition(key, self.scroll, pageHeight, displayLength)
     
     if self.scroll != newScroll:
       self.scroll = newScroll
       self.redraw(True)
   else: isKeystrokeConsumed = False
   
   return isKeystrokeConsumed
Exemple #9
0
def showHelpPopup():
  """
  Presents a popup with instructions for the current page's hotkeys. This
  returns the user input used to close the popup. If the popup didn't close
  properly, this is an arrow, enter, or scroll key then this returns None.
  """
  
  popup, _, height = init(9, 80)
  if not popup: return
  
  exitKey = None
  try:
    control = cli.controller.getController()
    pagePanels = control.getDisplayPanels()
    
    # the first page is the only one with multiple panels, and it looks better
    # with the log entries first, so reversing the order
    pagePanels.reverse()
    
    helpOptions = []
    for entry in pagePanels:
      helpOptions += entry.getHelp()
    
    # test doing afterward in case of overwriting
    popup.win.box()
    popup.addstr(0, 0, "Page %i Commands:" % (control.getPage() + 1), curses.A_STANDOUT)
    
    for i in range(len(helpOptions)):
      if i / 2 >= height - 2: break
      
      # draws entries in the form '<key>: <description>[ (<selection>)]', for
      # instance...
      # u: duplicate log entries (hidden)
      key, description, selection = helpOptions[i]
      if key: description = ": " + description
      row = (i / 2) + 1
      col = 2 if i % 2 == 0 else 41
      
      popup.addstr(row, col, key, curses.A_BOLD)
      col += len(key)
      popup.addstr(row, col, description)
      col += len(description)
      
      if selection:
        popup.addstr(row, col, " (")
        popup.addstr(row, col + 2, selection, curses.A_BOLD)
        popup.addstr(row, col + 2 + len(selection), ")")
    
    # tells user to press a key if the lower left is unoccupied
    if len(helpOptions) < 13 and height == 9:
      popup.addstr(7, 2, "Press any key...")
    
    popup.win.refresh()
    curses.cbreak()
    exitKey = control.getScreen().getch()
  finally: finalize()
  
  if not uiTools.isSelectionKey(exitKey) and \
    not uiTools.isScrollKey(exitKey) and \
    not exitKey in (curses.KEY_LEFT, curses.KEY_RIGHT):
    return exitKey
  else: return None
Exemple #10
0
def showSortDialog(title, options, oldSelection, optionColors):
  """
  Displays a sorting dialog of the form:
  
    Current Order: <previous selection>
    New Order: <selections made>
    
    <option 1>    <option 2>    <option 3>   Cancel
  
  Options are colored when among the "Current Order" or "New Order", but not
  when an option below them. If cancel is selected or the user presses escape
  then this returns None. Otherwise, the new ordering is provided.
  
  Arguments:
    title   - title displayed for the popup window
    options      - ordered listing of option labels
    oldSelection - current ordering
    optionColors - mappings of options to their color
  """
  
  popup, _, _ = init(9, 80)
  if not popup: return
  newSelections = []  # new ordering
  
  try:
    cursorLoc = 0     # index of highlighted option
    curses.cbreak()   # wait indefinitely for key presses (no timeout)
    
    selectionOptions = list(options)
    selectionOptions.append("Cancel")
    
    while len(newSelections) < len(oldSelection):
      popup.win.erase()
      popup.win.box()
      popup.addstr(0, 0, title, curses.A_STANDOUT)
      
      _drawSortSelection(popup, 1, 2, "Current Order: ", oldSelection, optionColors)
      _drawSortSelection(popup, 2, 2, "New Order: ", newSelections, optionColors)
      
      # presents remaining options, each row having up to four options with
      # spacing of nineteen cells
      row, col = 4, 0
      for i in range(len(selectionOptions)):
        optionFormat = curses.A_STANDOUT if cursorLoc == i else curses.A_NORMAL
        popup.addstr(row, col * 19 + 2, selectionOptions[i], optionFormat)
        col += 1
        if col == 4: row, col = row + 1, 0
      
      popup.win.refresh()
      
      key = cli.controller.getController().getScreen().getch()
      if key == curses.KEY_LEFT:
        cursorLoc = max(0, cursorLoc - 1)
      elif key == curses.KEY_RIGHT:
        cursorLoc = min(len(selectionOptions) - 1, cursorLoc + 1)
      elif key == curses.KEY_UP:
        cursorLoc = max(0, cursorLoc - 4)
      elif key == curses.KEY_DOWN:
        cursorLoc = min(len(selectionOptions) - 1, cursorLoc + 4)
      elif uiTools.isSelectionKey(key):
        selection = selectionOptions[cursorLoc]
        
        if selection == "Cancel": break
        else:
          newSelections.append(selection)
          selectionOptions.remove(selection)
          cursorLoc = min(cursorLoc, len(selectionOptions) - 1)
      elif key == 27: break # esc - cancel
  finally: finalize()
  
  if len(newSelections) == len(oldSelection):
    return newSelections
  else: return None
Exemple #11
0
def showConfirmationDialog(torrcContents, torrcLocation):
  """
  Shows a confirmation dialog with the given torrc contents, returning CANCEL,
  NEXT, or BACK based on the selection.
  
  Arguments:
    torrcContents - lines of torrc contents to be presented
    torrcLocation - path where the torrc will be placed
  """
  
  torrcLines = torrcContents.split("\n")
  options = ["Cancel", "Back to Setup", "Start Tor"]
  
  control = cli.controller.getController()
  screenHeight = control.getScreen().getmaxyx()[0]
  stickyHeight = sum([stickyPanel.getHeight() for stickyPanel in control.getStickyPanels()])
  isScrollbarVisible = len(torrcLines) + stickyHeight + 5 > screenHeight
  
  xOffset = 3 if isScrollbarVisible else 0
  popup, width, height = cli.popups.init(len(torrcLines) + 5, 84 + xOffset)
  if not popup: return False
  
  try:
    scroll, selection = 0, 2
    curses.cbreak()
    
    while True:
      popup.win.erase()
      popup.win.box()
      
      # renders the scrollbar
      if isScrollbarVisible:
        popup.addScrollBar(scroll, scroll + height - 5, len(torrcLines), 1, height - 4, 1)
      
      # shows the path where the torrc will be placed
      titleMsg = "The following will be placed at '%s':" % torrcLocation
      popup.addstr(0, 0, titleMsg, curses.A_STANDOUT)
      
      # renders the torrc contents
      for i in range(scroll, min(len(torrcLines), height - 5 + scroll)):
        # parses the argument and comment from options
        option, arg, comment = uiTools.cropStr(torrcLines[i], width - 4 - xOffset), "", ""
        
        div = option.find("#")
        if div != -1: option, comment = option[:div], option[div:]
        
        div = option.strip().find(" ")
        if div != -1: option, arg = option[:div], option[div:]
        
        drawX = 2 + xOffset
        popup.addstr(i + 1 - scroll, drawX, option, curses.A_BOLD | uiTools.getColor("green"))
        drawX += len(option)
        popup.addstr(i + 1 - scroll, drawX, arg, curses.A_BOLD | uiTools.getColor("cyan"))
        drawX += len(arg)
        popup.addstr(i + 1 - scroll, drawX, comment, uiTools.getColor("white"))
      
      # divider between the torrc and the options
      popup.addch(height - 4, 0, curses.ACS_LTEE)
      popup.addch(height - 4, width, curses.ACS_RTEE)
      popup.hline(height - 4, 1, width - 1)
      if isScrollbarVisible: popup.addch(height - 4, 2, curses.ACS_BTEE)
      
      # renders the selection options
      confirmationMsg = "Run tor with the above configuration?"
      popup.addstr(height - 3, width - len(confirmationMsg) - 1, confirmationMsg, uiTools.getColor("green") | curses.A_BOLD)
      
      drawX = width - 1
      for i in range(len(options) - 1, -1, -1):
        optionLabel = " %s " % options[i]
        drawX -= (len(optionLabel) + 4)
        
        selectionFormat = curses.A_STANDOUT if i == selection else curses.A_NORMAL
        popup.addstr(height - 2, drawX, "[", uiTools.getColor("green"))
        popup.addstr(height - 2, drawX + 1, optionLabel, uiTools.getColor("green") | selectionFormat | curses.A_BOLD)
        popup.addstr(height - 2, drawX + len(optionLabel) + 1, "]", uiTools.getColor("green"))
        
        drawX -= 1 # space gap between the options
      
      popup.win.refresh()
      key = cli.controller.getController().getScreen().getch()
      
      if key == curses.KEY_LEFT:
        selection = (selection - 1) % len(options)
      elif key == curses.KEY_RIGHT:
        selection = (selection + 1) % len(options)
      elif uiTools.isScrollKey(key):
        scroll = uiTools.getScrollPosition(key, scroll, height - 5, len(torrcLines))
      elif uiTools.isSelectionKey(key):
        if selection == 0: return CANCEL
        elif selection == 1: return BACK
        else: return NEXT
      elif key in (27, ord('q'), ord('Q')): return CANCEL
  finally:
    cli.popups.finalize()
Exemple #12
0
 def handleKey(self, key):
   self.valsLock.acquire()
   isKeystrokeConsumed = True
   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._getConfigOptions(), pageHeight)
     if isChanged: self.redraw(True)
   elif uiTools.isSelectionKey(key) and self._getConfigOptions():
     # Prompts the user to edit the selected configuration value. The
     # interface is locked to prevent updates between setting the value
     # and showing any errors.
     
     panel.CURSES_LOCK.acquire()
     try:
       selection = self.getSelection()
       configOption = selection.get(Field.OPTION)
       if selection.isUnset(): initialValue = ""
       else: initialValue = selection.get(Field.VALUE)
       
       promptMsg = "%s Value (esc to cancel): " % configOption
       isPrepopulated = self._config["features.config.prepopulateEditValues"]
       newValue = popups.inputPrompt(promptMsg, initialValue if isPrepopulated else "")
       
       if newValue != None and newValue != initialValue:
         try:
           if selection.get(Field.TYPE) == "Boolean":
             # if the value's a boolean then allow for 'true' and 'false' inputs
             if newValue.lower() == "true": newValue = "1"
             elif newValue.lower() == "false": newValue = "0"
           elif selection.get(Field.TYPE) == "LineList":
             # setOption accepts list inputs when there's multiple values
             newValue = newValue.split(",")
           
           torTools.getConn().setOption(configOption, newValue)
           
           # forces the label to be remade with the new value
           selection.labelCache = None
           
           # resets the isDefault flag
           customOptions = torConfig.getCustomOptions()
           selection.fields[Field.IS_DEFAULT] = not configOption in customOptions
           
           self.redraw(True)
         except Exception, exc:
           popups.showMsg("%s (press any key)" % exc)
     finally:
       panel.CURSES_LOCK.release()
   elif key == ord('a') or key == ord('A'):
     self.showAll = not self.showAll
     self.redraw(True)
   elif key == ord('s') or key == ord('S'):
     self.showSortDialog()
   elif key == ord('v') or key == ord('V'):
     self.showWriteDialog()
   else: isKeystrokeConsumed = False
   
   self.valsLock.release()
   return isKeystrokeConsumed
Exemple #13
0
def promptConfigOptions(relayType, config, disabledOpt):
    """
  Prompts the user for the configuration of an internal relay.
  """

    topContent = _splitStr(
        CONFIG.get("wizard.message.%s" % relayType.lower(), ""), 54)

    options = [
        config[opt] for opt in RelayOptions[relayType]
        if not opt in disabledOpt
    ]
    options.append(Options.DIVIDER)
    options.append(ConfigOption(BACK, "general", "(to role selection)"))
    options.append(ConfigOption(NEXT, "general", "(to confirm options)"))

    popupHeight = len(topContent) + len(options) + DESC_SIZE + 5
    popup, _, _ = cli.popups.init(popupHeight, 58)
    if not popup: return
    control = cli.controller.getController()
    key, selection = 0, 0

    try:
        curses.cbreak()

        while True:
            popup.win.erase()
            popup.win.box()

            # provides the description for the relay type
            for i in range(len(topContent)):
                popup.addstr(i + 1, 2, topContent[i],
                             curses.A_BOLD | uiTools.getColor(MSG_COLOR))

            y, offset = len(topContent) + 1, 0
            for opt in options:
                if opt == Options.DIVIDER:
                    offset += 1
                    continue

                optionFormat = opt.getDisplayAttr()
                if opt == options[selection]: optionFormat |= curses.A_STANDOUT

                offset, indent = offset + 1, 0
                if opt.getKey() in CONFIG["wizard.suboptions"]:
                    # If the next entry is also a suboption then show a 'T', otherwise
                    # end the bracketing.

                    bracketChar, nextIndex = curses.ACS_LLCORNER, options.index(
                        opt) + 1
                    if nextIndex < len(options) and isinstance(
                            options[nextIndex], ConfigOption):
                        if options[nextIndex].getKey(
                        ) in CONFIG["wizard.suboptions"]:
                            bracketChar = curses.ACS_LTEE

                    popup.addch(y + offset, 3, bracketChar,
                                opt.getDisplayAttr())
                    popup.addch(y + offset, 4, curses.ACS_HLINE,
                                opt.getDisplayAttr())

                    indent = 3

                labelFormat = " %%-%is%%s" % (30 - indent)
                label = labelFormat % (opt.getLabel(), opt.getDisplayValue())
                popup.addstr(y + offset, 2 + indent,
                             uiTools.padStr(label, 54 - indent), optionFormat)

                # little hack to make "Block" policies red
                if opt != options[selection] and not opt.getValue(
                ) and opt.getKey() in CUSTOM_POLICIES:
                    optionFormat = curses.A_BOLD | uiTools.getColor("red")
                    popup.addstr(y + offset, 33, opt.getDisplayValue(),
                                 optionFormat)

            # divider between the options and description
            offset += 2
            popup.addch(y + offset, 0, curses.ACS_LTEE)
            popup.addch(y + offset, popup.getWidth() - 1, curses.ACS_RTEE)
            popup.hline(y + offset, 1, popup.getWidth() - 2)

            # description for the currently selected option
            for line in options[selection].getDescription(54, " "):
                offset += 1
                popup.addstr(y + offset, 1, line, uiTools.getColor(MSG_COLOR))

            popup.win.refresh()
            key = control.getScreen().getch()

            if key in (curses.KEY_UP, curses.KEY_DOWN):
                posOffset = -1 if key == curses.KEY_UP else 1
                selection = (selection + posOffset) % len(options)

                # skips disabled options and dividers
                while options[selection] == Options.DIVIDER or not options[
                        selection].isEnabled():
                    selection = (selection + posOffset) % len(options)
            elif uiTools.isSelectionKey(key):
                if selection == len(options) - 2: return BACK  # selected back
                elif selection == len(options) - 1:
                    return NEXT  # selected next
                elif isinstance(options[selection], ToggleConfigOption):
                    options[selection].toggle()
                else:
                    newValue = popup.getstr(
                        y + selection + 1, 33, options[selection].getValue(),
                        curses.A_STANDOUT | uiTools.getColor(OPTION_COLOR), 23)
                    if newValue:
                        try:
                            options[selection].setValue(newValue.strip())
                        except ValueError, exc:
                            cli.popups.showMsg(str(exc), 3)
                            cli.controller.getController().redraw()
            elif key in (27, ord('q'), ord('Q')):
                return CANCEL
Exemple #14
0
 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
   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")
     
     # 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()
       
       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 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)
       
       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"))
       
       # 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
       
       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)
         
         popups.showMsg(msg, 2)
   finally: popups.finalize()
Exemple #15
0
 def handleKey(self, key):
   self.valsLock.acquire()
   isKeystrokeConsumed = True
   if uiTools.isScrollKey(key):
     pageHeight = self.getPreferredSize()[0] - 1
     detailPanelHeight = CONFIG["features.config.selectionDetails.height"]
     if detailPanelHeight > 0 and detailPanelHeight + 2 <= pageHeight:
       pageHeight -= (detailPanelHeight + 1)
     
     isChanged = self.scroller.handleKey(key, self._getConfigOptions(), pageHeight)
     if isChanged: self.redraw(True)
   elif uiTools.isSelectionKey(key) and self._getConfigOptions():
     # Prompts the user to edit the selected configuration value. The
     # interface is locked to prevent updates between setting the value
     # and showing any errors.
     
     panel.CURSES_LOCK.acquire()
     try:
       selection = self.getSelection()
       configOption = selection.get(Field.OPTION)
       if selection.isUnset(): initialValue = ""
       else: initialValue = selection.get(Field.VALUE)
       
       promptMsg = "%s Value (esc to cancel): " % configOption
       isPrepopulated = CONFIG["features.config.prepopulateEditValues"]
       newValue = popups.inputPrompt(promptMsg, initialValue if isPrepopulated else "")
       
       if newValue != None and newValue != initialValue:
         try:
           if selection.get(Field.TYPE) == "Boolean":
             # if the value's a boolean then allow for 'true' and 'false' inputs
             if newValue.lower() == "true": newValue = "1"
             elif newValue.lower() == "false": newValue = "0"
           elif selection.get(Field.TYPE) == "LineList":
             # setOption accepts list inputs when there's multiple values
             newValue = newValue.split(",")
           
           torTools.getConn().setOption(configOption, newValue)
           
           # forces the label to be remade with the new value
           selection.labelCache = None
           
           # resets the isDefault flag
           customOptions = torConfig.getCustomOptions()
           selection.fields[Field.IS_DEFAULT] = not configOption in customOptions
           
           self.redraw(True)
         except Exception, exc:
           popups.showMsg("%s (press any key)" % exc)
     finally:
       panel.CURSES_LOCK.release()
   elif key == ord('a') or key == ord('A'):
     self.showAll = not self.showAll
     self.redraw(True)
   elif key == ord('s') or key == ord('S'):
     self.showSortDialog()
   elif key == ord('v') or key == ord('V'):
     self.showWriteDialog()
   else: isKeystrokeConsumed = False
   
   self.valsLock.release()
   return isKeystrokeConsumed
Exemple #16
0
def showDescriptorPopup(connPanel):
  """
  Presents consensus descriptor in popup window with the following controls:
  Up, Down, Page Up, Page Down - scroll descriptor
  Right, Left - next / previous connection
  Enter, Space, d, D - close popup
  
  Arguments:
    connPanel - connection panel providing the dialog
  """
  
  # hides the title of the connection panel
  connPanel.setTitleVisible(False)
  connPanel.redraw(True)
  
  control = cli.controller.getController()
  panel.CURSES_LOCK.acquire()
  isDone = False
  
  try:
    while not isDone:
      selection = connPanel.getSelection()
      if not selection: break
      
      fingerprint = selection.foreign.getFingerprint()
      if fingerprint == "UNKNOWN": fingerprint = None
      
      displayText = getDisplayText(fingerprint)
      displayColor = cli.connections.connEntry.CATEGORY_COLOR[selection.getType()]
      showLineNumber = fingerprint != None
      
      # determines the maximum popup size the displayText can fill
      pHeight, pWidth = getPreferredSize(displayText, connPanel.maxX, showLineNumber)
      
      popup, _, height = cli.popups.init(pHeight, pWidth)
      if not popup: break
      scroll, isChanged = 0, True
      
      try:
        while not isDone:
          if isChanged:
            draw(popup, fingerprint, displayText, displayColor, scroll, showLineNumber)
            isChanged = False
          
          key = control.getScreen().getch()
          
          if uiTools.isScrollKey(key):
            # TODO: This is a bit buggy in that scrolling is by displayText
            # lines rather than the displayed lines, causing issues when
            # content wraps. The result is that we can't have a scrollbar and
            # can't scroll to the bottom if there's a multi-line being
            # displayed. However, trying to correct this introduces a big can
            # of worms and after hours decided that this isn't worth the
            # effort...
            
            newScroll = uiTools.getScrollPosition(key, scroll, height - 2, len(displayText))
            
            if scroll != newScroll:
              scroll, isChanged = newScroll, True
          elif uiTools.isSelectionKey(key) or key in (ord('d'), ord('D')):
            isDone = True # closes popup
          elif key in (curses.KEY_LEFT, curses.KEY_RIGHT):
            # navigation - pass on to connPanel and recreate popup
            connPanel.handleKey(curses.KEY_UP if key == curses.KEY_LEFT else curses.KEY_DOWN)
            break
      finally: cli.popups.finalize()
  finally:
    connPanel.setTitleVisible(True)
    connPanel.redraw(True)
    panel.CURSES_LOCK.release()
Exemple #17
0
def promptRelayType(initialSelection):
    """
  Provides a prompt for selecting the general role we'd like Tor to run with.
  This returns a RelayType enumeration for the selection, or CANCEL if the
  dialog was canceled.
  """

    options = [ConfigOption(opt, "role", opt) for opt in RelayType.values()]
    options.append(ConfigOption(CANCEL, "general", CANCEL))
    selection = RelayType.indexOf(initialSelection)
    height = 28

    # drops the resume option if it isn't applicable
    control = cli.controller.getController()
    if not control.getTorManager().isTorrcAvailable():
        options.pop(0)
        height -= 3
        selection -= 1

    popup, _, _ = cli.popups.init(height, 58)
    if not popup: return

    try:
        popup.win.box()
        curses.cbreak()

        # provides the welcoming message
        topContent = _splitStr(CONFIG["wizard.message.role"], 54)
        for i in range(len(topContent)):
            popup.addstr(i + 1, 2, topContent[i],
                         curses.A_BOLD | uiTools.getColor(MSG_COLOR))

        while True:
            y, offset = len(topContent) + 1, 0

            for opt in options:
                optionFormat = uiTools.getColor(MSG_COLOR)
                if opt == options[selection]: optionFormat |= curses.A_STANDOUT

                # Curses has a weird bug where there's a one-pixel alignment
                # difference between bold and regular text, so it looks better
                # to render the whitespace here as not being bold.

                offset += 1
                label = opt.getLabel(" ")
                popup.addstr(y + offset, 2, label,
                             optionFormat | curses.A_BOLD)
                popup.addstr(y + offset, 2 + len(label),
                             " " * (54 - len(label)), optionFormat)
                offset += 1

                for line in opt.getDescription(52, " "):
                    popup.addstr(y + offset, 2, uiTools.padStr(line, 54),
                                 optionFormat)
                    offset += 1

            popup.win.refresh()
            key = control.getScreen().getch()

            if key == curses.KEY_UP: selection = (selection - 1) % len(options)
            elif key == curses.KEY_DOWN:
                selection = (selection + 1) % len(options)
            elif uiTools.isSelectionKey(key):
                return options[selection].getValue()
            elif key in (27, ord('q'), ord('Q')):
                return CANCEL  # esc or q - cancel
    finally:
        cli.popups.finalize()
Exemple #18
0
def showDescriptorPopup(connPanel):
    """
  Presents consensus descriptor in popup window with the following controls:
  Up, Down, Page Up, Page Down - scroll descriptor
  Right, Left - next / previous connection
  Enter, Space, d, D - close popup
  
  Arguments:
    connPanel - connection panel providing the dialog
  """

    # hides the title of the connection panel
    connPanel.setTitleVisible(False)
    connPanel.redraw(True)

    control = cli.controller.getController()
    panel.CURSES_LOCK.acquire()
    isDone = False

    try:
        while not isDone:
            selection = connPanel.getSelection()
            if not selection: break

            fingerprint = selection.foreign.getFingerprint()
            if fingerprint == "UNKNOWN": fingerprint = None

            displayText = getDisplayText(fingerprint)
            displayColor = cli.connections.connEntry.CATEGORY_COLOR[
                selection.getType()]
            showLineNumber = fingerprint != None

            # determines the maximum popup size the displayText can fill
            pHeight, pWidth = getPreferredSize(displayText, connPanel.maxX,
                                               showLineNumber)

            popup, _, height = cli.popups.init(pHeight, pWidth)
            if not popup: break
            scroll, isChanged = 0, True

            try:
                while not isDone:
                    if isChanged:
                        draw(popup, fingerprint, displayText, displayColor,
                             scroll, showLineNumber)
                        isChanged = False

                    key = control.getScreen().getch()

                    if uiTools.isScrollKey(key):
                        # TODO: This is a bit buggy in that scrolling is by displayText
                        # lines rather than the displayed lines, causing issues when
                        # content wraps. The result is that we can't have a scrollbar and
                        # can't scroll to the bottom if there's a multi-line being
                        # displayed. However, trying to correct this introduces a big can
                        # of worms and after hours decided that this isn't worth the
                        # effort...

                        newScroll = uiTools.getScrollPosition(
                            key, scroll, height - 2, len(displayText))

                        if scroll != newScroll:
                            scroll, isChanged = newScroll, True
                    elif uiTools.isSelectionKey(key) or key in (ord('d'),
                                                                ord('D')):
                        isDone = True  # closes popup
                    elif key in (curses.KEY_LEFT, curses.KEY_RIGHT):
                        # navigation - pass on to connPanel and recreate popup
                        connPanel.handleKey(curses.KEY_UP if key == curses.
                                            KEY_LEFT else curses.KEY_DOWN)
                        break
            finally:
                cli.popups.finalize()
    finally:
        connPanel.setTitleVisible(True)
        connPanel.redraw(True)
        panel.CURSES_LOCK.release()
Exemple #19
0
def showConfirmationDialog(torrcContents, torrcLocation):
    """
  Shows a confirmation dialog with the given torrc contents, returning CANCEL,
  NEXT, or BACK based on the selection.
  
  Arguments:
    torrcContents - lines of torrc contents to be presented
    torrcLocation - path where the torrc will be placed
  """

    torrcLines = torrcContents.split("\n")
    options = ["Cancel", "Back to Setup", "Start Tor"]

    control = cli.controller.getController()
    screenHeight = control.getScreen().getmaxyx()[0]
    stickyHeight = sum(
        [stickyPanel.getHeight() for stickyPanel in control.getStickyPanels()])
    isScrollbarVisible = len(torrcLines) + stickyHeight + 5 > screenHeight

    xOffset = 3 if isScrollbarVisible else 0
    popup, width, height = cli.popups.init(len(torrcLines) + 5, 84 + xOffset)
    if not popup: return False

    try:
        scroll, selection = 0, 2
        curses.cbreak()

        while True:
            popup.win.erase()
            popup.win.box()

            # renders the scrollbar
            if isScrollbarVisible:
                popup.addScrollBar(scroll, scroll + height - 5,
                                   len(torrcLines), 1, height - 4, 1)

            # shows the path where the torrc will be placed
            titleMsg = "The following will be placed at '%s':" % torrcLocation
            popup.addstr(0, 0, titleMsg, curses.A_STANDOUT)

            # renders the torrc contents
            for i in range(scroll, min(len(torrcLines), height - 5 + scroll)):
                # parses the argument and comment from options
                option, arg, comment = uiTools.cropStr(torrcLines[i], width -
                                                       4 - xOffset), "", ""

                div = option.find("#")
                if div != -1: option, comment = option[:div], option[div:]

                div = option.strip().find(" ")
                if div != -1: option, arg = option[:div], option[div:]

                drawX = 2 + xOffset
                popup.addstr(i + 1 - scroll, drawX, option,
                             curses.A_BOLD | uiTools.getColor("green"))
                drawX += len(option)
                popup.addstr(i + 1 - scroll, drawX, arg,
                             curses.A_BOLD | uiTools.getColor("cyan"))
                drawX += len(arg)
                popup.addstr(i + 1 - scroll, drawX, comment,
                             uiTools.getColor("white"))

            # divider between the torrc and the options
            popup.addch(height - 4, 0, curses.ACS_LTEE)
            popup.addch(height - 4, width, curses.ACS_RTEE)
            popup.hline(height - 4, 1, width - 1)
            if isScrollbarVisible: popup.addch(height - 4, 2, curses.ACS_BTEE)

            # renders the selection options
            confirmationMsg = "Run tor with the above configuration?"
            popup.addstr(height - 3, width - len(confirmationMsg) - 1,
                         confirmationMsg,
                         uiTools.getColor("green") | curses.A_BOLD)

            drawX = width - 1
            for i in range(len(options) - 1, -1, -1):
                optionLabel = " %s " % options[i]
                drawX -= (len(optionLabel) + 4)

                selectionFormat = curses.A_STANDOUT if i == selection else curses.A_NORMAL
                popup.addstr(height - 2, drawX, "[", uiTools.getColor("green"))
                popup.addstr(
                    height - 2, drawX + 1, optionLabel,
                    uiTools.getColor("green") | selectionFormat
                    | curses.A_BOLD)
                popup.addstr(height - 2, drawX + len(optionLabel) + 1, "]",
                             uiTools.getColor("green"))

                drawX -= 1  # space gap between the options

            popup.win.refresh()
            key = cli.controller.getController().getScreen().getch()

            if key == curses.KEY_LEFT:
                selection = (selection - 1) % len(options)
            elif key == curses.KEY_RIGHT:
                selection = (selection + 1) % len(options)
            elif uiTools.isScrollKey(key):
                scroll = uiTools.getScrollPosition(key, scroll, height - 5,
                                                   len(torrcLines))
            elif uiTools.isSelectionKey(key):
                if selection == 0: return CANCEL
                elif selection == 1: return BACK
                else: return NEXT
            elif key in (27, ord('q'), ord('Q')): return CANCEL
    finally:
        cli.popups.finalize()
Exemple #20
0
def promptRelayType(initialSelection):
  """
  Provides a prompt for selecting the general role we'd like Tor to run with.
  This returns a RelayType enumeration for the selection, or CANCEL if the
  dialog was canceled.
  """
  
  options = [ConfigOption(opt, "role", opt) for opt in RelayType.values()]
  options.append(ConfigOption(CANCEL, "general", CANCEL))
  selection = RelayType.indexOf(initialSelection)
  height = 28
  
  # drops the resume option if it isn't applicable
  control = cli.controller.getController()
  if not control.getTorManager().isTorrcAvailable():
    options.pop(0)
    height -= 3
    selection -= 1
  
  popup, _, _ = cli.popups.init(height, 58)
  if not popup: return
  
  try:
    popup.win.box()
    curses.cbreak()
    
    # provides the welcoming message
    topContent = _splitStr(CONFIG["wizard.message.role"], 54)
    for i in range(len(topContent)):
      popup.addstr(i + 1, 2, topContent[i], curses.A_BOLD | uiTools.getColor(MSG_COLOR))
    
    while True:
      y, offset = len(topContent) + 1, 0
      
      for opt in options:
        optionFormat = uiTools.getColor(MSG_COLOR)
        if opt == options[selection]: optionFormat |= curses.A_STANDOUT
        
        # Curses has a weird bug where there's a one-pixel alignment
        # difference between bold and regular text, so it looks better
        # to render the whitespace here as not being bold.
        
        offset += 1
        label = opt.getLabel(" ")
        popup.addstr(y + offset, 2, label, optionFormat | curses.A_BOLD)
        popup.addstr(y + offset, 2 + len(label), " " * (54 - len(label)), optionFormat)
        offset += 1
        
        for line in opt.getDescription(52, " "):
          popup.addstr(y + offset, 2, uiTools.padStr(line, 54), optionFormat)
          offset += 1
      
      popup.win.refresh()
      key = control.getScreen().getch()
      
      if key == curses.KEY_UP: selection = (selection - 1) % len(options)
      elif key == curses.KEY_DOWN: selection = (selection + 1) % len(options)
      elif uiTools.isSelectionKey(key): return options[selection].getValue()
      elif key in (27, ord('q'), ord('Q')): return CANCEL # esc or q - cancel
  finally:
    cli.popups.finalize()
Exemple #21
0
 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
   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")
     
     # 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()
       
       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 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)
       
       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"))
       
       # 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
       
       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)
         
         popups.showMsg(msg, 2)
   finally: popups.finalize()
Exemple #22
0
def promptConfigOptions(relayType, config, disabledOpt):
  """
  Prompts the user for the configuration of an internal relay.
  """
  
  topContent = _splitStr(CONFIG.get("wizard.message.%s" % relayType.lower(), ""), 54)
  
  options = [config[opt] for opt in RelayOptions[relayType] if not opt in disabledOpt]
  options.append(Options.DIVIDER)
  options.append(ConfigOption(BACK, "general", "(to role selection)"))
  options.append(ConfigOption(NEXT, "general", "(to confirm options)"))
  
  popupHeight = len(topContent) + len(options) + DESC_SIZE + 5
  popup, _, _ = cli.popups.init(popupHeight, 58)
  if not popup: return
  control = cli.controller.getController()
  key, selection = 0, 0
  
  try:
    curses.cbreak()
    
    while True:
      popup.win.erase()
      popup.win.box()
      
      # provides the description for the relay type
      for i in range(len(topContent)):
        popup.addstr(i + 1, 2, topContent[i], curses.A_BOLD | uiTools.getColor(MSG_COLOR))
      
      y, offset = len(topContent) + 1, 0
      for opt in options:
        if opt == Options.DIVIDER:
          offset += 1
          continue
        
        optionFormat = opt.getDisplayAttr()
        if opt == options[selection]: optionFormat |= curses.A_STANDOUT
        
        offset, indent = offset + 1, 0
        if opt.getKey() in CONFIG["wizard.suboptions"]:
          # If the next entry is also a suboption then show a 'T', otherwise
          # end the bracketing.
          
          bracketChar, nextIndex = curses.ACS_LLCORNER, options.index(opt) + 1
          if nextIndex < len(options) and isinstance(options[nextIndex], ConfigOption):
            if options[nextIndex].getKey() in CONFIG["wizard.suboptions"]:
              bracketChar = curses.ACS_LTEE
          
          popup.addch(y + offset, 3, bracketChar, opt.getDisplayAttr())
          popup.addch(y + offset, 4, curses.ACS_HLINE, opt.getDisplayAttr())
          
          indent = 3
        
        labelFormat = " %%-%is%%s" % (30 - indent)
        label = labelFormat % (opt.getLabel(), opt.getDisplayValue())
        popup.addstr(y + offset, 2 + indent, uiTools.padStr(label, 54 - indent), optionFormat)
        
        # little hack to make "Block" policies red
        if opt != options[selection] and not opt.getValue() and opt.getKey() in CUSTOM_POLICIES:
          optionFormat = curses.A_BOLD | uiTools.getColor("red")
          popup.addstr(y + offset, 33, opt.getDisplayValue(), optionFormat)
      
      # divider between the options and description
      offset += 2
      popup.addch(y + offset, 0, curses.ACS_LTEE)
      popup.addch(y + offset, popup.getWidth() - 1, curses.ACS_RTEE)
      popup.hline(y + offset, 1, popup.getWidth() - 2)
      
      # description for the currently selected option
      for line in options[selection].getDescription(54, " "):
        offset += 1
        popup.addstr(y + offset, 1, line, uiTools.getColor(MSG_COLOR))
      
      popup.win.refresh()
      key = control.getScreen().getch()
      
      if key in (curses.KEY_UP, curses.KEY_DOWN):
        posOffset = -1 if key == curses.KEY_UP else 1
        selection = (selection + posOffset) % len(options)
        
        # skips disabled options and dividers
        while options[selection] == Options.DIVIDER or not options[selection].isEnabled():
          selection = (selection + posOffset) % len(options)
      elif uiTools.isSelectionKey(key):
        if selection == len(options) - 2: return BACK # selected back
        elif selection == len(options) - 1: return NEXT # selected next
        elif isinstance(options[selection], ToggleConfigOption):
          options[selection].toggle()
        else:
          newValue = popup.getstr(y + selection + 1, 33, options[selection].getValue(), curses.A_STANDOUT | uiTools.getColor(OPTION_COLOR), 23)
          if newValue:
            try: options[selection].setValue(newValue.strip())
            except ValueError, exc:
              cli.popups.showMsg(str(exc), 3)
              cli.controller.getController().redraw()
      elif key in (27, ord('q'), ord('Q')): return CANCEL
  finally:
    cli.popups.finalize()