merge missing changeset from default into plugin, so both branches are up to date

This commit is contained in:
Florent Le Coz 2011-09-29 00:25:01 +02:00
commit deea31d9b7
4 changed files with 252 additions and 37 deletions

View file

@ -1,9 +1,9 @@
.\" Copyright 2010 Le Coz Florent .\" Copyright 2010 Le Coz Florent
.\" This man page is distributed under the GPLv3 license. .\" This man page is distributed under the GPLv3 license.
.\" See COPYING file .\" See COPYING file
.TH "Poezio" "1" "August 1, 2010" "Poezio dev team" "" .TH "Poezio" "1" "September 26, 2011" "Poezio dev team" ""
.SH "NAME" .SH "NAME"
Poezio \- a ncurses jabber client Poezio \- a ncurses jabber client written in python3
.SH "SYNOPSIS" .SH "SYNOPSIS"
.B poezio [\-f \fICONFIG_FILE\fR] [\-d \fIDEBUG_FILE\fR] [\-h] .B poezio [\-f \fICONFIG_FILE\fR] [\-d \fIDEBUG_FILE\fR] [\-h]
.SH "DESCRIPTION" .SH "DESCRIPTION"
@ -20,10 +20,164 @@ Log debug from both poezio and SleekXMPP in \fIDEBUG_FILE\fR. Debug contains inc
.TP .TP
\fB\-h\fR \fB\-h\fR
Display an help message Display an help message
.SH "TABS"
A \fItab\fR, in Poezio, is the base structure of the interface. A tab may contains one or more \fIwindows\fR, and can be of different types:
.RS
.TP 6
.I Roster \fRtab
It contains a list of your contacts on the left, as well as an info window on the right.
.TP
.I MUC \fRtab
MUC stands for "Multi-User Chat".
.TP
.I Conversation \fRtab
It is used for one-to-one communication, usually when using a real Jabber account.
.TP
.I Private \fRtab
It is used to privately communicate with someone in a MUC.
.SH "KEY BINDINGS"
While most of the keyboard shortcuts are common to all types of tabs, some of them are tab-specific.
.SS Text edition
These shortcuts work in any kind of tab; most of them are identical to emacs' ones.
.RS
.TP 8
.B Ctrl+A
Move the cursor to the begining of the line.
.TP
.B Ctrl+E
Move the cursor to the end of the line.
.TP
.B Ctrl+W
Delete the word before the cursor.
.TP
.B Ctrl+K
Delete the text from the cursor to the end of the line and save it in the clipboard.
.TP
.B Ctrl+U
Delete the text from the beginning of the line to the cursor and save it in the clipboard.
.TP
.B Ctrl+Y
Insert the text in the clipboard after the cursor.
.TP
.B Ctrl+D
Delete the char after the cursor (same as the Suppr key)
.SS Navigation keybindings
.RS
.TP 8
.B F5, Ctrl+N
Go to the previous tab.
.TP
.B F6, Ctrl+P
Go to the next tab.
.TP
.B Alt+<number>
Go to the specified tab (from 0 to 9)
.TP
.B Alt+J <two-digits-number>
Go to the specified tab (from 00 to 99)
.TP
.B Alt+Z
Go to the last visited tab.
.TP
.B Alt+E
Go to the next important tab (private message, highlight, simple message)
.TP
.B F7
Decrease the information window size.
.TP
.B F8
Increase the information window size.
.TP
.B Alt+R
Go to the roster.
.TP
.B Ctrl+L
Redraw the screen.
.TP
.B Up, Down
Browse the history of the last messages or commands you've entered.
.SS Roster keybindings
.RS
.TP 8
.B o
Hide or show the offline contacts.
.TP
.B s
Search through your contact list.
.TP
.B Ctrl+G
Cancel a search.
.SS MUC-specific keybindings
.RS
.TP 8
.B Alt+V
Move the line separator at the bottom of the text window.
.TP
.B Tab
Complete the nickname that you're typing. If nothing has been entered, insert the nickname of the last user who spoke.
.TP
.B Alt+/
Complete the word that you're typing, based on the list of the recently said words in the conversation.
.SH "COMMANDS"
Most commands support tab completion, both for their names and for their arguments. You can use the \fI/help\fR command to list all available commands, and \fI/help <command>\fR for a complete description of <command>.
The following is a basic description of the most widely used commands; you should refer to \fI/help\fR inside poezio for more documentation. \fI<foo>\fR denotes a obligatory argument, while \fI[bar]\fR is an optional argument (without argument, the \fI/remove\fR command, for example, acts on the currently selected contact)
.SS Roster commands
.RS
.TP 5
.B /add <jid>
Add a JID to your roster.
.TP
.B /remove [jid]
Remove a contact from your roster.
.TP
.B /accept [jid]
Accept a JID that wants to subscribe to your presence.
.TP
.B /deny [jid]
The opposite of \fI/accept\fR.
.SS MUC-specific commands
.RS
.TP 5
.B /recolor
Change the color of the nicknames in the conversation. Useful when a few people are talking and their random color happen to be the same: using this command will let you differentiate them more easily.
.TP
.B /kick <user>
Kick the specified user from the room.
.TP
.B /show <status> [message]
Change your status, and status message, in the current room. You can use “avail”, “busy”, “away” and “xa” as your status, followed by an optional message.
.TP
.B /ignore <user>
Ignore the specified user.
.TP
.B /topic [topic text]
View or change the topic of the room.
.TP
.B /query <user>
Talk privately with the specified participant.
.TP
.B /part
Leave the current room.
.SH "BUGS" .SH "BUGS"
Sure. Sure.
.SH "KNOWN ISSUES"
If you're using a terminal multiplexer such as \fIscreen\fR or \fItmux\fR, it may be setting $TERM to "screen", which breaks 256-color support. Consider setting your $TERM to something like "screen-256color".
.SH "FEEDBACK" .SH "FEEDBACK"
You are encouraged to report bugs or feature requests on http://dev.louiz.org/project/poezio. You are encouraged to report bugs or feature requests on http://dev.louiz.org/project/poezio.
You can also find us on the Jabber chatroom poezio@kikoo.louiz.org You can also find us on the Jabber chatroom poezio@kikoo.louiz.org
.SH "AUTHORS" .SH "AUTHORS"
Written by Florent Le Coz <louiz@louiz.org> Written by Florent Le Coz <louiz@louiz.org>
Later completed by Baptiste Jonglez <baptiste--poezio@jonglez.org>

