poezio/src/core.py

1359 lines
53 KiB
Python
Raw Normal View History

2011-01-01 13:27:07 +00:00
# Copyright 2010-2011 Le Coz Florent <louiz@louiz.org>
2010-01-10 20:14:17 +00:00
#
# This file is part of Poezio.
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# Poezio is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Poezio. If not, see <http://www.gnu.org/licenses/>.
2011-03-07 19:11:47 +00:00
from gettext import gettext as _
2010-01-10 20:14:17 +00:00
2010-06-13 15:40:03 +00:00
from time import sleep
import os
import re
import sys
2010-01-31 05:33:54 +00:00
import curses
import threading
2010-12-26 20:51:14 +00:00
import traceback
from datetime import datetime
import common
import theme
import logging
import singleton
from sleekxmpp.xmlstream.stanzabase import JID
log = logging.getLogger(__name__)
2010-08-31 23:11:02 +00:00
import multiuserchat as muc
import tabs
import windows
import connection
from data_forms import DataFormsTab
from config import config
2010-11-15 11:59:09 +00:00
from logger import logger
2010-03-18 19:43:44 +00:00
from user import User
from room import Room
from roster import Roster, RosterGroup, roster
from contact import Contact, Resource
from text_buffer import TextBuffer
from keyboard import read_char
2010-06-13 13:51:02 +00:00
2010-08-05 22:24:23 +00:00
# http://xmpp.org/extensions/xep-0045.html#errorstatus
ERROR_AND_STATUS_CODES = {
'401': _('A password is required'),
'403': _('Permission denied'),
'404': _('The room does\'nt exist'),
'405': _('Your are not allowed to create a new room'),
'406': _('A reserved nick must be used'),
'407': _('You are not in the member list'),
'409': _('This nickname is already in use or has been reserved'),
'503': _('The maximum number of users has been reached'),
2010-08-05 22:24:23 +00:00
}
possible_show = {'available':None,
'chat':'chat',
'away':'away',
'afk':'away',
'dnd':'dnd',
'busy':'dnd',
'xa':'xa'
}
resize_lock = threading.Lock()
class Core(object):
2010-01-10 20:14:17 +00:00
"""
2010-05-18 13:29:02 +00:00
User interface using ncurses
2010-01-10 20:14:17 +00:00
"""
def __init__(self):
2010-12-26 20:51:14 +00:00
# All uncaught exception are given to this callback, instead
# of being displayed on the screen and exiting the program.
sys.excepthook = self.on_exception
2010-10-20 23:51:34 +00:00
self.running = True
self.xmpp = singleton.Singleton(connection.Connection)
2010-12-26 20:51:14 +00:00
# a unique buffer used to store global informations
# that are displayed in almost all tabs, in an
# information window.
self.information_buffer = TextBuffer()
self.information_win_size = config.get('info_win_height', 2, 'var')
self.information_win = windows.TextWin(20)
self.information_buffer.add_window(self.information_win)
self.tabs = []
self.previous_tab_nb = 0
self.own_nick = config.get('own_nick', '') or self.xmpp.boundjid.user
# global commands, available from all tabs
# a command is tuple of the form:
# (the function executing the command. Takes a string as argument,
# a string representing the help message,
# a completion function, taking a Input as argument. Can be None)
# The completion function should return True if a completion was
# made ; False otherwise
2010-01-13 01:04:30 +00:00
self.commands = {
2011-02-10 15:13:28 +00:00
'help': (self.command_help, '\_o< KOIN KOIN KOIN', self.completion_help),
'join': (self.command_join, _("Usage: /join [room_name][@server][/nick] [password]\nJoin: Join the specified room. You can specify a nickname after a slash (/). If no nickname is specified, you will use the default_nick in the configuration file. You can omit the room name: you will then join the room you\'re looking at (useful if you were kicked). You can also provide a room_name without specifying a server, the server of the room you're currently in will be used. You can also provide a password to join the room.\nExamples:\n/join room@server.tld\n/join room@server.tld/John\n/join room2\n/join /me_again\n/join\n/join room@server.tld/my_nick password\n/join / password"), self.completion_join),
'exit': (self.command_quit, _("Usage: /exit\nExit: Just disconnect from the server and exit poezio."), None),
'next': (self.rotate_rooms_right, _("Usage: /next\nNext: Go to the next room."), None),
'prev': (self.rotate_rooms_left, _("Usage: /prev\nPrev: Go to the previous room."), None),
'win': (self.command_win, _("Usage: /win <number>\nWin: Go to the specified room."), self.completion_win),
'w': (self.command_win, _("Usage: /w <number>\nW: Go to the specified room."), self.completion_win),
'show': (self.command_status, _('Usage: /show <availability> [status message]\nShow: Sets your availability and (optionaly) your status message. The <availability> argument is one of \"available, chat, away, afk, dnd, busy, xa\" and the optional [status] argument will be your status message.'), self.completion_status),
'status': (self.command_status, _('Usage: /status <availability> [status message]\nStatus: Sets your availability and (optionaly) your status message. The <availability> argument is one of \"available, chat, away, afk, dnd, busy, xa\" and the optional [status] argument will be your status message.'), self.completion_status),
'away': (self.command_away, _("Usage: /away [message]\nAway: Sets your availability to away and (optionaly) your status message. This is equivalent to '/status away [message]'"), None),
'busy': (self.command_busy, _("Usage: /busy [message]\nBusy: Sets your availability to busy and (optionaly) your status message. This is equivalent to '/status busy [message]'"), None),
'available': (self.command_avail, _("Usage: /available [message]\nAvailable: Sets your availability to available and (optionaly) your status message. This is equivalent to '/status available [message]'"), None),
'bookmark': (self.command_bookmark, _("Usage: /bookmark [roomname][/nick]\nBookmark: Bookmark the specified room (you will then auto-join it on each poezio start). This commands uses the same syntaxe as /join. Type /help join for syntaxe examples. Note that when typing \"/bookmark\" on its own, the room will be bookmarked with the nickname you\'re currently using in this room (instead of default_nick)"), None),
'set': (self.command_set, _("Usage: /set <option> [value]\nSet: Sets the value to the option in your configuration file. You can, for example, change your default nickname by doing `/set default_nick toto` or your resource with `/set resource blabla`. You can also set an empty value (nothing) by providing no [value] after <option>."), None),
'theme': (self.command_theme, _('Usage: /theme\nTheme: Reload the theme defined in the config file.'), None),
'list': (self.command_list, _('Usage: /list\nList: get the list of public chatrooms on the specified server'), self.completion_list),
'message': (self.command_message, _('Usage: /message <jid> [optional message]\nMessage: Open a conversation with the specified JID (even if it is not in our roster), and send a message to it, if specified'), None),
2011-03-15 22:40:54 +00:00
'version': (self.command_version, _('Usage: /version <jid>\nVersion: get the software version of the given JID (usually its XMPP client and Operating System)'), 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),
2010-01-13 01:04:30 +00:00
}
self.key_func = {
"KEY_PPAGE": self.scroll_page_up,
"KEY_NPAGE": self.scroll_page_down,
2010-01-28 00:55:02 +00:00
"KEY_F(5)": self.rotate_rooms_left,
"M-[1;6D": self.rotate_rooms_left,
2010-07-20 10:01:37 +00:00
"^P": self.rotate_rooms_left,
'kLFT3': self.rotate_rooms_left,
2010-01-28 00:55:02 +00:00
"KEY_F(6)": self.rotate_rooms_right,
"M-[1;6C": self.rotate_rooms_right,
"^N": self.rotate_rooms_right,
'kRIT3': self.rotate_rooms_right,
"KEY_F(7)": self.shrink_information_win,
"KEY_F(8)": self.grow_information_win,
"KEY_RESIZE": self.call_for_resize,
2010-10-27 23:11:35 +00:00
'M-e': self.go_to_important_room,
'M-r': self.go_to_roster,
2010-10-27 23:11:35 +00:00
'M-z': self.go_to_previous_tab,
'M-v': self.move_separator,
2010-12-17 21:06:59 +00:00
'^L': self.full_screen_redraw,
2011-03-20 02:32:48 +00:00
'M-j': self.go_to_room_number,
}
2010-08-31 23:11:02 +00:00
# Add handlers
self.xmpp.add_event_handler('connected', self.on_connected)
self.xmpp.add_event_handler('disconnected', self.on_disconnected)
self.xmpp.add_event_handler('failed_auth', self.on_failed_auth)
self.xmpp.add_event_handler("session_start", self.on_session_start)
2010-08-31 23:11:02 +00:00
self.xmpp.add_event_handler("groupchat_presence", self.on_groupchat_presence)
self.xmpp.add_event_handler("groupchat_message", self.on_groupchat_message)
self.xmpp.add_event_handler("groupchat_subject", self.on_groupchat_subject)
2010-08-31 23:11:02 +00:00
self.xmpp.add_event_handler("message", self.on_message)
self.xmpp.add_event_handler("got_online" , self.on_got_online)
self.xmpp.add_event_handler("got_offline" , self.on_got_offline)
self.xmpp.add_event_handler("roster_update", self.on_roster_update)
self.xmpp.add_event_handler("changed_status", self.on_presence)
self.xmpp.add_event_handler("changed_subscription", self.on_changed_subscription)
self.xmpp.add_event_handler("message_xform", self.on_data_form)
self.xmpp.add_event_handler("chatstate_active", self.on_chatstate_active)
self.xmpp.add_event_handler("chatstate_composing", self.on_chatstate_composing)
self.xmpp.add_event_handler("chatstate_paused", self.on_chatstate_paused)
self.xmpp.add_event_handler("chatstate_gone", self.on_chatstate_gone)
self.xmpp.add_event_handler("chatstate_inactive", self.on_chatstate_inactive)
def start(self):
"""
Init curses, create the first tab, etc
"""
self.stdscr = curses.initscr()
self.init_curses(self.stdscr)
# Init the tab's size.
tabs.Tab.resize(self.stdscr)
# resize the information_win to its initial size
self.resize_global_information_win()
default_tab = tabs.InfoTab() if self.xmpp.anon\
else tabs.RosterInfoTab()
default_tab.on_gain_focus()
self.tabs.append(default_tab)
self.information(_('Welcome to poezio!'))
self.refresh_window()
def resize_global_information_win(self):
"""
Resize the global_information_win only once at each resize.
"""
self.information_win.resize(self.information_win_size, tabs.Tab.width,
tabs.Tab.height - 2 - self.information_win_size, 0)
2010-12-26 20:51:14 +00:00
def on_exception(self, typ, value, trace):
"""
When an exception is raised, just reset curses and call
the original exception handler (will nicely print the traceback)
2010-12-26 20:51:14 +00:00
"""
try:
self.reset_curses()
except:
pass
sys.__excepthook__(typ, value, trace)
2010-12-26 20:51:14 +00:00
@property
def informations(self):
return self.information_buffer
def grow_information_win(self):
if self.information_win_size == 14:
return
self.information_win_size += 1
self.resize_global_information_win()
for tab in self.tabs:
tab.on_info_win_size_changed()
self.refresh_window()
def shrink_information_win(self):
if self.information_win_size == 0:
return
self.information_win_size -= 1
self.resize_global_information_win()
for tab in self.tabs:
tab.on_info_win_size_changed()
self.refresh_window()
2010-08-31 23:11:02 +00:00
def on_data_form(self, message):
"""
When a data form is received
"""
self.information('%s' % messsage)
def on_chatstate_active(self, message):
self.on_chatstate(message, "active")
def on_chatstate_inactive(self, message):
self.on_chatstate(message, "inactive")
def on_chatstate_composing(self, message):
self.on_chatstate(message, "composing")
def on_chatstate_paused(self, message):
self.on_chatstate(message, "paused")
def on_chatstate_gone(self, message):
self.on_chatstate(message, "gone")
def on_chatstate(self, message, state):
if message['type'] == 'chat':
if not self.on_chatstate_normal_conversation(message, state):
tab = self.get_tab_by_name(message['from'].full, tabs.PrivateTab)
if not tab:
return
self.on_chatstate_private_conversation(message, state)
elif message['type'] == 'groupchat':
self.on_chatstate_groupchat_conversation(message, state)
2011-03-07 19:11:47 +00:00
def on_chatstate_normal_conversation(self, message, state):
tab = self.get_tab_of_conversation_with_jid(message['from'], False)
if not tab:
return False
tab.chatstate = state
if tab == self.current_tab():
self.refresh_window()
return True
def on_chatstate_private_conversation(self, message, state):
tab = self.get_tab_by_name(message['from'].full, tabs.PrivateTab)
if not tab:
return
tab.chatstate = state
if tab == self.current_tab():
self.refresh_window()
return True
def on_chatstate_groupchat_conversation(self, message, state):
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 == self.current_tab():
self.refresh_window()
def open_new_form(self, form, on_cancel, on_send, **kwargs):
"""
Open a new tab containing the form
The callback are called with the completed form as parameter in
addition with kwargs
"""
form_tab = DataFormsTab(form, on_cancel, on_send, kwargs)
self.add_tab(form_tab, True)
def on_got_offline(self, presence):
jid = presence['from']
contact = roster.get_contact_by_jid(jid.bare)
if not contact:
return
resource = contact.get_resource_by_fulljid(jid.full)
assert resource
self.information('%s is offline' % (resource.get_jid()), "Roster")
# If a resource got offline, display the message in the conversation with this
# precise resource.
2011-01-05 01:46:19 +00:00
self.add_information_message_to_conversation_tab(jid.full, '%s is offline' % (resource.get_jid().full))
contact.remove_resource(resource)
# Display the message in the conversation with the bare JID only if that was
# the only resource online (i.e. now the contact is completely disconnected)
if not contact.get_highest_priority_resource(): # No resource left: that was the last one
2011-01-05 01:46:19 +00:00
self.add_information_message_to_conversation_tab(jid.bare, '%s is offline' % (jid.bare))
self.refresh_window()
def on_got_online(self, presence):
jid = presence['from']
contact = roster.get_contact_by_jid(jid.bare)
if not contact:
# Todo, handle presence comming from contacts not in roster
return
resource = contact.get_resource_by_fulljid(jid.full)
assert not resource
resource = Resource(jid.full)
status = presence['type']
2010-11-10 21:15:08 +00:00
status_message = presence['status']
priority = presence.getPriority() or 0
2010-11-10 21:15:08 +00:00
resource.set_status(status_message)
resource.set_presence(status)
resource.set_priority(priority)
self.information("%s is online (%s)" % (resource.get_jid().full, status), "Roster")
self.add_information_message_to_conversation_tab(jid.full, '%s is online' % (jid.full))
if not contact.get_highest_priority_resource():
# No connected resource yet: the user's just connecting
self.add_information_message_to_conversation_tab(jid.bare, '%s is online' % (jid.bare))
contact.add_resource(resource)
2011-01-05 01:46:19 +00:00
def add_information_message_to_conversation_tab(self, jid, msg):
"""
Search for a ConversationTab with the given jid (full or bare), if yes, add
the given message to it
"""
tab = self.get_tab_by_name(jid, tabs.ConversationTab)
if tab:
2011-01-05 01:46:19 +00:00
self.add_message_to_text_buffer(tab.get_room(), msg)
def on_failed_connection(self):
"""
We cannot contact the remote server
"""
self.information(_("Connection to remote server failed"))
def on_disconnected(self, event):
"""
When we are disconnected from remote server
"""
for tab in self.tabs:
if isinstance(tab, tabs.MucTab):
tab.get_room().disconnect()
self.information(_("Disconnected from server."))
def on_failed_auth(self, event):
"""
Authentication failed
"""
self.information(_("Authentication failed."))
2010-08-31 23:11:02 +00:00
def on_connected(self, event):
"""
Remote host responded, but we are not yet authenticated
"""
self.information(_("Connected to server."))
def on_session_start(self, event):
2010-08-31 23:11:02 +00:00
"""
Called when we are connected and authenticated
"""
self.information(_("Authentication success."))
self.information(_("Your JID is %s") % self.xmpp.boundjid.full)
if not self.xmpp.anon:
# request the roster
self.xmpp.getRoster()
# send initial presence
2010-11-10 21:15:08 +00:00
self.xmpp.makePresence().send()
2010-08-31 23:11:02 +00:00
rooms = config.get('rooms', '')
if rooms == '' or not isinstance(rooms, str):
return
rooms = rooms.split(':')
for room in rooms:
jid = JID(room)
if jid.bare == '':
2010-08-31 23:11:02 +00:00
return
if jid.resource != '':
nick = jid.resource
2010-08-31 23:11:02 +00:00
else:
default = os.environ.get('USER') if os.environ.get('USER') else 'poezio'
nick = config.get('default_nick', '') or default
tab = self.get_tab_by_name(jid.bare, tabs.MucTab)
if not tab:
self.open_new_room(jid.bare, nick, False)
muc.join_groupchat(self.xmpp, jid.bare, nick)
2010-08-31 23:11:02 +00:00
def on_groupchat_presence(self, presence):
"""
Triggered whenever a presence stanza is received from a user in a multi-user chat room.
Display the presence on the room window and update the
presence information of the concerned user
"""
from_room = presence['from'].bare
tab = self.get_tab_by_name(from_room, tabs.MucTab)
if tab:
tab.handle_presence(presence)
def rename_private_tabs(self, room_name, old_nick, new_nick):
2010-09-02 01:16:17 +00:00
"""
Call this method when someone changes his/her nick in a MUC, this updates
the name of all the opened private conversations with him/her
2010-09-02 01:16:17 +00:00
"""
tab = self.get_tab_by_name('%s/%s' % (room_name, old_nick), tabs.PrivateTab)
if tab:
tab.rename_user(old_nick, new_nick)
2010-09-02 01:16:17 +00:00
def on_user_left_private_conversation(self, room_name, nick, status_message):
2010-09-02 01:19:08 +00:00
"""
The user left the MUC: add a message in the associated private conversation
2010-09-02 01:19:08 +00:00
"""
tab = self.get_tab_by_name('%s/%s' % (room_name, nick), tabs.PrivateTab)
if tab:
tab.user_left(status_message, nick)
2010-09-02 01:19:08 +00:00
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, colorized=True)
2010-09-02 01:22:22 +00:00
2010-08-31 23:11:02 +00:00
def on_message(self, message):
"""
When receiving private message from a muc OR a normal message
(from one of our contacts)
"""
if message['type'] == 'groupchat':
return
2010-08-31 23:11:02 +00:00
# Differentiate both type of messages, and call the appropriate handler.
jid_from = message['from']
for tab in self.tabs:
if tab.get_name() == jid_from.bare and isinstance(tab, tabs.MucTab):
if message['type'] == 'error':
return self.room_error(message, tab.get_room().name)
else:
return self.on_groupchat_private_message(message)
2010-08-31 23:11:02 +00:00
return self.on_normal_message(message)
def on_groupchat_private_message(self, message):
"""
We received a Private Message (from someone in a Muc)
"""
jid = message['from']
nick_from = jid.resource
room_from = jid.bare
2010-08-31 23:11:02 +00:00
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)
2010-08-31 23:11:02 +00:00
if not room:
return
body = message['body']
room.add_message(body, time=None, nickname=nick_from,
colorized=False,
forced_user=self.get_room_by_name(room_from).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']:
conversation.remote_wants_chatstates = True
else:
conversation.remote_wants_chatstates = False
logger.log_message(jid.full.replace('/', '\\'), nick_from, body)
self.refresh_window()
self.doupdate()
2010-08-31 23:11:02 +00:00
def focus_tab_named(self, tab_name):
for tab in self.tabs:
if tab.get_name() == tab_name:
self.command_win('%s' % (tab.nb,))
def get_tab_of_conversation_with_jid(self, jid, create=True):
2010-08-31 23:11:02 +00:00
"""
From a JID, get the tab containing the conversation with it.
If none already exist, and create is "True", we create it
and return it. Otherwise, we return None
2010-08-31 23:11:02 +00:00
"""
# We first check if we have a conversation opened with this precise resource
conversation = self.get_tab_by_name(jid.full, tabs.ConversationTab)
if not conversation:
# If not, we search for a conversation with the bare jid
conversation = self.get_tab_by_name(jid.bare, tabs.ConversationTab)
if not conversation:
if create:
# We create the conversation with the bare Jid if nothing was found
conversation = self.open_conversation_window(jid.bare, False)
else:
conversation = None
return conversation
def on_normal_message(self, message):
"""
When receiving "normal" messages (from someone in our roster)
"""
jid = message['from']
body = message['body']
if not body:
return
conversation = self.get_tab_of_conversation_with_jid(jid, create=True)
if roster.get_contact_by_jid(jid.bare):
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, None, remote_nick, False, theme.COLOR_REMOTE_USER)
if conversation.remote_wants_chatstates is None:
if message['chat_state']:
conversation.remote_wants_chatstates = True
else:
conversation.remote_wants_chatstates = False
logger.log_message(jid.bare, remote_nick, body)
if self.current_tab() is not conversation:
conversation.set_color_state(theme.COLOR_TAB_PRIVATE)
self.refresh_window()
def on_presence(self, presence):
jid = presence['from']
contact = roster.get_contact_by_jid(jid.bare)
if not contact:
return
resource = contact.get_resource_by_fulljid(jid.full)
if not resource:
return
status = presence['type']
2010-11-10 21:15:08 +00:00
status_message = presence['status']
priority = presence.getPriority() or 0
resource.set_presence(status)
resource.set_priority(priority)
2010-11-10 21:15:08 +00:00
resource.set_status(status_message)
self.refresh_window()
def on_roster_update(self, iq):
"""
A subscription changed, or we received a roster item
after a roster request, etc
"""
for item in iq.findall('{jabber:iq:roster}query/{jabber:iq:roster}item'):
jid = item.attrib['jid']
contact = roster.get_contact_by_jid(jid)
if not contact:
contact = Contact(jid)
roster.add_contact(contact, jid)
if 'ask' in item.attrib:
contact.set_ask(item.attrib['ask'])
else:
contact.set_ask(None)
if 'name' in item.attrib:
contact.set_name(item.attrib['name'])
else:
contact.set_name(None)
if item.attrib['subscription']:
contact.set_subscription(item.attrib['subscription'])
groups = item.findall('{jabber:iq:roster}group')
roster.edit_groups_of_contact(contact, [group.text for group in groups])
2011-01-12 05:49:33 +00:00
if item.attrib['subscription'] == 'remove':
roster.remove_contact(contact.get_bare_jid())
if isinstance(self.current_tab(), tabs.RosterInfoTab):
self.refresh_window()
def on_changed_subscription(self, presence):
"""
Triggered whenever a presence stanza with a type of subscribe, subscribed, unsubscribe, or unsubscribed is received.
"""
if presence['type'] == 'subscribe':
jid = presence['from'].bare
contact = roster.get_contact_by_jid(jid)
if not contact:
contact = Contact(jid)
roster.add_contact(contact, jid)
roster.edit_groups_of_contact(contact, [])
contact.set_ask('asked')
self.tabs[0].set_color_state(theme.COLOR_TAB_HIGHLIGHT)
self.information('%s wants to subscribe to your presence'%jid)
if isinstance(self.current_tab(), tabs.RosterInfoTab):
self.refresh_window()
2010-12-17 21:06:59 +00:00
def full_screen_redraw(self):
"""
Completely erase and redraw the screen
"""
self.stdscr.clear()
self.call_for_resize()
def call_for_resize(self):
"""
Called when we want to resize the screen
2010-07-08 20:20:43 +00:00
"""
tabs.Tab.resize(self.stdscr)
self.resize_global_information_win()
with resize_lock:
for tab in self.tabs:
tab.resize()
self.refresh_window()
2010-07-08 20:20:43 +00:00
2010-08-31 23:11:02 +00:00
def main_loop(self):
2010-05-18 13:29:02 +00:00
"""
main loop waiting for the user to press a key
"""
# curses.ungetch(0) # FIXME
2010-10-20 23:51:34 +00:00
while self.running:
char = read_char(self.stdscr)
2011-03-20 01:42:29 +00:00
# Special case for M-x where x is a number
if char.startswith('M-') and len(char) == 3:
try:
nb = int(char[2])
except:
pass
else:
self.command_win('%s' % nb)
# search for keyboard shortcut
2011-03-20 01:44:51 +00:00
if char in list(self.key_func.keys()):
self.key_func[char]()
else:
self.do_command(char)
self.doupdate()
def current_tab(self):
2010-05-18 13:29:02 +00:00
"""
returns the current room, the one we are viewing
"""
return self.tabs[0]
2010-01-26 17:10:37 +00:00
def get_conversation_by_jid(self, jid):
"""
Return the room of the ConversationTab with the given jid
"""
for tab in self.tabs:
if isinstance(tab, tabs.ConversationTab):
if tab.get_name() == jid:
return tab.get_room()
return None
def get_tab_by_name(self, name, typ=None):
"""
Get the tab with the given name.
If typ is provided, return a tab of this type only
"""
for tab in self.tabs:
if tab.get_name() == name:
if (typ and isinstance(tab, typ)) or\
not typ:
return tab
return None
2010-01-26 17:10:37 +00:00
def get_room_by_name(self, name):
2010-05-18 13:29:02 +00:00
"""
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()
2010-05-18 13:29:02 +00:00
return None
2010-01-26 17:10:37 +00:00
def init_curses(self, stdscr):
2010-05-18 13:29:02 +00:00
"""
ncurses initialization
"""
curses.curs_set(1)
curses.noecho()
curses.nonl()
theme.init_colors()
stdscr.keypad(True)
curses.ungetch(" ") # H4X: without this, the screen is
stdscr.getkey() # erased on the first "getkey()"
2010-01-26 17:10:37 +00:00
def reset_curses(self):
2010-05-18 13:29:02 +00:00
"""
Reset terminal capabilities to what they were before ncurses
init
"""
curses.echo()
2010-02-14 04:18:34 +00:00
curses.nocbreak()
curses.curs_set(1)
curses.endwin()
2010-01-26 17:10:37 +00:00
def refresh_window(self):
"""
Refresh everything
"""
self.current_tab().set_color_state(theme.COLOR_TAB_CURRENT)
self.current_tab().refresh()
2010-10-17 12:43:56 +00:00
self.doupdate()
def add_tab(self, new_tab, focus=False):
2010-05-18 13:29:02 +00:00
"""
Appends the new_tab in the tab list and
focus it if focus==True
2010-05-18 13:29:02 +00:00
"""
if self.current_tab().nb == 0:
self.tabs.append(new_tab)
2010-02-11 04:59:58 +00:00
else:
for ta in self.tabs:
if ta.nb == 0:
self.tabs.insert(self.tabs.index(ta), new_tab)
2010-02-11 04:59:58 +00:00
break
2010-08-31 23:11:02 +00:00
if focus:
self.command_win("%s" % new_tab.nb)
def open_new_room(self, room, nick, focus=True):
"""
Open a new tab.MucTab containing a muc Room, using the specified nick
"""
r = Room(room, nick)
new_tab = tabs.MucTab(r)
self.add_tab(new_tab, focus)
self.refresh_window()
def go_to_roster(self):
self.command_win('0')
def go_to_previous_tab(self):
self.command_win('%s' % (self.previous_tab_nb,))
def go_to_important_room(self):
"""
Go to the next room with activity, in this order:
- A personal conversation with a new message
- A Muc with an highlight
- A Muc with any new message
"""
for tab in self.tabs:
if tab.get_color_state() == theme.COLOR_TAB_PRIVATE:
self.command_win('%s' % tab.nb)
return
for tab in self.tabs:
if tab.get_color_state() == theme.COLOR_TAB_HIGHLIGHT:
self.command_win('%s' % tab.nb)
return
for tab in self.tabs:
if tab.get_color_state() == theme.COLOR_TAB_NEW_MESSAGE:
self.command_win('%s' % tab.nb)
return
for tab in self.tabs:
if isinstance(tab, tabs.ChatTab) and not tab.input.is_empty():
self.command_win('%s' % tab.nb)
return
2010-02-11 04:59:58 +00:00
def rotate_rooms_right(self, args=None):
2010-05-18 13:29:02 +00:00
"""
rotate the rooms list to the right
"""
self.current_tab().on_lose_focus()
self.tabs.append(self.tabs.pop(0))
self.current_tab().on_gain_focus()
self.refresh_window()
2010-02-11 04:59:58 +00:00
def rotate_rooms_left(self, args=None):
2010-05-18 13:29:02 +00:00
"""
rotate the rooms list to the right
"""
self.current_tab().on_lose_focus()
self.tabs.insert(0, self.tabs.pop())
self.current_tab().on_gain_focus()
self.refresh_window()
def scroll_page_down(self, args=None):
self.current_tab().on_scroll_down()
self.refresh_window()
def scroll_page_up(self, args=None):
self.current_tab().on_scroll_up()
self.refresh_window()
2010-08-31 23:11:02 +00:00
def room_error(self, error, room_name):
2010-05-18 13:29:02 +00:00
"""
Display the error on the room window
"""
2010-08-31 23:11:02 +00:00
room = self.get_room_by_name(room_name)
msg = error['error']['type']
condition = error['error']['condition']
code = error['error']['code']
body = error['error']['text']
if not body:
if code in list(ERROR_AND_STATUS_CODES.keys()):
2010-08-05 22:24:51 +00:00
body = ERROR_AND_STATUS_CODES[code]
else:
2010-08-31 23:11:02 +00:00
body = condition or _('Unknown error')
if code:
msg = _('Error: %(code)s - %(msg)s: %(body)s') % {'msg':msg, 'body':body, 'code':code}
self.add_message_to_text_buffer(room, msg)
2010-08-31 23:11:02 +00:00
else:
msg = _('Error: %(msg)s: %(body)s') % {'msg':msg, 'body':body}
self.add_message_to_text_buffer(room, msg)
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)
2010-08-05 22:24:23 +00:00
if code == '409':
2010-08-07 00:57:36 +00:00
if config.get('alternative_nickname', '') != '':
self.command_join('%s/%s'% (room.name, room.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.refresh_window()
2010-11-10 21:15:08 +00:00
def open_conversation_window(self, jid, focus=True):
"""
open a new conversation tab and focus it if needed
"""
new_tab = tabs.ConversationTab(jid)
# insert it in the rooms
self.add_tab(new_tab, focus)
self.refresh_window()
return new_tab
2010-06-13 13:51:02 +00:00
def open_private_window(self, room_name, user_nick, focus=True):
complete_jid = room_name+'/'+user_nick
for tab in self.tabs: # if the room exists, focus it and return
if isinstance(tab, tabs.PrivateTab):
if tab.get_name() == complete_jid:
self.command_win('%s' % tab.nb)
return tab.get_room()
2010-06-13 13:51:02 +00:00
# create the new tab
room = self.get_room_by_name(room_name)
2010-07-19 19:38:33 +00:00
if not room:
return None
own_nick = room.own_nick
r = Room(complete_jid, own_nick) # PrivateRoom here
new_tab = tabs.PrivateTab(r)
# insert it in the tabs
self.add_tab(new_tab, focus)
2010-06-13 13:51:02 +00:00
# self.window.new_room(r)
self.refresh_window()
2010-06-13 13:51:02 +00:00
return r
def on_groupchat_subject(self, message):
"""
triggered when the topic is changed
"""
nick_from = message['mucnick']
room_from = message.getMucroom()
room = self.get_room_by_name(room_from)
subject = message['subject']
2011-01-09 15:34:06 +00:00
if not subject or not room:
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)
else:
self.add_message_to_text_buffer(room, _("The subject is: %(subject)s") % {'subject':subject}, time=None)
room.topic = subject.replace('\n', '|')
self.refresh_window()
2010-08-31 23:11:02 +00:00
def on_groupchat_message(self, message):
2010-05-18 13:29:02 +00:00
"""
2010-08-31 23:11:02 +00:00
Triggered whenever a message is received from a multi-user chat room.
2010-05-18 13:29:02 +00:00
"""
if message['subject']:
return
2010-08-31 23:11:02 +00:00
delay_tag = message.find('{urn:xmpp:delay}delay')
if delay_tag is not None:
delayed = True
2010-08-31 23:11:02 +00:00
date = common.datetime_tuple(delay_tag.attrib['stamp'])
else:
# We support the OLD and deprecated XEP: http://xmpp.org/extensions/xep-0091.html
# But it sucks, please, Jabber servers, don't do this :(
2010-08-31 23:11:02 +00:00
delay_tag = message.find('{jabber:x:delay}x')
if delay_tag is not None:
delayed = True
2010-08-31 23:11:02 +00:00
date = common.datetime_tuple(delay_tag.attrib['stamp'])
else:
delayed = False
2010-08-31 23:11:02 +00:00
date = None
nick_from = message['mucnick']
2010-08-31 23:11:02 +00:00
room_from = message.getMucroom()
if message['type'] == 'error': # Check if it's an error
return self.room_error(message, from_room)
if nick_from == room_from:
nick_from = None
2010-09-02 01:49:11 +00:00
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
2010-08-31 23:11:02 +00:00
if not room:
self.information(_("message received for a non-existing room: %s") % (room_from))
2010-01-28 00:55:02 +00:00
return
body = message['body']
if body:
date = date if delayed == True else None
self.add_message_to_text_buffer(room, body, date, nick_from)
# TODO, only if we are focused on this MUC
self.refresh_window()
self.doupdate()
def add_message_to_text_buffer(self, room, txt, time=None, nickname=None):
2010-03-18 19:43:44 +00:00
"""
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)
2010-03-18 19:43:44 +00:00
"""
if not room:
self.information('Error, trying to add a message in no room: %s' % txt)
else:
room.add_message(txt, time, nickname)
self.refresh_window()
2010-01-13 01:04:30 +00:00
def command_help(self, arg):
2010-05-18 13:29:02 +00:00
"""
/help <command_name>
"""
args = arg.split()
2010-01-30 04:42:29 +00:00
if len(args) == 0:
msg = _('Available commands are: ')
for command in list(self.commands.keys()):
2010-01-30 04:42:29 +00:00
msg += "%s " % command
for command in list(self.current_tab().commands.keys()):
msg += "%s " % command
2010-01-31 05:33:54 +00:00
msg += _("\nType /help <command_name> to know what each command does")
if len(args) >= 1:
if args[0] in list(self.commands.keys()):
2010-01-30 04:42:29 +00:00
msg = self.commands[args[0]][1]
elif args[0] in list(self.current_tab().commands.keys()):
msg = self.current_tab().commands[args[0]][1]
2010-01-30 04:42:29 +00:00
else:
2010-01-31 05:33:54 +00:00
msg = _('Unknown command: %s') % args[0]
self.information(msg)
2010-01-30 04:42:29 +00:00
2011-02-10 15:13:28 +00:00
def completion_help(self, the_input):
commands = list(self.commands.keys()) + list(self.current_tab().commands.keys())
return the_input.auto_completion(commands, ' ')
2011-01-23 21:49:58 +00:00
def command_status(self, arg):
"""
/status <status> [msg]
"""
2011-01-23 21:49:58 +00:00
args = arg.split()
if len(args) < 1:
return
if not args[0] in possible_show.keys():
self.command_help('status')
2011-01-23 21:49:58 +00:00
return
show = possible_show[args[0]]
if len(args) > 1:
msg = ' '.join(args[1:])
else:
msg = None
pres = self.xmpp.make_presence()
if msg:
pres['status'] = msg
pres['type'] = show
pres.send()
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)
2011-01-23 21:49:58 +00:00
def completion_status(self, the_input):
return the_input.auto_completion([status for status in list(possible_show.keys())], ' ')
2011-02-15 15:22:25 +00:00
def command_message(self, arg):
"""
/message <jid> [message]
"""
args = arg.split()
if len(args) < 1:
self.command_help('message')
return
jid = args[0]
tab = self.open_conversation_window(jid, focus=True)
if len(args) > 1:
tab.command_say(arg.strip()[len(jid)+1:])
2011-02-15 15:22:25 +00:00
2011-03-15 22:40:54 +00:00
def command_version(self, arg):
"""
/version <jid>
"""
args = arg.split()
if len(args) < 1:
return self.command_help('version')
jid = args[0]
res = self.xmpp.plugin['xep_0092'].get_version(jid)
if not res:
return self.information('Could not get the software version from %s' % (jid,))
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.information(version)
def command_reconnect(self, args):
"""
/reconnect
"""
self.disconnect(True)
def command_list(self, arg):
"""
/list <server>
Opens a MucListTab containing the list of the room in the specified server
"""
args = arg.split()
if len(args) > 1:
self.command_help('list')
return
elif len(args) == 0:
if not isinstance(self.current_tab(), tabs.MucTab):
return self.information('Warning: Please provide a server')
server = JID(self.current_tab().get_name()).server
else:
server = arg.strip()
list_tab = tabs.MucListTab(server)
self.add_tab(list_tab, True)
self.xmpp.plugin['xep_0030'].get_items(jid=server, block=False, callback=list_tab.on_muc_list_item_received)
def command_theme(self, arg):
"""
"""
theme.reload_theme()
self.refresh_window()
def command_win(self, arg):
2010-05-18 13:29:02 +00:00
"""
/win <number>
"""
args = arg.split()
2010-02-12 02:50:07 +00:00
if len(args) != 1:
self.command_help('win')
2010-02-12 02:50:07 +00:00
return
try:
nb = int(args[0])
except ValueError:
nb = arg.strip()
if self.current_tab().nb == nb:
2010-02-12 02:50:07 +00:00
return
self.previous_tab_nb = self.current_tab().nb
self.current_tab().on_lose_focus()
start = self.current_tab()
self.tabs.append(self.tabs.pop(0))
if isinstance(nb, int):
while self.current_tab().nb != nb:
self.tabs.append(self.tabs.pop(0))
if self.current_tab() == start:
break
else:
while nb not in JID(self.current_tab().get_name()).user:
self.tabs.append(self.tabs.pop(0))
if self.current_tab() is start:
break
self.current_tab().on_gain_focus()
self.refresh_window()
2010-02-12 02:50:07 +00:00
def completion_win(self, the_input):
l = [JID(tab.get_name()).user for tab in self.tabs]
l.remove('')
return the_input.auto_completion(l, ' ')
def completion_join(self, the_input):
"""
Try to complete the server of the MUC's jid (for now only from the currently
open ones)
TODO: have a history of recently joined MUCs, and use that too
"""
txt = the_input.get_text()
if len(txt.split()) != 2:
# we are not on the 1st argument of the command line
return False
jid = JID(txt.split()[1])
if jid.server:
if jid.resource or jid.full.endswith('/'):
# we are writing the resource: complete the node
if not the_input.last_completion:
response = self.xmpp.plugin['xep_0030'].get_items(jid=jid.server, block=True, timeout=1)
if response:
items = response['disco_items'].get_items()
else:
return True
items = ['%s/%s' % (tup[0], jid.resource) for tup in items]
for i in range(len(jid.server) + 2 + len(jid.resource)):
the_input.key_backspace()
else:
items = []
the_input.auto_completion(items, '')
else:
# we are writing the server: complete the server
serv = jid.server
serv_list = []
for tab in self.tabs:
if isinstance(tab, tabs.MucTab):
serv_list.append('%s@%s'% (jid.user, JID(tab.get_name()).host))
the_input.auto_completion(serv_list, '')
return True
def completion_list(self, the_input):
txt = the_input.get_text()
muc_serv_list = []
for tab in self.tabs: # TODO, also from an history
if isinstance(tab, tabs.MucTab) and\
tab.get_name() not in muc_serv_list:
muc_serv_list.append(JID(tab.get_name()).server)
if muc_serv_list:
return the_input.auto_completion(muc_serv_list, ' ')
def command_join(self, arg, histo_length=None):
2010-05-18 13:29:02 +00:00
"""
/join [room][/nick] [password]
"""
args = arg.split()
password = None
2010-01-30 04:02:23 +00:00
if len(args) == 0:
t = self.current_tab()
if not isinstance(t, tabs.MucTab) and not isinstance(t, tabs.PrivateTab):
2010-01-30 04:02:23 +00:00
return
room = t.get_name()
nick = t.get_room().own_nick
else:
info = JID(args[0])
if info.resource == '':
default = os.environ.get('USER') if os.environ.get('USER') else 'poezio'
nick = config.get('default_nick', '')
if nick == '':
nick = default
2010-01-30 04:02:23 +00:00
else:
nick = info.resource
if info.bare == '': # happens with /join /nickname, which is OK
t = self.current_tab()
if not isinstance(t, tabs.MucTab):
2010-01-30 04:02:23 +00:00
return
room = t.get_name()
if nick == '':
nick = t.get_room().own_nick
2010-01-30 04:02:23 +00:00
else:
room = info.bare
if room.find('@') == -1: # no server is provided, like "/join hello"
# use the server of the current room if available
# check if the current room's name has a server
if isinstance(self.current_tab(), tabs.MucTab) and\
self.current_tab().get_name().find('@') != -1:
room += '@%s' % JID(self.current_tab().get_name()).domain
else: # no server could be found, print a message and return
self.information(_("You didn't specify a server for the room you want to join"), 'Error')
return
r = self.get_room_by_name(room)
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)
return
if room.startswith('@'):
room = room[1:]
2010-08-31 23:11:02 +00:00
room = room.lower()
if r and not r.joined:
muc.join_groupchat(self.xmpp, room, nick, password, histo_length)
if not r: # if the room window exists, we don't recreate it.
2010-08-31 23:11:02 +00:00
self.open_new_room(room, nick)
muc.join_groupchat(self.xmpp, room, nick, password, histo_length)
else:
r.own_nick = nick
r.users = []
2010-01-13 01:04:30 +00:00
def command_bookmark(self, arg):
2010-05-18 13:29:02 +00:00
"""
/bookmark [room][/nick]
"""
args = arg.split()
2010-01-31 03:42:25 +00:00
nick = None
2011-01-13 19:34:17 +00:00
if len(args) == 0 and not isinstance(self.current_tab(), tabs.MucTab):
return
2010-01-31 03:42:25 +00:00
if len(args) == 0:
room = self.current_tab().get_room()
roomname = self.current_tab().get_name()
2010-01-31 03:42:25 +00:00
if room.joined:
nick = room.own_nick
else:
info = JID(args[0])
if info.resource != '':
nick = info.resource
roomname = info.bare
2010-02-14 04:10:13 +00:00
if roomname == '':
roomname = self.current_tab().get_name()
2010-01-31 03:42:25 +00:00
if nick:
res = roomname+'/'+nick
else:
res = roomname
2010-02-14 04:10:13 +00:00
bookmarked = config.get('rooms', '')
# check if the room is already bookmarked.
# if yes, replace it (i.e., update the associated nick)
bookmarked = bookmarked.split(':')
for room in bookmarked:
if JID(room).bare == roomname:
2010-02-14 04:10:13 +00:00
bookmarked.remove(room)
break
bookmarked = ':'.join(bookmarked)
if bookmarked:
bookmarks = bookmarked+':'+res
else:
bookmarks = res
config.set_and_save('rooms', bookmarks)
self.information(_('Your bookmarks are now: %s') % bookmarks)
2010-01-31 03:42:25 +00:00
def command_set(self, arg):
2010-05-18 13:29:02 +00:00
"""
/set <option> [value]
"""
args = arg.split()
if len(args) != 2 and len(args) != 1:
self.command_help('set')
2010-02-02 23:35:01 +00:00
return
option = args[0]
if len(args) == 2:
value = args[1]
else:
value = ''
2010-02-10 16:47:43 +00:00
config.set_and_save(option, value)
2010-02-02 23:35:01 +00:00
msg = "%s=%s" % (option, value)
self.information(msg)
2010-02-02 23:35:01 +00:00
def command_away(self, arg):
2010-05-18 13:29:02 +00:00
"""
/away [msg]
"""
self.command_status("away "+arg)
2010-01-31 03:07:30 +00:00
def command_busy(self, arg):
2010-05-18 13:29:02 +00:00
"""
/busy [msg]
"""
self.command_status("busy "+arg)
2010-01-31 03:07:30 +00:00
def command_avail(self, arg):
2010-05-18 13:29:02 +00:00
"""
/avail [msg]
"""
self.command_status("available "+arg)
2010-01-31 00:25:17 +00:00
def close_tab(self, tab=None):
2010-06-21 08:46:06 +00:00
"""
Close the given tab. If None, close the current one
2010-06-21 08:46:06 +00:00
"""
tab = tab or self.current_tab()
if isinstance(tab, tabs.RosterInfoTab) or\
isinstance(tab, tabs.InfoTab):
return # The tab 0 should NEVER be closed
del tab.key_func # Remove self references
del tab.commands # and make the object collectable
tab.on_close()
self.tabs.remove(tab)
import gc
gc.collect()
log.debug('___ Referrers of closing tab:\n%s\n______' % gc.get_referrers(tab))
del tab
self.refresh_window()
2010-06-21 08:46:06 +00:00
2011-03-20 02:32:48 +00:00
def go_to_room_number(self):
"""
Read 2 more chars and go to the tab
with the given number
"""
char = read_char(self.stdscr)
try:
nb1 = int(char)
except ValueError:
return
char = read_char(self.stdscr)
try:
nb2 = int(char)
except ValueError:
return
self.command_win('%s%s' % (nb1, nb2))
def move_separator(self):
"""
Move the new-messages separator at the bottom on the current
text.
"""
2010-12-15 17:05:52 +00:00
window = self.current_tab().get_text_window()
if not window:
return
2010-12-15 17:05:52 +00:00
window.remove_line_separator()
window.add_line_separator()
self.refresh_window()
def information(self, msg, typ=''):
2010-05-18 13:29:02 +00:00
"""
Displays an informational message in the "Info" room window
"""
self.information_buffer.add_message(msg, nickname=typ)
# TODO: refresh only the correct window in the current tab
self.refresh_window()
2010-01-28 00:55:02 +00:00
def disconnect(self, msg=None):
"""
Disconnect from remote server and correctly set the states of all
parts of the client (for example, set the MucTabs as not joined, etc)
"""
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)
self.save_config()
self.xmpp.disconnect(False)
def command_quit(self, arg):
2010-05-18 13:29:02 +00:00
"""
/quit
"""
if len(arg.strip()) != 0:
msg = arg
2010-06-13 15:40:03 +00:00
else:
msg = None
self.disconnect(msg)
2010-10-20 23:51:34 +00:00
self.running = False
2010-08-31 23:11:02 +00:00
self.reset_curses()
sys.exit()
def save_config(self):
"""
Save config in the file just before exit
"""
roster.save_to_config_file()
config.set_and_save('info_win_height', self.information_win_size, 'var')
def do_command(self, key):
if not key:
return
res = self.current_tab().on_input(key)
if res:
self.refresh_window()
def on_roster_enter_key(self, roster_row):
"""
when enter is pressed on the roster window
"""
if isinstance(roster_row, Contact):
if not self.get_conversation_by_jid(roster_row.get_bare_jid()):
self.open_conversation_window(roster_row.get_bare_jid())
else:
self.focus_tab_named(roster_row.get_bare_jid())
if isinstance(roster_row, Resource):
if not self.get_conversation_by_jid(roster_row.get_jid().full):
self.open_conversation_window(roster_row.get_jid().full)
else:
self.focus_tab_named(roster_row.get_jid().full)
self.refresh_window()
def execute(self,line):
"""
Execute the /command or just send the line on the current room
"""
if line == "":
return
2011-01-11 01:52:00 +00:00
if line.startswith('/'):
command = line.strip()[:].split()[0][1:]
arg = line[2+len(command):] # jump the '/' and the ' '
# example. on "/link 0 open", command = "link" and arg = "0 open"
if command in list(self.commands.keys()):
func = self.commands[command][0]
func(arg)
return
else:
self.information(_("unknown command (%s)") % (command), _('Error'))
def doupdate(self):
if not self.running:
return
2010-10-17 12:43:56 +00:00
self.current_tab().just_before_refresh()
curses.doupdate()