diff options
-rw-r--r-- | data/themes/dark | 2 | ||||
-rw-r--r-- | data/themes/poezio | 2 | ||||
-rw-r--r-- | src/core.py | 4 | ||||
-rw-r--r-- | src/room.py | 31 | ||||
-rw-r--r-- | src/tabs.py | 40 | ||||
-rw-r--r-- | src/text_buffer.py | 11 | ||||
-rw-r--r-- | src/theme.py | 2 | ||||
-rw-r--r-- | src/wcwidth.py | 18 | ||||
-rw-r--r-- | src/windows.py | 44 |
9 files changed, 97 insertions, 57 deletions
diff --git a/data/themes/dark b/data/themes/dark index 1fa9ed00..a7612c0b 100644 --- a/data/themes/dark +++ b/data/themes/dark @@ -4,7 +4,7 @@ # Message text color COLOR_NORMAL_TEXT = 0 COLOR_INFORMATION_TEXT = 5 -COLOR_HIGHLIGHT_TEXT = 3 +COLOR_HIGHLIGHT_NICK = 46 # User list color COLOR_USER_VISITOR = 7 diff --git a/data/themes/poezio b/data/themes/poezio index c5556280..facf68c8 100644 --- a/data/themes/poezio +++ b/data/themes/poezio @@ -4,7 +4,7 @@ # Message text color COLOR_NORMAL_TEXT = 0 COLOR_INFORMATION_TEXT = 5 -COLOR_HIGHLIGHT_TEXT = 1 +COLOR_HIGHLIGHT_NICK = 45 # User list color COLOR_USER_VISITOR = 7 diff --git a/src/core.py b/src/core.py index e2b2fb1e..46e10793 100644 --- a/src/core.py +++ b/src/core.py @@ -887,7 +887,7 @@ class Core(object): self.refresh_window() self.doupdate() - def add_message_to_text_buffer(self, room, txt, time=None, nickname=None, colorized=False): + def add_message_to_text_buffer(self, room, txt, time=None, nickname=None): """ Add the message to the room if possible, else, add it to the Info window (in the Info tab of the info window in the RosterTab) @@ -895,7 +895,7 @@ class Core(object): if not room: self.information('Error, trying to add a message in no room: %s' % txt) else: - room.add_message(txt, time, nickname, colorized) + room.add_message(txt, time, nickname) self.refresh_window() def command_help(self, arg): diff --git a/src/room.py b/src/room.py index 4196ce5f..d062e29c 100644 --- a/src/room.py +++ b/src/room.py @@ -55,19 +55,19 @@ class Room(TextBuffer): def do_highlight(self, txt, time, nickname): """ - Set the tab color and returns the txt color + Set the tab color and returns the nick color """ - color = theme.COLOR_NORMAL_TEXT + color = None if not time and nickname and nickname != self.own_nick and self.joined: if self.own_nick.lower() in txt.lower(): self.set_color_state(theme.COLOR_TAB_HIGHLIGHT) - color = theme.COLOR_HIGHLIGHT_TEXT + color = theme.COLOR_HIGHLIGHT_NICK else: highlight_words = config.get('highlight_on', '').split(':') for word in highlight_words: if word and word.lower() in txt.lower(): self.set_color_state(theme.COLOR_TAB_HIGHLIGHT) - color = theme.COLOR_HIGHLIGHT_TEXT + color = theme.COLOR_HIGHLIGHT_NICK break return color @@ -84,7 +84,7 @@ class Room(TextBuffer): """ self.color_state = color - def add_message(self, txt, time=None, nickname=None, colorized=False, forced_user=None, nick_color=None): + def add_message(self, txt, time=None, nickname=None, forced_user=None, nick_color=None): """ Note that user can be None even if nickname is not None. It happens when we receive an history message said by someone who is not @@ -92,29 +92,28 @@ class Room(TextBuffer): """ self.log_message(txt, time, nickname) if txt.startswith('/me '): - txt = "* " + nickname + ' ' + txt[4:] + txt = "\x192* \x195" + nickname + ' ' + txt[4:] nickname = None user = self.get_user_by_name(nickname) if nickname is not None else None if user: user.set_last_talked(datetime.now()) if not user and forced_user: user = forced_user - color = theme.COLOR_NORMAL_TEXT if not time and nickname and\ nickname != self.own_nick and\ self.color_state != theme.COLOR_TAB_CURRENT: if self.color_state != theme.COLOR_TAB_HIGHLIGHT: self.set_color_state(theme.COLOR_TAB_NEW_MESSAGE) - if not nickname: - color = theme.COLOR_INFORMATION_TEXT - else: - color = self.do_highlight(txt, time, nickname) - if time: # History messages are colored to be distinguished - color = theme.COLOR_INFORMATION_TEXT - time = time or datetime.now() nick_color = nick_color or user.color if user else None - message = Message(txt=txt, colorized=colorized, nick_color=nick_color, - time=time, nickname=nickname, color=color, user=user) + if not nickname or time: + txt = '\x195%s' % (txt,) + else: # TODO + highlight = self.do_highlight(txt, time, nickname) + if highlight: + nick_color = highlight + time = time or datetime.now() + message = Message(txt=txt, nick_color=nick_color, + time=time, nickname=nickname, user=user) # message = Message(txt, time, nickname, nick_color, color, colorized, user=user) while len(self.messages) > self.messages_nb_limit: self.messages.pop(0) diff --git a/src/tabs.py b/src/tabs.py index f6b833a7..ccafe881 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -727,11 +727,9 @@ class MucTab(ChatTab): if from_nick == room.own_nick: room.joined = True new_user.color = theme.COLOR_OWN_NICK - # self.add_message_to_text_buffer(room, _("Your nickname is %s") % (from_nick)) - room.add_message(_("Your nickname is %s") % (from_nick)) + room.add_message(_("\x195Your nickname is %s") % (from_nick)) if '170' in status_codes: - # self.add_message_to_text_buffer(room, 'Warning: this room is publicly logged') - room.add_message('Warning: this room is publicly logged') + room.add_message('\x195Warning: this room is publicly logged') else: change_nick = '303' in status_codes kick = '307' in status_codes and typ == 'unavailable' @@ -766,9 +764,9 @@ class MucTab(ChatTab): hide_exit_join = config.get('hide_exit_join', -1) if hide_exit_join != 0: if not jid.full: - room.add_message(_('%(spec)s "[%(nick)s]" joined the room') % {'nick':from_nick.replace('"', '\\"'), 'spec':theme.CHAR_JOIN.replace('"', '\\"')}, colorized=True) + room.add_message('\x194%(spec)s \x193%(nick)s\x195 joined the room' % {'nick':from_nick, 'spec':theme.CHAR_JOIN}) else: - room.add_message(_('%(spec)s "[%(nick)s]" "(%(jid)s)" joined the room') % {'spec':theme.CHAR_JOIN.replace('"', '\\"'), 'nick':from_nick.replace('"', '\\"'), 'jid':jid.full}, colorized=True) + room.add_message('\x194%(spec)s \x193%(nick)s (\x196%(jid)s\x195 joined the room' % {'spec':theme.CHAR_JOIN, 'nick':from_nick, 'jid':jid.full}) def on_user_nick_change(self, room, presence, user, from_nick, from_room): new_nick = presence.find('{http://jabber.org/protocol/muc#user}x/{http://jabber.org/protocol/muc#user}item').attrib['nick'] @@ -779,7 +777,7 @@ class MucTab(ChatTab): if isinstance(_tab, PrivateTab) and JID(_tab.get_name()).bare == room.name: _tab.get_room().own_nick = new_nick user.change_nick(new_nick) - room.add_message(_('"[%(old)s]" is now known as "[%(new)s]"') % {'old':from_nick.replace('"', '\\"'), 'new':new_nick.replace('"', '\\"')}, colorized=True) + room.add_message('\x193%(old)s\x195 is now known as \x193%(new)s' % {'old':from_nick, 'new':new_nick}) # rename the private tabs if needed self.core.rename_private_tabs(room.name, from_nick, new_nick) @@ -794,16 +792,16 @@ class MucTab(ChatTab): if from_nick == room.own_nick: # we are banned room.disconnect() if by: - kick_msg = _('%(spec)s [You] have been banned by "[%(by)s]"') % {'spec': theme.CHAR_KICK.replace('"', '\\"'), 'by':by} + kick_msg = _('\x191%(spec)s \x193You\x195 have been banned by \x194%(by)s') % {'spec': theme.CHAR_KICK, 'by':by} else: - kick_msg = _('%(spec)s [You] have been banned.') % {'spec':theme.CHAR_KICK.replace('"', '\\"')} + kick_msg = _('\x191%(spec)s \x193You\x195 have been banned.') % {'spec':theme.CHAR_KICK} else: if by: - kick_msg = _('%(spec)s "[%(nick)s]" has been banned by "[%(by)s]"') % {'spec':theme.CHAR_KICK.replace('"', '\\"'), 'nick':from_nick.replace('"', '\\"'), 'by':by.replace('"', '\\"')} + kick_msg = _('\x191%(spec)s \x193%(nick)s\x195 has been banned by \x194%(by)s') % {'spec':theme.CHAR_KICK, 'nick':from_nick, 'by':by} else: - kick_msg = _('%(spec)s "[%(nick)s]" has been banned') % {'spec':theme.CHAR_KICK, 'nick':from_nick.replace('"', '\\"')} + kick_msg = _('\x191%(spec)s \x193%(nick)s \x195has been banned') % {'spec':theme.CHAR_KICK, 'nick':from_nick.replace('"', '\\"')} if reason is not None and reason.text: - kick_msg += _(' Reason: %(reason)s') % {'reason': reason.text} + kick_msg += _(' Reason: \x196%(reason)s') % {'reason': reason.text} room.add_message(kick_msg, colorized=True) def on_user_kicked(self, room, presence, user, from_nick): @@ -817,20 +815,20 @@ class MucTab(ChatTab): if from_nick == room.own_nick: # we are kicked room.disconnect() if by: - kick_msg = _('%(spec)s [You] have been kicked by "[%(by)s]"') % {'spec': theme.CHAR_KICK.replace('"', '\\"'), 'by':by} + kick_msg = _('\x191%(spec)s \x193You \x195have been kicked by \x194%(by)s') % {'spec': theme.CHAR_KICK, 'by':by} else: - kick_msg = _('%(spec)s [You] have been kicked.') % {'spec':theme.CHAR_KICK.replace('"', '\\"')} + kick_msg = _('\x191%(spec)s \x193You \x195have been kicked.') % {'spec':theme.CHAR_KICK} # try to auto-rejoin if config.get('autorejoin', 'false') == 'true': - muc.join_groupchat(self.xmpp, room.name, room.own_nick) + muc.join_groupchat(self.core.xmpp, room.name, room.own_nick) else: if by: kick_msg = _('%(spec)s "[%(nick)s]" has been kicked by "[%(by)s]"') % {'spec':theme.CHAR_KICK.replace('"', '\\"'), 'nick':from_nick.replace('"', '\\"'), 'by':by.replace('"', '\\"')} else: kick_msg = _('%(spec)s "[%(nick)s]" has been kicked') % {'spec':theme.CHAR_KICK, 'nick':from_nick.replace('"', '\\"')} if reason is not None and reason.text: - kick_msg += _(' Reason: %(reason)s') % {'reason': reason.text} - room.add_message(kick_msg, colorized=True) + kick_msg += _(' Reason: (\x196%reason\x195)s') % {'reason': reason.text} + room.add_message(kick_msg) def on_user_leave_groupchat(self, room, user, jid, status, from_nick, from_room): """ @@ -843,12 +841,12 @@ class MucTab(ChatTab): hide_exit_join = config.get('hide_exit_join', -1) if config.get('hide_exit_join', -1) >= -1 else -1 if hide_exit_join == -1 or user.has_talked_since(hide_exit_join): if not jid.full: - leave_msg = _('%(spec)s "[%(nick)s]" has left the room') % {'nick':from_nick.replace('"', '\\"'), 'spec':theme.CHAR_QUIT.replace('"', '\\"')} + leave_msg = _('\x191%(spec)s \x193%(nick)s \x195has left the room') % {'nick':from_nick, 'spec':theme.CHAR_QUIT} else: - leave_msg = _('%(spec)s "[%(nick)s]" "(%(jid)s)" has left the room') % {'spec':theme.CHAR_QUIT.replace('"', '\\"'), 'nick':from_nick.replace('"', '\\"'), 'jid':jid.full.replace('"', '\\"')} + leave_msg = _('\x191%(spec)s \x193%(nick)s (\x194%(jid)s) \x195has left the room') % {'spec':theme.CHAR_QUIT, 'nick':from_nick, 'jid':jid.full} if status: leave_msg += ' (%s)' % status - room.add_message(leave_msg, colorized=True) + room.add_message(leave_msg) self.core.on_user_left_private_conversation(from_room, from_nick, status) def on_user_change_status(self, room, user, from_nick, from_room, affiliation, role, show, status): @@ -858,7 +856,7 @@ class MucTab(ChatTab): # build the message display_message = False # flag to know if something significant enough # to be displayed has changed - msg = _('"%s" changed: ')% from_nick.replace('"', '\\"') + msg = _('\x193%s \x195changed: ')% from_nick.replace('"', '\\"') if affiliation != user.affiliation: msg += _('affiliation: %s, ') % affiliation display_message = True diff --git a/src/text_buffer.py b/src/text_buffer.py index 65da6975..1c112222 100644 --- a/src/text_buffer.py +++ b/src/text_buffer.py @@ -27,7 +27,7 @@ from datetime import datetime import theme from config import config -Message = collections.namedtuple('Message', 'txt colorized nick_color time nickname color user') +Message = collections.namedtuple('Message', 'txt nick_color time nickname user') class TextBuffer(object): """ @@ -44,11 +44,12 @@ class TextBuffer(object): def add_window(self, win): self.windows.append(win) - def add_message(self, txt, time=None, nickname=None, colorized=False, nick_color=None): - color = theme.COLOR_NORMAL_TEXT if nickname is not None else theme.COLOR_INFORMATION_TEXT + def add_message(self, txt, time=None, nickname=None, nick_color=None): + if not nickname: + txt = '\x195%s' % (txt,) nick_color = nick_color - msg = Message(txt=txt, colorized=colorized, nick_color=nick_color, - time=time or datetime.now(), nickname=nickname, color=color, user=None) + msg = Message(txt=txt, nick_color=nick_color, + time=time or datetime.now(), nickname=nickname, user=None) self.messages.append(msg) while len(self.messages) > self.messages_nb_limit: self.messages.pop(0) diff --git a/src/theme.py b/src/theme.py index 42bc3aed..e5d10f84 100644 --- a/src/theme.py +++ b/src/theme.py @@ -34,7 +34,7 @@ log = logging.getLogger(__name__) # Message text color COLOR_NORMAL_TEXT = 0 COLOR_INFORMATION_TEXT = 5 -COLOR_HIGHLIGHT_TEXT = 1 +COLOR_HIGHLIGHT_NICK = 45 # User list color COLOR_USER_VISITOR = 7 diff --git a/src/wcwidth.py b/src/wcwidth.py index bb0c456e..d44464b4 100644 --- a/src/wcwidth.py +++ b/src/wcwidth.py @@ -186,9 +186,17 @@ def wcwidth(ucs): if ucs == '\u0000': return 0 + # special case for \x19 + if ucs == '\x19': + # -1 is not an error, that’s the real size that + # should be counted, because if a \x19 is found, + # the next char should not be counted + # So '\x19' and 'a' is -1 + 1 = 0 + return -1 + # non-printable chars. if ucs < '\u0020' or (ucs >= '\u007f' and ucs < '\u00a0'): - return -1 + return -2 # binary search in table of non-spacing characters if bisearch(ucs): @@ -225,7 +233,7 @@ def wcswidth(s): width = 0 for c in s: w = wcwidth(c) - if w < 0: + if w < -1: # If s contains a non-printable char, we should return -1. # This includes newlines and tabs! return -1 @@ -241,8 +249,8 @@ def wcsislonger(s, l): width = 0 for c in s: w = wcwidth(c) - if w < 0: - return -1 + if w < -1: + return True else: width += w if width > l: @@ -258,7 +266,7 @@ def widthcut(s, m): width = 0 for c in s: w = wcwidth(c) - if w < 0: + if w < -1: return None else: width += w diff --git a/src/windows.py b/src/windows.py index 4300b240..0bad8c26 100644 --- a/src/windows.py +++ b/src/windows.py @@ -50,7 +50,7 @@ import wcwidth import singleton import collections -Line = collections.namedtuple('Line', 'text colorized text_offset text_color nickname_color time nickname') +Line = collections.namedtuple('Line', 'text text_offset nickname_color time nickname') g_lock = Lock() @@ -98,6 +98,38 @@ class Win(object): except: pass + def addstr_colored(self, string, y=None, x=None): + """ + Write a string on the window, setting the + attributes as they are in the string. + For example: + \x19bhello → hello in bold + \xc1Bonj\xc2our → 'Bonj' in red and 'our' in green + next_attr_char is the \x19 delimiter + attr_char is the char following it, it can be + one of 'u', 'b', 'c[0-9]' + """ + if y is not None and x is not None: + self._win.move(y, x) + next_attr_char = string.find('\x19') + self._win.attrset(0) # reset all attr + while next_attr_char != -1: + attr_char = string[next_attr_char+1].lower() + if next_attr_char != 0: + self.addstr(string[:next_attr_char]) + string = string[next_attr_char+2:] + if attr_char == 'o': + self._win.attrset(0) + elif attr_char == 'u': + self._win.attron(curses.A_UNDERLINE) + elif attr_char == 'b': + self._win.attron(curses.A_BOLD) + elif attr_char.isdigit(): + self._win.attron(common.curses_color_pair(int(attr_char))) + next_attr_char = string.find('\x19') + self.addstr(string) + self._win.attrset(0) + def finish_line(self, color): """ Write colored spaces until the end of line @@ -546,8 +578,8 @@ class TextWin(Win): else: time = None nickname = None - self.built_lines.append(Line(text=cutted_txt, colorized=message.colorized, - text_offset=offset, text_color=message.color, + self.built_lines.append(Line(text=cutted_txt, + text_offset=offset, nickname_color=color, time=time, nickname=nickname)) nb += 1 @@ -576,7 +608,7 @@ class TextWin(Win): else: self.write_time(line.time) self.write_nickname(line.nickname, line.nickname_color) - self.write_text(y, line.text_offset, line.text, line.text_color, line.colorized) + self.write_text(y, line.text_offset, line.text) if y != self.height-1: self.addstr('\n') self._refresh() @@ -584,10 +616,12 @@ class TextWin(Win): def write_line_separator(self): self.addnstr('- '*(self.width//2-1)+'-', self.width, common.curses_color_pair(theme.COLOR_NEW_TEXT_SEPARATOR)) - def write_text(self, y, x, txt, color, colorized): + def write_text(self, y, x, txt): """ write the text of a line. """ + self.addstr_colored(txt, y, x) + return if not colorized: if color: self._win.attron(common.curses_color_pair(color)) |