View file

@ -128,7 +128,6 @@ class Core(object):
'connect': (self.command_reconnect, _('Usage: /connect\nConnect: disconnect from the remote server if you are currently connected and then connect to it again'), None), 'connect': (self.command_reconnect, _('Usage: /connect\nConnect: disconnect from the remote server if you are currently connected and then connect to it again'), None),
'server_cycle': (self.command_server_cycle, _('Usage: /server_cycle [domain] [message]\nServer Cycle: disconnect and reconnects in all the rooms in domain.'), None), 'server_cycle': (self.command_server_cycle, _('Usage: /server_cycle [domain] [message]\nServer Cycle: disconnect and reconnects in all the rooms in domain.'), None),
'bind': (self.command_bind, _('Usage: /bind <key> <equ>\nBind: bind a key to an other key or to a “command”. For example "/bind ^H KEY_UP" makes Control + h do the same same than the Up key.'), None), 'bind': (self.command_bind, _('Usage: /bind <key> <equ>\nBind: bind a key to an other key or to a “command”. For example "/bind ^H KEY_UP" makes Control + h do the same same than the Up key.'), None),
'pubsub': (self.command_pubsub, _('Usage: /pubsub <domain>\nPubsub: Open a pubsub browser on the given domain'), None),
'load': (self.command_load, _('Usage: /load <plugin>\nLoad: Load the specified plugin'), self.plugin_manager.completion_load), 'load': (self.command_load, _('Usage: /load <plugin>\nLoad: Load the specified plugin'), self.plugin_manager.completion_load),
'unload': (self.command_unload, _('Usage: /unload <plugin>\nUnload: Unload the specified plugin'), self.plugin_manager.completion_unload), 'unload': (self.command_unload, _('Usage: /unload <plugin>\nUnload: Unload the specified plugin'), self.plugin_manager.completion_unload),
} }
@ -1308,7 +1307,7 @@ class Core(object):
t = self.current_tab() t = self.current_tab()
if not isinstance(t, tabs.MucTab) and not isinstance(t, tabs.PrivateTab): if not isinstance(t, tabs.MucTab) and not isinstance(t, tabs.PrivateTab):
return return
room = t.get_name() room = JID(t.get_name()).bare
nick = t.get_room().own_nick nick = t.get_room().own_nick
else: else:
info = JID(args[0]) info = JID(args[0])

