commit 55166969f6caad16c080f3cbd85d9a372eb0cc90
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Jun 27 12:31:00 2015 -0700
Rewrite descriptor dialog's draw() method
Not quite done with the dialog, but damn its draw method is now better. :P
---
nyx/connections/descriptor_popup.py | 194 ++++++++++++-----------------------
nyx/popups.py | 19 ++--
2 files changed, 73 insertions(+), 140 deletions(-)
diff --git a/nyx/connections/descriptor_popup.py b/nyx/connections/descriptor_popup.py
index 7eeb9df..1d155c3 100644
--- a/nyx/connections/descriptor_popup.py
+++ b/nyx/connections/descriptor_popup.py
@@ -12,14 +12,11 @@ from nyx.util import panel, tor_controller, ui_tools
from stem.util import str_tools
-# field keywords used to identify areas for coloring
-
-LINE_NUM_COLOR = 'yellow'
+HEADERS = ['Consensus:', 'Microdescriptor:', 'Server Descriptor:']
HEADER_COLOR = 'cyan'
-HEADER_PREFIX = ['Consensus:', 'Microdescriptor:', 'Server Descriptor:']
+LINE_NUMBER_COLOR = 'yellow'
-SIG_START_KEYS = ['-----BEGIN RSA PUBLIC KEY-----', '-----BEGIN SIGNATURE-----']
-SIG_END_KEYS = ['-----END RSA PUBLIC KEY-----', '-----END SIGNATURE-----']
+BLOCK_START, BLOCK_END = '-----BEGIN ', '-----END '
UNRESOLVED_MSG = 'No consensus data available'
ERROR_MSG = 'Unable to retrieve data'
@@ -42,11 +39,9 @@ def show_descriptor_popup(conn_panel):
conn_panel.redraw(True)
control = nyx.controller.get_controller()
- panel.CURSES_LOCK.acquire()
- is_done = False
- try:
- while not is_done:
+ with panel.CURSES_LOCK:
+ while True:
selection = conn_panel.get_selection()
if not selection:
@@ -55,52 +50,42 @@ def show_descriptor_popup(conn_panel):
fingerprint = selection.foreign.get_fingerprint()
if fingerprint == 'UNKNOWN':
- fingerprint = None
-
- display_text = _display_text(fingerprint)
- display_color = nyx.connections.conn_entry.CATEGORY_COLOR[selection.get_type()]
- show_line_numbers = fingerprint is not None
-
- # determines the maximum popup size the display_text can fill
+ title = 'Consensus Descriptor (%s):' % fingerprint
+ lines = [UNRESOLVED_MSG]
+ show_line_numbers = False
+ else:
+ title = 'Consensus Descriptor:'
+ lines = _display_text(fingerprint)
+ show_line_numbers = True
- popup_height, popup_width = _preferred_size(display_text, conn_panel.max_x, show_line_numbers)
+ color = nyx.connections.conn_entry.CATEGORY_COLOR[selection.get_type()]
+ popup_height, popup_width = _preferred_size(lines, conn_panel.max_x, show_line_numbers)
with nyx.popups.popup_window(popup_height, popup_width) as (popup, _, height):
if popup:
- scroll, is_changed = 0, True
-
- while not is_done:
- if is_changed:
- draw(popup, fingerprint, display_text, display_color, scroll, show_line_numbers)
- is_changed = False
+ scroll = 0
+ _draw(popup, title, lines, color, scroll, show_line_numbers)
+ while True:
key = control.key_input()
if key.is_scroll():
- # TODO: This is a bit buggy in that scrolling is by display_text
- # 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...
-
- new_scroll = ui_tools.get_scroll_position(key, scroll, height - 2, len(display_text))
+ new_scroll = ui_tools.get_scroll_position(key, scroll, height - 2, len(lines))
if scroll != new_scroll:
- scroll, is_changed = new_scroll, True
+ scroll = new_scroll
+ _draw(popup, title, lines, color, scroll, show_line_numbers)
elif key.is_selection() or key.match('d'):
- is_done = True # closes popup
+ return # closes popup
elif key.match('left'):
conn_panel.handle_key(panel.KeyInput(curses.KEY_UP))
break
elif key.match('right'):
conn_panel.handle_key(panel.KeyInput(curses.KEY_DOWN))
break
- finally:
- conn_panel.set_title_visible(True)
- conn_panel.redraw(True)
- panel.CURSES_LOCK.release()
+
+ conn_panel.set_title_visible(True)
+ conn_panel.redraw(True)
def _display_text(fingerprint):
@@ -112,9 +97,6 @@ def _display_text(fingerprint):
:returns: **list** with the lines that should be displayed in the dialog
"""
- if not fingerprint:
- return [UNRESOLVED_MSG]
-
controller = tor_controller()
router_status_entry = controller.get_network_status(fingerprint, None)
microdescriptor = controller.get_microdescriptor(fingerprint, None)
@@ -153,108 +135,60 @@ def _preferred_size(text, max_width, show_line_numbers):
return (height, width)
-def draw(popup, fingerprint, display_text, display_color, scroll, show_line_numbers):
- popup.win.erase()
- popup.win.box()
- x_offset = 2
-
- if fingerprint:
- title = 'Consensus Descriptor (%s):' % fingerprint
- else:
- title = 'Consensus Descriptor:'
-
- popup.addstr(0, 0, title, curses.A_STANDOUT)
-
- line_number_width = int(math.log10(len(display_text))) + 1
- is_encryption_block = False # flag indicating if we're currently displaying a key
-
- # checks if first line is in an encryption block
+def _draw(popup, title, lines, entry_color, scroll, show_line_numbers):
+ def draw_msg(popup, min_x, x, y, width, msg, *attr):
+ while msg:
+ draw_msg, msg = str_tools.crop(msg, width - x, None, ending = None, get_remainder = True)
- for i in range(0, scroll):
- line_text = display_text[i].strip()
+ if not draw_msg:
+ draw_msg, msg = str_tools.crop(msg, width - x), '' # first word is longer than the line
- if line_text in SIG_START_KEYS:
- is_encryption_block = True
- elif line_text in SIG_END_KEYS:
- is_encryption_block = False
+ x = popup.addstr(y, x, draw_msg, *attr)
- draw_line, page_height = 1, popup.max_y - 2
+ if msg:
+ x, y = min_x, y + 1
- for i in range(scroll, scroll + page_height):
- line_text = display_text[i].strip()
- x_offset = 2
+ return x, y
- if show_line_numbers:
- line_number_label = ('%%%ii' % line_number_width) % (i + 1)
-
- popup.addstr(draw_line, x_offset, line_number_label, curses.A_BOLD, LINE_NUM_COLOR)
- x_offset += line_number_width + 1
-
- # Most consensus and descriptor lines are keyword/value pairs. Both are
- # shown with the same color, but the keyword is bolded.
-
- keyword, value = line_text, ''
- draw_format = display_color
-
- if line_text.startswith(HEADER_PREFIX[0]) or line_text.startswith(HEADER_PREFIX[1]):
- keyword, value = line_text, ''
- draw_format = HEADER_COLOR
- elif line_text == UNRESOLVED_MSG or line_text == ERROR_MSG:
- keyword, value = line_text, ''
- elif line_text in SIG_START_KEYS:
- keyword, value = line_text, ''
- is_encryption_block = True
- elif line_text in SIG_END_KEYS:
- keyword, value = line_text, ''
- is_encryption_block = False
- elif is_encryption_block:
- keyword, value = '', line_text
- elif ' ' in line_text:
- div_index = line_text.find(' ')
- keyword, value = line_text[:div_index], line_text[div_index:]
-
- display_queue = [(keyword, (draw_format, curses.A_BOLD)), (value, (draw_format,))]
- cursor_location = x_offset
-
- while display_queue:
- msg, msg_format = display_queue.pop(0)
-
- if not msg:
- continue
-
- max_msg_size = popup.max_x - 1 - cursor_location
-
- if len(msg) >= max_msg_size:
- # needs to split up the line
+ popup.win.erase()
- msg, remainder = str_tools.crop(msg, max_msg_size, None, ending = None, get_remainder = True)
+ line_number_width = int(math.log10(len(lines))) + 1
+ in_block = False # flag indicating if we're currently in crypto content
+ width = popup.max_x - 2 # leave space on the right for the border and an empty line
+ height = popup.max_y - 2 # height of the dialog without the top and bottom border
+ offset = line_number_width + 3 if show_line_numbers else 2
- if x_offset == cursor_location and msg == '':
- # first word is longer than the line
+ y = 1
- msg = str_tools.crop(remainder, max_msg_size)
+ for i, line in enumerate(lines):
+ keyword, value = line, ''
+ color = entry_color
- if ' ' in remainder:
- remainder = remainder.split(' ', 1)[1]
- else:
- remainder = ''
+ if line in HEADERS:
+ color = HEADER_COLOR
+ elif line.startswith(BLOCK_START):
+ in_block = True
+ elif line.startswith(BLOCK_END):
+ in_block = False
+ elif in_block:
+ keyword, value = '', line
+ elif ' ' in line and line != UNRESOLVED_MSG and line != ERROR_MSG:
+ keyword, value = line.split(' ', 1)
- popup.addstr(draw_line, cursor_location, msg, *msg_format)
- cursor_location = x_offset
+ if i < scroll:
+ continue
- if remainder:
- display_queue.insert(0, (remainder.strip(), msg_format))
- draw_line += 1
- else:
- popup.addstr(draw_line, cursor_location, msg, *msg_format)
- cursor_location += len(msg)
+ if show_line_numbers:
+ popup.addstr(y, 2, str(i + 1).rjust(line_number_width), curses.A_BOLD, LINE_NUMBER_COLOR)
- if draw_line > page_height:
- break
+ x, y = draw_msg(popup, offset, offset, y, width, keyword, color, curses.A_BOLD)
+ x, y = draw_msg(popup, offset, x + 1, y, width, value, color)
- draw_line += 1
+ y += 1
- if draw_line > page_height:
+ if y > height:
break
+ popup.win.box()
+ popup.addstr(0, 0, title, curses.A_STANDOUT)
popup.win.refresh()
diff --git a/nyx/popups.py b/nyx/popups.py
index 355d7f1..38eade0 100644
--- a/nyx/popups.py
+++ b/nyx/popups.py
@@ -71,16 +71,15 @@ def input_prompt(msg, initial_value = ''):
initial_value - initial value of the field
"""
- panel.CURSES_LOCK.acquire()
- control = nyx.controller.get_controller()
- msg_panel = control.get_panel('msg')
- msg_panel.set_message(msg)
- msg_panel.redraw(True)
- user_input = msg_panel.getstr(0, len(msg), initial_value)
- control.set_msg()
- panel.CURSES_LOCK.release()
-
- return user_input
+ with panel.CURSES_LOCK:
+ control = nyx.controller.get_controller()
+ msg_panel = control.get_panel('msg')
+ msg_panel.set_message(msg)
+ msg_panel.redraw(True)
+ user_input = msg_panel.getstr(0, len(msg), initial_value)
+ control.set_msg()
+
+ return user_input
def show_msg(msg, max_wait = -1, attr = curses.A_STANDOUT):