diff --git a/rtv/submission.py b/rtv/submission.py index 5db0e2d..7d18506 100644 --- a/rtv/submission.py +++ b/rtv/submission.py @@ -1,7 +1,8 @@ import curses import sys import time -import praw.errors + +from praw.errors import APIException from .content import SubmissionContent from .page import BasePage @@ -21,7 +22,8 @@ class SubmissionPage(BasePage): else: raise ValueError('Must specify url or submission') - super(SubmissionPage, self).__init__(stdscr, reddit, content, page_index=-1) + super(SubmissionPage, self).__init__(stdscr, reddit, content, + page_index=-1) def loop(self): @@ -116,7 +118,7 @@ class SubmissionPage(BasePage): n_rows, n_cols = win.getmaxyx() n_cols -= 1 - # 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 text. valid_rows = range(0, n_rows) offset = 0 if not inverted else -(data['n_rows'] - n_rows) @@ -231,39 +233,48 @@ class SubmissionPage(BasePage): """ if not self.reddit.is_logged_in(): - display_message(self.stdscr, ["You are not logged in!"]) + display_message(self.stdscr, ["Login to reply"]) return - cursor_position = self.nav.absolute_index - if (self.content.get(cursor_position)['type'] != 'Comment') \ - & (self.content.get(cursor_position)['type'] != 'Submission'): - display_message(self.stdscr, ['Expand the comments first!']) + data = self.content.get(self.nav.absolute_index) + if data['type'] not in ('Comment', 'Submission'): + curses.flash() return + # Fill the bottom half of the screen with the comment box n_rows, n_cols = self.stdscr.getmaxyx() - box_height = n_rows/2 - + box_height = n_rows // 2 attr = curses.A_BOLD | Color.CYAN + + for x in range(n_cols): + y = box_height - 1 + # http://bugs.python.org/issue21088 + if (sys.version_info.major, + sys.version_info.minor, + sys.version_info.micro) == (3, 4, 0): + x, y = y, x + + self.stdscr.addch(y, x, curses.ACS_HLINE, attr) + prompt = 'Enter comment: ESC to cancel, Ctrl+g to submit' - prompt = '-'*((n_cols-len(prompt))/2) + prompt \ - + '-'*((n_cols-len(prompt)+1)/2) - self.stdscr.addstr(n_rows-box_height-1, 0, prompt, attr) + scol = max(0, (n_cols // 2) - (len(prompt) // 2)) + self.stdscr.addnstr(box_height-1, scol, prompt, n_cols-scol, attr) self.stdscr.refresh() - window = self.stdscr.derwin(box_height, n_cols, - n_rows-box_height, 0) - window.attrset(attr) + window = self.stdscr.derwin(n_rows-box_height, n_cols, box_height, 0) + window.attrset(Color.CYAN) - comment_text = text_input(window, show_cursor=True, insert_mode=False) - if comment_text is not None: - try: - if cursor_position == -1: # comment on submission - self.content._submission.add_comment(comment_text) - else: # reply on a selected comment - self.content.get(cursor_position)['object']\ - .reply(comment_text) - except praw.errors.APIException as e: - display_message(self.stdscr, [e.message]) + comment_text = text_input(window, allow_resize=False) + if comment_text is None: + return + + try: + if data['type'] == 'Submission': + data['object'].add_comment(comment_text) else: - time.sleep(0.5) - self.refresh_content() + data['object'].reply(comment_text) + except APIException as e: + display_message(self.stdscr, [e.message]) + else: + time.sleep(0.5) + self.refresh_content() \ No newline at end of file diff --git a/rtv/utils.py b/rtv/utils.py index af1149f..10abd39 100644 --- a/rtv/utils.py +++ b/rtv/utils.py @@ -114,7 +114,7 @@ def load_config(): return defaults -def text_input(window, show_cursor=False, insert_mode=True): +def text_input(window, allow_resize=True): """ Transform a window into a text box that will accept user input and loop until an escape sequence is entered. @@ -124,8 +124,16 @@ def text_input(window, show_cursor=False, insert_mode=True): """ window.clear() - curses.curs_set(1 if show_cursor else 2) - textbox = textpad.Textbox(window, insert_mode=insert_mode) + + # Set cursor mode to 1 because 2 doesn't display on some terminals + curses.curs_set(1) + + # Turn insert_mode off to avoid the recursion error described here + # http://bugs.python.org/issue13051 + textbox = textpad.Textbox(window, insert_mode=False) + + # Strip whitespace from the textbox 'smarter' than textpad.Textbox() does. + textbox.stripspaces = 0 def validate(ch): "Filters characters for special key sequences" @@ -133,6 +141,9 @@ def text_input(window, show_cursor=False, insert_mode=True): if ch == Symbol.ESCAPE: raise EscapePressed + if (not allow_resize) and (ch == curses.KEY_RESIZE): + raise EscapePressed + # Fix backspace for iterm if ch == ascii.DEL: ch = curses.KEY_BACKSPACE @@ -144,11 +155,42 @@ def text_input(window, show_cursor=False, insert_mode=True): # input. try: out = textbox.edit(validate=validate) - out = out.strip() except EscapePressed: out = None curses.curs_set(0) + + if out is None: + return out + else: + return strip_text(out) + +def strip_text(text): + "Intelligently strip whitespace from the text output of a curses textpad." + + # Trivial case where the textbox is only one line long. + if '\n' not in text: + return text.rstrip() + + # Allow one space at the end of the line. If there is more than one space, + # assume that a newline operation was intended by the user + stack, current_line = [], '' + for line in text.split('\n'): + if line.endswith(' '): + stack.append(current_line + line.rstrip()) + current_line = '' + else: + current_line += line + stack.append(current_line) + + # Prune empty lines at the bottom of the textbox. + for item in stack[::-1]: + if len(item) == 0: + stack.pop() + else: + break + + out = '\n'.join(stack) return out def display_message(stdscr, message):