HUGE performance improvement on refresh. fixed #1855

parent 24d6894b
...@@ -40,6 +40,7 @@ log = logging.getLogger(__name__) ...@@ -40,6 +40,7 @@ log = logging.getLogger(__name__)
import multiuserchat as muc import multiuserchat as muc
import tabs import tabs
import windows
from connection import connection from connection import connection
from config import config from config import config
...@@ -83,6 +84,8 @@ class Core(object): ...@@ -83,6 +84,8 @@ class Core(object):
self.stdscr = curses.initscr() self.stdscr = curses.initscr()
self.init_curses(self.stdscr) self.init_curses(self.stdscr)
self.xmpp = xmpp self.xmpp = xmpp
self.information_buffer = TextBuffer()
self.information_win_size = 0 # Todo, get this from config
default_tab = tabs.InfoTab(self, "Info") if self.xmpp.anon\ default_tab = tabs.InfoTab(self, "Info") if self.xmpp.anon\
else tabs.RosterInfoTab(self) else tabs.RosterInfoTab(self)
default_tab.on_gain_focus() default_tab.on_gain_focus()
...@@ -90,8 +93,7 @@ class Core(object): ...@@ -90,8 +93,7 @@ class Core(object):
# a unique buffer used to store global informations # a unique buffer used to store global informations
# that are displayed in almost all tabs, in an # that are displayed in almost all tabs, in an
# information window. # information window.
self.information_buffer = TextBuffer()
self.information_win_size = 2 # Todo, get this from config
self.resize_timer = None self.resize_timer = None
self.previous_tab_nb = 0 self.previous_tab_nb = 0
self.own_nick = config.get('own_nick', self.xmpp.boundjid.bare) self.own_nick = config.get('own_nick', self.xmpp.boundjid.bare)
...@@ -548,7 +550,6 @@ class Core(object): ...@@ -548,7 +550,6 @@ class Core(object):
Resize the whole screen Resize the whole screen
""" """
with resize_lock: with resize_lock:
# self.resize_timer = None
for tab in self.tabs: for tab in self.tabs:
tab.resize() tab.resize()
self.refresh_window() self.refresh_window()
...@@ -1225,6 +1226,7 @@ class Core(object): ...@@ -1225,6 +1226,7 @@ class Core(object):
Displays an informational message in the "Info" room window Displays an informational message in the "Info" room window
""" """
self.information_buffer.add_message(msg, nickname=typ) self.information_buffer.add_message(msg, nickname=typ)
# TODO: refresh only the correct window in the current tab
self.refresh_window() self.refresh_window()
def command_quit(self, arg): def command_quit(self, arg):
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Poezio. If not, see <http://www.gnu.org/licenses/>. # along with Poezio. If not, see <http://www.gnu.org/licenses/>.
from text_buffer import TextBuffer from text_buffer import TextBuffer, MESSAGE_NB_LIMIT
from datetime import datetime from datetime import datetime
from random import randrange from random import randrange
from config import config from config import config
...@@ -25,8 +25,6 @@ import common ...@@ -25,8 +25,6 @@ import common
import theme import theme
class Room(TextBuffer): class Room(TextBuffer):
"""
"""
def __init__(self, name, nick): def __init__(self, name, nick):
TextBuffer.__init__(self) TextBuffer.__init__(self)
self.name = name self.name = name
...@@ -118,6 +116,13 @@ class Room(TextBuffer): ...@@ -118,6 +116,13 @@ class Room(TextBuffer):
if time: # History messages are colored to be distinguished if time: # History messages are colored to be distinguished
color = theme.COLOR_INFORMATION_TEXT color = theme.COLOR_INFORMATION_TEXT
time = time if time is not None else datetime.now() time = time if time is not None else datetime.now()
if self.pos: # avoid scrolling of one line when one line is received message = Message(txt, time, nickname, user, color, colorized)
self.pos += 1 while len(self.messages) > MESSAGE_NB_LIMIT:
self.messages.append(Message(txt, time, nickname, user, color, colorized)) self.messages.pop(0)
self.messages.append(message)
for window in self.windows: # make the associated windows
# build the lines from the new message
nb = window.build_new_message(message)
if window.pos != 0:
window.scroll_up(nb)
...@@ -199,6 +199,7 @@ class InfoTab(Tab): ...@@ -199,6 +199,7 @@ class InfoTab(Tab):
self.tab_win.resize(1, self.width, self.height-2, 0, self.core.stdscr) self.tab_win.resize(1, self.width, self.height-2, 0, self.core.stdscr)
self.tab_win.resize(1, self.width, self.height-2, 0, self.core.stdscr) self.tab_win.resize(1, self.width, self.height-2, 0, self.core.stdscr)
self.text_win.resize(self.height-2, self.width, 0, 0, self.core.stdscr) self.text_win.resize(self.height-2, self.width, 0, 0, self.core.stdscr)
self.text_win.rebuild_everything(self._room)
self.input.resize(1, self.width, self.height-1, 0, self.core.stdscr) self.input.resize(1, self.width, self.height-1, 0, self.core.stdscr)
def refresh(self, tabs, informations, _): def refresh(self, tabs, informations, _):
...@@ -308,10 +309,12 @@ class MucTab(ChatTab): ...@@ -308,10 +309,12 @@ class MucTab(ChatTab):
ChatTab.__init__(self, core, room) ChatTab.__init__(self, core, room)
self.topic_win = windows.Topic() self.topic_win = windows.Topic()
self.text_win = windows.TextWin() self.text_win = windows.TextWin()
room.add_window(self.text_win)
self.v_separator = windows.VerticalSeparator() self.v_separator = windows.VerticalSeparator()
self.user_win = windows.UserList() self.user_win = windows.UserList()
self.info_header = windows.MucInfoWin() self.info_header = windows.MucInfoWin()
self.info_win = windows.TextWin() self.info_win = windows.TextWin()
self.core.information_buffer.add_window(self.info_win)
self.tab_win = windows.GlobalInfoBar() self.tab_win = windows.GlobalInfoBar()
self.input = windows.MessageInput() self.input = windows.MessageInput()
self.ignores = [] # set of Users self.ignores = [] # set of Users
...@@ -485,10 +488,11 @@ class MucTab(ChatTab): ...@@ -485,10 +488,11 @@ class MucTab(ChatTab):
text_width = (self.width//10)*9 text_width = (self.width//10)*9
self.topic_win.resize(1, self.width, 0, 0, self.core.stdscr) self.topic_win.resize(1, self.width, 0, 0, self.core.stdscr)
self.text_win.resize(self.height-4-self.core.information_win_size, text_width, 1, 0, self.core.stdscr) self.text_win.resize(self.height-4-self.core.information_win_size, text_width, 1, 0, self.core.stdscr)
self.text_win.rebuild_everything(self._room)
self.v_separator.resize(self.height-3, 1, 1, 9*(self.width//10), self.core.stdscr) self.v_separator.resize(self.height-3, 1, 1, 9*(self.width//10), self.core.stdscr)
self.user_win.resize(self.height-3, self.width-text_width-1, 1, text_width+1, self.core.stdscr) self.user_win.resize(self.height-3, self.width-text_width-1, 1, text_width+1, self.core.stdscr)
self.info_header.resize(1, (self.width//10)*9, self.height-3-self.core.information_win_size, 0, self.core.stdscr) self.info_header.resize(1, (self.width//10)*9, self.height-3-self.core.information_win_size, 0, self.core.stdscr)
self.info_win.resize(self.core.information_win_size, (self.width//10)*9, self.height-2-self.core.information_win_size, 0, self.core.stdscr) self.info_win.resize(self.core.information_win_size, (self.width//10)*9, self.height-2-self.core.information_win_size, 0, self.core.stdscr, self.core.information_buffer)
self.tab_win.resize(1, self.width, self.height-2, 0, self.core.stdscr) self.tab_win.resize(1, self.width, self.height-2, 0, self.core.stdscr)
self.input.resize(1, self.width, self.height-1, 0, self.core.stdscr) self.input.resize(1, self.width, self.height-1, 0, self.core.stdscr)
...@@ -499,9 +503,9 @@ class MucTab(ChatTab): ...@@ -499,9 +503,9 @@ class MucTab(ChatTab):
self.text_win.refresh(self._room) self.text_win.refresh(self._room)
self.v_separator.refresh() self.v_separator.refresh()
self.user_win.refresh(self._room.users) self.user_win.refresh(self._room.users)
self.info_header.refresh(self._room) self.info_header.refresh(self._room, self.text_win)
self.info_win.refresh(informations)
self.tab_win.refresh(tabs, tabs[0]) self.tab_win.refresh(tabs, tabs[0])
self.info_win.refresh(informations)
self.input.refresh() self.input.refresh()
def on_input(self, key): def on_input(self, key):
...@@ -549,10 +553,10 @@ class MucTab(ChatTab): ...@@ -549,10 +553,10 @@ class MucTab(ChatTab):
curses.curs_set(1) curses.curs_set(1)
def on_scroll_up(self): def on_scroll_up(self):
self._room.scroll_up(self.text_win.height-1) self.text_win.scroll_up(self.text_win.height-1)
def on_scroll_down(self): def on_scroll_down(self):
self._room.scroll_down(self.text_win.height-1) self.text_win.scroll_down(self.text_win.height-1)
def on_info_win_size_changed(self): def on_info_win_size_changed(self):
text_width = (self.width//10)*9 text_width = (self.width//10)*9
...@@ -573,8 +577,10 @@ class PrivateTab(ChatTab): ...@@ -573,8 +577,10 @@ class PrivateTab(ChatTab):
def __init__(self, core, room): def __init__(self, core, room):
ChatTab.__init__(self, core, room) ChatTab.__init__(self, core, room)
self.text_win = windows.TextWin() self.text_win = windows.TextWin()
room.add_window(self.text_win)
self.info_header = windows.PrivateInfoWin() self.info_header = windows.PrivateInfoWin()
self.info_win = windows.TextWin() self.info_win = windows.TextWin()
self.core.information_buffer.add_window(self.info_win)
self.tab_win = windows.GlobalInfoBar() self.tab_win = windows.GlobalInfoBar()
self.input = windows.MessageInput() self.input = windows.MessageInput()
# keys # keys
...@@ -601,8 +607,9 @@ class PrivateTab(ChatTab): ...@@ -601,8 +607,9 @@ class PrivateTab(ChatTab):
def resize(self): def resize(self):
Tab.resize(self) Tab.resize(self)
self.text_win.resize(self.height-3-self.core.information_win_size, self.width, 0, 0, self.core.stdscr) self.text_win.resize(self.height-3-self.core.information_win_size, self.width, 0, 0, self.core.stdscr)
self.text_win.rebuild_everything(self._room)
self.info_header.resize(1, self.width, self.height-3-self.core.information_win_size, 0, self.core.stdscr) self.info_header.resize(1, self.width, self.height-3-self.core.information_win_size, 0, self.core.stdscr)
self.info_win.resize(self.core.information_win_size, self.width, self.height-2-self.core.information_win_size, 0, self.core.stdscr) self.info_win.resize(self.core.information_win_size, self.width, self.height-2-self.core.information_win_size, 0, self.core.stdscr, self.core.information_buffer)
self.tab_win.resize(1, self.width, self.height-2, 0, self.core.stdscr) self.tab_win.resize(1, self.width, self.height-2, 0, self.core.stdscr)
self.input.resize(1, self.width, self.height-1, 0, self.core.stdscr) self.input.resize(1, self.width, self.height-1, 0, self.core.stdscr)
...@@ -610,7 +617,7 @@ class PrivateTab(ChatTab): ...@@ -610,7 +617,7 @@ class PrivateTab(ChatTab):
if not self.visible: if not self.visible:
return return
self.text_win.refresh(self._room) self.text_win.refresh(self._room)
self.info_header.refresh(self._room) self.info_header.refresh(self._room, self.text_win)
self.info_win.refresh(informations) self.info_win.refresh(informations)
self.tab_win.refresh(tabs, tabs[0]) self.tab_win.refresh(tabs, tabs[0])
self.input.refresh() self.input.refresh()
...@@ -644,15 +651,15 @@ class PrivateTab(ChatTab): ...@@ -644,15 +651,15 @@ class PrivateTab(ChatTab):
curses.curs_set(1) curses.curs_set(1)
def on_scroll_up(self): def on_scroll_up(self):
self._room.scroll_up(self.text_win.height-1) self.text_win.scroll_up(self.text_win.height-1)
def on_scroll_down(self): def on_scroll_down(self):
self._room.scroll_down(self.text_win.height-1) self.text_win.scroll_down(self.text_win.height-1)
def on_info_win_size_changed(self): def on_info_win_size_changed(self):
self.text_win.resize(self.height-3-self.core.information_win_size, self.width, 0, 0, self.core.stdscr) self.text_win.resize(self.height-3-self.core.information_win_size, self.width, 0, 0, self.core.stdscr)
self.info_header.resize(1, self.width, self.height-3-self.core.information_win_size, 0, self.core.stdscr) self.info_header.resize(1, self.width, self.height-3-self.core.information_win_size, 0, self.core.stdscr)
self.info_win.resize(self.core.information_win_size, self.width, self.height-2-self.core.information_win_size, 0, self.core.stdscr) self.info_win.resize(self.core.information_win_size, self.width, self.height-2-self.core.information_win_size, 0, self.core.stdscr, None)
def get_room(self): def get_room(self):
return self._room return self._room
...@@ -673,6 +680,7 @@ class RosterInfoTab(Tab): ...@@ -673,6 +680,7 @@ class RosterInfoTab(Tab):
self.v_separator = windows.VerticalSeparator() self.v_separator = windows.VerticalSeparator()
self.tab_win = windows.GlobalInfoBar() self.tab_win = windows.GlobalInfoBar()
self.info_win = windows.TextWin() self.info_win = windows.TextWin()
self.core.information_buffer.add_window(self.info_win)
self.roster_win = windows.RosterWin() self.roster_win = windows.RosterWin()
self.contact_info_win = windows.ContactInfoWin() self.contact_info_win = windows.ContactInfoWin()
self.default_help_message = windows.HelpText("Enter commands with “/”. “o”: toggle offline show") self.default_help_message = windows.HelpText("Enter commands with “/”. “o”: toggle offline show")
...@@ -696,7 +704,7 @@ class RosterInfoTab(Tab): ...@@ -696,7 +704,7 @@ class RosterInfoTab(Tab):
info_width = self.width-roster_width-1 info_width = self.width-roster_width-1
self.v_separator.resize(self.height-2, 1, 0, roster_width, self.core.stdscr) self.v_separator.resize(self.height-2, 1, 0, roster_width, self.core.stdscr)
self.tab_win.resize(1, self.width, self.height-2, 0, self.core.stdscr) self.tab_win.resize(1, self.width, self.height-2, 0, self.core.stdscr)
self.info_win.resize(self.height-2, info_width, 0, roster_width+1, self.core.stdscr) self.info_win.resize(self.height-2, info_width, 0, roster_width+1, self.core.stdscr, self.core.information_buffer)
self.roster_win.resize(self.height-2-3, roster_width, 0, 0, self.core.stdscr) self.roster_win.resize(self.height-2-3, roster_width, 0, 0, self.core.stdscr)
self.contact_info_win.resize(3, roster_width, self.height-2-3, 0, self.core.stdscr) self.contact_info_win.resize(3, roster_width, self.height-2-3, 0, self.core.stdscr)
self.input.resize(1, self.width, self.height-1, 0, self.core.stdscr) self.input.resize(1, self.width, self.height-1, 0, self.core.stdscr)
...@@ -706,13 +714,14 @@ class RosterInfoTab(Tab): ...@@ -706,13 +714,14 @@ class RosterInfoTab(Tab):
if isinstance(self.input, windows.CommandInput) and\ if isinstance(self.input, windows.CommandInput) and\
not self.input.help_message: not self.input.help_message:
self.complete_commands(self.input) self.complete_commands(self.input)
def refresh(self, tabs, informations, roster): def refresh(self, tabs, informations, roster):
if not self.visible: if not self.visible:
return return
self.v_separator.refresh() self.v_separator.refresh()
self.roster_win.refresh(roster) self.roster_win.refresh(roster)
self.contact_info_win.refresh(self.roster_win.get_selected_row()) self.contact_info_win.refresh(self.roster_win.get_selected_row())
# self.core.global_information_win.refresh(informations)
self.info_win.refresh(informations) self.info_win.refresh(informations)
self.tab_win.refresh(tabs, tabs[0]) self.tab_win.refresh(tabs, tabs[0])
self.input.refresh() self.input.refresh()
...@@ -840,9 +849,11 @@ class ConversationTab(ChatTab): ...@@ -840,9 +849,11 @@ class ConversationTab(ChatTab):
self.color_state = theme.COLOR_TAB_NORMAL self.color_state = theme.COLOR_TAB_NORMAL
self._name = jid # a conversation tab is linked to one specific full jid OR bare jid self._name = jid # a conversation tab is linked to one specific full jid OR bare jid
self.text_win = windows.TextWin() self.text_win = windows.TextWin()
text_buffer.add_window(self.text_win)
self.upper_bar = windows.ConversationStatusMessageWin() self.upper_bar = windows.ConversationStatusMessageWin()
self.info_header = windows.ConversationInfoWin() self.info_header = windows.ConversationInfoWin()
self.info_win = windows.TextWin() self.info_win = windows.TextWin()
self.core.information_buffer.add_window(self.info_win)
self.tab_win = windows.GlobalInfoBar() self.tab_win = windows.GlobalInfoBar()
self.input = windows.MessageInput() self.input = windows.MessageInput()
# keys # keys
...@@ -869,9 +880,10 @@ class ConversationTab(ChatTab): ...@@ -869,9 +880,10 @@ class ConversationTab(ChatTab):
def resize(self): def resize(self):
Tab.resize(self) Tab.resize(self)
self.text_win.resize(self.height-3-self.core.information_win_size, self.width, 1, 0, self.core.stdscr) self.text_win.resize(self.height-3-self.core.information_win_size, self.width, 1, 0, self.core.stdscr)
self.text_win.rebuild_everything(self._room)
self.upper_bar.resize(1, self.width, 0, 0, self.core.stdscr) self.upper_bar.resize(1, self.width, 0, 0, self.core.stdscr)
self.info_header.resize(1, self.width, self.height-3-self.core.information_win_size, 0, self.core.stdscr) self.info_header.resize(1, self.width, self.height-3-self.core.information_win_size, 0, self.core.stdscr)
self.info_win.resize(self.core.information_win_size, self.width, self.height-2-self.core.information_win_size, 0, self.core.stdscr) self.info_win.resize(self.core.information_win_size, self.width, self.height-2-self.core.information_win_size, 0, self.core.stdscr, self.core.information_buffer)
self.tab_win.resize(1, self.width, self.height-2, 0, self.core.stdscr) self.tab_win.resize(1, self.width, self.height-2, 0, self.core.stdscr)
self.input.resize(1, self.width, self.height-1, 0, self.core.stdscr) self.input.resize(1, self.width, self.height-1, 0, self.core.stdscr)
...@@ -914,10 +926,10 @@ class ConversationTab(ChatTab): ...@@ -914,10 +926,10 @@ class ConversationTab(ChatTab):
curses.curs_set(1) curses.curs_set(1)
def on_scroll_up(self): def on_scroll_up(self):
self._room.scroll_up(self.text_win.height-1) self.text_win.scroll_up(self.text_win.height-1)
def on_scroll_down(self): def on_scroll_down(self):
self._room.scroll_down(self.text_win.height-1) self.text_win.scroll_down(self.text_win.height-1)
def on_info_win_size_changed(self): def on_info_win_size_changed(self):
self.text_win.resize(self.height-3-self.core.information_win_size, self.width, 0, 0, self.core.stdscr) self.text_win.resize(self.height-3-self.core.information_win_size, self.width, 0, 0, self.core.stdscr)
......
...@@ -18,10 +18,15 @@ ...@@ -18,10 +18,15 @@
Define the TextBuffer class Define the TextBuffer class
""" """
import logging
log = logging.getLogger(__name__)
from message import Message from message import Message
from datetime import datetime from datetime import datetime
import theme import theme
MESSAGE_NB_LIMIT = 16384
class TextBuffer(object): class TextBuffer(object):
""" """
This class just keep trace of messages, in a list with various This class just keep trace of messages, in a list with various
...@@ -29,15 +34,28 @@ class TextBuffer(object): ...@@ -29,15 +34,28 @@ class TextBuffer(object):
""" """
def __init__(self): def __init__(self):
self.messages = [] # Message objects self.messages = [] # Message objects
self.pos = 0 self.windows = [] # we keep track of one or more windows
# so we can pass the new messages to them, as they are added, so
# they (the windows) can built the lines from the new message
def add_window(self, win):
self.windows.append(win)
def add_message(self, txt, time=None, nickname=None, colorized=False): def add_message(self, txt, time=None, nickname=None, colorized=False):
color = theme.COLOR_NORMAL_TEXT color = theme.COLOR_NORMAL_TEXT
user = None user = None
time = time or datetime.now() time = time or datetime.now()
if self.pos: # avoid scrolling of one line when one line is received # if self.pos: # avoid scrolling of one line when one line is received
self.pos += 1 # self.pos += 1
self.messages.append(Message(txt, time, nickname, user, color, colorized)) msg = Message(txt, time, nickname, user, color, colorized)
self.messages.append(msg)
while len(self.messages) > MESSAGE_NB_LIMIT:
self.messages.pop(0)
for window in self.windows: # make the associated windows
# build the lines from the new message
nb = window.build_new_message(msg)
if window.pos != 0:
window.scroll_up(nb)
def remove_line_separator(self): def remove_line_separator(self):
""" """
...@@ -52,15 +70,3 @@ class TextBuffer(object): ...@@ -52,15 +70,3 @@ class TextBuffer(object):
""" """
if None not in self.messages: if None not in self.messages:
self.messages.append(None) self.messages.append(None)
def scroll_up(self, dist=14):
# The pos can grow a lot over the top of the number of
# available lines, it will be fixed on the next refresh of the
# screen anyway
self.pos += dist
def scroll_down(self, dist=14):
self.pos -= dist
if self.pos <= 0:
self.pos = 0
...@@ -49,6 +49,8 @@ import theme ...@@ -49,6 +49,8 @@ import theme
g_lock = Lock() g_lock = Lock()
LINES_NB_LIMIT = 16384
class Win(object): class Win(object):
def __init__(self): def __init__(self):
pass pass
...@@ -186,14 +188,14 @@ class InfoWin(Win): ...@@ -186,14 +188,14 @@ class InfoWin(Win):
def __init__(self): def __init__(self):
Win.__init__(self) Win.__init__(self)
def print_scroll_position(self, text_buffer): def print_scroll_position(self, window):
""" """
Print, link in Weechat, a -PLUS(n)- where n Print, link in Weechat, a -PLUS(n)- where n
is the number of available lines to scroll is the number of available lines to scroll
down down
""" """
if text_buffer.pos > 0: if window.pos > 0:
plus = ' -PLUS(%s)-' % text_buffer.pos plus = ' -PLUS(%s)-' % window.pos
self.addstr(plus, curses.color_pair(theme.COLOR_SCROLLABLE_NUMBER) | curses.A_BOLD) self.addstr(plus, curses.color_pair(theme.COLOR_SCROLLABLE_NUMBER) | curses.A_BOLD)
class PrivateInfoWin(InfoWin): class PrivateInfoWin(InfoWin):
...@@ -341,14 +343,15 @@ class MucInfoWin(InfoWin): ...@@ -341,14 +343,15 @@ class MucInfoWin(InfoWin):
def resize(self, height, width, y, x, stdscr): def resize(self, height, width, y, x, stdscr):
self._resize(height, width, y, x, stdscr) self._resize(height, width, y, x, stdscr)
def refresh(self, room): def refresh(self, room, window=None):
with g_lock: with g_lock:
self._win.erase() self._win.erase()
self.write_room_name(room) self.write_room_name(room)
self.write_own_nick(room) self.write_own_nick(room)
self.write_disconnected(room) self.write_disconnected(room)
self.write_role(room) self.write_role(room)
self.print_scroll_position(room) if window:
self.print_scroll_position(window)
self.finish_line(theme.COLOR_INFORMATION_BAR) self.finish_line(theme.COLOR_INFORMATION_BAR)
self._refresh() self._refresh()
...@@ -397,64 +400,81 @@ class MucInfoWin(InfoWin): ...@@ -397,64 +400,81 @@ class MucInfoWin(InfoWin):
class TextWin(Win): class TextWin(Win):
def __init__(self): def __init__(self):
Win.__init__(self) Win.__init__(self)
self.pos = 0
self.built_lines = [] # Each new message is built and kept here.
# on resize, we rebuild all the messages
def scroll_up(self, dist=14):
# The pos can grow a lot over the top of the number of
# available lines, it will be fixed on the next refresh of the
# screen anyway
self.pos += dist
def scroll_down(self, dist=14):
self.pos -= dist
if self.pos <= 0:
self.pos = 0
def build_lines_from_messages(self, messages): def build_new_message(self, message):
""" """
From all the existing messages in the window, create the that will Take one message, build it and add it to the list
be displayed on the screen Return the number of lines that are built for the given
message.
""" """
lines = [] if message == None: # line separator
for message in messages: self.built_lines.append(None)
if message == None: # line separator return 0
lines.append(None) txt = message.txt
continue if not txt:
txt = message.txt return 0
if not txt:
continue
# length of the time # length of the time
offset = 9+len(theme.CHAR_TIME_LEFT[:1])+len(theme.CHAR_TIME_RIGHT[:1]) offset = 9+len(theme.CHAR_TIME_LEFT[:1])+len(theme.CHAR_TIME_RIGHT[:1])
if message.nickname and len(message.nickname) >= 30: if message.nickname and len(message.nickname) >= 30:
nick = message.nickname[:30]+'…' nick = message.nickname[:30]+'…'
else:
nick = message.nickname
if nick:
offset += len(nick) + 2 # + nick + spaces length
first = True
this_line_was_broken_by_space = False
nb = 0
while txt != '':
if txt[:self.width-offset].find('\n') != -1:
limit = txt[:self.width-offset].find('\n')
else: else:
nick = message.nickname # break between words if possible
if nick: if len(txt) >= self.width-offset:
offset += len(nick) + 2 # + nick + spaces length limit = txt[:self.width-offset].rfind(' ')
first = True this_line_was_broken_by_space = True
this_line_was_broken_by_space = False if limit <= 0:
while txt != '': limit = self.width-offset
if txt[:self.width-offset].find('\n') != -1:
limit = txt[:self.width-offset].find('\n')
else:
# break between words if possible
if len(txt) >= self.width-offset:
limit = txt[:self.width-offset].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 this_line_was_broken_by_space = False
color = message.user.color if message.user else None
if not first:
nick = None
time = None
else:
time = message.time
l = Line(nick, color,
time,
txt[:limit], message.color,
offset,
message.colorized)
lines.append(l)
if this_line_was_broken_by_space:
txt = txt[limit+1:] # jump the space at the start of the line
else: else:
txt = txt[limit:] limit = self.width-offset-1
if txt.startswith('\n'): this_line_was_broken_by_space = False
txt = txt[1:] color = message.user.color if message.user else None
first = False if not first:
return lines nick = None
time = None
else:
time = message.time
l = Line(nick, color,
time,
txt[:limit], message.color,
offset,
message.colorized)
self.built_lines.append(l)
nb += 1
if this_line_was_broken_by_space:
txt = txt[limit+1:] # jump the space at the start of the line
else:
txt = txt[limit:]
if txt.startswith('\n'):
txt = txt[1:]
first = False
while len(self.built_lines) > LINES_NB_LIMIT:
self.built_lines.pop(0)
return nb
def refresh(self, room): def refresh(self, room):
""" """
...@@ -465,13 +485,14 @@ class TextWin(Win): ...@@ -465,13 +485,14 @@ class TextWin(Win):
return return
with g_lock: with g_lock:
self._win.erase() self._win.erase()
lines = self.build_lines_from_messages(room.messages) # lines = self.build_lines_from_messages(room.messages)
if room.pos + self.height > len(lines): lines = self.built_lines
room.pos = len(lines) - self.height if self.pos + self.height > len(lines):
if room.pos < 0: self.pos = len(lines) - self.height
room.pos = 0 if self.pos < 0:
if room.pos != 0: self.pos = 0
lines = lines[-self.height-room.pos:-room.pos] if self.pos != 0:
lines = lines[-self.height-self.pos:-self.pos]
else: else:
lines = lines[-self.height:] lines = lines[-self.height:]