From 80ce8453f50ccaad4d71fda8811ee33f5ffa3624 Mon Sep 17 00:00:00 2001 From: mathieui Date: Sat, 28 Sep 2019 18:35:23 +0200 Subject: Rewrite part of the message handling/rendering --- poezio/core/core.py | 5 +- poezio/mam.py | 1 - poezio/tabs/basetabs.py | 11 +-- poezio/tabs/xmltab.py | 5 +- poezio/text_buffer.py | 32 +++---- poezio/ui/consts.py | 10 ++ poezio/ui/funcs.py | 10 +- poezio/ui/render.py | 234 +++++++++++++++++++++++++++++++++++++++++++++ poezio/ui/types.py | 172 +++++++++++++++++---------------- poezio/windows/text_win.py | 204 ++------------------------------------- 10 files changed, 374 insertions(+), 310 deletions(-) create mode 100644 poezio/ui/render.py diff --git a/poezio/core/core.py b/poezio/core/core.py index 14852ac2..2b8252b1 100644 --- a/poezio/core/core.py +++ b/poezio/core/core.py @@ -1349,7 +1349,10 @@ class Core: colors = get_theme().INFO_COLORS color = colors.get(typ.lower(), colors.get('default', None)) nb_lines = self.information_buffer.add_message( - msg, nickname=typ, nick_color=color) + txt=msg, + nickname=typ, + nick_color=color + ) popup_on = config.get('information_buffer_popup_on').split() if isinstance(self.tabs.current_tab, tabs.RosterInfoTab): self.refresh_window() diff --git a/poezio/mam.py b/poezio/mam.py index 0f745f30..6e13c074 100644 --- a/poezio/mam.py +++ b/poezio/mam.py @@ -72,7 +72,6 @@ def add_line( highlight=False, top=top, identifier=None, - str_time=None, jid=None, ) diff --git a/poezio/tabs/basetabs.py b/poezio/tabs/basetabs.py index 40868e2f..eadc9a3f 100644 --- a/poezio/tabs/basetabs.py +++ b/poezio/tabs/basetabs.py @@ -45,7 +45,8 @@ from poezio.decorators import command_args_parser, refresh_wrapper from poezio.logger import logger from poezio.text_buffer import TextBuffer from poezio.theming import get_theme, dump_tuple -from poezio.windows.funcs import truncate_nick +from poezio.ui.funcs import truncate_nick +from poezio.ui.consts import LONG_FORMAT_LENGTH from slixmpp import JID, InvalidJID, Message @@ -839,12 +840,8 @@ class ChatTab(Tab): if message.me: offset += 1 if timestamp: - if message.str_time: - offset += 1 + len(message.str_time) - if theme.CHAR_TIME_LEFT and message.str_time: - offset += 1 - if theme.CHAR_TIME_RIGHT and message.str_time: - offset += 1 + if message.history: + offset += 1 + LONG_FORMAT_LENGTH lines = poopt.cut_text(txt, self.text_win.width - offset - 1) for line in lines: built_lines.append(line) diff --git a/poezio/tabs/xmltab.py b/poezio/tabs/xmltab.py index c4a50df8..c73eddd0 100644 --- a/poezio/tabs/xmltab.py +++ b/poezio/tabs/xmltab.py @@ -262,7 +262,10 @@ class XMLTab(Tab): else: xml = self.core_buffer.messages[:] text = '\n'.join( - ('%s %s %s' % (msg.str_time, msg.nickname, clean_text(msg.txt)) + ('%s %s %s' % ( + msg.time.strftime('%H:%M:%S'), + msg.nickname, + clean_text(msg.txt)) for msg in xml)) filename = os.path.expandvars(os.path.expanduser(args[0])) try: diff --git a/poezio/text_buffer.py b/poezio/text_buffer.py index 1667f0dc..c03f84f5 100644 --- a/poezio/text_buffer.py +++ b/poezio/text_buffer.py @@ -63,7 +63,6 @@ class TextBuffer: highlight: bool = False, top: Optional[bool] = False, identifier: Optional[str] = None, - str_time: Optional[str] = None, jid: Optional[str] = None, ack: int = 0) -> int: """ @@ -71,14 +70,13 @@ class TextBuffer: """ msg = Message( txt, - time, - nickname, - nick_color, - history, - user, - identifier, - top, - str_time=str_time, + time=time, + nickname=nickname, + nick_color=nick_color, + history=history, + user=user, + identifier=identifier, + top=top, highlight=highlight, jid=jid, ack=ack) @@ -180,7 +178,7 @@ class TextBuffer: if msg.user and msg.user is not user: raise CorrectionError("Different users") - elif len(msg.str_time) > 8: # ugly + elif msg.history: raise CorrectionError("Delayed message") elif not msg.user and (msg.jid is None or jid is None): raise CorrectionError('Could not check the ' @@ -195,13 +193,13 @@ class TextBuffer: self.correction_ids[new_id] = orig_id message = Message( - txt, - time, - msg.nickname, - msg.nick_color, - False, - msg.user, - orig_id, + txt=txt, + time=time, + nickname=msg.nickname, + nick_color=msg.nick_color, + history=False, + user=msg.user, + identifier=orig_id, highlight=highlight, old_message=msg, revisions=msg.revisions + 1, diff --git a/poezio/ui/consts.py b/poezio/ui/consts.py index 91f19a82..0838d953 100644 --- a/poezio/ui/consts.py +++ b/poezio/ui/consts.py @@ -1,4 +1,14 @@ +from datetime import datetime + FORMAT_CHAR = '\x19' # These are non-printable chars, so they should never appear in the input, # I guess. But maybe we can find better chars that are even less risky. FORMAT_CHARS = '\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x1A' + +# Short date format (only show time) +SHORT_FORMAT = '%H:%M:%S' +SHORT_FORMAT_LENGTH = len(datetime.now().strftime(SHORT_FORMAT)) + +# Long date format (show date and time) +LONG_FORMAT = '%Y-%m-%d %H:%M:%S' +LONG_FORMAT_LENGTH = len(datetime.now().strftime(LONG_FORMAT)) diff --git a/poezio/ui/funcs.py b/poezio/ui/funcs.py index 260cc037..023432ee 100644 --- a/poezio/ui/funcs.py +++ b/poezio/ui/funcs.py @@ -22,12 +22,14 @@ def find_first_format_char(text: str, return pos -def truncate_nick(nick: Optional[str], size=10) -> Optional[str]: +def truncate_nick(nick: Optional[str], size=10) -> str: if size < 1: size = 1 - if nick and len(nick) > size: - return nick[:size] + '…' - return nick + if nick: + if len(nick) > size: + return nick[:size] + '…' + return nick + return '' def parse_attrs(text: str, previous: Optional[List[str]] = None) -> List[str]: diff --git a/poezio/ui/render.py b/poezio/ui/render.py new file mode 100644 index 00000000..64b480a9 --- /dev/null +++ b/poezio/ui/render.py @@ -0,0 +1,234 @@ +import logging +import curses + +from datetime import datetime +from functools import singledispatch +from typing import List, Optional, Tuple +from math import ceil, log10 + +from poezio import poopt +from poezio.ui.consts import ( + FORMAT_CHAR, + LONG_FORMAT, + SHORT_FORMAT, +) +from poezio.ui.funcs import ( + truncate_nick, + parse_attrs, +) +from poezio.theming import ( + get_theme, +) +from poezio.ui.types import ( + BaseMessage, + Message, + XMLLog, +) + +# msg is a reference to the corresponding Message object. text_start and +# text_end are the position delimiting the text in this line. +class Line: + __slots__ = ('msg', 'start_pos', 'end_pos', 'prepend') + + def __init__(self, msg: BaseMessage, start_pos: int, end_pos: int, prepend: str) -> None: + self.msg = msg + self.start_pos = start_pos + self.end_pos = end_pos + self.prepend = prepend + + def __repr__(self): + return '(%s, %s)' % (self.start_pos, self.end_pos) + + +LinePos = Tuple[int, int] + +def generate_lines(lines: List[LinePos], msg: BaseMessage, default_color: str = '') -> List[Line]: + line_objects = [] + attrs = [] # type: List[str] + prepend = default_color if default_color else '' + for line in lines: + saved = Line( + msg=msg, + start_pos=line[0], + end_pos=line[1], + prepend=prepend) + attrs = parse_attrs(msg.txt[line[0]:line[1]], attrs) + if attrs: + prepend = FORMAT_CHAR + FORMAT_CHAR.join(attrs) + else: + if default_color: + prepend = default_color + else: + prepend = '' + line_objects.append(saved) + return line_objects + + +@singledispatch +def build_lines(msg: BaseMessage, width: int, timestamp: bool, nick_size: int = 10) -> List[Line]: + offset = msg.compute_offset(timestamp, nick_size) + lines = poopt.cut_text(msg.txt, width - offset - 1) + return generate_lines(lines, msg, default_color='') + + +@build_lines.register(type(None)) +def build_separator(*args, **kwargs): + return [None] + + +@build_lines.register(Message) +def build_message(msg: Message, width: int, timestamp: bool, nick_size: int = 10) -> List[Line]: + """ + Build a list of lines from this message. + """ + txt = msg.txt + if not txt: + return [] + offset = msg.compute_offset(timestamp, nick_size) + lines = poopt.cut_text(txt, width - offset - 1) + return generate_lines(lines, msg, default_color='') + + +@build_lines.register(XMLLog) +def build_xmllog(msg: XMLLog, width: int, timestamp: bool, nick_size: int = 10) -> List[Line]: + offset = msg.compute_offset(timestamp, nick_size) + lines = poopt.cut_text(msg.txt, width - offset - 1) + return generate_lines(lines, msg, default_color='') + + +@singledispatch +def write_pre(msg: BaseMessage, win, with_timestamps: bool, nick_size: int) -> int: + """Write the part before text (only the timestamp)""" + if with_timestamps: + return 1 + PreMessageHelpers.write_time(win, False, msg.time) + return 0 + + +@write_pre.register(Message) +def write_pre_message(msg: Message, win, with_timestamps: bool, nick_size: int) -> int: + """Write the part before the body: + - timestamp (short or long) + - ack/nack + - nick (with a "* " for /me) + - LMC number if present + """ + offset = 0 + if with_timestamps: + logging.debug(msg) + offset += PreMessageHelpers.write_time(win, msg.history, msg.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 += PreMessageHelpers.write_ack(win) + else: + offset += PreMessageHelpers.write_nack(win) + if msg.me: + with win.colored_text(color=get_theme().COLOR_ME_MESSAGE): + win.addstr('* ') + PreMessageHelpers.write_nickname(win, nick, color, msg.highlight) + offset += PreMessageHelpers.write_revisions(win, msg) + win.addstr(' ') + offset += 3 + else: + PreMessageHelpers.write_nickname(win, nick, color, msg.highlight) + offset += PreMessageHelpers.write_revisions(win, msg) + win.addstr('> ') + offset += 2 + return offset + + +@write_pre.register(XMLLog) +def write_pre_xmllog(msg: XMLLog, win, with_timestamps: bool, nick_size: int) -> int: + """Write the part before the stanza (timestamp + IN/OUT)""" + offset = 0 + if with_timestamps: + offset += PreMessageHelpers.write_time(win, False, msg.time) + theme = get_theme() + if msg.incoming: + char = theme.CHAR_XML_IN + color = theme.COLOR_XML_IN + else: + char = theme.CHAR_XML_OUT + color = theme.COLOR_XML_OUT + nick = truncate_nick(char, nick_size) + offset += poopt.wcswidth(nick) + PreMessageHelpers.write_nickname(win, char, color) + win.addstr(' ') + return offset + +class PreMessageHelpers: + + @staticmethod + def write_revisions(buffer, msg: Message) -> int: + if msg.revisions: + color = get_theme().COLOR_REVISIONS_MESSAGE + with buffer.colored_text(color=color): + buffer.addstr('%d' % msg.revisions) + return ceil(log10(msg.revisions + 1)) + return 0 + + @staticmethod + def write_ack(buffer) -> int: + theme = get_theme() + color = theme.COLOR_CHAR_ACK + with buffer.colored_text(color=color): + buffer.addstr(theme.CHAR_ACK_RECEIVED) + buffer.addstr(' ') + return poopt.wcswidth(theme.CHAR_ACK_RECEIVED) + 1 + + @staticmethod + def write_nack(buffer) -> int: + theme = get_theme() + color = theme.COLOR_CHAR_NACK + with buffer.colored_text(color=color): + buffer.addstr(theme.CHAR_NACK) + buffer.addstr(' ') + return poopt.wcswidth(theme.CHAR_NACK) + 1 + + @staticmethod + def write_nickname(buffer, nickname: str, color, highlight=False) -> None: + """ + Write the nickname, using the user's color + and return the number of written characters + """ + if not nickname: + return + attr = None + if highlight: + hl_color = get_theme().COLOR_HIGHLIGHT_NICK + if hl_color == "reverse": + attr = curses.A_REVERSE + else: + color = hl_color + with buffer.colored_text(color=color, attr=attr): + buffer.addstr(nickname) + + @staticmethod + def write_time(buffer, history: bool, time: datetime) -> int: + """ + Write the date on the yth line of the window + """ + if time: + if history: + format = LONG_FORMAT + else: + format = SHORT_FORMAT + logging.debug(time) + time_str = time.strftime(format) + color = get_theme().COLOR_TIME_STRING + with buffer.colored_text(color=color): + buffer.addstr(time_str) + buffer.addstr(' ') + return poopt.wcswidth(time_str) + 1 + return 0 diff --git a/poezio/ui/types.py b/poezio/ui/types.py index 69d77a07..18e51427 100644 --- a/poezio/ui/types.py +++ b/poezio/ui/types.py @@ -2,28 +2,78 @@ from datetime import datetime from math import ceil, log10 from typing import Union, Optional, List, Tuple - -from poezio.theming import get_theme, dump_tuple -from poezio.ui.funcs import truncate_nick, parse_attrs +from poezio.ui.funcs import truncate_nick from poezio import poopt -from poezio.ui.consts import FORMAT_CHAR +from poezio.user import User +from poezio.theming import dump_tuple, get_theme +from poezio.ui.consts import ( + SHORT_FORMAT_LENGTH, + LONG_FORMAT_LENGTH, +) + + +class BaseMessage: + __slots__ = ('txt', 'time', 'identifier') + + def __init__(self, txt: str, identifier: str = '', time: Optional[datetime] = None): + self.txt = txt + self.identifier = identifier + if time is not None: + self.time = time + else: + self.time = datetime.now() + + def compute_offset(self, with_timestamps: bool, nick_size: int) -> int: + return SHORT_FORMAT_LENGTH + 1 + + +class XMLLog(BaseMessage): + """XML Log message""" + __slots__ = ('txt', 'time', 'identifier', 'incoming') + + def __init__( + self, + txt: str, + incoming: bool, + ): + BaseMessage.__init__( + self, + txt=txt, + identifier='', + ) + self.txt = txt + self.identifier = '' + self.incoming = incoming + + def compute_offset(self, with_timestamps: bool, nick_size: int) -> int: + offset = 0 + theme = get_theme() + IN, OUT = theme.CHAR_XML_IN, theme.CHAR_XML_OUT + if with_timestamps: + offset += 1 + SHORT_FORMAT_LENGTH + if self.incoming: + nick = IN + else: + nick = OUT + nick = truncate_nick(nick, nick_size) or '' + offset += 1 + len(nick) + return offset -class Message: - __slots__ = ('txt', 'nick_color', 'time', 'str_time', 'nickname', 'user', +class Message(BaseMessage): + __slots__ = ('txt', 'nick_color', 'time', 'nickname', 'user', 'history', 'identifier', 'top', 'highlight', 'me', 'old_message', 'revisions', 'jid', 'ack') def __init__(self, txt: str, - time: Optional[datetime], nickname: Optional[str], - nick_color: Optional[Tuple], - history: bool, - user: Optional[str], - identifier: Optional[str], + time: Optional[datetime] = None, + nick_color: Optional[Tuple] = None, + history: bool = False, + user: Optional[User] = None, + identifier: Optional[str] = '', top: Optional[bool] = False, - str_time: Optional[str] = None, highlight: bool = False, old_message: Optional['Message'] = None, revisions: int = 0, @@ -33,27 +83,22 @@ class Message: Create a new Message object with parameters, check for /me messages, and delayed messages """ - time = time if time is not None else datetime.now() + BaseMessage.__init__( + self, + txt=txt.replace('\t', ' ') + '\x19o', + identifier=identifier or '', + time=time, + ) if txt.startswith('/me '): me = True txt = '\x19%s}%s\x19o' % (dump_tuple(get_theme().COLOR_ME_MESSAGE), txt[4:]) else: me = False - str_time = time.strftime("%H:%M:%S") - if history: - txt = txt.replace( - '\x19o', - '\x19o\x19%s}' % dump_tuple(get_theme().COLOR_LOG_MSG)) - str_time = time.strftime("%Y-%m-%d %H:%M:%S") - - self.txt = txt.replace('\t', ' ') + '\x19o' - self.nick_color = nick_color - self.time = time - self.str_time = str_time + self.history = history self.nickname = nickname + self.nick_color = nick_color self.user = user - self.identifier = identifier self.top = top self.highlight = highlight self.me = me @@ -91,68 +136,29 @@ class Message: rev -= 1 return ''.join(acc) - def render(self, width: int, timestamp: bool = False, nick_size: int = 10) -> List["Line"]: - """ - Build a list of lines from this message. - """ - txt = self.txt - if not txt: - return [] - theme = get_theme() - if len(self.str_time) > 8: - default_color = ( - FORMAT_CHAR + dump_tuple(theme.COLOR_LOG_MSG) + '}') # type: Optional[str] - else: - default_color = None - ret = [] # type: List[Union[None, Line]] - nick = truncate_nick(self.nickname, nick_size) + def compute_offset(self, with_timestamps: bool, nick_size: int) -> int: offset = 0 + if with_timestamps: + if self.history: + offset += 1 + LONG_FORMAT_LENGTH + else: + offset += 1 + SHORT_FORMAT_LENGTH + + if not self.nickname: # not a message, nothing to do afterwards + return offset + + nick = truncate_nick(self.nickname, nick_size) or '' + offset += poopt.wcswidth(nick) if self.ack: + theme = get_theme() if self.ack > 0: offset += poopt.wcswidth(theme.CHAR_ACK_RECEIVED) + 1 else: offset += poopt.wcswidth(theme.CHAR_NACK) + 1 - if nick: - offset += poopt.wcswidth(nick) + 2 # + nick + '> ' length - if self.revisions > 0: - offset += ceil(log10(self.revisions + 1)) if self.me: - offset += 1 # '* ' before and ' ' after - if timestamp: - if self.str_time: - offset += 1 + len(self.str_time) - if theme.CHAR_TIME_LEFT and self.str_time: - offset += 1 - if theme.CHAR_TIME_RIGHT and self.str_time: - offset += 1 - lines = poopt.cut_text(txt, width - offset - 1) - prepend = default_color if default_color else '' - attrs = [] # type: List[str] - for line in lines: - saved = Line( - msg=self, - start_pos=line[0], - end_pos=line[1], - prepend=prepend) - attrs = parse_attrs(self.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 - - -# msg is a reference to the corresponding Message object. text_start and -# text_end are the position delimiting the text in this line. -class Line: - __slots__ = ('msg', 'start_pos', 'end_pos', 'prepend') - - def __init__(self, msg: Message, start_pos: int, end_pos: int, prepend: str) -> None: - self.msg = msg - self.start_pos = start_pos - self.end_pos = end_pos - self.prepend = prepend + offset += 3 + else: + offset += 2 + if self.revisions: + offset += ceil(log10(self.revisions + 1)) + return offset diff --git a/poezio/windows/text_win.py b/poezio/windows/text_win.py index bba66d60..904a709b 100644 --- a/poezio/windows/text_win.py +++ b/poezio/windows/text_win.py @@ -14,7 +14,8 @@ from poezio.ui.funcs import truncate_nick, parse_attrs from poezio import poopt from poezio.config import config from poezio.theming import to_curses_attr, get_theme, dump_tuple -from poezio.ui.types import Line, Message +from poezio.ui.types import Message +from poezio.ui.render import Line, build_lines, write_pre log = logging.getLogger(__name__) @@ -106,7 +107,7 @@ class BaseTextWin(Win): Build a list of lines from a message, without adding it to a list """ - return [] + return build_lines(message, self.width, timestamp, nick_size) def refresh(self) -> None: pass @@ -117,20 +118,6 @@ class BaseTextWin(Win): """ self.addstr_colored(txt, y, x) - def write_time(self, time: str) -> int: - """ - 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 - # TODO: figure out the type of room. def resize(self, height: int, width: int, y: int, x: int, room=None) -> None: if hasattr(self, 'width'): @@ -341,15 +328,6 @@ class TextWin(BaseTextWin): self.built_lines.pop(0) return len(lines) - def build_message(self, message: Optional[Message], timestamp: bool = False, nick_size: int = 10) -> List[Union[None, Line]]: - """ - Build a list of lines from a message, without adding it - to a list - """ - if message is None: # line separator - return [None] - return message.render(self.width, timestamp, nick_size) - def refresh(self) -> None: log.debug('Refresh: %s', self.__class__.__name__) if self.height <= 0: @@ -367,11 +345,10 @@ class TextWin(BaseTextWin): if line: msg = line.msg if line.start_pos == 0: - offset = self.write_pre_msg(msg, with_timestamps, - nick_size) + offset = write_pre(msg, self, with_timestamps, nick_size) elif y == 0: - offset = self.compute_offset(msg, with_timestamps, - nick_size) + offset = msg.compute_offset(with_timestamps, + nick_size) self.write_text( y, offset, line.prepend + line.msg.txt[line.start_pos:line.end_pos]) @@ -382,120 +359,11 @@ class TextWin(BaseTextWin): self._win.attrset(0) self._refresh() - def compute_offset(self, msg, with_timestamps, nick_size) -> int: - 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: - theme = get_theme() - if msg.ack > 0: - offset += poopt.wcswidth(theme.CHAR_ACK_RECEIVED) + 1 - else: - offset += poopt.wcswidth(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) -> int: - 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) -> int: - 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) -> None: theme = get_theme() char = theme.CHAR_NEW_TEXT_SEPARATOR self.addnstr(y, 0, char * (self.width // len(char) - 1), self.width, to_curses_attr(theme.COLOR_NEW_TEXT_SEPARATOR)) - - def write_ack(self) -> int: - theme = get_theme() - color = theme.COLOR_CHAR_ACK - self._win.attron(to_curses_attr(color)) - self.addstr(theme.CHAR_ACK_RECEIVED) - self._win.attroff(to_curses_attr(color)) - self.addstr(' ') - return poopt.wcswidth(theme.CHAR_ACK_RECEIVED) + 1 - - def write_nack(self) -> int: - theme = get_theme() - color = theme.COLOR_CHAR_NACK - self._win.attron(to_curses_attr(color)) - self.addstr(theme.CHAR_NACK) - self._win.attroff(to_curses_attr(color)) - self.addstr(' ') - return poopt.wcswidth(theme.CHAR_NACK) + 1 - - def write_nickname(self, nickname, color, highlight=False) -> None: - """ - 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) -> None: """ Find a message, and replace it with a new one @@ -544,28 +412,12 @@ class XMLTextWin(BaseTextWin): 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(' ') + offset = write_pre(msg, self, True, 10) 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 - + offset = msg.compute_offset(True, 10) self.write_text( y, offset, line.prepend + line.msg.txt[line.start_pos:line.end_pos]) @@ -573,43 +425,3 @@ class XMLTextWin(BaseTextWin): self.addstr('\n') self._win.attrset(0) self._refresh() - - def build_message(self, message: Message, timestamp: bool = False, nick_size: int = 10) -> List[Line]: - 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) - theme = get_theme() - if theme.CHAR_TIME_LEFT and message.str_time: - offset += 1 - if 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 = [] # type: List[str] - 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) -> None: - self._win.attron(to_curses_attr(color)) - self.addstr(truncate_nick(nickname)) - self._win.attroff(to_curses_attr(color)) -- cgit v1.2.3