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.

This commit is contained in:
Florent Le Coz 2011-03-09 04:56:53 +01:00
parent 1a2252b3e5
commit a516e78bcf
6 changed files with 79 additions and 150 deletions

View file

@ -1,85 +0,0 @@
# Copyright 2010-2011 Le Coz Florent <louiz@louiz.org>
#
# 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 <http://www.gnu.org/licenses/>.
"""
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 "<Message txt=%s, nickname=%s, time=%s, user=%s, colorized=%s>" % (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

View file

@ -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)

View file

@ -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):
"""

View file

@ -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)

View file

@ -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

View file

@ -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):