From a516e78bcf76f07545310332290b6c5443d437a7 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 9 Mar 2011 04:56:53 +0100 Subject: [PATCH] Some optimizations in build_new_message. Also cleaned up. Added an optimized way to do "wcswidth(string) > n": wcsislonger. And should use less memory because the dict replacing Message and Lines object stores ONLY the needed attributes. --- src/message.py | 85 ------------------------------------- src/room.py | 13 +++++- src/tabs.py | 2 +- src/text_buffer.py | 11 +++-- src/wcwidth.py | 16 +++++++ src/windows.py | 102 +++++++++++++++++++-------------------------- 6 files changed, 79 insertions(+), 150 deletions(-) delete mode 100644 src/message.py diff --git a/src/message.py b/src/message.py deleted file mode 100644 index 004111d7..00000000 --- a/src/message.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright 2010-2011 Le Coz Florent -# -# This file is part of Poezio. -# -# Poezio is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, version 3 of the License. -# -# Poezio is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Poezio. If not, see . - -""" -Define the Message class -""" - -from datetime import datetime - -class Message(object): - """ - A message with all the associated data (nickname, time, color, etc) - The color can be a single number OR a list of numbers, for - specials cases like join or quit messages. - """ - def __init__(self, txt, time=None, nickname=None, nick_color=None, color=None, colorized=False, user=None): - """ - time is a datetime object, None means 'now'. - If no nickname is specified, it's an information. - """ - self.txt = txt - self.nickname = nickname - self.time = time - self.nick_color = nick_color - self.color = color - self.colorized = colorized - self.user = user - - def __repr__(self): - return "" % (self.txt, self.nickname, str(self.time), str(self.user), self.colorized) - - def __str__(self): - return self.__repr__() - -class Line(object): - """ - A line, corresponding to ONE row of a TextWin. - A message is composed of ONE line or MORE. - The same particularity for colors in Message class applies - here too. - Example: - - Text area limit text area limit - v v - |[12:12:01] nickone has just joined the room named | - | test@kikoo.louiz.org | - |[12:12:23] nickone> hello good morning everyone, I am| - | seeking for informations about | - | poezio | - |[12:12:35] secondnick> Hello nickone, you can get | - | informations here :\n | - | http://blablablabla | - - To get this result, the three messages should be converted to: - - Line(None, None, Datetime(12, 12, 01), "nickone has just joined the room named", 0, 10) - Line(None, None, None, "test@kikoo.louiz.org", 0, 10) - Line("nickone", 1, Datetime(12, 12, 23), "hello good morning everyone, I am", 0, 20) - Line(None, None, None, "seeking for informations about", 0, 20) - Line(None, None, None, "poezio", 0, 20) - Line("secondnick", 2, Datetime(12, 12, 35), "Hello nickone, you can get", 0, 23) - Line(None, None, None, "informations here:", 0, 23) - Line(None, None, None, "http://blablablabla", 0, 23) - """ - def __init__(self, nickname, nickname_color, time, text, text_color, text_offset, colorized=False): - self.nickname = nickname - self.nickname_color = nickname_color - self.time = time - self.text = text - self.text_color = text_color - self.text_offset = text_offset - self.colorized = colorized diff --git a/src/room.py b/src/room.py index df97c638..99674aa0 100644 --- a/src/room.py +++ b/src/room.py @@ -19,7 +19,6 @@ from datetime import datetime from random import randrange from config import config from logger import logger -from message import Message import common import theme @@ -114,7 +113,17 @@ class Room(TextBuffer): color = theme.COLOR_INFORMATION_TEXT time = time if time is not None else datetime.now() nick_color = nick_color or user.color if user else None - message = Message(txt, time, nickname, nick_color, color, colorized, user=user) + message = {'txt': txt, 'colorized':colorized, + 'time':time} + if nickname: + message['nickname'] = nickname + if nick_color: + message['nick_color'] = nick_color + if color: + message['color'] = color + if user: + message['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) self.messages.append(message) diff --git a/src/tabs.py b/src/tabs.py index ac485d6c..7270208b 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -781,7 +781,7 @@ class MucTab(ChatTab): 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) # rename the private tabs if needed - self.core.rename_private_tab(room.name, from_nick, new_nick) + self.core.rename_private_tabs(room.name, from_nick, new_nick) def on_user_banned(self, room, presence, user, from_nick): """ diff --git a/src/text_buffer.py b/src/text_buffer.py index 3b2ddd1a..06847f28 100644 --- a/src/text_buffer.py +++ b/src/text_buffer.py @@ -21,7 +21,6 @@ Define the TextBuffer class import logging log = logging.getLogger(__name__) -from message import Message from datetime import datetime import theme from config import config @@ -44,8 +43,14 @@ class TextBuffer(object): 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 nick_color = nick_color - time = time or datetime.now() - msg = Message(txt, time, nickname, nick_color, color, colorized) + msg = {'txt': txt, 'colorized':colorized, + 'time':time or datetime.now()} + if nickname: + message['nickname'] = nickname + if nick_color: + message['nick_color'] = nick_color + if color: + message['color'] = color self.messages.append(msg) while len(self.messages) > self.messages_nb_limit: self.messages.pop(0) diff --git a/src/wcwidth.py b/src/wcwidth.py index b72c3ca3..bb0c456e 100644 --- a/src/wcwidth.py +++ b/src/wcwidth.py @@ -233,6 +233,22 @@ def wcswidth(s): width += w return width +def wcsislonger(s, l): + """ + Returns the same result than "wcswidth(s) > l" but + is faster. + """ + width = 0 + for c in s: + w = wcwidth(c) + if w < 0: + return -1 + else: + width += w + if width > l: + return True + return False + def widthcut(s, m): """ Return the first characters of s that can be contained in diff --git a/src/windows.py b/src/windows.py index 7ad382d3..316dd6d5 100644 --- a/src/windows.py +++ b/src/windows.py @@ -38,7 +38,7 @@ from threading import Lock from contact import Contact, Resource from roster import RosterGroup, roster -from message import Line +# from message import Line from tabs import MIN_WIDTH, MIN_HEIGHT from sleekxmpp.xmlstream.stanzabase import JID @@ -495,67 +495,57 @@ class TextWin(Win): Return the number of lines that are built for the given message. """ - if message == None: # line separator + def cut_text(text, width): + """ + returns the text that should be displayed on the line, and the rest + of the text, in a tuple + """ + cutted = wcwidth.widthcut(text, width) or text[:width] + limit = cutted.find('\n') + if limit >= 0: + return (text[limit+1:], text[:limit]) + if not wcwidth.wcsislonger(text, width): + return ('', text) + limit = cutted.rfind(' ') + if limit <= 0: + return (text[len(cutted):], cutted) + else: + return (text[limit+1:], text[:limit]) + + if message is None: # line separator self.built_lines.append(None) return 0 - txt = message.txt + txt = message.get('txt') if not txt: return 0 else: txt = txt.replace('\t', ' ') # length of the time offset = 9+len(theme.CHAR_TIME_LEFT[:1])+len(theme.CHAR_TIME_RIGHT[:1]) - if message.nickname and wcwidth.wcswidth(message.nickname) >= 25: - nick = message.nickname[:25]+'…' + nickname = message.get('nickname') + if nickname and len(nickname) >= 25: + nick = nickname[:25]+'…' else: - nick = message.nickname + nick = nickname if nick: offset += wcwidth.wcswidth(nick) + 2 # + nick + spaces length first = True - this_line_was_broken_by_space = False nb = 0 while txt != '': - cutted_txt = wcwidth.widthcut(txt, self.width-offset) or txt[:self.width-offset] - limit = cutted_txt.find('\n') - if limit < 0: - # break between words if possible - if wcwidth.wcswidth(txt) >= self.width-offset: - cutted_txt = wcwidth.widthcut(txt, self.width-offset) or txt[:self.width-offset] - limit = cutted_txt.rfind(' ') - this_line_was_broken_by_space = True - if limit <= 0: - limit = self.width-offset - this_line_was_broken_by_space = False - else: - limit = self.width-offset-1 - this_line_was_broken_by_space = False - color = message.user.color if message.user else message.nick_color - if not first: - nick = None - time = None - else: # strftime is VERY slow, improve performance - # by calling it only one time here, and - # not at each refresh - time = {'hour': '%s'%(message.time.strftime("%H"),), - 'minute': '%s'%(message.time.strftime("%M"),), - 'second': '%s'%(message.time.strftime("%S"),), - } - l = Line(nick, color, - time, - wcwidth.widthcut(txt, limit) or txt[:limit], message.color, - offset, - message.colorized) + (txt, cutted_txt) = cut_text(txt, self.width-offset) + l = {'colorized': message.get('colorized'), + 'text_offset':offset, + 'text_color':message.get('color'), + 'text': cutted_txt + } + color = message.get('user').color if message.get('user') else message.get('nick_color') + if first and color: + l['nickname_color'] = color + if first: + l['time'] = message.get('time').strftime("%H:%M:%S") + l['nickname'] = nick self.built_lines.append(l) nb += 1 - if this_line_was_broken_by_space: - limit += 1 # jump the space at the start of the line - cutted_txt = wcwidth.widthcut(txt, limit) - if not cutted_txt: - txt = txt[limit:] - else: - txt = txt[len(cutted_txt):] - if txt.startswith('\n'): - txt = txt[1:] first = False while len(self.built_lines) > self.lines_nb_limit: self.built_lines.pop(0) @@ -579,12 +569,12 @@ class TextWin(Win): if line is None: self.write_line_separator() else: - if line.time: - self.write_time(line.time) - if line.nickname: - self.write_nickname(line.nickname, line.nickname_color) - self.write_text(y, line.text_offset, line.text, line.text_color, line.colorized) - if y != self.height-1 or (not line or line.text_offset+wcwidth.wcswidth(line.text) < self.width): + if line.get('time'): + self.write_time(line.get('time')) + if line.get('nickname'): + self.write_nickname(line.get('nickname'), line.get('nickname_color')) + self.write_text(y, line.get('text_offset'), line.get('text'), line.get('text_color'), line.get('colorized')) + if y != self.height-1 or (not line or line.get('text_offset')+wcwidth.wcswidth(line.get('text')) < self.width): self.addstr('\n') self._refresh() @@ -648,13 +638,7 @@ class TextWin(Win): """ Write the date on the yth line of the window """ - self.addstr(theme.CHAR_TIME_LEFT, common.curses_color_pair(theme.COLOR_TIME_LIMITER)) - self.addstr(time['hour'], common.curses_color_pair(theme.COLOR_TIME_NUMBERS)) - self.addstr(':', common.curses_color_pair(theme.COLOR_TIME_SEPARATOR)) - self.addstr(time['minute'], common.curses_color_pair(theme.COLOR_TIME_NUMBERS)) - self.addstr(':', common.curses_color_pair(theme.COLOR_TIME_SEPARATOR)) - self.addstr(time['second'], common.curses_color_pair(theme.COLOR_TIME_NUMBERS)) - self.addstr(theme.CHAR_TIME_RIGHT, common.curses_color_pair(theme.COLOR_TIME_LIMITER)) + self.addstr(time) self.addstr(' ') def resize(self, height, width, y, x, room=None):