View file

@ -16,6 +16,7 @@ import curses
from windows import g_lock from windows import g_lock
import windows import windows
from tabs import Tab from tabs import Tab
from theming import to_curses_attr, get_theme
class DataFormsTab(Tab): class DataFormsTab(Tab):
""" """
@ -85,7 +86,7 @@ class FieldInput(object):
""" """
def __init__(self, field): def __init__(self, field):
self._field = field self._field = field
self.color = 14 self.color = (14, -1)
def set_color(self, color): def set_color(self, color):
self.color = color self.color = color
@ -113,6 +114,27 @@ class FieldInput(object):
""" """
return '' return ''
class ColoredLabel(windows.Win):
def __init__(self, text):
self.text = text
self.color = get_theme().COLOR_NORMAL_TEXT
windows.Win.__init__(self)
def resize(self, height, width, y, x):
self._resize(height, width, y, x)
def set_color(self, color):
self.color = color
self.refresh()
def refresh(self):
with g_lock:
self._win.attron(to_curses_attr(self.color))
self.addstr(0, 0, self.text)
self._win.attroff(to_curses_attr(self.color))
self._refresh()
class DummyInput(FieldInput, windows.Win): class DummyInput(FieldInput, windows.Win):
""" """
Used for fields that do not require any input ('fixed') Used for fields that do not require any input ('fixed')
@ -165,7 +187,7 @@ class BooleanWin(FieldInput, windows.Win):
def refresh(self): def refresh(self):
with g_lock: with g_lock:
self._win.attron(curses.color_pair(self.color)) self._win.attron(to_curses_attr(self.color))
self.addnstr(0, 0, ' '*(8), self.width) self.addnstr(0, 0, ' '*(8), self.width)
self.addstr(0, 2, "%s"%self.value) self.addstr(0, 2, "%s"%self.value)
self.addstr(0, 8, '') self.addstr(0, 8, '')
@ -174,7 +196,7 @@ class BooleanWin(FieldInput, windows.Win):
self.addstr(0, 8, '') self.addstr(0, 8, '')
else: else:
self.addstr(0, 0, '') self.addstr(0, 0, '')
self._win.attroff(curses.color_pair(self.color)) self._win.attroff(to_curses_attr(self.color))
self._refresh() self._refresh()
def reply(self): def reply(self):
@ -229,7 +251,7 @@ class TextMultiWin(FieldInput, windows.Win):
def refresh(self): def refresh(self):
if not self.edition_input: if not self.edition_input:
with g_lock: with g_lock:
self._win.attron(curses.color_pair(self.color)) self._win.attron(to_curses_attr(self.color))
self.addnstr(0, 0, ' '*self.width, self.width) self.addnstr(0, 0, ' '*self.width, self.width)
option = self.options[self.val_pos] option = self.options[self.val_pos]
self.addstr(0, self.width//2-len(option)//2, option) self.addstr(0, self.width//2-len(option)//2, option)
@ -237,7 +259,7 @@ class TextMultiWin(FieldInput, windows.Win):
self.addstr(0, 0, '') self.addstr(0, 0, '')
if self.val_pos < len(self.options)-1: if self.val_pos < len(self.options)-1:
self.addstr(0, self.width-1, '') self.addstr(0, self.width-1, '')
self._win.attroff(curses.color_pair(self.color)) self._win.attroff(to_curses_attr(self.color))
self._refresh() self._refresh()
else: else:
self.edition_input.refresh() self.edition_input.refresh()
@ -281,7 +303,7 @@ class ListMultiWin(FieldInput, windows.Win):
def refresh(self): def refresh(self):
with g_lock: with g_lock:
self._win.attron(curses.color_pair(self.color)) self._win.attron(to_curses_attr(self.color))
self.addnstr(0, 0, ' '*self.width, self.width) self.addnstr(0, 0, ' '*self.width, self.width)
if self.val_pos > 0: if self.val_pos > 0:
self.addstr(0, 0, '') self.addstr(0, 0, '')
@ -290,7 +312,7 @@ class ListMultiWin(FieldInput, windows.Win):
option = self.options[self.val_pos] option = self.options[self.val_pos]
self.addstr(0, self.width//2-len(option)//2, option[0]['label']) self.addstr(0, self.width//2-len(option)//2, option[0]['label'])
self.addstr(0, 2, '' if option[1] else '') self.addstr(0, 2, '' if option[1] else '')
self._win.attroff(curses.color_pair(self.color)) self._win.attroff(to_curses_attr(self.color))
self._refresh() self._refresh()
def reply(self): def reply(self):
@ -327,7 +349,7 @@ class ListSingleWin(FieldInput, windows.Win):
def refresh(self): def refresh(self):
with g_lock: with g_lock:
self._win.attron(curses.color_pair(self.color)) self._win.attron(to_curses_attr(self.color))
self.addnstr(0, 0, ' '*self.width, self.width) self.addnstr(0, 0, ' '*self.width, self.width)
if self.val_pos > 0: if self.val_pos > 0:
self.addstr(0, 0, '') self.addstr(0, 0, '')
@ -335,7 +357,7 @@ class ListSingleWin(FieldInput, windows.Win):
self.addstr(0, self.width-1, '') self.addstr(0, self.width-1, '')
option = self.options[self.val_pos]['label'] option = self.options[self.val_pos]['label']
self.addstr(0, self.width//2-len(option)//2, option) self.addstr(0, self.width//2-len(option)//2, option)
self._win.attroff(curses.color_pair(self.color)) self._win.attroff(to_curses_attr(self.color))
self._refresh() self._refresh()
def reply(self): def reply(self):
@ -353,7 +375,7 @@ class TextSingleWin(FieldInput, windows.Input):
self.text = field.getValue() if isinstance(field.getValue(), str)\ self.text = field.getValue() if isinstance(field.getValue(), str)\
else "" else ""
self.pos = len(self.text) self.pos = len(self.text)
self.color = 14 self.color = (14, -1)
def reply(self): def reply(self):
self._field['label'] = '' self._field['label'] = ''
@ -370,15 +392,15 @@ class TextPrivateWin(TextSingleWin):
with g_lock: with g_lock:
self._win.erase() self._win.erase()
if self.color: if self.color:
self._win.attron(curses.color_pair(self.color)) self._win.attron(to_curses_attr(self.color))
self.addstr('*'*len(self.text[self.line_pos:self.line_pos+self.width-1])) self.addstr('*'*len(self.text[self.line_pos:self.line_pos+self.width-1]))
if self.color: if self.color:
(y, x) = self._win.getyx() (y, x) = self._win.getyx()
size = self.width-x size = self.width-x
self.addnstr(' '*size, size, curses.color_pair(self.color)) self.addnstr(' '*size, size, to_curses_attr(self.color))
self.addstr(0, self.pos, '') self.addstr(0, self.pos, '')
if self.color: if self.color:
self._win.attroff(curses.color_pair(self.color)) self._win.attroff(to_curses_attr(self.color))
self._refresh() self._refresh()
def get_help_message(self): def get_help_message(self):
@ -447,8 +469,8 @@ class FormWin(object):
return return
if self.current_input == len(self.inputs) - 1 or self.current_input >= self.height-1: if self.current_input == len(self.inputs) - 1 or self.current_input >= self.height-1:
return return
self.inputs[self.current_input]['label'].set_color(14) self.inputs[self.current_input]['input'].set_color(get_theme().COLOR_NORMAL_TEXT)
self.inputs[self.current_input]['input'].set_color(14) self.inputs[self.current_input]['label'].set_color(get_theme().COLOR_NORMAL_TEXT)
self.current_input += 1 self.current_input += 1
jump = 0 jump = 0
while self.current_input+jump != len(self.inputs) - 1 and self.inputs[self.current_input+jump]['input'].is_dummy(): while self.current_input+jump != len(self.inputs) - 1 and self.inputs[self.current_input+jump]['input'].is_dummy():
@ -456,16 +478,16 @@ class FormWin(object):
if self.inputs[self.current_input+jump]['input'].is_dummy(): if self.inputs[self.current_input+jump]['input'].is_dummy():
return return
self.current_input += jump self.current_input += jump
self.inputs[self.current_input]['label'].set_color(13) self.inputs[self.current_input]['input'].set_color(get_theme().COLOR_SELECTED_ROW)
self.inputs[self.current_input]['input'].set_color(13) self.inputs[self.current_input]['label'].set_color(get_theme().COLOR_SELECTED_ROW)
def go_to_previous_input(self): def go_to_previous_input(self):
if not self.inputs: if not self.inputs:
return return
if self.current_input == 0: if self.current_input == 0:
return return
self.inputs[self.current_input]['label'].set_color(14) self.inputs[self.current_input]['input'].set_color(get_theme().COLOR_NORMAL_TEXT)
self.inputs[self.current_input]['input'].set_color(14) self.inputs[self.current_input]['label'].set_color(get_theme().COLOR_NORMAL_TEXT)
self.current_input -= 1 self.current_input -= 1
jump = 0 jump = 0
while self.current_input-jump > 0 and self.inputs[self.current_input+jump]['input'].is_dummy(): while self.current_input-jump > 0 and self.inputs[self.current_input+jump]['input'].is_dummy():
@ -473,8 +495,8 @@ class FormWin(object):
if self.inputs[self.current_input+jump]['input'].is_dummy(): if self.inputs[self.current_input+jump]['input'].is_dummy():
return return
self.current_input -= jump self.current_input -= jump
self.inputs[self.current_input]['label'].set_color(13) self.inputs[self.current_input]['input'].set_color(get_theme().COLOR_SELECTED_ROW)
self.inputs[self.current_input]['input'].set_color(13) self.inputs[self.current_input]['label'].set_color(get_theme().COLOR_SELECTED_ROW)
def on_input(self, key): def on_input(self, key):
if not self.inputs: if not self.inputs:
@ -502,10 +524,11 @@ class FormWin(object):
break break
inp['label'].refresh() inp['label'].refresh()
inp['input'].refresh() inp['input'].refresh()
inp['label'].refresh()
if self.current_input < self.height-1: if self.current_input < self.height-1:
self.inputs[self.current_input]['input'].set_color(13) self.inputs[self.current_input]['input'].set_color(get_theme().COLOR_SELECTED_ROW)
self.inputs[self.current_input]['input'].refresh() self.inputs[self.current_input]['input'].refresh()
self.inputs[self.current_input]['label'].set_color(13) self.inputs[self.current_input]['label'].set_color(get_theme().COLOR_SELECTED_ROW)
self.inputs[self.current_input]['label'].refresh() self.inputs[self.current_input]['label'].refresh()
def refresh_current_input(self): def refresh_current_input(self):

View file

@ -681,6 +681,7 @@ class MucTab(ChatTab):
""" """
if not self.visible: if not self.visible:
return return
self.need_resize = False
text_width = (self.width//10)*9 text_width = (self.width//10)*9
self.topic_win.resize(1, self.width, 0, 0) self.topic_win.resize(1, self.width, 0, 0)
self.v_separator.resize(self.height-3, 1, 1, 9*(self.width//10)) self.v_separator.resize(self.height-3, 1, 1, 9*(self.width//10))
@ -799,7 +800,8 @@ class MucTab(ChatTab):
room.users.append(new_user) room.users.append(new_user)
if from_nick == room.own_nick: if from_nick == room.own_nick:
room.joined = True room.joined = True
self.send_chat_state('active') if self.core.current_tab() == self and self.core.status.show not in ('xa', 'away'):
self.send_chat_state('active')
new_user.color = get_theme().COLOR_OWN_NICK new_user.color = get_theme().COLOR_OWN_NICK
room.add_message(_("\x195}Your nickname is \x193}%s") % (from_nick)) room.add_message(_("\x195}Your nickname is \x193}%s") % (from_nick))
if '170' in status_codes: if '170' in status_codes:
@ -999,10 +1001,12 @@ class PrivateTab(ChatTab):
# keys # keys
self.key_func['^I'] = self.completion self.key_func['^I'] = self.completion
# commands # commands
#self.commands['info'] = (self.command_info, _('Usage: /info\nInfo: Display some information about the user in the MUC: '), None) self.commands['info'] = (self.command_info, _('Usage: /info\nInfo: Display some information about the user in the MUC: '), None)
self.commands['unquery'] = (self.command_unquery, _("Usage: /unquery\nUnquery: close the tab"), None) self.commands['unquery'] = (self.command_unquery, _("Usage: /unquery\nUnquery: close the tab"), None)
self.commands['part'] = (self.command_unquery, _("Usage: /part\nPart: close the tab"), None) self.commands['part'] = (self.command_unquery, _("Usage: /part\nPart: 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.resize()
self.parent_muc = self.core.get_tab_by_name(JID(room.name).bare, MucTab)
self.on = True self.on = True
def completion(self): def completion(self):
@ -1019,7 +1023,8 @@ class PrivateTab(ChatTab):
msg['body'] = xhtml.clean_text(line) msg['body'] = xhtml.clean_text(line)
msg['xhtml_im'] = xhtml.poezio_colors_to_html(line) msg['xhtml_im'] = xhtml.poezio_colors_to_html(line)
if config.get('send_chat_states', 'true') == 'true' and self.remote_wants_chatstates is not False: if config.get('send_chat_states', 'true') == 'true' and self.remote_wants_chatstates is not False:
msg['chat_state'] = 'active' needed = 'inactive' if self.core.status.show in ('xa', 'away') else 'active'
msg['chat_state'] = needed
msg.send() msg.send()
self.core.add_message_to_text_buffer(self.get_room(), line, None, self.core.own_nick or self.get_room().own_nick) 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) logger.log_message(JID(self.get_name()).bare, self.core.own_nick, line)
@ -1033,9 +1038,35 @@ class PrivateTab(ChatTab):
""" """
self.core.close_tab() self.core.close_tab()
def command_version(self, arg):
"""
/version
"""
def callback(res):
if not res:
return self.core.information('Could not get the software version from %s' % (jid,), 'Warning')
version = '%s is running %s version %s on %s' % (jid,
res.get('name') or _('an unknown software'),
res.get('version') or _('unknown'),
res.get('os') or _('on an unknown platform'))
self.core.information(version, 'Info')
jid = self.get_room().name
self.core.xmpp.plugin['xep_0092'].get_version(jid, callback=callback)
def command_info(self, arg):
"""
/info
"""
if arg:
self.parent_muc.command_info(arg)
else:
user = JID(self.get_room().name).resource
self.parent_muc.command_info(user)
def resize(self): def resize(self):
if self.core.information_win_size >= self.height-3 or not self.visible: if self.core.information_win_size >= self.height-3 or not self.visible:
return return
self.need_resize = False
self.text_win.resize(self.height-3-self.core.information_win_size, self.width, 0, 0) 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._room)
self.info_header.resize(1, self.width, self.height-3-self.core.information_win_size, 0) self.info_header.resize(1, self.width, self.height-3-self.core.information_win_size, 0)
@ -1124,22 +1155,25 @@ class PrivateTab(ChatTab):
""" """
The user left the associated MUC The user left the associated MUC
""" """
self.deactivate()
if not status_message: 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.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('"', '\\"')})
else: 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.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.deactivate() if self.core.current_tab() is self:
self.refresh() self.refresh()
self.core.doupdate() self.core.doupdate()
def user_rejoined(self, nick): def user_rejoined(self, nick):
""" """
The user (or at least someone with the same nick) came back in the MUC The user (or at least someone with the same nick) came back in the MUC
""" """
self.get_room().add_message('\x194}%(spec)s \x193}%(nick)s\x195} joined the room' % {'nick':nick, 'spec':get_theme().CHAR_JOIN})
self.activate() self.activate()
self.refresh() self.get_room().add_message('\x194}%(spec)s \x193}%(nick)s\x195} joined the room' % {'nick':nick, 'spec':get_theme().CHAR_JOIN})
self.core.doupdate() if self.core.current_tab() is self:
self.refresh()
self.core.doupdate()
def activate(self): def activate(self):
self.on = True self.on = True
@ -1185,6 +1219,7 @@ class RosterInfoTab(Tab):
def resize(self): def resize(self):
if not self.visible: if not self.visible:
return return
self.need_resize = False
roster_width = self.width//2 roster_width = self.width//2
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.v_separator.resize(self.height-2, 1, 0, roster_width)
@ -1527,7 +1562,8 @@ class ConversationTab(ChatTab):
msg['body'] = xhtml.clean_text(line) msg['body'] = xhtml.clean_text(line)
msg['xhtml_im'] = xhtml.poezio_colors_to_html(line) msg['xhtml_im'] = xhtml.poezio_colors_to_html(line)
if config.get('send_chat_states', 'true') == 'true' and self.remote_wants_chatstates is not False: if config.get('send_chat_states', 'true') == 'true' and self.remote_wants_chatstates is not False:
msg['chat_state'] = 'active' needed = 'inactive' if self.core.status.show in ('xa', 'away') else 'active'
msg['chat_state'] = needed
msg.send() 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.get_room(), line, None, self.core.own_nick)
logger.log_message(JID(self.get_name()).bare, self.core.own_nick, line) logger.log_message(JID(self.get_name()).bare, self.core.own_nick, line)
@ -1541,6 +1577,7 @@ class ConversationTab(ChatTab):
def resize(self): def resize(self):
if self.core.information_win_size >= self.height-3 or not self.visible: if self.core.information_win_size >= self.height-3 or not self.visible:
return return
self.need_resize = False
self.text_win.resize(self.height-4-self.core.information_win_size, self.width, 1, 0) 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._room)
self.upper_bar.resize(1, self.width, 0, 0) self.upper_bar.resize(1, self.width, 0, 0)
@ -1658,6 +1695,7 @@ class MucListTab(Tab):
def resize(self): def resize(self):
if not self.visible: if not self.visible:
return return
self.need_resize = False
self.upper_message.resize(1, self.width, 0, 0) self.upper_message.resize(1, self.width, 0, 0)
column_size = {'node-part': (self.width-5)//4, column_size = {'node-part': (self.width-5)//4,
'name': (self.width-5)//4*3, 'name': (self.width-5)//4*3,
@ -1792,6 +1830,7 @@ class SimpleTextTab(Tab):
def resize(self): def resize(self):
if not self.visible: if not self.visible:
return return
self.need_resize = False
self.text_win.resize(self.height-2, self.width, 0, 0) self.text_win.resize(self.height-2, self.width, 0, 0)
self.input.resize(1, self.width, self.height-1, 0) self.input.resize(1, self.width, self.height-1, 0)