Submission view up and running.
This commit is contained in:
@@ -1,28 +1,70 @@
|
|||||||
import textwrap
|
import textwrap
|
||||||
|
import praw
|
||||||
|
|
||||||
from utils import clean, strip_subreddit_url, humanize_timestamp
|
from utils import clean, strip_subreddit_url, humanize_timestamp
|
||||||
|
|
||||||
class SubmissionContent(object):
|
class BaseContent(object):
|
||||||
"""
|
|
||||||
Facilitates navigating through the comments in a PRAW submission.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, submission):
|
|
||||||
|
|
||||||
self.submission = submission
|
|
||||||
self._comments = []
|
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def flatten_comments(submission):
|
def strip_praw_comment(comment):
|
||||||
|
"""
|
||||||
|
Parse through a submission comment and return a dict with data ready to
|
||||||
|
be displayed through the terminal.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
data['object'] = comment
|
||||||
|
data['level'] = comment.nested_level
|
||||||
|
|
||||||
|
|
||||||
|
if isinstance(comment, praw.objects.MoreComments):
|
||||||
|
data['type'] = 'MoreComments'
|
||||||
|
data['body'] = 'More comments [{}]'.format(comment.count)
|
||||||
|
else:
|
||||||
|
data['type'] = 'Comment'
|
||||||
|
data['body'] = clean(comment.body)
|
||||||
|
data['created'] = humanize_timestamp(comment.created_utc)
|
||||||
|
data['score'] = '{} pts'.format(comment.score)
|
||||||
|
data['author'] = (clean(comment.author.name) if
|
||||||
|
getattr(comment, 'author') else '[deleted]')
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def strip_praw_submission(sub):
|
||||||
|
"""
|
||||||
|
Parse through a submission and return a dict with data ready to be
|
||||||
|
displayed through the terminal.
|
||||||
|
"""
|
||||||
|
|
||||||
|
is_selfpost = lambda s: s.startswith('http://www.reddit.com/r/')
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
data['object'] = sub
|
||||||
|
data['type'] = 'Submission'
|
||||||
|
data['title'] = clean(sub.title)
|
||||||
|
data['text'] = clean(sub.selftext)
|
||||||
|
data['created'] = humanize_timestamp(sub.created_utc)
|
||||||
|
data['comments'] = '{} comments'.format(sub.num_comments)
|
||||||
|
data['score'] = '{} pts'.format(sub.score)
|
||||||
|
data['author'] = (clean(sub.author.name) if getattr(sub, 'author')
|
||||||
|
else '[deleted]')
|
||||||
|
data['permalink'] = clean(sub.permalink)
|
||||||
|
data['subreddit'] = strip_subreddit_url(sub.permalink)
|
||||||
|
data['url'] = ('(selfpost)' if is_selfpost(sub.url) else clean(sub.url))
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def flatten_comments(comments, initial_level=0):
|
||||||
"""
|
"""
|
||||||
Flatten a PRAW comment tree while preserving the nested level of each
|
Flatten a PRAW comment tree while preserving the nested level of each
|
||||||
comment via the `nested_level` attribute.
|
comment via the `nested_level` attribute.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
stack = submission[:]
|
stack = comments[:]
|
||||||
for item in stack:
|
for item in stack:
|
||||||
item.nested_level = 0
|
item.nested_level = initial_level
|
||||||
|
|
||||||
retval = []
|
retval = []
|
||||||
while stack:
|
while stack:
|
||||||
@@ -35,24 +77,8 @@ class SubmissionContent(object):
|
|||||||
retval.append(item)
|
retval.append(item)
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def strip_praw_comment(comment):
|
|
||||||
"""
|
|
||||||
Parse through a submission comment and return a dict with data ready to
|
|
||||||
be displayed through the terminal.
|
|
||||||
"""
|
|
||||||
|
|
||||||
data = {}
|
class SubredditContent(BaseContent):
|
||||||
data['body'] = clean(comment.body)
|
|
||||||
data['created'] = humanize_timestamp(comment.created_utc)
|
|
||||||
data['score'] = '{} pts'.format(comment.score)
|
|
||||||
data['author'] = (clean(comment.author.name) if
|
|
||||||
getattr(comment, 'author') else '[deleted]')
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
class SubredditContent(object):
|
|
||||||
"""
|
"""
|
||||||
Grabs a subreddit from PRAW and lazily stores submissions to an internal
|
Grabs a subreddit from PRAW and lazily stores submissions to an internal
|
||||||
list for repeat access.
|
list for repeat access.
|
||||||
@@ -74,28 +100,6 @@ class SubredditContent(object):
|
|||||||
|
|
||||||
self.reset(subreddit=subreddit)
|
self.reset(subreddit=subreddit)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def strip_praw_submission(sub):
|
|
||||||
"""
|
|
||||||
Parse through a submission and return a dict with data ready to be
|
|
||||||
displayed through the terminal.
|
|
||||||
"""
|
|
||||||
|
|
||||||
is_selfpost = lambda s: s.startswith('http://www.reddit.com/r/')
|
|
||||||
|
|
||||||
data = {}
|
|
||||||
data['title'] = clean(sub.title)
|
|
||||||
data['text'] = clean(sub.selftext)
|
|
||||||
data['created'] = humanize_timestamp(sub.created_utc)
|
|
||||||
data['comments'] = '{} comments'.format(sub.num_comments)
|
|
||||||
data['score'] = '{} pts'.format(sub.score)
|
|
||||||
data['author'] = (clean(sub.author.name) if getattr(sub, 'author')
|
|
||||||
else '[deleted]')
|
|
||||||
data['subreddit'] = strip_subreddit_url(sub.permalink)
|
|
||||||
data['url'] = ('(selfpost)' if is_selfpost(sub.url) else clean(sub.url))
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def get(self, index, n_cols=70):
|
def get(self, index, n_cols=70):
|
||||||
"""
|
"""
|
||||||
Grab the `i`th submission, with the title field formatted to fit inside
|
Grab the `i`th submission, with the title field formatted to fit inside
|
||||||
@@ -119,6 +123,7 @@ class SubredditContent(object):
|
|||||||
data = self._submission_data[index]
|
data = self._submission_data[index]
|
||||||
data['split_title'] = textwrap.wrap(data['title'], width=n_cols)
|
data['split_title'] = textwrap.wrap(data['title'], width=n_cols)
|
||||||
data['n_rows'] = len(data['split_title']) + 3
|
data['n_rows'] = len(data['split_title']) + 3
|
||||||
|
data['offset'] = 0
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@@ -144,3 +149,70 @@ class SubredditContent(object):
|
|||||||
else:
|
else:
|
||||||
self._submissions = self.r.get_subreddit(self.subreddit, limit=None)
|
self._submissions = self.r.get_subreddit(self.subreddit, limit=None)
|
||||||
self.display_name = self._submissions.display_name
|
self.display_name = self._submissions.display_name
|
||||||
|
|
||||||
|
|
||||||
|
class SubmissionContent(BaseContent):
|
||||||
|
"""
|
||||||
|
Grabs a submission from PRAW and lazily store comments to an internal
|
||||||
|
list for repeat access and to allow expanding and hiding comments.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, submission, indent_size=2, max_indent_level=4):
|
||||||
|
|
||||||
|
self.submission = submission
|
||||||
|
self.indent_size = indent_size
|
||||||
|
self.max_indent_level = max_indent_level
|
||||||
|
|
||||||
|
self.display_name = None
|
||||||
|
self._submission_data = None
|
||||||
|
self._comments = None
|
||||||
|
self._comment_data = None
|
||||||
|
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
def get(self, index, n_cols=70):
|
||||||
|
"""
|
||||||
|
Grab the `i`th submission, with the title field formatted to fit inside
|
||||||
|
of a window of width `n`
|
||||||
|
"""
|
||||||
|
|
||||||
|
if index < -1:
|
||||||
|
raise IndexError
|
||||||
|
|
||||||
|
elif index == -1:
|
||||||
|
data = self._submission_data
|
||||||
|
data['split_title'] = textwrap.wrap(data['title'], width=n_cols)
|
||||||
|
data['n_rows'] = len(data['split_title']) + 3
|
||||||
|
data['offset'] = 0
|
||||||
|
|
||||||
|
else:
|
||||||
|
data = self._comment_data[index]
|
||||||
|
indent_level = min(data['level'], self.max_indent_level)
|
||||||
|
data['offset'] = indent_level * self.indent_size
|
||||||
|
|
||||||
|
if data['type'] == 'Comment':
|
||||||
|
data['split_body'] = textwrap.wrap(
|
||||||
|
data['body'], width=n_cols-data['offset'])
|
||||||
|
data['n_rows'] = len(data['split_body']) + 1
|
||||||
|
else:
|
||||||
|
data['n_rows'] = 1
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def iterate(self, index, step, n_cols):
|
||||||
|
|
||||||
|
while True:
|
||||||
|
yield self.get(index, n_cols)
|
||||||
|
index += step
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
"""
|
||||||
|
Fetch changes to the submission from PRAW and clear the internal list.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.submission.refresh()
|
||||||
|
self._submission_data = self.strip_praw_submission(self.submission)
|
||||||
|
|
||||||
|
self.display_name = self._submission_data['permalink']
|
||||||
|
self._comments = self.flatten_comments(self.submission.comments)
|
||||||
|
self._comment_data = [self.strip_praw_comment(c) for c in self._comments]
|
||||||
128
rtv/submission_viewer.py
Normal file
128
rtv/submission_viewer.py
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
import praw
|
||||||
|
import curses
|
||||||
|
|
||||||
|
from content_generators import SubmissionContent, SubredditContent
|
||||||
|
from utils import curses_session
|
||||||
|
from viewer import BaseViewer
|
||||||
|
|
||||||
|
class SubmissionViewer(BaseViewer):
|
||||||
|
|
||||||
|
def __init__(self, stdscr, content):
|
||||||
|
|
||||||
|
page_index, cursor_index = -1, 1
|
||||||
|
super(SubmissionViewer, self).__init__(
|
||||||
|
stdscr, content,
|
||||||
|
page_index=page_index, cursor_index=cursor_index)
|
||||||
|
|
||||||
|
def loop(self):
|
||||||
|
|
||||||
|
self.draw()
|
||||||
|
while True:
|
||||||
|
cmd = self.stdscr.getch()
|
||||||
|
|
||||||
|
if cmd == curses.KEY_UP:
|
||||||
|
self.move_cursor_up()
|
||||||
|
self.clear_input_queue()
|
||||||
|
|
||||||
|
elif cmd == curses.KEY_DOWN:
|
||||||
|
self.move_cursor_down()
|
||||||
|
self.clear_input_queue()
|
||||||
|
|
||||||
|
# Refresh page
|
||||||
|
elif cmd in (curses.KEY_F5, ord('r')):
|
||||||
|
self.content.reset()
|
||||||
|
self.stdscr.clear()
|
||||||
|
self.draw()
|
||||||
|
|
||||||
|
elif cmd == curses.KEY_RESIZE:
|
||||||
|
self.draw()
|
||||||
|
|
||||||
|
# Quit
|
||||||
|
elif cmd == ord('q'):
|
||||||
|
break
|
||||||
|
|
||||||
|
else:
|
||||||
|
curses.beep()
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
|
||||||
|
n_rows, n_cols = self.stdscr.getmaxyx()
|
||||||
|
self._header_window = self.stdscr.derwin(1, n_cols, 0, 0)
|
||||||
|
self._content_window = self.stdscr.derwin(1, 0)
|
||||||
|
|
||||||
|
self.draw_header()
|
||||||
|
self.draw_content()
|
||||||
|
self.add_cursor()
|
||||||
|
|
||||||
|
def draw_item(self, win, data, inverted=False):
|
||||||
|
|
||||||
|
if data['type'] == 'MoreComments':
|
||||||
|
self.draw_more_comments(win, data)
|
||||||
|
|
||||||
|
elif data['type'] == 'Comment':
|
||||||
|
self.draw_comment(win, data, inverted=inverted)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.draw_submission(win, data)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def draw_comment(win, data, inverted=False):
|
||||||
|
|
||||||
|
n_rows, n_cols = win.getmaxyx()
|
||||||
|
n_cols -= 2
|
||||||
|
|
||||||
|
# Handle the case where the window is not large enough to fit the data.
|
||||||
|
valid_rows = range(0, n_rows)
|
||||||
|
offset = 0 if not inverted else -(data['n_rows'] - n_rows)
|
||||||
|
|
||||||
|
row = offset
|
||||||
|
text = '{} {} {}'.format(data['author'], data['score'], data['created'])
|
||||||
|
if row in valid_rows:
|
||||||
|
win.addnstr(row, 1, text, n_cols)
|
||||||
|
|
||||||
|
n_body = len(data['split_body'])
|
||||||
|
for row, text in enumerate(data['split_body'], start=offset+1):
|
||||||
|
if row in valid_rows:
|
||||||
|
win.addnstr(row, 1, text, n_cols)
|
||||||
|
|
||||||
|
# Vertical line, unfortunately vline() doesn't support custom color so
|
||||||
|
# we have to build it one chr at a time.
|
||||||
|
for y in xrange(n_rows):
|
||||||
|
win.addch(y, 0, curses.ACS_VLINE)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def draw_more_comments(win, data):
|
||||||
|
|
||||||
|
n_rows, n_cols = win.getmaxyx()
|
||||||
|
n_cols -= 2
|
||||||
|
win.addnstr(0, 1, data['body'], n_cols)
|
||||||
|
|
||||||
|
for y in xrange(n_rows):
|
||||||
|
win.addch(y, 0, curses.ACS_VLINE)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def draw_submission(win, data):
|
||||||
|
|
||||||
|
n_rows, n_cols = win.getmaxyx()
|
||||||
|
n_cols -= 1
|
||||||
|
|
||||||
|
# Don't print anything if there is not enough room
|
||||||
|
if data['n_rows'] > n_rows:
|
||||||
|
return
|
||||||
|
|
||||||
|
win.border()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
with curses_session() as stdscr:
|
||||||
|
r = praw.Reddit(user_agent='reddit terminal viewer (rtv) v0.0')
|
||||||
|
submission = SubredditContent(r).get(0)['object']
|
||||||
|
generator = SubmissionContent(submission)
|
||||||
|
|
||||||
|
viewer = SubmissionViewer(stdscr, generator)
|
||||||
|
viewer.loop()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
main()
|
||||||
@@ -8,14 +8,6 @@ from viewer import BaseViewer
|
|||||||
|
|
||||||
class SubredditViewer(BaseViewer):
|
class SubredditViewer(BaseViewer):
|
||||||
|
|
||||||
def __init__(self, stdscr, subreddit_content):
|
|
||||||
|
|
||||||
self.stdscr = stdscr
|
|
||||||
self._title_window = None
|
|
||||||
self._content_window = None
|
|
||||||
|
|
||||||
super(SubredditViewer, self).__init__(subreddit_content)
|
|
||||||
|
|
||||||
def loop(self):
|
def loop(self):
|
||||||
|
|
||||||
self.draw()
|
self.draw()
|
||||||
@@ -24,9 +16,11 @@ class SubredditViewer(BaseViewer):
|
|||||||
|
|
||||||
if cmd == curses.KEY_UP:
|
if cmd == curses.KEY_UP:
|
||||||
self.move_cursor_up()
|
self.move_cursor_up()
|
||||||
|
self.clear_input_queue()
|
||||||
|
|
||||||
elif cmd == curses.KEY_DOWN:
|
elif cmd == curses.KEY_DOWN:
|
||||||
self.move_cursor_down()
|
self.move_cursor_down()
|
||||||
|
self.clear_input_queue()
|
||||||
|
|
||||||
# View submission
|
# View submission
|
||||||
elif cmd in (curses.KEY_RIGHT, ord(' ')):
|
elif cmd in (curses.KEY_RIGHT, ord(' ')):
|
||||||
@@ -54,59 +48,19 @@ class SubredditViewer(BaseViewer):
|
|||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
|
|
||||||
# Refresh window bounds incase the screen has been resized
|
|
||||||
n_rows, n_cols = self.stdscr.getmaxyx()
|
n_rows, n_cols = self.stdscr.getmaxyx()
|
||||||
self._title_window = self.stdscr.derwin(1, n_cols, 0, 0)
|
self._header_window = self.stdscr.derwin(1, n_cols, 0, 0)
|
||||||
self._content_window = self.stdscr.derwin(1, 0)
|
self._content_window = self.stdscr.derwin(1, 0)
|
||||||
|
|
||||||
self.draw_header()
|
self.draw_header()
|
||||||
self.draw_content()
|
self.draw_content()
|
||||||
self.add_cursor()
|
self.add_cursor()
|
||||||
|
|
||||||
def draw_content(self):
|
|
||||||
"""
|
|
||||||
Loop through submissions and fill up the content page.
|
|
||||||
"""
|
|
||||||
|
|
||||||
n_rows, n_cols = self._content_window.getmaxyx()
|
|
||||||
self._content_window.erase()
|
|
||||||
self._subwindows = []
|
|
||||||
|
|
||||||
page_index, cursor_index, inverted = self.nav.position
|
|
||||||
step = self.nav.step
|
|
||||||
|
|
||||||
# If not inverted, align the first submission with the top and draw
|
|
||||||
# downwards. If inverted, align the first submission with the bottom
|
|
||||||
# and draw upwards.
|
|
||||||
current_row = n_rows if inverted else 0
|
|
||||||
available_rows = n_rows
|
|
||||||
for data in self.content.iterate(page_index, step, n_cols-2):
|
|
||||||
window_rows = min(available_rows, data['n_rows'])
|
|
||||||
start = current_row - window_rows if inverted else current_row
|
|
||||||
window = self._content_window.derwin(window_rows, n_cols, start, 0)
|
|
||||||
self.draw_submission(window, data, inverted)
|
|
||||||
self._subwindows.append(window)
|
|
||||||
available_rows -= (window_rows + 1) # Add one for the blank line
|
|
||||||
current_row += step * (window_rows + 1)
|
|
||||||
if available_rows <= 0:
|
|
||||||
break
|
|
||||||
|
|
||||||
self._content_window.refresh()
|
|
||||||
|
|
||||||
def draw_header(self):
|
|
||||||
|
|
||||||
n_rows, n_cols = self._title_window.getmaxyx()
|
|
||||||
sub_name = self.content.display_name
|
|
||||||
|
|
||||||
self._title_window.erase()
|
|
||||||
self._title_window.addnstr(0, 0, sub_name, n_cols)
|
|
||||||
self._title_window.refresh()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def draw_submission(win, data, inverted=False):
|
def draw_item(win, data, inverted=False):
|
||||||
|
|
||||||
n_rows, n_cols = win.getmaxyx()
|
n_rows, n_cols = win.getmaxyx()
|
||||||
n_cols -= 1 # Leave space for the cursor in the first column
|
n_cols -= 2 # Leave space for the cursor in the first column
|
||||||
|
|
||||||
# Handle the case where the window is not large enough to fit the data.
|
# Handle the case where the window is not large enough to fit the data.
|
||||||
valid_rows = range(0, n_rows)
|
valid_rows = range(0, n_rows)
|
||||||
@@ -119,15 +73,15 @@ class SubredditViewer(BaseViewer):
|
|||||||
|
|
||||||
row = n_title + offset
|
row = n_title + offset
|
||||||
if row in valid_rows:
|
if row in valid_rows:
|
||||||
win.addnstr(row, 1, '{url}'.format(**data), n_cols-1)
|
win.addnstr(row, 1, '{url}'.format(**data), n_cols)
|
||||||
|
|
||||||
row = n_title + offset + 1
|
row = n_title + offset + 1
|
||||||
if row in valid_rows:
|
if row in valid_rows:
|
||||||
win.addnstr(row, 1, '{created} {comments} {score}'.format(**data), n_cols-1)
|
win.addnstr(row, 1, '{created} {comments} {score}'.format(**data), n_cols)
|
||||||
|
|
||||||
row = n_title + offset + 2
|
row = n_title + offset + 2
|
||||||
if row in valid_rows:
|
if row in valid_rows:
|
||||||
win.addnstr(row, 1, '{author} {subreddit}'.format(**data), n_cols-1)
|
win.addnstr(row, 1, '{author} {subreddit}'.format(**data), n_cols)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
||||||
@@ -12,11 +12,12 @@ class Navigator(object):
|
|||||||
cursor_index=0,
|
cursor_index=0,
|
||||||
inverted=False):
|
inverted=False):
|
||||||
|
|
||||||
self._page_cb = valid_page_cb
|
|
||||||
|
|
||||||
self.page_index = page_index
|
self.page_index = page_index
|
||||||
self.cursor_index = cursor_index
|
self.cursor_index = cursor_index
|
||||||
self.inverted = inverted
|
self.inverted = inverted
|
||||||
|
self._page_cb = valid_page_cb
|
||||||
|
self._header_window = None
|
||||||
|
self._content_window = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def step(self):
|
def step(self):
|
||||||
@@ -26,6 +27,10 @@ class Navigator(object):
|
|||||||
def position(self):
|
def position(self):
|
||||||
return (self.page_index, self.cursor_index, self.inverted)
|
return (self.page_index, self.cursor_index, self.inverted)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def absolute_index(self):
|
||||||
|
return self.page_index + (self.step * self.cursor_index)
|
||||||
|
|
||||||
def move(self, direction, n_windows):
|
def move(self, direction, n_windows):
|
||||||
"Move the cursor down (positive direction) or up (negative direction)"
|
"Move the cursor down (positive direction) or up (negative direction)"
|
||||||
|
|
||||||
@@ -70,10 +75,12 @@ class BaseViewer(object):
|
|||||||
Base terminal viewer incorperates a cursor to navigate content
|
Base terminal viewer incorperates a cursor to navigate content
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, content):
|
def __init__(self, stdscr, content, **kwargs):
|
||||||
|
|
||||||
|
self.stdscr = stdscr
|
||||||
self.content = content
|
self.content = content
|
||||||
self.nav = Navigator(self.content.get)
|
|
||||||
|
self.nav = Navigator(self.content.get, **kwargs)
|
||||||
|
|
||||||
self._subwindows = None
|
self._subwindows = None
|
||||||
|
|
||||||
@@ -87,13 +94,61 @@ class BaseViewer(object):
|
|||||||
self._move_cursor(1)
|
self._move_cursor(1)
|
||||||
|
|
||||||
def add_cursor(self):
|
def add_cursor(self):
|
||||||
curses.curs_set(2)
|
|
||||||
self._edit_cursor(curses.A_REVERSE)
|
self._edit_cursor(curses.A_REVERSE)
|
||||||
|
|
||||||
def remove_cursor(self):
|
def remove_cursor(self):
|
||||||
curses.curs_set(0)
|
|
||||||
self._edit_cursor(curses.A_NORMAL)
|
self._edit_cursor(curses.A_NORMAL)
|
||||||
|
|
||||||
|
def clear_input_queue(self):
|
||||||
|
"Clear excessive input caused by the scroll wheel or holding down a key"
|
||||||
|
self.stdscr.nodelay(1)
|
||||||
|
while self.stdscr.getch() != -1:
|
||||||
|
continue
|
||||||
|
self.stdscr.nodelay(0)
|
||||||
|
|
||||||
|
def draw_header(self):
|
||||||
|
|
||||||
|
n_rows, n_cols = self._header_window.getmaxyx()
|
||||||
|
|
||||||
|
self._header_window.erase()
|
||||||
|
self._header_window.addnstr(0, 0, self.content.display_name, n_cols-1)
|
||||||
|
self._header_window.refresh()
|
||||||
|
|
||||||
|
def draw_content(self):
|
||||||
|
"""
|
||||||
|
Loop through submissions and fill up the content page.
|
||||||
|
"""
|
||||||
|
|
||||||
|
n_rows, n_cols = self._content_window.getmaxyx()
|
||||||
|
self._content_window.erase()
|
||||||
|
self._subwindows = []
|
||||||
|
|
||||||
|
page_index, cursor_index, inverted = self.nav.position
|
||||||
|
step = self.nav.step
|
||||||
|
|
||||||
|
# If not inverted, align the first submission with the top and draw
|
||||||
|
# downwards. If inverted, align the first submission with the bottom
|
||||||
|
# and draw upwards.
|
||||||
|
current_row = n_rows if inverted else 0
|
||||||
|
available_rows = n_rows
|
||||||
|
for data in self.content.iterate(page_index, step, n_cols-2):
|
||||||
|
window_rows = min(available_rows, data['n_rows'])
|
||||||
|
window_cols = n_cols - data['offset']
|
||||||
|
start = current_row - window_rows if inverted else current_row
|
||||||
|
subwindow = self._content_window.derwin(
|
||||||
|
window_rows, window_cols, start, data['offset'])
|
||||||
|
self.draw_item(subwindow, data, inverted)
|
||||||
|
self._subwindows.append(subwindow)
|
||||||
|
available_rows -= (window_rows + 1) # Add one for the blank line
|
||||||
|
current_row += step * (window_rows + 1)
|
||||||
|
if available_rows <= 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
self._content_window.refresh()
|
||||||
|
|
||||||
|
def draw_item(self, window, data, inverted):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
def _move_cursor(self, direction):
|
def _move_cursor(self, direction):
|
||||||
|
|
||||||
self.remove_cursor()
|
self.remove_cursor()
|
||||||
@@ -101,18 +156,24 @@ class BaseViewer(object):
|
|||||||
valid, redraw = self.nav.move(direction, len(self._subwindows))
|
valid, redraw = self.nav.move(direction, len(self._subwindows))
|
||||||
if not valid:
|
if not valid:
|
||||||
curses.flash()
|
curses.flash()
|
||||||
if redraw:
|
|
||||||
|
# If we don't redraw, ACS_VLINE gets screwed up when changing the
|
||||||
|
# attr back to normal. There may be a way around this.
|
||||||
|
if True: #if redraw
|
||||||
self.draw_content()
|
self.draw_content()
|
||||||
|
|
||||||
self.add_cursor()
|
self.add_cursor()
|
||||||
|
|
||||||
def _edit_cursor(self, attribute):
|
def _edit_cursor(self, attribute):
|
||||||
|
|
||||||
|
# Don't alow the cursor to go below page index 0
|
||||||
|
if self.nav.absolute_index == -1:
|
||||||
|
window = self._subwindows[self.nav.cursor_index + 1]
|
||||||
|
else:
|
||||||
window = self._subwindows[self.nav.cursor_index]
|
window = self._subwindows[self.nav.cursor_index]
|
||||||
|
|
||||||
n_rows, _ = window.getmaxyx()
|
n_rows, _ = window.getmaxyx()
|
||||||
for row in xrange(n_rows):
|
for row in xrange(n_rows):
|
||||||
window.chgat(row, 0, 1, attribute)
|
window.chgat(row, 0, 1, attribute)
|
||||||
window.move(0, 0)
|
|
||||||
|
|
||||||
window.refresh()
|
window.refresh()
|
||||||
Reference in New Issue
Block a user