diff options
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> | 2016-03-31 18:54:41 +0100 |
---|---|---|
committer | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> | 2016-06-11 20:49:43 +0100 |
commit | 332a5c2553db41de777473a1e1be9cd1522c9496 (patch) | |
tree | 3ee06a59f147ccc4009b35cccfbe2461bcd18310 /src/windows/text_win.py | |
parent | cf44cf7cdec9fdb35caa372563d57e7045dc29dd (diff) | |
download | poezio-332a5c2553db41de777473a1e1be9cd1522c9496.tar.gz poezio-332a5c2553db41de777473a1e1be9cd1522c9496.tar.bz2 poezio-332a5c2553db41de777473a1e1be9cd1522c9496.tar.xz poezio-332a5c2553db41de777473a1e1be9cd1522c9496.zip |
Move the src directory to poezio, for better cython compatibility.
Diffstat (limited to 'src/windows/text_win.py')
-rw-r--r-- | src/windows/text_win.py | 597 |
1 files changed, 0 insertions, 597 deletions
diff --git a/src/windows/text_win.py b/src/windows/text_win.py deleted file mode 100644 index fd1fe546..00000000 --- a/src/windows/text_win.py +++ /dev/null @@ -1,597 +0,0 @@ -""" -TextWin, the window showing the text messages and info messages in poezio. -Can be locked, scrolled, has a separator, etc… -""" - -import logging -log = logging.getLogger(__name__) - -import curses -from math import ceil, log10 - -from . import Win -from . base_wins import FORMAT_CHAR, Line -from . funcs import truncate_nick, parse_attrs - -import poopt -from config import config -from theming import to_curses_attr, get_theme, dump_tuple - - -class BaseTextWin(Win): - def __init__(self, lines_nb_limit=None): - if lines_nb_limit is None: - lines_nb_limit = config.get('max_lines_in_memory') - Win.__init__(self) - self.lines_nb_limit = lines_nb_limit - self.pos = 0 - self.built_lines = [] # Each new message is built and kept here. - # on resize, we rebuild all the messages - - self.lock = False - self.lock_buffer = [] - self.separator_after = None - - def toggle_lock(self): - if self.lock: - self.release_lock() - else: - self.acquire_lock() - return self.lock - - def acquire_lock(self): - self.lock = True - - def release_lock(self): - for line in self.lock_buffer: - self.built_lines.append(line) - self.lock = False - - def scroll_up(self, dist=14): - pos = self.pos - self.pos += dist - if self.pos + self.height > len(self.built_lines): - self.pos = len(self.built_lines) - self.height - if self.pos < 0: - self.pos = 0 - return self.pos != pos - - def scroll_down(self, dist=14): - pos = self.pos - self.pos -= dist - if self.pos <= 0: - self.pos = 0 - return self.pos != pos - - def build_new_message(self, message, history=None, clean=True, highlight=False, timestamp=False, nick_size=10): - """ - Take one message, build it and add it to the list - Return the number of lines that are built for the given - message. - """ - lines = self.build_message(message, timestamp=timestamp, nick_size=nick_size) - if self.lock: - self.lock_buffer.extend(lines) - else: - self.built_lines.extend(lines) - if not lines or not lines[0]: - return 0 - if clean: - while len(self.built_lines) > self.lines_nb_limit: - self.built_lines.pop(0) - return len(lines) - - def build_message(self, message, timestamp=False, nick_size=10): - """ - Build a list of lines from a message, without adding it - to a list - """ - pass - - def refresh(self): - pass - - def write_text(self, y, x, txt): - """ - write the text of a line. - """ - self.addstr_colored(txt, y, x) - - def write_time(self, time): - """ - Write the date on the yth line of the window - """ - if time: - color = get_theme().COLOR_TIME_STRING - curses_color = to_curses_attr(color) - self._win.attron(curses_color) - self.addstr(time) - self._win.attroff(curses_color) - self.addstr(' ') - return poopt.wcswidth(time) + 1 - return 0 - - def resize(self, height, width, y, x, room=None): - if hasattr(self, 'width'): - old_width = self.width - else: - old_width = None - self._resize(height, width, y, x) - if room and self.width != old_width: - self.rebuild_everything(room) - - # reposition the scrolling after resize - # (see #2450) - buf_size = len(self.built_lines) - if buf_size - self.pos < self.height: - self.pos = buf_size - self.height - if self.pos < 0: - self.pos = 0 - - def rebuild_everything(self, room): - self.built_lines = [] - with_timestamps = config.get('show_timestamps') - nick_size = config.get('max_nick_length') - for message in room.messages: - self.build_new_message(message, clean=False, timestamp=with_timestamps, nick_size=nick_size) - if self.separator_after is message: - self.build_new_message(None) - while len(self.built_lines) > self.lines_nb_limit: - self.built_lines.pop(0) - - def __del__(self): - log.debug('** TextWin: deleting %s built lines', (len(self.built_lines))) - del self.built_lines - -class TextWin(BaseTextWin): - def __init__(self, lines_nb_limit=None): - BaseTextWin.__init__(self, lines_nb_limit) - - # the Lines of the highlights in that buffer - self.highlights = [] - # the current HL position in that list NaN means that we’re not on - # an hl. -1 is a valid position (it's before the first hl of the - # list. i.e the separator, in the case where there’s no hl before - # it.) - self.hl_pos = float('nan') - - # Keep track of the number of hl after the separator. - # This is useful to make “go to next highlight“ work after a “move to separator”. - self.nb_of_highlights_after_separator = 0 - - self.separator_after = None - - def next_highlight(self): - """ - Go to the next highlight in the buffer. - (depending on which highlight was selected before) - if the buffer is already positionned on the last, of if there are no - highlights, scroll to the end of the buffer. - """ - log.debug('Going to the next highlight…') - if (not self.highlights or self.hl_pos != self.hl_pos or - self.hl_pos >= len(self.highlights) - 1): - self.hl_pos = float('nan') - self.pos = 0 - return - hl_size = len(self.highlights) - 1 - if self.hl_pos < hl_size: - self.hl_pos += 1 - else: - self.hl_pos = hl_size - log.debug("self.hl_pos = %s", self.hl_pos) - hl = self.highlights[self.hl_pos] - pos = None - while not pos: - try: - pos = self.built_lines.index(hl) - except ValueError: - self.highlights = self.highlights[self.hl_pos+1:] - if not self.highlights: - self.hl_pos = float('nan') - self.pos = 0 - return - self.hl_pos = 0 - hl = self.highlights[0] - self.pos = len(self.built_lines) - pos - self.height - if self.pos < 0 or self.pos >= len(self.built_lines): - self.pos = 0 - - def previous_highlight(self): - """ - Go to the previous highlight in the buffer. - (depending on which highlight was selected before) - if the buffer is already positionned on the first, or if there are no - highlights, scroll to the end of the buffer. - """ - log.debug('Going to the previous highlight…') - if not self.highlights or self.hl_pos <= 0: - self.hl_pos = float('nan') - self.pos = 0 - return - if self.hl_pos != self.hl_pos: - self.hl_pos = len(self.highlights) - 1 - else: - self.hl_pos -= 1 - log.debug("self.hl_pos = %s", self.hl_pos) - hl = self.highlights[self.hl_pos] - pos = None - while not pos: - try: - pos = self.built_lines.index(hl) - except ValueError: - self.highlights = self.highlights[self.hl_pos+1:] - if not self.highlights: - self.hl_pos = float('nan') - self.pos = 0 - return - self.hl_pos = 0 - hl = self.highlights[0] - self.pos = len(self.built_lines) - pos - self.height - if self.pos < 0 or self.pos >= len(self.built_lines): - self.pos = 0 - - def scroll_to_separator(self): - """ - Scroll until separator is centered. If no separator is - present, scroll at the top of the window - """ - if None in self.built_lines: - self.pos = len(self.built_lines) - self.built_lines.index(None) - self.height + 1 - if self.pos < 0: - self.pos = 0 - else: - self.pos = len(self.built_lines) - self.height + 1 - # Chose a proper position (not too high) - self.scroll_up(0) - # Make “next highlight” work afterwards. This makes it easy to - # review all the highlights since the separator was placed, in - # the correct order. - self.hl_pos = len(self.highlights) - self.nb_of_highlights_after_separator - 1 - log.debug("self.hl_pos = %s", self.hl_pos) - - def remove_line_separator(self): - """ - Remove the line separator - """ - log.debug('remove_line_separator') - if None in self.built_lines: - self.built_lines.remove(None) - self.separator_after = None - - def add_line_separator(self, room=None): - """ - add a line separator at the end of messages list - room is a textbuffer that is needed to get the previous message - (in case of resize) - """ - if None not in self.built_lines: - self.built_lines.append(None) - self.nb_of_highlights_after_separator = 0 - log.debug("Reseting number of highlights after separator") - if room and room.messages: - self.separator_after = room.messages[-1] - - def build_new_message(self, message, history=None, clean=True, highlight=False, timestamp=False, nick_size=10): - """ - Take one message, build it and add it to the list - Return the number of lines that are built for the given - message. - """ - lines = self.build_message(message, timestamp=timestamp, nick_size=nick_size) - if self.lock: - self.lock_buffer.extend(lines) - else: - self.built_lines.extend(lines) - if not lines or not lines[0]: - return 0 - if highlight: - self.highlights.append(lines[0]) - self.nb_of_highlights_after_separator += 1 - log.debug("Number of highlights after separator is now %s", - self.nb_of_highlights_after_separator) - if clean: - while len(self.built_lines) > self.lines_nb_limit: - self.built_lines.pop(0) - return len(lines) - - def build_message(self, message, timestamp=False, nick_size=10): - """ - Build a list of lines from a message, without adding it - to a list - """ - if message is None: # line separator - return [None] - txt = message.txt - if not txt: - return [] - if len(message.str_time) > 8: - default_color = (FORMAT_CHAR + dump_tuple(get_theme().COLOR_LOG_MSG) - + '}') - else: - default_color = None - ret = [] - nick = truncate_nick(message.nickname, nick_size) - offset = 0 - if message.ack: - if message.ack > 0: - offset += poopt.wcswidth(get_theme().CHAR_ACK_RECEIVED) + 1 - else: - offset += poopt.wcswidth(get_theme().CHAR_NACK) + 1 - if nick: - offset += poopt.wcswidth(nick) + 2 # + nick + '> ' length - if message.revisions > 0: - offset += ceil(log10(message.revisions + 1)) - if message.me: - offset += 1 # '* ' before and ' ' after - if timestamp: - if message.str_time: - offset += 1 + len(message.str_time) - if get_theme().CHAR_TIME_LEFT and message.str_time: - offset += 1 - if get_theme().CHAR_TIME_RIGHT and message.str_time: - offset += 1 - lines = poopt.cut_text(txt, self.width-offset-1) - prepend = default_color if default_color else '' - attrs = [] - for line in lines: - saved = Line(msg=message, start_pos=line[0], end_pos=line[1], prepend=prepend) - attrs = parse_attrs(message.txt[line[0]:line[1]], attrs) - if attrs: - prepend = FORMAT_CHAR + FORMAT_CHAR.join(attrs) - else: - if default_color: - prepend = default_color - else: - prepend = '' - ret.append(saved) - return ret - - def refresh(self): - log.debug('Refresh: %s', self.__class__.__name__) - if self.height <= 0: - return - if self.pos == 0: - lines = self.built_lines[-self.height:] - else: - lines = self.built_lines[-self.height-self.pos:-self.pos] - with_timestamps = config.get("show_timestamps") - nick_size = config.get("max_nick_length") - self._win.move(0, 0) - self._win.erase() - offset = 0 - for y, line in enumerate(lines): - if line: - msg = line.msg - if line.start_pos == 0: - offset = self.write_pre_msg(msg, with_timestamps, nick_size) - elif y == 0: - offset = self.compute_offset(msg, with_timestamps, nick_size) - self.write_text(y, offset, line.prepend - + line.msg.txt[line.start_pos:line.end_pos]) - else: - self.write_line_separator(y) - if y != self.height-1: - self.addstr('\n') - self._win.attrset(0) - self._refresh() - - def compute_offset(self, msg, with_timestamps, nick_size): - offset = 0 - if with_timestamps and msg.str_time: - offset += poopt.wcswidth(msg.str_time) + 1 - - if not msg.nickname: # not a message, nothing to do afterwards - return offset - - nick = truncate_nick(msg.nickname, nick_size) - offset += poopt.wcswidth(nick) - if msg.ack: - if msg.ack > 0: - offset += poopt.wcswidth(get_theme().CHAR_ACK_RECEIVED) + 1 - else: - offset += poopt.wcswidth(get_theme().CHAR_NACK) + 1 - if msg.me: - offset += 3 - else: - offset += 2 - if msg.revisions: - offset += ceil(log10(msg.revisions + 1)) - offset += self.write_revisions(msg) - return offset - - - def write_pre_msg(self, msg, with_timestamps, nick_size): - offset = 0 - if with_timestamps: - offset += self.write_time(msg.str_time) - - if not msg.nickname: # not a message, nothing to do afterwards - return offset - - nick = truncate_nick(msg.nickname, nick_size) - offset += poopt.wcswidth(nick) - if msg.nick_color: - color = msg.nick_color - elif msg.user: - color = msg.user.color - else: - color = None - if msg.ack: - if msg.ack > 0: - offset += self.write_ack() - else: - offset += self.write_nack() - if msg.me: - self._win.attron(to_curses_attr(get_theme().COLOR_ME_MESSAGE)) - self.addstr('* ') - self.write_nickname(nick, color, msg.highlight) - offset += self.write_revisions(msg) - self.addstr(' ') - offset += 3 - else: - self.write_nickname(nick, color, msg.highlight) - offset += self.write_revisions(msg) - self.addstr('> ') - offset += 2 - return offset - - def write_revisions(self, msg): - if msg.revisions: - self._win.attron(to_curses_attr(get_theme().COLOR_REVISIONS_MESSAGE)) - self.addstr('%d' % msg.revisions) - self._win.attrset(0) - return ceil(log10(msg.revisions + 1)) - return 0 - - def write_line_separator(self, y): - char = get_theme().CHAR_NEW_TEXT_SEPARATOR - self.addnstr(y, 0, - char * (self.width // len(char) - 1), - self.width, - to_curses_attr(get_theme().COLOR_NEW_TEXT_SEPARATOR)) - - def write_ack(self): - color = get_theme().COLOR_CHAR_ACK - self._win.attron(to_curses_attr(color)) - self.addstr(get_theme().CHAR_ACK_RECEIVED) - self._win.attroff(to_curses_attr(color)) - self.addstr(' ') - return poopt.wcswidth(get_theme().CHAR_ACK_RECEIVED) + 1 - - def write_nack(self): - color = get_theme().COLOR_CHAR_NACK - self._win.attron(to_curses_attr(color)) - self.addstr(get_theme().CHAR_NACK) - self._win.attroff(to_curses_attr(color)) - self.addstr(' ') - return poopt.wcswidth(get_theme().CHAR_NACK) + 1 - - def write_nickname(self, nickname, color, highlight=False): - """ - Write the nickname, using the user's color - and return the number of written characters - """ - if not nickname: - return - if highlight: - hl_color = get_theme().COLOR_HIGHLIGHT_NICK - if hl_color == "reverse": - self._win.attron(curses.A_REVERSE) - else: - color = hl_color - if color: - self._win.attron(to_curses_attr(color)) - self.addstr(nickname) - if color: - self._win.attroff(to_curses_attr(color)) - if highlight and hl_color == "reverse": - self._win.attroff(curses.A_REVERSE) - - def modify_message(self, old_id, message): - """ - Find a message, and replace it with a new one - (instead of rebuilding everything in order to correct a message) - """ - with_timestamps = config.get('show_timestamps') - nick_size = config.get('max_nick_length') - for i in range(len(self.built_lines)-1, -1, -1): - if self.built_lines[i] and self.built_lines[i].msg.identifier == old_id: - index = i - while index >= 0 and self.built_lines[index] and self.built_lines[index].msg.identifier == old_id: - self.built_lines.pop(index) - index -= 1 - index += 1 - lines = self.build_message(message, timestamp=with_timestamps, nick_size=nick_size) - for line in lines: - self.built_lines.insert(index, line) - index += 1 - break - - def __del__(self): - log.debug('** TextWin: deleting %s built lines', (len(self.built_lines))) - del self.built_lines - -class XMLTextWin(BaseTextWin): - def __init__(self): - BaseTextWin.__init__(self) - - def refresh(self): - log.debug('Refresh: %s', self.__class__.__name__) - theme = get_theme() - if self.height <= 0: - return - if self.pos == 0: - lines = self.built_lines[-self.height:] - else: - lines = self.built_lines[-self.height-self.pos:-self.pos] - self._win.move(0, 0) - self._win.erase() - for y, line in enumerate(lines): - if line: - msg = line.msg - if line.start_pos == 0: - if msg.nickname == theme.CHAR_XML_OUT: - color = theme.COLOR_XML_OUT - elif msg.nickname == theme.CHAR_XML_IN: - color = theme.COLOR_XML_IN - self.write_time(msg.str_time) - self.write_prefix(msg.nickname, color) - self.addstr(' ') - if y != self.height-1: - self.addstr('\n') - self._win.attrset(0) - for y, line in enumerate(lines): - offset = 0 - # Offset for the timestamp (if any) plus a space after it - offset += len(line.msg.str_time) - # space - offset += 1 - - # Offset for the prefix - offset += poopt.wcswidth(truncate_nick(line.msg.nickname)) - # space - offset += 1 - - self.write_text(y, offset, line.prepend - + line.msg.txt[line.start_pos:line.end_pos]) - if y != self.height-1: - self.addstr('\n') - self._win.attrset(0) - self._refresh() - - def build_message(self, message, timestamp=False, nick_size=10): - txt = message.txt - ret = [] - default_color = None - nick = truncate_nick(message.nickname, nick_size) - offset = 0 - if nick: - offset += poopt.wcswidth(nick) + 1 # + nick + ' ' length - if message.str_time: - offset += 1 + len(message.str_time) - if get_theme().CHAR_TIME_LEFT and message.str_time: - offset += 1 - if get_theme().CHAR_TIME_RIGHT and message.str_time: - offset += 1 - lines = poopt.cut_text(txt, self.width-offset-1) - prepend = default_color if default_color else '' - attrs = [] - for line in lines: - saved = Line(msg=message, start_pos=line[0], end_pos=line[1], prepend=prepend) - attrs = parse_attrs(message.txt[line[0]:line[1]], attrs) - if attrs: - prepend = FORMAT_CHAR + FORMAT_CHAR.join(attrs) - else: - if default_color: - prepend = default_color - else: - prepend = '' - ret.append(saved) - return ret - - def write_prefix(self, nickname, color): - self._win.attron(to_curses_attr(color)) - self.addstr(truncate_nick(nickname)) - self._win.attroff(to_curses_attr(color)) - |