commit 9f4329ea439b815e8b220e1e851699f0b53fcbbe
Author: Damian Johnson <atagar(a)torproject.org>
Date: Mon May 4 21:31:59 2015 -0700
Rewrite log panel's draw function
---
nyx/log_panel.py | 117 +++++++++++++++++---------------------------------
nyx/util/ui_tools.py | 25 +++++++----
2 files changed, 57 insertions(+), 85 deletions(-)
diff --git a/nyx/log_panel.py b/nyx/log_panel.py
index 4211e09..9685c77 100644
--- a/nyx/log_panel.py
+++ b/nyx/log_panel.py
@@ -300,106 +300,53 @@ class LogPanel(panel.Panel, threading.Thread):
def draw(self, width, height):
with self._lock:
event_log = list(self.get_attr('_logged_events'))
-
- # draws the top label
-
- if self.is_title_visible():
- title_comp = list(nyx.util.log.condense_runlevels(*self._logged_event_types))
-
- if self._filter.selection():
- title_comp.append('filter: %s' % self._filter.selection())
-
- title_comp_str = join(title_comp, ', ', width - 10)
- title = 'Events (%s):' % title_comp_str if title_comp_str else 'Events:'
-
- self.addstr(0, 0, title, curses.A_STANDOUT)
-
- # restricts scroll location to valid bounds
-
self._scroll = max(0, min(self._scroll, self._last_content_height - height + 1))
- # draws left-hand scroll bar if content's longer than the height
-
- msg_indent, divider_indent = 1, 0 # offsets for scroll bar
is_scroll_bar_visible = self._last_content_height > height - 1
if is_scroll_bar_visible:
- msg_indent, divider_indent = 3, 2
self.add_scroll_bar(self._scroll, self._scroll + height - 1, self._last_content_height, 1)
- # draws log entries
-
- line_count = 1 - self._scroll
- seen_first_date_divider = False
- divider_attr = (curses.A_BOLD, 'yellow')
+ x, y = 3 if is_scroll_bar_visible else 1, 1 - self._scroll
- # determines if we have the minimum width to show date dividers
+ # group entries by date, filtering out those that aren't visible
- show_daybreaks = width - divider_indent >= 3
- last_day = event_log[0].days_since() if event_log else 0
-
- for i in range(len(event_log)):
- entry = event_log[i]
- is_last = i == len(event_log) - 1
+ days_ago_to_entries = {}
+ for entry in event_log:
if entry.is_duplicate and not CONFIG['features.log.showDuplicateEntries']:
continue # deduplicated message
elif not self._filter.match(entry.display_message):
continue # filter doesn't match log message
- # checks if we should be showing a divider with the date
-
- if last_day != entry.days_since():
- # bottom of the divider
-
- if seen_first_date_divider:
- if line_count >= 1 and line_count < height and show_daybreaks:
- self.addch(line_count, divider_indent, curses.ACS_LLCORNER, *divider_attr)
- self.hline(line_count, divider_indent + 1, width - divider_indent - 2, *divider_attr)
- self.addch(line_count, width - 1, curses.ACS_LRCORNER, *divider_attr)
+ days_ago_to_entries.setdefault(entry.days_since(), []).append(entry)
- line_count += 1
+ for days_ago in sorted(days_ago_to_entries.keys()):
+ if days_ago == 0:
+ for entry in days_ago_to_entries[days_ago]:
+ y = self._draw_entry(x, y, width, entry)
+ else:
+ original_y, y = y, y + 1
- # top of the divider
+ for entry in days_ago_to_entries[days_ago]:
+ y = self._draw_entry(x, y, width, entry)
- if line_count >= 1 and line_count < height and show_daybreaks:
- time_label = time.strftime(' %B %d, %Y ', time.localtime(entry.timestamp))
- self.addch(line_count, divider_indent, curses.ACS_ULCORNER, *divider_attr)
- self.addch(line_count, divider_indent + 1, curses.ACS_HLINE, *divider_attr)
- self.addstr(line_count, divider_indent + 2, time_label, curses.A_BOLD, *divider_attr)
+ ui_tools.draw_box(self, original_y, x - 1, width - x + 1, y - original_y + 1, curses.A_BOLD, 'yellow')
+ time_label = time.strftime(' %B %d, %Y ', time.localtime(days_ago_to_entries[days_ago][0].timestamp))
+ self.addstr(original_y, x + 1, time_label, curses.A_BOLD, curses.A_BOLD, 'yellow')
- line_length = width - divider_indent - len(time_label) - 3
- self.hline(line_count, divider_indent + len(time_label) + 2, line_length, *divider_attr)
- self.addch(line_count, divider_indent + len(time_label) + 2 + line_length, curses.ACS_URCORNER, *divider_attr)
+ y += 1
- seen_first_date_divider = True
- line_count += 1
+ # drawing the title after the content, so we'll clear content from the top line
- line_count_start = line_count
- line_count = self._draw_entry(msg_indent, line_count, width, entry, height)
-
- for y in range(line_count_start, line_count):
- if seen_first_date_divider and width - divider_indent >= 3 and show_daybreaks:
- self.addch(y, divider_indent, curses.ACS_VLINE, *divider_attr)
- self.addch(y, width - 1, curses.ACS_VLINE, *divider_attr)
-
- # if this is the last line and there's room, then draw the bottom of the divider
-
- if is_last and seen_first_date_divider:
- if line_count < height and show_daybreaks:
- self.addch(line_count, divider_indent, curses.ACS_LLCORNER, *divider_attr)
- self.hline(line_count, divider_indent + 1, width - divider_indent - 2, *divider_attr)
- self.addch(line_count, width - 1, curses.ACS_LRCORNER, *divider_attr)
-
- line_count += 1
-
- last_day = entry.days_since()
+ if self.is_title_visible():
+ self._draw_title(width)
# redraw the display if...
# - last_content_height was off by too much
# - we're off the bottom of the page
- new_content_height = line_count + self._scroll - 1
+ new_content_height = y + self._scroll - 1
content_height_delta = abs(self._last_content_height - new_content_height)
force_redraw, force_redraw_reason = True, ''
@@ -421,7 +368,23 @@ class LogPanel(panel.Panel, threading.Thread):
log.debug('redrawing the log panel with the corrected content height (%s)' % force_redraw_reason)
self.redraw(True)
- def _draw_entry(self, x, y, width, entry, height):
+ def _draw_title(self, width):
+ """
+ Panel title with the event types we're logging and our regex filter if set.
+ """
+
+ self.addstr(0, 0, ' ' * width) # clear line
+ title_comp = list(nyx.util.log.condense_runlevels(*self._logged_event_types))
+
+ if self._filter.selection():
+ title_comp.append('filter: %s' % self._filter.selection())
+
+ title_comp_str = join(title_comp, ', ', width - 10)
+ title = 'Events (%s):' % title_comp_str if title_comp_str else 'Events:'
+
+ self.addstr(0, 0, title, curses.A_STANDOUT)
+
+ def _draw_entry(self, x, y, width, entry):
"""
Presents a log entry with line wrapping.
"""
@@ -429,7 +392,7 @@ class LogPanel(panel.Panel, threading.Thread):
def draw_line(x, y, width, msg, *attr):
msg, remaining_lines = msg.split('\n', 1) if ('\n' in msg) else (msg, '')
msg, cropped = str_tools.crop(msg, width - x - 1, min_crop = 4, ending = str_tools.Ending.HYPHEN, get_remainder = True)
- x = self.addstr(y, x, msg, *attr) if y > 0 else 0 # draw unless it would cover the title
+ x = self.addstr(y, x, msg, *attr)
return x, (cropped + '\n' + remaining_lines).strip()
def draw_msg(min_x, x, y, width, msg, *attr):
@@ -439,7 +402,7 @@ class LogPanel(panel.Panel, threading.Thread):
x, msg = draw_line(x, y, width, msg, *attr)
if (y - orig_y + 1) >= CONFIG['features.log.max_lines_per_entry']:
- break
+ break # filled up the maximum number of lines we're allowing for
if msg:
msg = ' ' + msg # indent the next line
diff --git a/nyx/util/ui_tools.py b/nyx/util/ui_tools.py
index 8d7edad..901676b 100644
--- a/nyx/util/ui_tools.py
+++ b/nyx/util/ui_tools.py
@@ -179,7 +179,7 @@ def get_printable(line, keep_newlines = True):
return line
-def draw_box(panel, top, left, width, height, attr=curses.A_NORMAL):
+def draw_box(panel, top, left, width, height, *attributes):
"""
Draws a box in the panel with the given bounds.
@@ -192,21 +192,30 @@ def draw_box(panel, top, left, width, height, attr=curses.A_NORMAL):
attr - text attributes
"""
+ format_attr = curses.A_NORMAL
+
+ for attr in attributes:
+ if isinstance(attr, str):
+ format_attr |= get_color(attr)
+ else:
+ format_attr |= attr
+
# draws the top and bottom
- panel.hline(top, left + 1, width - 2, attr)
- panel.hline(top + height - 1, left + 1, width - 2, attr)
+ panel.hline(top, left + 1, width - 2, format_attr)
+ panel.hline(top + height - 1, left + 1, width - 2, format_attr)
# draws the left and right sides
- panel.vline(top + 1, left, height - 2, attr)
- panel.vline(top + 1, left + width - 1, height - 2, attr)
+ panel.vline(top + 1, left, height - 2, format_attr)
+ panel.vline(top + 1, left + width - 1, height - 2, format_attr)
# draws the corners
- panel.addch(top, left, curses.ACS_ULCORNER, attr)
- panel.addch(top, left + width - 1, curses.ACS_URCORNER, attr)
- panel.addch(top + height - 1, left, curses.ACS_LLCORNER, attr)
+ panel.addch(top, left, curses.ACS_ULCORNER, format_attr)
+ panel.addch(top, left + width - 1, curses.ACS_URCORNER, format_attr)
+ panel.addch(top + height - 1, left, curses.ACS_LLCORNER, format_attr)
+ panel.addch(top + height - 1, left + width - 1, curses.ACS_LRCORNER, format_attr)
def get_scroll_position(key, position, page_height, content_height, is_cursor = False):