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] txt = message.txt if not txt: return [] theme = get_theme() if len(message.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(message.nickname, nick_size) offset = 0 if message.ack: if message.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 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 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 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_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 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 rewrite_text(self) -> None: """ Refresh the line onscreen, but first, always adjust the view_pos. Also, each FORMAT_CHAR+attr_char count only take one screen column (this is done in _addstr_colored_lite), we have to do some special calculations to find the correct length of text to display, and the position of the cursor. """ self.adjust_view_pos() text = self.text self._win.erase() if self.color: self._win.attron(to_curses_attr(self.color)) displayed_text = text[self.view_pos:self.view_pos + self.width - 1].replace('\t', '\x18') self._win.attrset(0) self._addstr_colored_lite(displayed_text) # Fill the rest of the line with the input color if self.color: (_, x) = self._win.getyx() size = self.width - x self.addnstr(' ' * size, size, to_curses_attr(self.color)) self.addstr(0, poopt.wcswidth( displayed_text[:self.pos - self.view_pos]), '') if self.color: self._win.attroff(to_curses_attr(self.color)) curses.curs_set(1) self._refresh()
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 rewrite_text(self): """ Refresh the line onscreen, but first, always adjust the view_pos. Also, each FORMAT_CHAR+attr_char count only take one screen column (this is done in _addstr_colored_lite), we have to do some special calculations to find the correct length of text to display, and the position of the cursor. """ self.adjust_view_pos() text = self.text self._win.erase() if self.color: self._win.attron(to_curses_attr(self.color)) displayed_text = text[self.view_pos:self.view_pos+self.width-1].replace('\t', '\x18') self._win.attrset(0) self._addstr_colored_lite(displayed_text) # Fill the rest of the line with the input color if self.color: (_, x) = self._win.getyx() size = self.width - x self.addnstr(' ' * size, size, to_curses_attr(self.color)) self.addstr(0, poopt.wcswidth(displayed_text[:self.pos-self.view_pos]), '') if self.color: self._win.attroff(to_curses_attr(self.color)) curses.curs_set(1) self._refresh()
def write_nack(buffer: 'Win') -> 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
def write_ack(self) -> int: 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 goto_build_lines(self, new_date): text_buffer = self._text_buffer built_lines = [] message_count = 0 timestamp = config.get('show_timestamps') nick_size = config.get('max_nick_length') for message in text_buffer.messages: # Build lines of a message txt = message.txt nick = truncate_nick(message.nickname, nick_size) offset = 0 theme = get_theme() if message.ack: if message.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 if message.revisions > 0: offset += ceil(log10(message.revisions + 1)) 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 lines = poopt.cut_text(txt, self.text_win.width - offset - 1) for line in lines: built_lines.append(line) # Find the message with timestamp less than or equal to the queried # timestamp and goto that location in the tab. if message.time <= new_date: message_count += 1 if len(self.text_win.built_lines ) - self.text_win.height >= len(built_lines): self.text_win.pos = len( self.text_win.built_lines ) - self.text_win.height - len(built_lines) + 1 else: self.text_win.pos = 0 if message_count == 0: self.text_win.scroll_up(len(self.text_win.built_lines)) self.core.refresh_window()
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
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 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_time(buffer: 'Win', history: bool, time: datetime) -> int: """ Write the date on the yth line of the window """ theme = get_theme() if time: if history and time.date() != date.today(): format = theme.LONG_TIME_FORMAT else: format = theme.SHORT_TIME_FORMAT 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
def write_pre_xmllog(msg: XMLLog, win: 'Win', with_timestamps: bool, nick_size: int) -> int: """Write the part before the stanza (timestamp + IN/OUT)""" offset = 0 if with_timestamps: offset += 1 + 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
def refresh(self) -> None: 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 write_pre_message(msg: Message, win: '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: 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
def adjust_view_pos(self) -> None: """ Adjust the position of the View, if needed (for example if the cursor moved and would now be out of the view, we adapt the view_pos so that we can always see our cursor) """ # start of the input if self.pos == 0: self.view_pos = 0 return # cursor outside of the screen (left) if self.pos <= self.view_pos: self.view_pos = self.pos - max(1 * self.width // 3, 1) # cursor outside of the screen (right) elif self.pos >= self.view_pos + self.width - 1: self.view_pos = self.pos - max(2 * self.width // 3, 2) if self.view_pos < 0: self.view_pos = 0 # text small enough to fit inside the window entirely: # remove scrolling if present if poopt.wcswidth(self.text) < self.width: self.view_pos = 0
def adjust_view_pos(self): """ Adjust the position of the View, if needed (for example if the cursor moved and would now be out of the view, we adapt the view_pos so that we can always see our cursor) """ # start of the input if self.pos == 0: self.view_pos = 0 return # cursor outside of the screen (left) if self.pos <= self.view_pos: self.view_pos = self.pos - max(1 * self.width // 3, 1) # cursor outside of the screen (right) elif self.pos >= self.view_pos + self.width - 1: self.view_pos = self.pos - max(2 * self.width // 3, 2) if self.view_pos < 0: self.view_pos = 0 # text small enough to fit inside the window entirely: # remove scrolling if present if poopt.wcswidth(self.text) < self.width: self.view_pos = 0