diff options
-rw-r--r-- | src/core.py | 156 | ||||
-rw-r--r-- | src/room.py | 132 | ||||
-rw-r--r-- | src/tabs.py | 242 | ||||
-rw-r--r-- | src/text_buffer.py | 7 | ||||
-rw-r--r-- | src/windows.py | 10 |
5 files changed, 241 insertions, 306 deletions
diff --git a/src/core.py b/src/core.py index ec821cbd..35cc6951 100644 --- a/src/core.py +++ b/src/core.py @@ -40,7 +40,6 @@ from data_forms import DataFormsTab from config import config, options from logger import logger from user import User -from room import Room from roster import Roster, RosterGroup, roster from contact import Contact, Resource from text_buffer import TextBuffer @@ -335,10 +334,10 @@ class Core(object): nick = message['mucnick'] room_from = message.getMucroom() tab = self.get_tab_by_name(room_from, tabs.MucTab) - if tab and tab.get_room() and tab.get_room().get_user_by_name(nick): - tab.get_room().get_user_by_name(nick).chatstate = state + if tab and tab.get_user_by_name(nick): + tab.get_user_by_name(nick).chatstate = state if tab == self.current_tab(): - tab.user_win.refresh(tab._room.users) + tab.user_win.refresh(tab.users) tab.input.refresh() self.doupdate() @@ -357,7 +356,6 @@ class Core(object): logger.log_roster_change(jid.bare, 'got offline') if not contact: return - log.debug('on_got_offline: %s' % presence) resource = contact.get_resource_by_fulljid(jid.full) if not resource: return @@ -403,7 +401,7 @@ class Core(object): """ tab = self.get_tab_by_name(jid, tabs.ConversationTab) if tab: - self.add_message_to_text_buffer(tab.get_room(), msg) + self.add_message_to_text_buffer(tab._text_buffer, msg) def on_failed_connection(self): """ @@ -417,7 +415,7 @@ class Core(object): """ for tab in self.tabs: if isinstance(tab, tabs.MucTab): - tab.get_room().disconnect() + tab.disconnect() self.information(_("Disconnected from server.")) def on_failed_auth(self, event): @@ -518,7 +516,7 @@ class Core(object): def on_user_changed_status_in_private(self, jid, msg): tab = self.get_tab_by_name(jid) if tab: # display the message in private - tab.get_room().add_message(msg) + tab.add_message(msg) def on_message(self, message): """ @@ -544,16 +542,16 @@ class Core(object): jid = message['from'] nick_from = jid.resource room_from = jid.bare - room = self.get_room_by_name(jid.full) # get the tab with the private conversation - if not room: # It's the first message we receive: create the tab - room = self.open_private_window(room_from, nick_from, False) - if not room: + tab = self.get_tab_by_name(jid.full, tabs.PrivateTab) # get the tab with the private conversation + if not tab: # It's the first message we receive: create the tab + tab = self.open_private_window(room_from, nick_from, False) + if not tab: return body = xhtml.get_body_from_message_stanza(message) if not body: return - room.add_message(body, time=None, nickname=nick_from, - forced_user=self.get_room_by_name(room_from).get_user_by_name(nick_from)) + tab.add_message(body, time=None, nickname=nick_from, + forced_user=self.get_tab_by_name(room_from, tabs.MucTab).get_user_by_name(nick_from)) conversation = self.get_tab_by_name(jid.full, tabs.PrivateTab) if conversation and conversation.remote_wants_chatstates is None: if message['chat_state']: @@ -607,7 +605,7 @@ class Core(object): remote_nick = roster.get_contact_by_jid(jid.bare).get_name() or jid.user else: remote_nick = jid.user - conversation.get_room().add_message(body, nickname=remote_nick, nick_color=get_theme().COLOR_REMOTE_USER) + conversation._text_buffer.add_message(body, nickname=remote_nick, nick_color=get_theme().COLOR_REMOTE_USER) if conversation.remote_wants_chatstates is None: if message['chat_state']: conversation.remote_wants_chatstates = True @@ -767,7 +765,7 @@ class Core(object): for tab in self.tabs: if isinstance(tab, tabs.ConversationTab): if tab.get_name() == jid: - return tab.get_room() + return tab return None def get_tab_by_name(self, name, typ=None): @@ -788,16 +786,6 @@ class Core(object): return tab return None - def get_room_by_name(self, name): - """ - returns the room that has this name - """ - for tab in self.tabs: - if (isinstance(tab, tabs.MucTab) or - isinstance(tab, tabs.PrivateTab)) and tab.get_name() == name: - return tab.get_room() - return None - def init_curses(self, stdscr): """ ncurses initialization @@ -859,8 +847,7 @@ class Core(object): """ Open a new tab.MucTab containing a muc Room, using the specified nick """ - r = Room(room, nick) - new_tab = tabs.MucTab(r) + new_tab = tabs.MucTab(room, nick) self.add_tab(new_tab, focus) self.refresh_window() @@ -947,20 +934,20 @@ class Core(object): def room_error(self, error, room_name): """ - Display the error on the room window + Display the error in the tab """ - room = self.get_room_by_name(room_name) + tab = self.get_tab_by_name(room_name) error_message = self.get_error_message_from_error_stanza(error) - self.add_message_to_text_buffer(room, error_message) + self.add_message_to_text_buffer(tab._text_buffer, error_message) code = error['error']['code'] if code == '401': msg = _('To provide a password in order to join the room, type "/join / password" (replace "password" by the real password)') - self.add_message_to_text_buffer(room, msg) + self.add_message_to_text_buffer(tab._text_buffer, msg) if code == '409': if config.get('alternative_nickname', '') != '': - self.command_join('%s/%s'% (room.name, room.own_nick+config.get('alternative_nickname', ''))) + self.command_join('%s/%s'% (tab.name, tab.own_nick+config.get('alternative_nickname', ''))) else: - self.add_message_to_text_buffer(room, _('You can join the room with an other nick, by typing "/join /other_nick"')) + self.add_message_to_text_buffer(tab._text_buffer, _('You can join the room with an other nick, by typing "/join /other_nick"')) self.refresh_window() def open_conversation_window(self, jid, focus=True): @@ -986,21 +973,18 @@ class Core(object): if isinstance(tab, tabs.PrivateTab): if tab.get_name() == complete_jid: self.command_win('%s' % tab.nb) - return tab.get_room() + return tab # create the new tab - room = self.get_room_by_name(room_name) - if not room: + tab = self.get_tab_by_name(room_name, tabs.MucTab) + if not tab: return None - own_nick = room.own_nick - r = Room(complete_jid, own_nick) # PrivateRoom here - new_tab = tabs.PrivateTab(r) + new_tab = tabs.PrivateTab(complete_jid, tab.own_nick) if not focus: new_tab.state = "private" # insert it in the tabs self.add_tab(new_tab, focus) - # self.window.new_room(r) self.refresh_window() - return r + return new_tab def on_groupchat_subject(self, message): """ @@ -1008,15 +992,15 @@ class Core(object): """ nick_from = message['mucnick'] room_from = message.getMucroom() - room = self.get_room_by_name(room_from) + tab = self.get_tab_by_name(room_from, tabs.MucTab) subject = message['subject'] - if not subject or not room: + if not subject or not tab: return if nick_from: - self.add_message_to_text_buffer(room, _("%(nick)s set the subject to: %(subject)s") % {'nick':nick_from, 'subject':subject}, time=None) + self.add_message_to_text_buffer(tab._text_buffer, _("%(nick)s set the subject to: %(subject)s") % {'nick':nick_from, 'subject':subject}, time=None) else: - self.add_message_to_text_buffer(room, _("The subject is: %(subject)s") % {'subject':subject}, time=None) - room.topic = subject + self.add_message_to_text_buffer(tab._text_buffer, _("The subject is: %(subject)s") % {'subject':subject}, time=None) + tab.topic = subject if self.get_tab_by_name(room_from, tabs.MucTab) is self.current_tab(): self.refresh_window() @@ -1044,34 +1028,33 @@ class Core(object): room_from = message.getMucroom() if message['type'] == 'error': # Check if it's an error return self.room_error(message, room_from) - room = self.get_room_by_name(room_from) tab = self.get_tab_by_name(room_from, tabs.MucTab) - if tab and tab.get_room() and tab.get_room().get_user_by_name(nick_from) and\ - tab.get_room().get_user_by_name(nick_from) in tab.ignores: - return - if not room: + if not tab: self.information(_("message received for a non-existing room: %s") % (room_from)) return + if tab.get_user_by_name(nick_from) and\ + tab.get_user_by_name(nick_from) in tab.ignores: + return body = xhtml.get_body_from_message_stanza(message) if body: date = date if delayed == True else None - self.add_message_to_text_buffer(room, body, date, nick_from, history=True if date else False) + tab.add_message(body, date, nick_from, history=True if date else False) if tab is self.current_tab(): - tab.text_win.refresh(tab._room) - tab.info_header.refresh(tab._room, tab.text_win) + tab.text_win.refresh() + tab.info_header.refresh(tab, tab.text_win) self.refresh_tab_win() if 'message' in config.get('beep_on', 'highlight private').split(): curses.beep() - def add_message_to_text_buffer(self, room, txt, time=None, nickname=None, history=None): + def add_message_to_text_buffer(self, buff, txt, time=None, nickname=None, history=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) """ - if not room: + if not buff: self.information('Trying to add a message in no room: %s' % txt, 'Error') else: - room.add_message(txt, time, nickname, history=history) + buff.add_message(txt, time, nickname, history=history) def command_help(self, arg): """ @@ -1121,13 +1104,13 @@ class Core(object): pres['type'] = show pres.send() current = self.current_tab() - if isinstance(current, tabs.MucTab) and current.get_room().joined and show in ('away', 'xa'): + if isinstance(current, tabs.MucTab) and current.joined and show in ('away', 'xa'): current.send_chat_state('inactive') for tab in self.tabs: - if isinstance(tab, tabs.MucTab) and tab.get_room().joined: - muc.change_show(self.xmpp, tab.get_room().name, tab.get_room().own_nick, show, msg) + if isinstance(tab, tabs.MucTab) and tab.joined: + muc.change_show(self.xmpp, tab.name, tab.own_nick, show, msg) self.set_status(show, msg) - if isinstance(current, tabs.MucTab) and current.get_room().joined and show not in ('away', 'xa'): + if isinstance(current, tabs.MucTab) and current.joined and show not in ('away', 'xa'): current.send_chat_state('active') def completion_status(self, the_input): @@ -1281,11 +1264,11 @@ class Core(object): args = common.shell_split(arg) password = None if len(args) == 0: - t = self.current_tab() - if not isinstance(t, tabs.MucTab) and not isinstance(t, tabs.PrivateTab): + tab = self.current_tab() + if not isinstance(tab, tabs.MucTab) and not isinstance(tab, tabs.PrivateTab): return - room = JID(t.get_name()).bare - nick = t.get_room().own_nick + room = JID(tab.get_name()).bare + nick = tab.own_nick else: info = JID(args[0]) if info.resource == '': @@ -1296,12 +1279,12 @@ class Core(object): else: nick = info.resource if info.bare == '': # happens with /join /nickname, which is OK - t = self.current_tab() - if not isinstance(t, tabs.MucTab): + tab = self.current_tab() + if not isinstance(tab, tabs.MucTab): return - room = t.get_name() + room = tab.get_name() if nick == '': - nick = t.get_room().own_nick + nick = tab.own_nick else: room = info.bare if room.find('@') == -1: # no server is provided, like "/join hello" @@ -1314,25 +1297,25 @@ class Core(object): self.information(_("You didn't specify a server for the room you want to join"), 'Error') return room = room.lower() - r = self.get_room_by_name(room) + tab = self.get_tab_by_name(room, tabs.MucTab) if len(args) == 2: # a password is provided password = args[1] - if r and r.joined: # if we are already in the room - self.focus_tab_named(r.name) + if tab and tab.joined: # if we are already in the room + self.focus_tab_named(tab.name) return if room.startswith('@'): room = room[1:] current_status = self.get_status() - if r and not r.joined: + if tab and not tab.joined: muc.join_groupchat(self.xmpp, room, nick, password, histo_length, current_status.message, current_status.show) - if not r: # if the room window exists, we don't recreate it. + if not tab: self.open_new_room(room, nick) muc.join_groupchat(self.xmpp, room, nick, password, histo_length, current_status.message, current_status.show) else: - r.own_nick = nick - r.users = [] + tab.own_nick = nick + tab.users = [] self.enable_private_tabs(room) def get_bookmark_nickname(self, room_name): @@ -1356,10 +1339,10 @@ class Core(object): if len(args) == 0 and not isinstance(self.current_tab(), tabs.MucTab): return if len(args) == 0: - room = self.current_tab().get_room() - roomname = self.current_tab().get_name() - if room.joined: - nick = room.own_nick + tab = self.current_tab() + roomname = tab.get_name() + if tab.joined: + nick = tab.own_nick else: info = JID(args[0]) if info.resource != '': @@ -1459,9 +1442,9 @@ class Core(object): return for tab in self.tabs: if isinstance(tab, tabs.MucTab) and JID(tab.get_name()).domain == domain: - if tab.get_room().joined: - muc.leave_groupchat(tab.core.xmpp, tab.get_name(), tab.get_room().own_nick, message) - tab.get_room().joined = False + if tab.joined: + muc.leave_groupchat(tab.core.xmpp, tab.get_name(), tab.own_nick, message) + tab.joined = False self.command_join(tab.get_name()) def command_bind(self, arg): @@ -1518,7 +1501,7 @@ class Core(object): self.pop_information_win_up(nb_lines, popup_time) else: if self.information_win_size != 0: - self.information_win.refresh(self.information_buffer) + self.information_win.refresh() self.current_tab().input.refresh() def disconnect(self, msg=None, reconnect=False): @@ -1528,7 +1511,7 @@ class Core(object): """ for tab in self.tabs: if isinstance(tab, tabs.MucTab): - muc.leave_groupchat(self.xmpp, tab.get_room().name, tab.get_room().own_nick, msg) + muc.leave_groupchat(self.xmpp, tab.name, tab.own_nick, msg) roster.empty() self.save_config() # Ugly fix thanks to gmail servers @@ -1577,7 +1560,6 @@ class Core(object): def remove_timed_event(self, event): if event and event in self.timed_events: - log.debug('removing event') self.timed_events.remove(event) def add_timed_event(self, event): diff --git a/src/room.py b/src/room.py deleted file mode 100644 index ad52451c..00000000 --- a/src/room.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright 2010-2011 Florent Le Coz <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 zlib license. See the COPYING file. - -from text_buffer import TextBuffer, Message -from datetime import datetime -from random import randrange -from config import config -from logger import logger - -import common -from theming import get_theme - -import logging -import curses - -log = logging.getLogger(__name__) - -class Room(TextBuffer): - def __init__(self, name, nick, messages_nb_limit=config.get('max_messages_in_memory', 2048)): - TextBuffer.__init__(self, messages_nb_limit) - self.name = name - self.own_nick = nick - self.state = 'normal' # color used in RoomInfo - self.joined = False # false until self presence is receied - self.users = [] # User objects - self.topic = '' - - def disconnect(self): - """ - Set the state of the room as not joined, so - we can know if we can join it, send messages to it, etc - """ - self.users = [] - self.state = 'disconnected' - self.joined = False - - def get_single_line_topic(self): - """ - Return the topic as a single-line string (for the window header) - """ - return self.topic.replace('\n', '|') - - def log_message(self, txt, time, nickname): - """ - Log the messages in the archives, if it needs - to be - """ - if time is None and self.joined: # don't log the history messages - logger.log_message(self.name, nickname, txt) - - def do_highlight(self, txt, time, nickname): - """ - Set the tab color and returns the nick color - """ - color = None - if not time and nickname and nickname != self.own_nick and self.joined: - if self.own_nick.lower() in txt.lower(): - if self.state != 'current': - self.state = 'highlight' - color = get_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(): - if self.state != 'current': - self.state = 'highlight' - color = get_theme().COLOR_HIGHLIGHT_NICK - break - if color: - beep_on = config.get('beep_on', 'highlight private').split() - if 'highlight' in beep_on and 'message' not in beep_on: - curses.beep() - return color - - def get_user_by_name(self, nick): - """ - Gets the user associated with the given nick, or None if not found - """ - for user in self.users: - if user.nick == nick: - return user - return None - - def add_message(self, txt, time=None, nickname=None, forced_user=None, nick_color=None, history=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 - in the room anymore - """ - self.log_message(txt, time, nickname) - special_message = False - if txt.startswith('/me '): - txt = "\x192}* \x195}" + nickname + ' ' + txt[4:] - special_message = True - 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 - if not time and nickname and\ - nickname != self.own_nick and\ - self.state != 'current': - if self.state != 'highlight': - self.state = 'message' - nick_color = nick_color or None - if not nickname or time: - txt = '\x195}%s' % (txt,) - else: # TODO - highlight = self.do_highlight(txt, time, nickname) - if highlight: - nick_color = highlight - if special_message: - txt = '\x195}%s' % (txt,) - nickname = None - time = time or datetime.now() - message = Message(txt='%s\x19o'%(txt.replace('\t', ' '),), nick_color=nick_color, - time=time, str_time=time.strftime("%Y-%m-%d %H:%M:%S")\ - if history else time.strftime("%H:%M:%S"),\ - nickname=nickname, user=user) - while len(self.messages) > self.messages_nb_limit: - 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, history=history) - if window.pos != 0: - window.scroll_up(nb) - return nb diff --git a/src/tabs.py b/src/tabs.py index 9fe685a3..6afb0dd1 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -234,15 +234,6 @@ class Tab(object): """ self.state = 'current' - def add_message(self): - """ - Adds a message in the tab. - If the tab cannot add a message in itself (for example - FormTab, where text is not intented to be appened), it returns False. - If the tab can, it returns True - """ - return False - def on_scroll_down(self): """ Defines what happens when we scrol down @@ -410,15 +401,20 @@ class MucTab(ChatTab): It contains an userlist, an input, a topic, an information and a chat zone """ message_type = 'groupchat' - def __init__(self, room): - ChatTab.__init__(self, room) + def __init__(self, jid, nick): + ChatTab.__init__(self) + self.own_nick = nick + self.name = jid + self.joined = False + self.users = [] + self.topic = '' self.remote_wants_chatstates = True # We send active, composing and paused states to the MUC because # the chatstate may or may not be filtered by the MUC, # that’s not our problem. self.topic_win = windows.Topic() self.text_win = windows.TextWin() - room.add_window(self.text_win) + self._text_buffer.add_window(self.text_win) self.v_separator = windows.VerticalSeparator() self.user_win = windows.UserList() self.info_header = windows.MucInfoWin() @@ -793,7 +789,7 @@ class MucTab(ChatTab): self.user_win.refresh(self.users) self.info_header.refresh(self, self.text_win) self.tab_win.refresh() - self.info_win.refresh(self.core.informations) + self.info_win.refresh() self.input.refresh() def on_input(self, key): @@ -839,9 +835,6 @@ class MucTab(ChatTab): def get_text_window(self): return self.text_win - # def get_room(self): - # return self._room - @property def state(self): return self._room.state @@ -974,7 +967,7 @@ class MucTab(ChatTab): by = by.attrib['jid'] if by is not None else None if from_nick == self.own_nick: # we are banned self.disconnect() - self.core.disable_private_tabs(room.name) + self.core.disable_private_tabs(self.name) self.tab_win.refresh() self.core.doupdate() if by: @@ -989,19 +982,19 @@ class MucTab(ChatTab): kick_msg = _('\x191}%(spec)s \x19%(color)d}%(nick)s\x195} has been banned') % {'spec':get_theme().CHAR_KICK, 'nick':from_nick.replace('"', '\\"'), 'color':color} if reason is not None and reason.text: kick_msg += _('\x195} Reason: \x196}%(reason)s\x195}') % {'reason': reason.text} - room.add_message(kick_msg) + self._text_buffer.add_message(kick_msg) - def on_user_kicked(self, room, presence, user, from_nick): + def on_user_kicked(self, presence, user, from_nick): """ When someone is kicked from a muc """ - room.users.remove(user) + self.users.remove(user) by = presence.find('{%s}x/{%s}item/{%s}actor' % (NS_MUC_USER, NS_MUC_USER, NS_MUC_USER)) reason = presence.find('{%s}x/{%s}item/{%s}reason' % (NS_MUC_USER, NS_MUC_USER, NS_MUC_USER)) by = by.attrib['jid'] if by is not None else None - if from_nick == room.own_nick: # we are kicked - room.disconnect() - self.core.disable_private_tabs(room.name) + if from_nick == self.own_nick: # we are kicked + self.disconnect() + self.core.disable_private_tabs(self.name) self.tab_win.refresh() self.core.doupdate() if by: @@ -1010,7 +1003,7 @@ class MucTab(ChatTab): kick_msg = _('\x191}%(spec)s \x193}You\x195} have been kicked.') % {'spec':get_theme().CHAR_KICK} # try to auto-rejoin if config.get('autorejoin', 'false') == 'true': - muc.join_groupchat(self.core.xmpp, room.name, room.own_nick) + muc.join_groupchat(self.core.xmpp, self.name, self.own_nick) else: color = user.color[0] if config.get('display_user_color_in_join_part', '') == 'true' else 3 if by: @@ -1019,16 +1012,16 @@ class MucTab(ChatTab): kick_msg = _('\x191}%(spec)s \x19%(color)d}%(nick)s\x195} has been kicked') % {'spec':get_theme().CHAR_KICK, 'nick':from_nick.replace('"', '\\"'), 'color':color} if reason is not None and reason.text: kick_msg += _('\x195} Reason: \x196}%(reason)s') % {'reason': reason.text} - room.add_message(kick_msg) + self.add_message(kick_msg) - def on_user_leave_groupchat(self, room, user, jid, status, from_nick, from_room): + def on_user_leave_groupchat(self, user, jid, status, from_nick, from_room): """ When an user leaves a groupchat """ - room.users.remove(user) - if room.own_nick == user.nick: + self.users.remove(user) + if self.own_nick == user.nick: # We are now out of the room. Happens with some buggy (? not sure) servers - room.disconnect() + self.disconnect() self.core.disable_private_tabs(from_room) self.tab_win.refresh() self.core.doupdate() @@ -1042,11 +1035,11 @@ class MucTab(ChatTab): leave_msg = _('\x191}%(spec)s \x19%(color)d}%(nick)s\x195} (\x194}%(jid)s\x195}) has left the room') % {'spec':get_theme().CHAR_QUIT, 'nick':from_nick, 'color':color, 'jid':jid.full} if status: leave_msg += ' (%s)' % status - room.add_message(leave_msg) + self.add_message(leave_msg) self.core.refresh_window() 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): + def on_user_change_status(self, user, from_nick, from_room, affiliation, role, show, status): """ When an user changes her status """ @@ -1054,7 +1047,7 @@ class MucTab(ChatTab): display_message = False # flag to know if something significant enough # to be displayed has changed color = user.color[0] if config.get('display_user_color_in_join_part', '') == 'true' else 3 - if from_nick == room.own_nick: + if from_nick == self.own_nick: msg = _('\x193}You\x195} changed: ') else: msg = _('\x19%(color)d}%(nick)s\x195} changed: ') % {'nick': from_nick.replace('"', '\\"'), 'color': color} @@ -1086,27 +1079,119 @@ class MucTab(ChatTab): hide_status_change = -1 if (hide_status_change == -1 or \ user.has_talked_since(hide_status_change) or\ - user.nick == room.own_nick)\ + user.nick == self.own_nick)\ and\ (affiliation != user.affiliation or\ role != user.role or\ show != user.show or\ status != user.status): # display the message in the room - room.add_message(msg) + self._text_buffer.add_message(msg) self.core.on_user_changed_status_in_private('%s/%s' % (from_room, from_nick), msg) # finally, effectively change the user status user.update(affiliation, show, status, role) + def disconnect(self): + """ + Set the state of the room as not joined, so + we can know if we can join it, send messages to it, etc + """ + self.users = [] + self.state = 'disconnected' + self.joined = False + + def get_single_line_topic(self): + """ + Return the topic as a single-line string (for the window header) + """ + return self.topic.replace('\n', '|') + + def log_message(self, txt, time, nickname): + """ + Log the messages in the archives, if it needs + to be + """ + if time is None and self.joined: # don't log the history messages + logger.log_message(self.name, nickname, txt) + + def do_highlight(self, txt, time, nickname): + """ + Set the tab color and returns the nick color + """ + color = None + if not time and nickname and nickname != self.own_nick and self.joined: + if self.own_nick.lower() in txt.lower(): + if self.state != 'current': + self.state = 'highlight' + color = get_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(): + if self.state != 'current': + self.state = 'highlight' + color = get_theme().COLOR_HIGHLIGHT_NICK + break + if color: + beep_on = config.get('beep_on', 'highlight private').split() + if 'highlight' in beep_on and 'message' not in beep_on: + curses.beep() + return color + + def get_user_by_name(self, nick): + """ + Gets the user associated with the given nick, or None if not found + """ + for user in self.users: + if user.nick == nick: + return user + return None + + def add_message(self, txt, time=None, nickname=None, forced_user=None, nick_color=None, history=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 + in the room anymore + """ + self.log_message(txt, time, nickname) + special_message = False + if txt.startswith('/me '): + txt = "\x192}* \x195}" + nickname + ' ' + txt[4:] + special_message = True + 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 + if not time and nickname and\ + nickname != self.own_nick and\ + self.state != 'current': + if self.state != 'highlight': + self.state = 'message' + nick_color = nick_color or None + if not nickname or time: + txt = '\x195}%s' % (txt,) + else: # TODO + highlight = self.do_highlight(txt, time, nickname) + if highlight: + nick_color = highlight + if special_message: + txt = '\x195}%s' % (txt,) + nickname = None + time = time or datetime.now() + self._text_buffer.add_message(txt, time, nickname, nick_color, history, user) + class PrivateTab(ChatTab): """ The tab containg a private conversation (someone from a MUC) """ message_type = 'chat' - def __init__(self, room): - ChatTab.__init__(self, room) + def __init__(self, name, nick): + ChatTab.__init__(self) + self.own_nick = nick + self.name = name self.text_win = windows.TextWin() - room.add_window(self.text_win) + self._text_buffer.add_window(self.text_win) self.info_header = windows.PrivateInfoWin() self.input = windows.MessageInput() # keys @@ -1117,7 +1202,7 @@ class PrivateTab(ChatTab): self.commands['close'] = (self.command_unquery, _("Usage: /close\nClose: close the tab"), None) self.commands['version'] = (self.command_version, _('Usage: /version\nVersion: get the software version of the current interlocutor (usually its XMPP client and Operating System)'), None) self.resize() - self.parent_muc = self.core.get_tab_by_name(JID(room.name).bare, MucTab) + self.parent_muc = self.core.get_tab_by_name(JID(name).bare, MucTab) self.on = True def completion(self): @@ -1137,10 +1222,9 @@ class PrivateTab(ChatTab): needed = 'inactive' if self.core.status.show in ('xa', 'away') else 'active' msg['chat_state'] = needed msg.send() - self.core.add_message_to_text_buffer(self.get_room(), line, None, self.core.own_nick or self.get_room().own_nick) - logger.log_message(JID(self.get_name()).bare, self.core.own_nick, line) + self.core.add_message_to_text_buffer(self._text_buffer, line, None, self.core.own_nick or self.own_nick) self.cancel_paused_delay() - self.text_win.refresh(self._room) + self.text_win.refresh() self.input.refresh() def command_unquery(self, arg): @@ -1161,7 +1245,7 @@ class PrivateTab(ChatTab): res.get('version') or _('unknown'), res.get('os') or _('on an unknown platform')) self.core.information(version, 'Info') - jid = self.get_room().name + jid = self.name self.core.xmpp.plugin['xep_0092'].get_version(jid, callback=callback) def command_info(self, arg): @@ -1171,7 +1255,7 @@ class PrivateTab(ChatTab): if arg: self.parent_muc.command_info(arg) else: - user = JID(self.get_room().name).resource + user = JID(self.name).resource self.parent_muc.command_info(user) def resize(self): @@ -1179,7 +1263,7 @@ class PrivateTab(ChatTab): return self.need_resize = False self.text_win.resize(self.height-3-self.core.information_win_size, self.width, 0, 0) - self.text_win.rebuild_everything(self._room) + self.text_win.rebuild_everything(self._text_buffer) self.info_header.resize(1, self.width, self.height-3-self.core.information_win_size, 0) self.input.resize(1, self.width, self.height-1, 0) @@ -1187,14 +1271,14 @@ class PrivateTab(ChatTab): if self.need_resize: self.resize() log.debug(' TAB Refresh: %s'%self.__class__.__name__) - self.text_win.refresh(self._room) - self.info_header.refresh(self._room, self.text_win, self.chatstate) - self.info_win.refresh(self.core.informations) + self.text_win.refresh() + self.info_header.refresh(self.name, self.text_win, self.chatstate) + self.info_win.refresh() self.tab_win.refresh() self.input.refresh() def refresh_info_header(self): - self.info_header.refresh(self._room, self.text_win, self.chatstate) + self.info_header.refresh(self.name, self.text_win, self.chatstate) self.input.refresh() @property @@ -1206,7 +1290,7 @@ class PrivateTab(ChatTab): self._room.state = value def get_name(self): - return self._room.name + return self.name def on_input(self, key): if key in self.key_func: @@ -1216,8 +1300,8 @@ class PrivateTab(ChatTab): if not self.on: return False empty_after = self.input.get_text() == '' or (self.input.get_text().startswith('/') and not self.input.get_text().startswith('//')) - tab = self.core.get_tab_by_name(JID(self.get_room().name).bare, MucTab) - if tab and tab.get_room().joined: + tab = self.core.get_tab_by_name(JID(self.name).bare, MucTab) + if tab and tab.joined: self.send_composing_chat_state(empty_after) return False @@ -1225,13 +1309,15 @@ class PrivateTab(ChatTab): self.state = 'normal' self.text_win.remove_line_separator() self.text_win.add_line_separator() - if self.get_room().joined and config.get('send_chat_states', 'true') == 'true' and not self.input.get_text(): + tab = self.core.get_tab_by_name(JID(self.name).bare, MucTab) + if tab.joined and config.get('send_chat_states', 'true') == 'true' and not self.input.get_text(): self.send_chat_state('inactive') def on_gain_focus(self): self.state = 'current' curses.curs_set(1) - if self.get_room().joined and config.get('send_chat_states', 'true') == 'true' and not self.input.get_text(): + tab = self.core.get_tab_by_name(JID(self.name).bare, MucTab) + if tab.joined and config.get('send_chat_states', 'true') == 'true' and not self.input.get_text(): self.send_chat_state('active') def on_scroll_up(self): @@ -1246,9 +1332,6 @@ class PrivateTab(ChatTab): self.text_win.resize(self.height-3-self.core.information_win_size, self.width, 0, 0) self.info_header.resize(1, self.width, self.height-3-self.core.information_win_size, 0) - def get_room(self): - return self._room - def get_text_window(self): return self.text_win @@ -1257,9 +1340,9 @@ class PrivateTab(ChatTab): The user changed her nick in the corresponding muc: update the tab’s name and display a message. """ - self.get_room().add_message(_('"[%(old_nick)s]" is now known as "[%(new_nick)s]"') % {'old_nick':old_nick.replace('"', '\\"'), 'new_nick':new_nick.replace('"', '\\"')}) - new_jid = JID(self.get_room().name).bare+'/'+new_nick - self.get_room().name = new_jid + self.add_message('\x193}%(old)s\x195} is now known as \x193}%(new)s' % {'old':old_nick, 'new':new_nick}) + new_jid = JID(self.name).bare+'/'+new_nick + self.name = new_jid def user_left(self, status_message, from_nick): """ @@ -1267,9 +1350,9 @@ class PrivateTab(ChatTab): """ self.deactivate() if not status_message: - self.get_room().add_message(_('\x191}%(spec)s \x193}%(nick)s\x195} has left the room') % {'nick':from_nick.replace('"', '\\"'), 'spec':get_theme().CHAR_QUIT.replace('"', '\\"')}) + self.add_message(_('\x191}%(spec)s \x193}%(nick)s\x195} has left the room') % {'nick':from_nick.replace('"', '\\"'), 'spec':get_theme().CHAR_QUIT.replace('"', '\\"')}) else: - self.get_room().add_message(_('\x191}%(spec)s \x193}%(nick)s\x195} has left the room (%(status)s)"') % {'nick':from_nick.replace('"', '\\"'), 'spec':get_theme().CHAR_QUIT, 'status': status_message.replace('"', '\\"')}) + self.add_message(_('\x191}%(spec)s \x193}%(nick)s\x195} has left the room (%(status)s)"') % {'nick':from_nick.replace('"', '\\"'), 'spec':get_theme().CHAR_QUIT, 'status': status_message.replace('"', '\\"')}) if self.core.current_tab() is self: self.refresh() self.core.doupdate() @@ -1279,18 +1362,26 @@ class PrivateTab(ChatTab): The user (or at least someone with the same nick) came back in the MUC """ self.activate() - self.get_room().add_message('\x194}%(spec)s \x19%(color)d}%(nick)s\x195} joined the room' % {'nick':nick, 'color':user.color, 'spec':get_theme().CHAR_JOIN}) + tab = self.core.get_tab_by_name(JID(self.name).bare, MucTab) + color = None + if tab: + user = tab.get_user_by_name(nick) + if user: + color = user.color + self.add_message('\x194}%(spec)s \x19%(color)d}%(nick)s\x195} joined the room' % {'nick':nick, 'color': color or 3, 'spec':get_theme().CHAR_JOIN}) if self.core.current_tab() is self: self.refresh() self.core.doupdate() - def activate(self): self.on = True def deactivate(self): self.on = False + def add_message(self, txt, time=None, nickname=None, forced_user=None): + self._text_buffer.add_message(txt, time, nickname, None, None, forced_user) + class RosterInfoTab(Tab): """ A tab, splitted in two, containing the roster and infos @@ -1675,9 +1766,6 @@ class RosterInfoTab(Tab): else: curses.curs_set(1) - def add_message(self): - return False - def move_cursor_down(self): if isinstance(self.input, windows.CommandInput): return @@ -1774,12 +1862,11 @@ class ConversationTab(ChatTab): """ message_type = 'chat' def __init__(self, jid): - txt_buff = text_buffer.TextBuffer() - ChatTab.__init__(self, txt_buff) + ChatTab.__init__(self) self.state = 'normal' self._name = jid # a conversation tab is linked to one specific full jid OR bare jid self.text_win = windows.TextWin() - txt_buff.add_window(self.text_win) + self._text_buffer.add_window(self.text_win) self.upper_bar = windows.ConversationStatusMessageWin() self.info_header = windows.ConversationInfoWin() self.input = windows.MessageInput() @@ -1805,10 +1892,10 @@ class ConversationTab(ChatTab): needed = 'inactive' if self.core.status.show in ('xa', 'away') else 'active' msg['chat_state'] = needed msg.send() - self.core.add_message_to_text_buffer(self.get_room(), line, None, self.core.own_nick) + self.core.add_message_to_text_buffer(self._text_buffer, line, None, self.core.own_nick) logger.log_message(JID(self.get_name()).bare, self.core.own_nick, line) self.cancel_paused_delay() - self.text_win.refresh(self._room) + self.text_win.refresh() self.input.refresh() def command_unquery(self, arg): @@ -1819,7 +1906,7 @@ class ConversationTab(ChatTab): return self.need_resize = False self.text_win.resize(self.height-4-self.core.information_win_size, self.width, 1, 0) - self.text_win.rebuild_everything(self._room) + self.text_win.rebuild_everything(self._text_buffer) self.upper_bar.resize(1, self.width, 0, 0) self.info_header.resize(1, self.width, self.height-3-self.core.information_win_size, 0) self.input.resize(1, self.width, self.height-1, 0) @@ -1828,15 +1915,15 @@ class ConversationTab(ChatTab): if self.need_resize: self.resize() log.debug(' TAB Refresh: %s'%self.__class__.__name__) - self.text_win.refresh(self._room) + self.text_win.refresh() self.upper_bar.refresh(self.get_name(), roster.get_contact_by_jid(self.get_name())) - self.info_header.refresh(self.get_name(), roster.get_contact_by_jid(self.get_name()), self._room, self.text_win, self.chatstate) - self.info_win.refresh(self.core.informations) + self.info_header.refresh(self.get_name(), roster.get_contact_by_jid(self.get_name()), self.text_win, self.chatstate) + self.info_win.refresh() self.tab_win.refresh() self.input.refresh() def refresh_info_header(self): - self.info_header.refresh(self.get_name(), roster.get_contact_by_jid(self.get_name()), self._room, self.text_win, self.chatstate) + self.info_header.refresh(self.get_name(), roster.get_contact_by_jid(self.get_name()), self.text_win, self.chatstate) self.input.refresh() def get_name(self): @@ -1876,9 +1963,6 @@ class ConversationTab(ChatTab): self.text_win.resize(self.height-4-self.core.information_win_size, self.width, 1, 0) self.info_header.resize(1, self.width, self.height-3-self.core.information_win_size, 0) - def get_room(self): - return self._room - def get_text_window(self): return self.text_win diff --git a/src/text_buffer.py b/src/text_buffer.py index f39f147a..eb4b7b79 100644 --- a/src/text_buffer.py +++ b/src/text_buffer.py @@ -34,19 +34,20 @@ class TextBuffer(object): def add_window(self, win): self.windows.append(win) - def add_message(self, txt, time=None, nickname=None, nick_color=None, history=None): + def add_message(self, txt, time=None, nickname=None, nick_color=None, history=None, user=None): time = time or datetime.now() msg = Message(txt='%s\x19o'%(txt.replace('\t', ' '),), nick_color=nick_color, time=time, str_time=time.strftime("%Y-%m-%d %H:%M:%S")\ if history else time.strftime("%H:%M:%S"),\ - nickname=nickname, user=None) + nickname=nickname, user=user) + log.debug('Coucou, le message ajouté : %s' % (msg,)) self.messages.append(msg) while len(self.messages) > self.messages_nb_limit: self.messages.pop(0) ret_val = None for window in self.windows: # make the associated windows # build the lines from the new message - nb = window.build_new_message(msg) + nb = window.build_new_message(msg, history=history) if ret_val is None: ret_val = nb if window.pos != 0: diff --git a/src/windows.py b/src/windows.py index dfe46a14..42c9bfa0 100644 --- a/src/windows.py +++ b/src/windows.py @@ -341,18 +341,18 @@ class PrivateInfoWin(InfoWin): def __init__(self): InfoWin.__init__(self) - def refresh(self, room, window, chatstate): + def refresh(self, name, window, chatstate): log.debug('Refresh: %s'%self.__class__.__name__) with g_lock: self._win.erase() - self.write_room_name(room) + self.write_room_name(name) self.print_scroll_position(window) self.write_chatstate(chatstate) self.finish_line(get_theme().COLOR_INFORMATION_BAR) self._refresh() - def write_room_name(self, room): - jid = JID(room.name) + def write_room_name(self, name): + jid = JID(name) room_name, nick = jid.bare, jid.resource self.addstr(nick, to_curses_attr(get_theme().COLOR_PRIVATE_NAME)) txt = ' from room %s' % room_name @@ -380,7 +380,7 @@ class ConversationInfoWin(InfoWin): def __init__(self): InfoWin.__init__(self) - def refresh(self, jid, contact, text_buffer, window, chatstate): + def refresh(self, jid, contact, window, chatstate): # contact can be None, if we receive a message # from someone not in our roster. In this case, we display # only the maximum information from the message we can get. |