[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [nyx/master] Consolidate menu into a single file
commit 1dd19b7f9f34d894178f7d603b257a623a78edf9
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Tue Apr 5 08:29:50 2016 -0700
Consolidate menu into a single file
All told the menu isn't terribly big, and it's about to get even smaller with
its rewrite.
---
nyx/__init__.py | 1 +
nyx/controller.py | 4 +-
nyx/menu.py | 652 +++++++++++++++++++++++++++++++++++++++++++++++++++
nyx/menu/__init__.py | 12 -
nyx/menu/actions.py | 306 ------------------------
nyx/menu/item.py | 210 -----------------
nyx/menu/menu.py | 157 -------------
setup.py | 2 +-
8 files changed, 656 insertions(+), 688 deletions(-)
diff --git a/nyx/__init__.py b/nyx/__init__.py
index 33656f1..fbd765b 100644
--- a/nyx/__init__.py
+++ b/nyx/__init__.py
@@ -27,6 +27,7 @@ __all__ = [
'controller',
'curses',
'log',
+ 'menu',
'panel',
'popups',
'starter',
diff --git a/nyx/controller.py b/nyx/controller.py
index e1cc1f8..c4ccc15 100644
--- a/nyx/controller.py
+++ b/nyx/controller.py
@@ -10,7 +10,7 @@ import time
import threading
import nyx.curses
-import nyx.menu.menu
+import nyx.menu
import nyx.popups
import nyx.panel
@@ -335,7 +335,7 @@ def start_nyx():
elif key.match('p'):
control.set_paused(not control.is_paused())
elif key.match('m'):
- nyx.menu.menu.show_menu()
+ nyx.menu.show_menu()
elif key.match('q'):
# provides prompt to confirm that nyx should exit
diff --git a/nyx/menu.py b/nyx/menu.py
new file mode 100644
index 0000000..1b6b6bf
--- /dev/null
+++ b/nyx/menu.py
@@ -0,0 +1,652 @@
+# Copyright 2016, Damian Johnson and The Tor Project
+# See LICENSE for licensing information
+
+"""
+Display logic for presenting the menu.
+"""
+
+import functools
+
+import nyx.controller
+import nyx.curses
+import nyx.popups
+import nyx.panel.graph
+import nyx.controller
+import nyx.tracker
+
+import stem
+import stem.util.connection
+
+from nyx import tor_controller
+from nyx.curses import RED, WHITE, NORMAL, BOLD, UNDERLINE
+from stem.util import conf, str_tools
+
+CONFIG = conf.config_dict('nyx', {
+ 'features.log.showDuplicateEntries': False,
+})
+
+
+def make_menu():
+ """
+ Constructs the base menu and all of its contents.
+ """
+
+ base_menu = Submenu('')
+ base_menu.add(make_actions_menu())
+ base_menu.add(make_view_menu())
+
+ control = nyx.controller.get_controller()
+
+ for page_panel in control.get_display_panels():
+ if page_panel.get_name() == 'graph':
+ base_menu.add(make_graph_menu(page_panel))
+ elif page_panel.get_name() == 'log':
+ base_menu.add(make_log_menu(page_panel))
+ elif page_panel.get_name() == 'connections':
+ base_menu.add(make_connections_menu(page_panel))
+ elif page_panel.get_name() == 'configuration':
+ base_menu.add(make_configuration_menu(page_panel))
+ elif page_panel.get_name() == 'torrc':
+ base_menu.add(make_torrc_menu(page_panel))
+
+ base_menu.add(make_help_menu())
+
+ return base_menu
+
+
+def make_actions_menu():
+ """
+ Submenu consisting of...
+ Close Menu
+ New Identity
+ Reset Tor
+ Pause / Unpause
+ Exit
+ """
+
+ control = nyx.controller.get_controller()
+ controller = tor_controller()
+ header_panel = control.header_panel()
+ actions_menu = Submenu('Actions')
+ actions_menu.add(MenuItem('Close Menu', None))
+ actions_menu.add(MenuItem('New Identity', header_panel.send_newnym))
+ actions_menu.add(MenuItem('Reset Tor', functools.partial(controller.signal, stem.Signal.RELOAD)))
+
+ if control.is_paused():
+ label, arg = 'Unpause', False
+ else:
+ label, arg = 'Pause', True
+
+ actions_menu.add(MenuItem(label, functools.partial(control.set_paused, arg)))
+ actions_menu.add(MenuItem('Exit', control.quit))
+
+ return actions_menu
+
+
+def make_view_menu():
+ """
+ Submenu consisting of...
+ [X] <Page 1>
+ [ ] <Page 2>
+ [ ] etc...
+ Color (Submenu)
+ """
+
+ view_menu = Submenu('View')
+ control = nyx.controller.get_controller()
+
+ if control.get_page_count() > 0:
+ page_group = SelectionGroup(control.set_page, control.get_page())
+
+ for i in range(control.get_page_count()):
+ page_panels = control.get_display_panels(page_number = i)
+ label = ' / '.join([str_tools._to_camel_case(panel.get_name()) for panel in page_panels])
+
+ view_menu.add(SelectionMenuItem(label, page_group, i))
+
+ if nyx.curses.is_color_supported():
+ color_menu = Submenu('Color')
+ color_group = SelectionGroup(nyx.curses.set_color_override, nyx.curses.get_color_override())
+
+ color_menu.add(SelectionMenuItem('All', color_group, None))
+
+ for color in nyx.curses.Color:
+ color_menu.add(SelectionMenuItem(str_tools._to_camel_case(color), color_group, color))
+
+ view_menu.add(color_menu)
+
+ return view_menu
+
+
+def make_help_menu():
+ """
+ Submenu consisting of...
+ Hotkeys
+ About
+ """
+
+ help_menu = Submenu('Help')
+ help_menu.add(MenuItem('Hotkeys', nyx.popups.show_help))
+ help_menu.add(MenuItem('About', nyx.popups.show_about))
+ return help_menu
+
+
+def make_graph_menu(graph_panel):
+ """
+ Submenu for the graph panel, consisting of...
+ [X] <Stat 1>
+ [ ] <Stat 2>
+ [ ] <Stat 2>
+ Resize...
+ Interval (Submenu)
+ Bounds (Submenu)
+
+ Arguments:
+ graph_panel - instance of the graph panel
+ """
+
+ graph_menu = Submenu('Graph')
+
+ # stats options
+
+ stat_group = SelectionGroup(functools.partial(setattr, graph_panel, 'displayed_stat'), graph_panel.displayed_stat)
+ available_stats = graph_panel.stat_options()
+ available_stats.sort()
+
+ for stat_key in ['None'] + available_stats:
+ label = str_tools._to_camel_case(stat_key, divider = ' ')
+ stat_key = None if stat_key == 'None' else stat_key
+ graph_menu.add(SelectionMenuItem(label, stat_group, stat_key))
+
+ # resizing option
+
+ graph_menu.add(MenuItem('Resize...', graph_panel.resize_graph))
+
+ # interval submenu
+
+ interval_menu = Submenu('Interval')
+ interval_group = SelectionGroup(functools.partial(setattr, graph_panel, 'update_interval'), graph_panel.update_interval)
+
+ for interval in nyx.panel.graph.Interval:
+ interval_menu.add(SelectionMenuItem(interval, interval_group, interval))
+
+ graph_menu.add(interval_menu)
+
+ # bounds submenu
+
+ bounds_menu = Submenu('Bounds')
+ bounds_group = SelectionGroup(functools.partial(setattr, graph_panel, 'bounds_type'), graph_panel.bounds_type)
+
+ for bounds_type in nyx.panel.graph.Bounds:
+ bounds_menu.add(SelectionMenuItem(bounds_type, bounds_group, bounds_type))
+
+ graph_menu.add(bounds_menu)
+
+ return graph_menu
+
+
+def make_log_menu(log_panel):
+ """
+ Submenu for the log panel, consisting of...
+ Events...
+ Snapshot...
+ Clear
+ Show / Hide Duplicates
+ Filter (Submenu)
+
+ Arguments:
+ log_panel - instance of the log panel
+ """
+
+ log_menu = Submenu('Log')
+
+ log_menu.add(MenuItem('Events...', log_panel.show_event_selection_prompt))
+ log_menu.add(MenuItem('Snapshot...', log_panel.show_snapshot_prompt))
+ log_menu.add(MenuItem('Clear', log_panel.clear))
+
+ if CONFIG['features.log.showDuplicateEntries']:
+ label, arg = 'Hide', False
+ else:
+ label, arg = 'Show', True
+
+ log_menu.add(MenuItem('%s Duplicates' % label, functools.partial(log_panel.set_duplicate_visability, arg)))
+
+ # filter submenu
+
+ log_filter = log_panel.get_filter()
+
+ filter_menu = Submenu('Filter')
+ filter_group = SelectionGroup(log_filter.select, log_filter.selection())
+
+ filter_menu.add(SelectionMenuItem('None', filter_group, None))
+
+ for option in log_filter.latest_selections():
+ filter_menu.add(SelectionMenuItem(option, filter_group, option))
+
+ filter_menu.add(MenuItem('New...', log_panel.show_filter_prompt))
+ log_menu.add(filter_menu)
+
+ return log_menu
+
+
+def make_connections_menu(conn_panel):
+ """
+ Submenu for the connections panel, consisting of...
+ Sorting...
+ Resolver (Submenu)
+
+ Arguments:
+ conn_panel - instance of the connections panel
+ """
+
+ connections_menu = Submenu('Connections')
+
+ # sorting option
+
+ connections_menu.add(MenuItem('Sorting...', conn_panel.show_sort_dialog))
+
+ # resolver submenu
+
+ conn_resolver = nyx.tracker.get_connection_tracker()
+ resolver_menu = Submenu('Resolver')
+ resolver_group = SelectionGroup(conn_resolver.set_custom_resolver, conn_resolver.get_custom_resolver())
+
+ resolver_menu.add(SelectionMenuItem('auto', resolver_group, None))
+
+ for option in stem.util.connection.Resolver:
+ resolver_menu.add(SelectionMenuItem(option, resolver_group, option))
+
+ connections_menu.add(resolver_menu)
+
+ return connections_menu
+
+
+def make_configuration_menu(config_panel):
+ """
+ Submenu for the configuration panel, consisting of...
+ Save Config...
+ Sorting...
+ Filter / Unfilter Options
+
+ Arguments:
+ config_panel - instance of the configuration panel
+ """
+
+ config_menu = Submenu('Configuration')
+ config_menu.add(MenuItem('Save Config...', config_panel.show_write_dialog))
+ config_menu.add(MenuItem('Sorting...', config_panel.show_sort_dialog))
+ return config_menu
+
+
+def make_torrc_menu(torrc_panel):
+ """
+ Submenu for the torrc panel, consisting of...
+ Reload
+ Show / Hide Comments
+ Show / Hide Line Numbers
+
+ Arguments:
+ torrc_panel - instance of the torrc panel
+ """
+
+ torrc_menu = Submenu('Torrc')
+
+ if torrc_panel._show_comments:
+ label, arg = 'Hide', False
+ else:
+ label, arg = 'Show', True
+
+ torrc_menu.add(MenuItem('%s Comments' % label, functools.partial(torrc_panel.set_comments_visible, arg)))
+
+ if torrc_panel._show_line_numbers:
+ label, arg = 'Hide', False
+ else:
+ label, arg = 'Show', True
+ torrc_menu.add(MenuItem('%s Line Numbers' % label, functools.partial(torrc_panel.set_line_number_visible, arg)))
+
+ return torrc_menu
+
+
+class MenuCursor:
+ """
+ Tracks selection and key handling in the menu.
+ """
+
+ def __init__(self, initial_selection):
+ self._selection = initial_selection
+ self._is_done = False
+
+ def is_done(self):
+ """
+ Provides true if a selection has indicated that we should close the menu.
+ False otherwise.
+ """
+
+ return self._is_done
+
+ def get_selection(self):
+ """
+ Provides the currently selected menu item.
+ """
+
+ return self._selection
+
+ def handle_key(self, key):
+ is_selection_submenu = isinstance(self._selection, Submenu)
+ selection_hierarchy = self._selection.get_hierarchy()
+
+ if key.is_selection():
+ if is_selection_submenu:
+ if not self._selection.is_empty():
+ self._selection = self._selection.get_children()[0]
+ else:
+ self._is_done = self._selection.select()
+ elif key.match('up'):
+ self._selection = self._selection.prev()
+ elif key.match('down'):
+ self._selection = self._selection.next()
+ elif key.match('left'):
+ if len(selection_hierarchy) <= 3:
+ # shift to the previous main submenu
+
+ prev_submenu = selection_hierarchy[1].prev()
+ self._selection = prev_submenu.get_children()[0]
+ else:
+ # go up a submenu level
+
+ self._selection = self._selection.get_parent()
+ elif key.match('right'):
+ if is_selection_submenu:
+ # open submenu (same as making a selection)
+
+ if not self._selection.is_empty():
+ self._selection = self._selection.get_children()[0]
+ else:
+ # shift to the next main submenu
+
+ next_submenu = selection_hierarchy[1].next()
+ self._selection = next_submenu.get_children()[0]
+ elif key.match('esc', 'm'):
+ self._is_done = True
+
+
+def show_menu():
+ selection_left = [0]
+
+ def _render(subwindow):
+ x = 0
+
+ for top_level_item in menu.get_children():
+ if top_level_item == selection_hierarchy[1]:
+ selection_left[0] = x
+ attr = UNDERLINE
+ else:
+ attr = NORMAL
+
+ x = subwindow.addstr(x, 0, ' %s ' % top_level_item.get_label()[1], BOLD, attr)
+ subwindow.vline(x, 0, 1)
+ x += 1
+
+ with nyx.curses.CURSES_LOCK:
+ # generates the menu and uses the initial selection of the first item in
+ # the file menu
+
+ menu = make_menu()
+ cursor = MenuCursor(menu.get_children()[0].get_children()[0])
+
+ while not cursor.is_done():
+ selection_hierarchy = cursor.get_selection().get_hierarchy()
+
+ # provide a message saying how to close the menu
+
+ nyx.controller.show_message('Press m or esc to close the menu.', BOLD)
+ nyx.curses.draw(_render, height = 1, background = RED)
+ _draw_submenu(cursor, 1, 1, selection_left[0])
+ cursor.handle_key(nyx.curses.key_input())
+
+ # redraws the rest of the interface if we're rendering on it again
+
+ if not cursor.is_done():
+ nyx.controller.get_controller().redraw()
+
+ nyx.controller.show_message()
+
+
+def _draw_submenu(cursor, level, top, left):
+ selection_hierarchy = cursor.get_selection().get_hierarchy()
+
+ # checks if there's nothing to display
+
+ if len(selection_hierarchy) < level + 2:
+ return
+
+ # fetches the submenu and selection we're displaying
+
+ submenu = selection_hierarchy[level]
+ selection = selection_hierarchy[level + 1]
+
+ # gets the size of the prefix, middle, and suffix columns
+
+ all_label_sets = [entry.get_label() for entry in submenu.get_children()]
+ prefix_col_size = max([len(entry[0]) for entry in all_label_sets])
+ middle_col_size = max([len(entry[1]) for entry in all_label_sets])
+ suffix_col_size = max([len(entry[2]) for entry in all_label_sets])
+
+ # formatted string so we can display aligned menu entries
+
+ label_format = ' %%-%is%%-%is%%-%is ' % (prefix_col_size, middle_col_size, suffix_col_size)
+ menu_width = len(label_format % ('', '', ''))
+ selection_top = submenu.get_children().index(selection) if selection in submenu.get_children() else 0
+
+ def _render(subwindow):
+ for y, menu_item in enumerate(submenu.get_children()):
+ if menu_item == selection:
+ subwindow.addstr(0, y, label_format % menu_item.get_label(), WHITE, BOLD)
+ else:
+ subwindow.addstr(0, y, label_format % menu_item.get_label())
+
+ with nyx.curses.CURSES_LOCK:
+ nyx.curses.draw(_render, top = top, left = left, width = menu_width, height = len(submenu.get_children()), background = RED)
+ _draw_submenu(cursor, level + 1, top + selection_top, left + menu_width)
+
+
+class MenuItem():
+ """
+ Option in a drop-down menu.
+ """
+
+ def __init__(self, label, callback):
+ self._label = label
+ self._callback = callback
+ self._parent = None
+
+ def get_label(self):
+ """
+ Provides a tuple of three strings representing the prefix, label, and
+ suffix for this item.
+ """
+
+ return ('', self._label, '')
+
+ def get_parent(self):
+ """
+ Provides the Submenu we're contained within.
+ """
+
+ return self._parent
+
+ def get_hierarchy(self):
+ """
+ Provides a list with all of our parents, up to the root.
+ """
+
+ my_hierarchy = [self]
+ while my_hierarchy[-1].get_parent():
+ my_hierarchy.append(my_hierarchy[-1].get_parent())
+
+ my_hierarchy.reverse()
+ return my_hierarchy
+
+ def get_root(self):
+ """
+ Provides the base submenu we belong to.
+ """
+
+ if self._parent:
+ return self._parent.get_root()
+ else:
+ return self
+
+ def select(self):
+ """
+ Performs the callback for the menu item, returning true if we should close
+ the menu and false otherwise.
+ """
+
+ if self._callback:
+ control = nyx.controller.get_controller()
+ control.redraw()
+ self._callback()
+
+ return True
+
+ def next(self):
+ """
+ Provides the next option for the submenu we're in, raising a ValueError
+ if we don't have a parent.
+ """
+
+ return self._get_sibling(1)
+
+ def prev(self):
+ """
+ Provides the previous option for the submenu we're in, raising a ValueError
+ if we don't have a parent.
+ """
+
+ return self._get_sibling(-1)
+
+ def _get_sibling(self, offset):
+ """
+ Provides our sibling with a given index offset from us, raising a
+ ValueError if we don't have a parent.
+
+ Arguments:
+ offset - index offset for the sibling to be returned
+ """
+
+ if self._parent:
+ my_siblings = self._parent.get_children()
+
+ try:
+ my_index = my_siblings.index(self)
+ return my_siblings[(my_index + offset) % len(my_siblings)]
+ except ValueError:
+ # We expect a bidirectional references between submenus and their
+ # children. If we don't have this then our menu's screwed up.
+
+ msg = "The '%s' submenu doesn't contain '%s' (children: '%s')" % (self, self._parent, "', '".join(my_siblings))
+ raise ValueError(msg)
+ else:
+ raise ValueError("Menu option '%s' doesn't have a parent" % self)
+
+ def __str__(self):
+ return self._label
+
+
+class Submenu(MenuItem):
+ """
+ Menu item that lists other menu options.
+ """
+
+ def __init__(self, label):
+ MenuItem.__init__(self, label, None)
+ self._children = []
+
+ def get_label(self):
+ """
+ Provides our label with a '>' suffix to indicate that we have suboptions.
+ """
+
+ my_label = MenuItem.get_label(self)[1]
+ return ('', my_label, ' >')
+
+ def add(self, menu_item):
+ """
+ Adds the given menu item to our listing. This raises a ValueError if the
+ item already has a parent.
+
+ Arguments:
+ menu_item - menu option to be added
+ """
+
+ if menu_item.get_parent():
+ raise ValueError("Menu option '%s' already has a parent" % menu_item)
+ else:
+ menu_item._parent = self
+ self._children.append(menu_item)
+
+ def get_children(self):
+ """
+ Provides the menu and submenus we contain.
+ """
+
+ return list(self._children)
+
+ def is_empty(self):
+ """
+ True if we have no children, false otherwise.
+ """
+
+ return not bool(self._children)
+
+ def select(self):
+ return False
+
+
+class SelectionGroup():
+ """
+ Radio button groups that SelectionMenuItems can belong to.
+ """
+
+ def __init__(self, action, selected_arg):
+ self.action = action
+ self.selected_arg = selected_arg
+
+
+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 is_selected(self):
+ """
+ True if we're the selected item, false otherwise.
+ """
+
+ return self._arg == self._group.selected_arg
+
+ def get_label(self):
+ """
+ Provides our label with a '[X]' prefix if selected and '[ ]' if not.
+ """
+
+ my_label = MenuItem.get_label(self)[1]
+ my_prefix = '[X] ' if self.is_selected() else '[ ] '
+ return (my_prefix, my_label, '')
+
+ def select(self):
+ """
+ Performs the group's setter action with our argument.
+ """
+
+ if not self.is_selected():
+ self._group.action(self._arg)
+
+ return True
diff --git a/nyx/menu/__init__.py b/nyx/menu/__init__.py
deleted file mode 100644
index c195054..0000000
--- a/nyx/menu/__init__.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# Copyright 2016, Damian Johnson and The Tor Project
-# See LICENSE for licensing information
-
-"""
-Resources for displaying the menu.
-"""
-
-__all__ = [
- 'actions',
- 'item',
- 'menu',
-]
diff --git a/nyx/menu/actions.py b/nyx/menu/actions.py
deleted file mode 100644
index 7f25504..0000000
--- a/nyx/menu/actions.py
+++ /dev/null
@@ -1,306 +0,0 @@
-# Copyright 2016, Damian Johnson and The Tor Project
-# See LICENSE for licensing information
-
-"""
-Generates the menu for nyx, binding options with their related actions.
-"""
-
-import functools
-
-import nyx.controller
-import nyx.curses
-import nyx.panel.graph
-import nyx.popups
-import nyx.menu.item
-import nyx.tracker
-
-import stem
-import stem.util.connection
-
-from nyx import tor_controller
-from stem.util import conf, str_tools
-
-CONFIG = conf.config_dict('nyx', {
- 'features.log.showDuplicateEntries': False,
-})
-
-
-def make_menu():
- """
- Constructs the base menu and all of its contents.
- """
-
- base_menu = nyx.menu.item.Submenu('')
- base_menu.add(make_actions_menu())
- base_menu.add(make_view_menu())
-
- control = nyx.controller.get_controller()
-
- for page_panel in control.get_display_panels():
- if page_panel.get_name() == 'graph':
- base_menu.add(make_graph_menu(page_panel))
- elif page_panel.get_name() == 'log':
- base_menu.add(make_log_menu(page_panel))
- elif page_panel.get_name() == 'connections':
- base_menu.add(make_connections_menu(page_panel))
- elif page_panel.get_name() == 'configuration':
- base_menu.add(make_configuration_menu(page_panel))
- elif page_panel.get_name() == 'torrc':
- base_menu.add(make_torrc_menu(page_panel))
-
- base_menu.add(make_help_menu())
-
- return base_menu
-
-
-def make_actions_menu():
- """
- Submenu consisting of...
- Close Menu
- New Identity
- Reset Tor
- Pause / Unpause
- Exit
- """
-
- control = nyx.controller.get_controller()
- controller = tor_controller()
- header_panel = control.header_panel()
- actions_menu = nyx.menu.item.Submenu('Actions')
- actions_menu.add(nyx.menu.item.MenuItem('Close Menu', None))
- actions_menu.add(nyx.menu.item.MenuItem('New Identity', header_panel.send_newnym))
- actions_menu.add(nyx.menu.item.MenuItem('Reset Tor', functools.partial(controller.signal, stem.Signal.RELOAD)))
-
- if control.is_paused():
- label, arg = 'Unpause', False
- else:
- label, arg = 'Pause', True
-
- actions_menu.add(nyx.menu.item.MenuItem(label, functools.partial(control.set_paused, arg)))
- actions_menu.add(nyx.menu.item.MenuItem('Exit', control.quit))
-
- return actions_menu
-
-
-def make_view_menu():
- """
- Submenu consisting of...
- [X] <Page 1>
- [ ] <Page 2>
- [ ] etc...
- Color (Submenu)
- """
-
- view_menu = nyx.menu.item.Submenu('View')
- control = nyx.controller.get_controller()
-
- if control.get_page_count() > 0:
- page_group = nyx.menu.item.SelectionGroup(control.set_page, control.get_page())
-
- for i in range(control.get_page_count()):
- page_panels = control.get_display_panels(page_number = i)
- label = ' / '.join([str_tools._to_camel_case(panel.get_name()) for panel in page_panels])
-
- view_menu.add(nyx.menu.item.SelectionMenuItem(label, page_group, i))
-
- if nyx.curses.is_color_supported():
- color_menu = nyx.menu.item.Submenu('Color')
- color_group = nyx.menu.item.SelectionGroup(nyx.curses.set_color_override, nyx.curses.get_color_override())
-
- color_menu.add(nyx.menu.item.SelectionMenuItem('All', color_group, None))
-
- for color in nyx.curses.Color:
- color_menu.add(nyx.menu.item.SelectionMenuItem(str_tools._to_camel_case(color), color_group, color))
-
- view_menu.add(color_menu)
-
- return view_menu
-
-
-def make_help_menu():
- """
- Submenu consisting of...
- Hotkeys
- About
- """
-
- help_menu = nyx.menu.item.Submenu('Help')
- help_menu.add(nyx.menu.item.MenuItem('Hotkeys', nyx.popups.show_help))
- help_menu.add(nyx.menu.item.MenuItem('About', nyx.popups.show_about))
- return help_menu
-
-
-def make_graph_menu(graph_panel):
- """
- Submenu for the graph panel, consisting of...
- [X] <Stat 1>
- [ ] <Stat 2>
- [ ] <Stat 2>
- Resize...
- Interval (Submenu)
- Bounds (Submenu)
-
- Arguments:
- graph_panel - instance of the graph panel
- """
-
- graph_menu = nyx.menu.item.Submenu('Graph')
-
- # stats options
-
- stat_group = nyx.menu.item.SelectionGroup(functools.partial(setattr, graph_panel, 'displayed_stat'), graph_panel.displayed_stat)
- available_stats = graph_panel.stat_options()
- available_stats.sort()
-
- for stat_key in ['None'] + available_stats:
- label = str_tools._to_camel_case(stat_key, divider = ' ')
- stat_key = None if stat_key == 'None' else stat_key
- graph_menu.add(nyx.menu.item.SelectionMenuItem(label, stat_group, stat_key))
-
- # resizing option
-
- graph_menu.add(nyx.menu.item.MenuItem('Resize...', graph_panel.resize_graph))
-
- # interval submenu
-
- interval_menu = nyx.menu.item.Submenu('Interval')
- interval_group = nyx.menu.item.SelectionGroup(functools.partial(setattr, graph_panel, 'update_interval'), graph_panel.update_interval)
-
- for interval in nyx.panel.graph.Interval:
- interval_menu.add(nyx.menu.item.SelectionMenuItem(interval, interval_group, interval))
-
- graph_menu.add(interval_menu)
-
- # bounds submenu
-
- bounds_menu = nyx.menu.item.Submenu('Bounds')
- bounds_group = nyx.menu.item.SelectionGroup(functools.partial(setattr, graph_panel, 'bounds_type'), graph_panel.bounds_type)
-
- for bounds_type in nyx.panel.graph.Bounds:
- bounds_menu.add(nyx.menu.item.SelectionMenuItem(bounds_type, bounds_group, bounds_type))
-
- graph_menu.add(bounds_menu)
-
- return graph_menu
-
-
-def make_log_menu(log_panel):
- """
- Submenu for the log panel, consisting of...
- Events...
- Snapshot...
- Clear
- Show / Hide Duplicates
- Filter (Submenu)
-
- Arguments:
- log_panel - instance of the log panel
- """
-
- log_menu = nyx.menu.item.Submenu('Log')
-
- log_menu.add(nyx.menu.item.MenuItem('Events...', log_panel.show_event_selection_prompt))
- log_menu.add(nyx.menu.item.MenuItem('Snapshot...', log_panel.show_snapshot_prompt))
- log_menu.add(nyx.menu.item.MenuItem('Clear', log_panel.clear))
-
- if CONFIG['features.log.showDuplicateEntries']:
- label, arg = 'Hide', False
- else:
- label, arg = 'Show', True
-
- log_menu.add(nyx.menu.item.MenuItem('%s Duplicates' % label, functools.partial(log_panel.set_duplicate_visability, arg)))
-
- # filter submenu
-
- log_filter = log_panel.get_filter()
-
- filter_menu = nyx.menu.item.Submenu('Filter')
- filter_group = nyx.menu.item.SelectionGroup(log_filter.select, log_filter.selection())
-
- filter_menu.add(nyx.menu.item.SelectionMenuItem('None', filter_group, None))
-
- for option in log_filter.latest_selections():
- filter_menu.add(nyx.menu.item.SelectionMenuItem(option, filter_group, option))
-
- filter_menu.add(nyx.menu.item.MenuItem('New...', log_panel.show_filter_prompt))
- log_menu.add(filter_menu)
-
- return log_menu
-
-
-def make_connections_menu(conn_panel):
- """
- Submenu for the connections panel, consisting of...
- Sorting...
- Resolver (Submenu)
-
- Arguments:
- conn_panel - instance of the connections panel
- """
-
- connections_menu = nyx.menu.item.Submenu('Connections')
-
- # sorting option
-
- connections_menu.add(nyx.menu.item.MenuItem('Sorting...', conn_panel.show_sort_dialog))
-
- # resolver submenu
-
- conn_resolver = nyx.tracker.get_connection_tracker()
- resolver_menu = nyx.menu.item.Submenu('Resolver')
- resolver_group = nyx.menu.item.SelectionGroup(conn_resolver.set_custom_resolver, conn_resolver.get_custom_resolver())
-
- resolver_menu.add(nyx.menu.item.SelectionMenuItem('auto', resolver_group, None))
-
- for option in stem.util.connection.Resolver:
- resolver_menu.add(nyx.menu.item.SelectionMenuItem(option, resolver_group, option))
-
- connections_menu.add(resolver_menu)
-
- return connections_menu
-
-
-def make_configuration_menu(config_panel):
- """
- Submenu for the configuration panel, consisting of...
- Save Config...
- Sorting...
- Filter / Unfilter Options
-
- Arguments:
- config_panel - instance of the configuration panel
- """
-
- config_menu = nyx.menu.item.Submenu('Configuration')
- config_menu.add(nyx.menu.item.MenuItem('Save Config...', config_panel.show_write_dialog))
- config_menu.add(nyx.menu.item.MenuItem('Sorting...', config_panel.show_sort_dialog))
- return config_menu
-
-
-def make_torrc_menu(torrc_panel):
- """
- Submenu for the torrc panel, consisting of...
- Reload
- Show / Hide Comments
- Show / Hide Line Numbers
-
- Arguments:
- torrc_panel - instance of the torrc panel
- """
-
- torrc_menu = nyx.menu.item.Submenu('Torrc')
-
- if torrc_panel._show_comments:
- label, arg = 'Hide', False
- else:
- label, arg = 'Show', True
-
- torrc_menu.add(nyx.menu.item.MenuItem('%s Comments' % label, functools.partial(torrc_panel.set_comments_visible, arg)))
-
- if torrc_panel._show_line_numbers:
- label, arg = 'Hide', False
- else:
- label, arg = 'Show', True
- torrc_menu.add(nyx.menu.item.MenuItem('%s Line Numbers' % label, functools.partial(torrc_panel.set_line_number_visible, arg)))
-
- return torrc_menu
diff --git a/nyx/menu/item.py b/nyx/menu/item.py
deleted file mode 100644
index ef5e359..0000000
--- a/nyx/menu/item.py
+++ /dev/null
@@ -1,210 +0,0 @@
-# Copyright 2016, Damian Johnson and The Tor Project
-# See LICENSE for licensing information
-
-"""
-Menu item, representing an option in the drop-down menu.
-"""
-
-import nyx.controller
-
-
-class MenuItem():
- """
- Option in a drop-down menu.
- """
-
- def __init__(self, label, callback):
- self._label = label
- self._callback = callback
- self._parent = None
-
- def get_label(self):
- """
- Provides a tuple of three strings representing the prefix, label, and
- suffix for this item.
- """
-
- return ('', self._label, '')
-
- def get_parent(self):
- """
- Provides the Submenu we're contained within.
- """
-
- return self._parent
-
- def get_hierarchy(self):
- """
- Provides a list with all of our parents, up to the root.
- """
-
- my_hierarchy = [self]
- while my_hierarchy[-1].get_parent():
- my_hierarchy.append(my_hierarchy[-1].get_parent())
-
- my_hierarchy.reverse()
- return my_hierarchy
-
- def get_root(self):
- """
- Provides the base submenu we belong to.
- """
-
- if self._parent:
- return self._parent.get_root()
- else:
- return self
-
- def select(self):
- """
- Performs the callback for the menu item, returning true if we should close
- the menu and false otherwise.
- """
-
- if self._callback:
- control = nyx.controller.get_controller()
- control.redraw()
- self._callback()
-
- return True
-
- def next(self):
- """
- Provides the next option for the submenu we're in, raising a ValueError
- if we don't have a parent.
- """
-
- return self._get_sibling(1)
-
- def prev(self):
- """
- Provides the previous option for the submenu we're in, raising a ValueError
- if we don't have a parent.
- """
-
- return self._get_sibling(-1)
-
- def _get_sibling(self, offset):
- """
- Provides our sibling with a given index offset from us, raising a
- ValueError if we don't have a parent.
-
- Arguments:
- offset - index offset for the sibling to be returned
- """
-
- if self._parent:
- my_siblings = self._parent.get_children()
-
- try:
- my_index = my_siblings.index(self)
- return my_siblings[(my_index + offset) % len(my_siblings)]
- except ValueError:
- # We expect a bidirectional references between submenus and their
- # children. If we don't have this then our menu's screwed up.
-
- msg = "The '%s' submenu doesn't contain '%s' (children: '%s')" % (self, self._parent, "', '".join(my_siblings))
- raise ValueError(msg)
- else:
- raise ValueError("Menu option '%s' doesn't have a parent" % self)
-
- def __str__(self):
- return self._label
-
-
-class Submenu(MenuItem):
- """
- Menu item that lists other menu options.
- """
-
- def __init__(self, label):
- MenuItem.__init__(self, label, None)
- self._children = []
-
- def get_label(self):
- """
- Provides our label with a '>' suffix to indicate that we have suboptions.
- """
-
- my_label = MenuItem.get_label(self)[1]
- return ('', my_label, ' >')
-
- def add(self, menu_item):
- """
- Adds the given menu item to our listing. This raises a ValueError if the
- item already has a parent.
-
- Arguments:
- menu_item - menu option to be added
- """
-
- if menu_item.get_parent():
- raise ValueError("Menu option '%s' already has a parent" % menu_item)
- else:
- menu_item._parent = self
- self._children.append(menu_item)
-
- def get_children(self):
- """
- Provides the menu and submenus we contain.
- """
-
- return list(self._children)
-
- def is_empty(self):
- """
- True if we have no children, false otherwise.
- """
-
- return not bool(self._children)
-
- def select(self):
- return False
-
-
-class SelectionGroup():
- """
- Radio button groups that SelectionMenuItems can belong to.
- """
-
- def __init__(self, action, selected_arg):
- self.action = action
- self.selected_arg = selected_arg
-
-
-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 is_selected(self):
- """
- True if we're the selected item, false otherwise.
- """
-
- return self._arg == self._group.selected_arg
-
- def get_label(self):
- """
- Provides our label with a '[X]' prefix if selected and '[ ]' if not.
- """
-
- my_label = MenuItem.get_label(self)[1]
- my_prefix = '[X] ' if self.is_selected() else '[ ] '
- return (my_prefix, my_label, '')
-
- def select(self):
- """
- Performs the group's setter action with our argument.
- """
-
- if not self.is_selected():
- self._group.action(self._arg)
-
- return True
diff --git a/nyx/menu/menu.py b/nyx/menu/menu.py
deleted file mode 100644
index 01aa25e..0000000
--- a/nyx/menu/menu.py
+++ /dev/null
@@ -1,157 +0,0 @@
-# Copyright 2016, Damian Johnson and The Tor Project
-# See LICENSE for licensing information
-
-"""
-Display logic for presenting the menu.
-"""
-
-import nyx.curses
-import nyx.popups
-import nyx.controller
-import nyx.menu.item
-import nyx.menu.actions
-
-from nyx.curses import RED, WHITE, NORMAL, BOLD, UNDERLINE
-
-
-class MenuCursor:
- """
- Tracks selection and key handling in the menu.
- """
-
- def __init__(self, initial_selection):
- self._selection = initial_selection
- self._is_done = False
-
- def is_done(self):
- """
- Provides true if a selection has indicated that we should close the menu.
- False otherwise.
- """
-
- return self._is_done
-
- def get_selection(self):
- """
- Provides the currently selected menu item.
- """
-
- return self._selection
-
- def handle_key(self, key):
- is_selection_submenu = isinstance(self._selection, nyx.menu.item.Submenu)
- selection_hierarchy = self._selection.get_hierarchy()
-
- if key.is_selection():
- if is_selection_submenu:
- if not self._selection.is_empty():
- self._selection = self._selection.get_children()[0]
- else:
- self._is_done = self._selection.select()
- elif key.match('up'):
- self._selection = self._selection.prev()
- elif key.match('down'):
- self._selection = self._selection.next()
- elif key.match('left'):
- if len(selection_hierarchy) <= 3:
- # shift to the previous main submenu
-
- prev_submenu = selection_hierarchy[1].prev()
- self._selection = prev_submenu.get_children()[0]
- else:
- # go up a submenu level
-
- self._selection = self._selection.get_parent()
- elif key.match('right'):
- if is_selection_submenu:
- # open submenu (same as making a selection)
-
- if not self._selection.is_empty():
- self._selection = self._selection.get_children()[0]
- else:
- # shift to the next main submenu
-
- next_submenu = selection_hierarchy[1].next()
- self._selection = next_submenu.get_children()[0]
- elif key.match('esc', 'm'):
- self._is_done = True
-
-
-def show_menu():
- selection_left = [0]
-
- def _render(subwindow):
- x = 0
-
- for top_level_item in menu.get_children():
- if top_level_item == selection_hierarchy[1]:
- selection_left[0] = x
- attr = UNDERLINE
- else:
- attr = NORMAL
-
- x = subwindow.addstr(x, 0, ' %s ' % top_level_item.get_label()[1], BOLD, attr)
- subwindow.vline(x, 0, 1)
- x += 1
-
- with nyx.curses.CURSES_LOCK:
- # generates the menu and uses the initial selection of the first item in
- # the file menu
-
- menu = nyx.menu.actions.make_menu()
- cursor = MenuCursor(menu.get_children()[0].get_children()[0])
-
- while not cursor.is_done():
- selection_hierarchy = cursor.get_selection().get_hierarchy()
-
- # provide a message saying how to close the menu
-
- nyx.controller.show_message('Press m or esc to close the menu.', BOLD)
- nyx.curses.draw(_render, height = 1, background = RED)
- _draw_submenu(cursor, 1, 1, selection_left[0])
- cursor.handle_key(nyx.curses.key_input())
-
- # redraws the rest of the interface if we're rendering on it again
-
- if not cursor.is_done():
- nyx.controller.get_controller().redraw()
-
- nyx.controller.show_message()
-
-
-def _draw_submenu(cursor, level, top, left):
- selection_hierarchy = cursor.get_selection().get_hierarchy()
-
- # checks if there's nothing to display
-
- if len(selection_hierarchy) < level + 2:
- return
-
- # fetches the submenu and selection we're displaying
-
- submenu = selection_hierarchy[level]
- selection = selection_hierarchy[level + 1]
-
- # gets the size of the prefix, middle, and suffix columns
-
- all_label_sets = [entry.get_label() for entry in submenu.get_children()]
- prefix_col_size = max([len(entry[0]) for entry in all_label_sets])
- middle_col_size = max([len(entry[1]) for entry in all_label_sets])
- suffix_col_size = max([len(entry[2]) for entry in all_label_sets])
-
- # formatted string so we can display aligned menu entries
-
- label_format = ' %%-%is%%-%is%%-%is ' % (prefix_col_size, middle_col_size, suffix_col_size)
- menu_width = len(label_format % ('', '', ''))
- selection_top = submenu.get_children().index(selection) if selection in submenu.get_children() else 0
-
- def _render(subwindow):
- for y, menu_item in enumerate(submenu.get_children()):
- if menu_item == selection:
- subwindow.addstr(0, y, label_format % menu_item.get_label(), WHITE, BOLD)
- else:
- subwindow.addstr(0, y, label_format % menu_item.get_label())
-
- with nyx.curses.CURSES_LOCK:
- nyx.curses.draw(_render, top = top, left = left, width = menu_width, height = len(submenu.get_children()), background = RED)
- _draw_submenu(cursor, level + 1, top + selection_top, left + menu_width)
diff --git a/setup.py b/setup.py
index b446c67..d83c8aa 100644
--- a/setup.py
+++ b/setup.py
@@ -99,7 +99,7 @@ setup(
author = nyx.__author__,
author_email = nyx.__contact__,
url = nyx.__url__,
- packages = ['nyx', 'nyx.panel', 'nyx.menu'],
+ packages = ['nyx', 'nyx.panel'],
keywords = 'tor onion controller',
install_requires = ['stem>=1.4.1'],
package_data = {'nyx': ['settings/*', 'resources/*']},
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits