gui.py 33.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#!/usr/bin/python
# -*- coding:utf-8 -*-
#
# Copyright 2010 Le Coz Florent <louizatakk@fedoraproject.org>
#
# This file is part of Poezio.
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# Poezio is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Poezio.  If not, see <http://www.gnu.org/licenses/>.

20 21
from gettext import (bindtextdomain, textdomain, bind_textdomain_codeset,
                     gettext as _)
22
from os.path import isfile
23

24 25
from time import sleep

26 27 28
import sys

import curses
29
from datetime import datetime
30 31

import common
32

33
from handler import Handler
34
from config import config
35
from window import Window
36 37
from user import User
from room import Room
38 39
from message import Message

40 41
from connection import is_jid_the_same

42 43
def doupdate():
    curses.doupdate()
44

45 46
class Gui(object):
    """
47
    User interface using ncurses
48
    """
49 50 51 52
    def __init__(self, stdscr=None, muc=None):
        self.init_curses(stdscr)
        self.stdscr = stdscr
        self.window = Window(stdscr)
53
        self.rooms = [Room('Info', '', self.window)]
54
        self.ignores = {}
55

56
        self.muc = muc
57

58
        self.commands = {
59
            'help': (self.command_help, u'\_o< KOIN KOIN KOIN'),
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
            'join': (self.command_join, _("Usage: /join [room_name][/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 password to join the room.\nExamples:\n/join room@server.tld\n/join room@server.tld/ John\n/join /me_again\n/join\n/join room@server.tld/my_nick password\n/join / pass")),
            'quit': (self.command_quit, _("Usage: /quit\nQuit: Just disconnect from the server and exit poezio.")),
            'exit': (self.command_quit, _("Usage: /exit\nExit: Just disconnect from the server and exit poezio.")),
            'next': (self.rotate_rooms_right, _("Usage: /next\nNext: Go to the next room.")),
            'n': (self.rotate_rooms_right, _("Usage: /n\nN: Go to the next room.")),
            'prev': (self.rotate_rooms_left, _("Usage: /prev\nPrev: Go to the previous room.")),
            'p': (self.rotate_rooms_left, _("Usage: /p\nP: Go to the previous room.")),
            'win': (self.command_win, _("Usage: /win <number>\nWin: Go to the specified room.")),
            'w': (self.command_win, _("Usage: /w <number>\nW: Go to the specified room.")),
            'ignore': (self.command_ignore, _("Usage: /ignore <nickname> \Ignore: Ignore a specified nickname.")),
            'unignore': (self.command_unignore, _("Usage: /unignore <nickname>\Unignore: Remove the specified nickname from the ignore list.")),
            'part': (self.command_part, _("Usage: /part [message]\n Part: disconnect from a room. You can specify an optional message.")),
            'show': (self.command_show, _("Usage: /show <availability> [status]\nShow: Change your availability and (optionaly) your status. The <availability> argument is one of \"avail, available, ok, here, chat, away, afk, dnd, busy, xa\" and the optional [message] argument will be your status message")),
            'away': (self.command_away, _("Usage: /away [message]\nAway: Sets your availability to away and (optional) sets your status message. This is equivalent to '/show away [message]'")),
            'busy': (self.command_busy, _("Usage: /busy [message]\nBusy: Sets your availability to busy and (optional) sets your status message. This is equivalent to '/show busy [message]'")),
            'avail': (self.command_avail, _("Usage: /avail [message]\nAvail: Sets your availability to available and (optional) sets your status message. This is equivalent to '/show available [message]'")),
            'available': (self.command_avail, _("Usage: /available [message]\nAvailable: Sets your availability to available and (optional) sets your status message. This is equivalent to '/show available [message]'")),
           '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)")),
            '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>.")),
            'kick': (self.command_kick, _("Usage: /kick <nick> [reason]\nKick: Kick the user with the specified nickname. You also can give an optional reason.")),
            'topic': (self.command_topic, _("Usage: /topic <subject> \nTopic: Change the subject of the room")),
81 82
            'query': (self.command_query, _('Usage: /query <nick>\nQuery: Open a private conversation with <nick>. This nick has to be present in the room you\'re currently in.')),

83
            'nick': (self.command_nick, _("Usage: /nick <nickname> \nNick: Change your nickname in the current room"))
84 85
            }

86 87 88 89 90 91 92
        self.key_func = {
            "KEY_LEFT": self.window.input.key_left,
            "KEY_RIGHT": self.window.input.key_right,
            "KEY_UP": self.window.input.key_up,
            "KEY_END": self.window.input.key_end,
            "KEY_HOME": self.window.input.key_home,
            "KEY_DOWN": self.window.input.key_down,
93 94
            "KEY_PPAGE": self.scroll_page_up,
            "KEY_NPAGE": self.scroll_page_down,
95
            "KEY_DC": self.window.input.key_dc,
96 97 98 99
            "KEY_F(5)": self.rotate_rooms_left,
            "KEY_F(6)": self.rotate_rooms_right,
            "kLFT5": self.rotate_rooms_left,
            "kRIT5": self.rotate_rooms_right,
100
            "\t": self.auto_completion,
101 102 103
            "KEY_BACKSPACE": self.window.input.key_backspace
            }

104 105 106 107 108
        self.handler = Handler()
        self.handler.connect('on-connected', self.on_connected)
        self.handler.connect('join-room', self.join_room)
        self.handler.connect('room-presence', self.room_presence)
        self.handler.connect('room-message', self.room_message)
109
        self.handler.connect('private-message', self.private_message)
110
        self.handler.connect('error-message', self.room_error)
111
        self.handler.connect('error', self.information)
112

113
    def main_loop(self, stdscr):
114 115 116
        """
        main loop waiting for the user to press a key
        """
117
        while 1:
118
            doupdate()
119 120 121
            try:
                key = stdscr.getkey()
            except:
122
                continue
123
            if str(key) in self.key_func.keys():
124
                self.key_func[key]()
125 126
            elif str(key) == 'KEY_RESIZE':
                self.window.resize(stdscr)
127
                self.window.refresh(self.rooms)
128 129
            elif len(key) >= 4:
                continue
130 131
            elif ord(key) == 10:
                self.execute()
132
            elif ord(key) == 8 or ord(key) == 127:
133
                self.window.input.key_backspace()
134 135
            elif ord(key) < 32:
                continue
136
            else:
137
                if ord(key) == 27 and ord(stdscr.getkey()) == 91:
138
                    last = ord(stdscr.getkey()) # FIXME: ugly ugly workaround.
139 140 141
                    if last == 51:
                        self.window.input.key_dc()
                    continue
142
                elif ord(key) > 190 and ord(key) < 225:
143 144 145 146 147 148
                    key = key+stdscr.getkey()
                elif ord(key) == 226:
                    key = key+stdscr.getkey()
                    key = key+stdscr.getkey()
                self.window.do_command(key)

149
    def current_room(self):
150 151 152
        """
        returns the current room, the one we are viewing
        """
153
        return self.rooms[0]
154 155

    def get_room_by_name(self, name):
156 157 158 159 160 161 162
        """
        returns the room that has this name
        """
        for room in self.rooms:
            if room.name == name:
                return room
        return None
163

164
    def init_curses(self, stdscr):
165 166 167
        """
        ncurses initialization
        """
168 169
        curses.start_color()
        curses.noecho()
170
        curses.curs_set(0)
171
        curses.use_default_colors()
172
        stdscr.keypad(True)
173 174
        curses.init_pair(1, curses.COLOR_WHITE,
                         curses.COLOR_BLUE)
175 176 177
        curses.init_pair(4, curses.COLOR_CYAN, -1)
        curses.init_pair(2, curses.COLOR_RED, -1) # Admin
        curses.init_pair(3, curses.COLOR_BLUE, -1) # Participant
178 179 180 181 182
        curses.init_pair(5, curses.COLOR_WHITE, -1) # Visitor
        curses.init_pair(6, curses.COLOR_CYAN, -1)
        curses.init_pair(7, curses.COLOR_GREEN, -1)
        curses.init_pair(8, curses.COLOR_MAGENTA, -1)
        curses.init_pair(9, curses.COLOR_YELLOW, -1)
183 184 185 186 187 188 189 190 191 192
        curses.init_pair(10, curses.COLOR_WHITE,
                         curses.COLOR_CYAN) # current room
        curses.init_pair(11, curses.COLOR_WHITE,
                         curses.COLOR_BLUE) # normal room
        curses.init_pair(12, curses.COLOR_WHITE,
                         curses.COLOR_MAGENTA) # new message room
        curses.init_pair(13, curses.COLOR_WHITE,
                         curses.COLOR_RED) # highlight room
        curses.init_pair(14, curses.COLOR_WHITE,
                         curses.COLOR_YELLOW)
193
        curses.init_pair(15, curses.COLOR_WHITE, # new message in private room
194
                         curses.COLOR_GREEN)
195

196
    def reset_curses(self):
197 198 199 200 201
        """
        Reset terminal capabilities to what they were before ncurses
        init
        """
        curses.echo()
202
        curses.nocbreak()
203
        curses.endwin()
204

205
    def on_connected(self, jid):
206
        """
207
        We are connected when authentification confirmation is received
208
        """
209 210
        self.information(_("Welcome on Poezio \o/!"))
        self.information(_("Your JID is %s") % jid)
211 212

    def join_room(self, room, nick):
213 214 215
        """
        join the specified room (muc), using the specified nick
        """
216
        r = Room(room, nick, self.window)
217 218 219 220 221 222 223 224 225 226 227
        self.current_room().set_color_state(11)
        if self.current_room().nb == 0:
            self.rooms.append(r)
        else:
            for ro in self.rooms:
                if ro.nb == 0:
                    self.rooms.insert(self.rooms.index(ro), r)
                    break
        while self.current_room().nb != r.nb:
            self.rooms.insert(0, self.rooms.pop())
        self.window.refresh(self.rooms)
228

229
    def auto_completion(self):
230 231 232
        """
        Called when Tab is pressed, complete the nickname in the input
        """
233 234
        self.window.input.auto_completion(self.current_room().users)

235
    def rotate_rooms_right(self, args=None):
236 237 238
        """
        rotate the rooms list to the right
        """
239
        self.current_room().set_color_state(11)
240
        self.rooms.append(self.rooms.pop(0))
241
        self.window.refresh(self.rooms)
242

243
    def rotate_rooms_left(self, args=None):
244 245 246
        """
        rotate the rooms list to the right
        """
247
        self.current_room().set_color_state(11)
248
        self.rooms.insert(0, self.rooms.pop())
249
        self.window.refresh(self.rooms)
250

251 252 253 254 255
    def scroll_page_down(self, args=None):
        self.current_room().scroll_down()
        self.window.text_win.refresh(self.current_room())

    def scroll_page_up(self, args=None):
256
        self.current_room().scroll_up(self.window.size)
257 258
        self.window.text_win.refresh(self.current_room())

259
    def room_error(self, room, error, msg):
260 261 262
        """
        Display the error on the room window
        """
263 264
        if not error:
            return
265
        room = self.get_room_by_name(room)
266 267
        code = error.getAttr('code')
        typ = error.getAttr('type')
268 269 270 271
        if error.getTag('text'):
            body = error.getTag('text').getData()
        else:
            body = _('Unknown error')
272 273
        self.add_message_to_room(room, _('Error: %(code)s-%(msg)s: %(body)s' %
                                   {'msg':msg, 'code':code, 'body':body}))
274
        if code == '401':
275
            room.add(_('To provide a password in order to join the room, type "/join / password" (replace "password" by the real password)'))
276

277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
    def private_message(self, stanza):
        """
        When a private message is received
        """
        jid = stanza.getFrom()
        nick_from = stanza.getFrom().getResource()
        room_from = stanza.getFrom().getStripped()
        room = self.get_room_by_name(jid) # 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)
        body = stanza.getBody()
        self.add_message_to_room(room, body, None, nick_from)
        self.window.input.refresh()
        doupdate()

    def open_private_window(self, room_name, user_nick, focus=True):
        complete_jid = room_name+'/'+user_nick
        for room in self.rooms: # if the room exists, focus it and return
            if room.jid:
                if room.jid == complete_jid:
                    self.command_win(str(room.nb))
                    return
        # create the new tab
        own_nick = self.get_room_by_name(room_name).own_nick
        r = Room(complete_jid, own_nick, self.window, complete_jid)
        # insert it in the rooms
        if self.current_room().nb == 0:
            self.rooms.append(r)
        else:
            for ro in self.rooms:
                if ro.nb == 0:
                    self.rooms.insert(self.rooms.index(ro), r)
                    break
        if focus:               # focus the room if needed
            while self.current_room().nb != r.nb:
                self.rooms.insert(0, self.rooms.pop())
        # self.window.new_room(r)
        self.window.refresh(self.rooms)
        return r

317
    def room_message(self, stanza, date=None):
318 319 320
        """
        Display the message on the room window
        """
321 322 323 324 325 326
        delay_tag = stanza.getTag('delay', namespace='urn:xmpp:delay')
        if delay_tag and not date:
            delayed = True
            date = common.datetime_tuple(delay_tag.getAttr('stamp'))
        else:
            delayed = False
327 328 329
        if stanza.getType() != 'groupchat':
            return  # ignore all messages not comming from a MUC
        nick_from = stanza.getFrom().getResource()
330
        room_from = stanza.getFrom().getStripped()
331 332
        if (self.ignores.has_key(room_from)) and (nick_from in self.ignores[room_from]):
            return
333
        room = self.get_room_by_name(room_from)
334
	if not room:
335
	    self.information(_("message received for a non-existing room: %s") % (room_from))
336
            return
337
        body = stanza.getBody()
338 339
        subject = stanza.getSubject()
        if subject:
340
            if nick_from:
341
                self.add_message_to_room(room, _("%(nick)s changed the subject to: %(subject)s") % {'nick':nick_from, 'subject':subject}, date)
342
            else:
343
                self.add_message_to_room(room, _("The subject is: %(subject)s") % {'subject':subject}, date)
344
            room.topic = subject.encode('utf-8').replace('\n', '|')
345 346
            if room == self.current_room():
                self.window.topic_win.refresh(room.topic)
347
        elif body:
348
            if body.startswith('/me '):
349
                self.add_message_to_room(room, "* "+nick_from + ' ' + body[4:], date)
350
            else:
351 352
                date = date if delayed == True else None
                self.add_message_to_room(room, body, date, nick_from)
353
        self.window.input.refresh()
354
        doupdate()
355 356

    def room_presence(self, stanza):
357 358 359 360
        """
        Display the presence on the room window and update the
        presence information of the concerned user
        """
361 362
        from_nick = stanza.getFrom().getResource()
        from_room = stanza.getFrom().getStripped()
363
	room = self.get_room_by_name(from_room)
364
	if not room:
365
            return
366
        else:
367 368 369 370 371 372
            msg = None
            affiliation = stanza.getAffiliation()
            show = stanza.getShow()
            status = stanza.getStatus()
            role = stanza.getRole()
            if not room.joined:     # user in the room BEFORE us.
373 374
                room.users.append(User(from_nick, affiliation, show, status,
                                       role))
375 376
                if from_nick.encode('utf-8') == room.own_nick:
                    room.joined = True
377
                    self.add_message_to_room(room, _("Your nickname is %s") % (from_nick))
378
                else:
379
                    self.add_message_to_room(room, _("%s is in the room") % (from_nick))
380 381 382 383 384 385
            else:
                change_nick = stanza.getStatusCode() == '303'
                kick = stanza.getStatusCode() == '307'
                user = room.get_user_by_name(from_nick)
                # New user
                if not user:
386 387
                    room.users.append(User(from_nick, affiliation,
                                           show, status, role))
388 389
                    hide_exit_join = config.get('hide_exit_join', -1)
                    if hide_exit_join != 0:
390
                        self.add_message_to_room(room, _("%(nick)s joined the room %(roomname)s") % {'nick':from_nick, 'roomname': room.name})
391 392 393 394
                # nick change
                elif change_nick:
                    if user.nick == room.own_nick:
                        room.own_nick = stanza.getNick().encode('utf-8')
395 396 397 398
                        # also change our nick in all private discussion of this room
                        for _room in self.rooms:
                            if _room.jid is not None and is_jid_the_same(_room.jid, room.name):
                                _room.own_nick = stanza.getNick()
399
                    user.change_nick(stanza.getNick())
400
                    self.add_message_to_room(room, _('%(old)s is now known as %(new)s') % {'old':from_nick, 'new':stanza.getNick()})
401 402 403 404 405 406 407 408
                    # rename the private tabs if needed
                    private_room = self.get_room_by_name(stanza.getFrom())
                    if private_room:
                        self.add_message_to_room(private_room, _('%(old_nick)s is now known as %(new_nick)s') % {'old_nick':from_nick, 'new_nick':stanza.getNick()})
                        new_jid = private_room.name.split('/')[0]+'/'+stanza.getNick()
                        private_room.jid = new_jid
                        private_room.name = new_jid

409 410 411
                # kick
                elif kick:
                    room.users.remove(user)
412 413 414 415
                    try:
                        reason = stanza.getReason().encode('utf-8')
                    except:
                        reason = ''
416 417 418 419 420 421 422
                    try:
                        by = stanza.getActor().encode('utf-8')
                    except:
                        by = None
                    if from_nick == room.own_nick: # we are kicked
                        room.disconnect()
                        if by:
423
                            self.add_message_to_room(room,  _("You have been kicked by %(by)s. Reason: %(reason)s") % {'by':by, 'reason':reason})
424
                        else:
425
                            self.add_message_to_room(room, _("You have been kicked. Reason: %s") % (reason))
426 427
                    else:
                        if by:
428
                            self.add_message_to_room(room, _("%(nick)s has been kicked by %(by)s. Reason: %(reason)s") % {'nick':from_nick, 'by':by, 'reason':reason})
429
                        else:
430
                            self.add_message_to_room(room, _("%(nick)s has been kicked. Reason: %(reason)s") % {'nick':from_nick, 'reason':reason})
431 432 433
                # user quit
                elif status == 'offline' or role == 'none':
                    room.users.remove(user)
434 435 436
                    hide_exit_join = config.get('hide_exit_join', -1) if config.get('hide_exit_join', -1) >= -1 else -1
                    if hide_exit_join == -1 or user.has_talked_since(hide_exit_join):
                        self.add_message_to_room(room, _('%s has left the room') % (from_nick))
437 438 439
                    private_room = self.get_room_by_name(stanza.getFrom())
                    if private_room:
                        self.add_message_to_room(private_room, _('%s has left the room') % (from_nick))
440 441
                # status change
                else:
442 443 444 445 446 447 448 449 450 451 452
                    # build the message
                    msg = _('%s changed his/her status: ')% from_nick
                    if affiliation != user.affiliation:
                        msg += _('affiliation: %s,') % affiliation
                    if role != user.role:
                        msg += _('role: %s,') % role
                    if show != user.show:
                        msg += _('show: %s,') % show
                    if status != user.status:
                        msg += _('status: %s,') % status
                    msg = msg[:-1] # remove the last ","
453
                    hide_status_change = config.get('hide_status_change', -1) if config.get('hide_status_change', -1) >= -1 else -1
454
                    if (hide_status_change == -1 or \
455
                            user.has_talked_since(hide_status_change) or\
456 457 458 459 460 461
                            user.nick == room.own_nick)\
                            and\
                            (affiliation != user.affiliation or\
                                role != user.role or\
                                show != user.show or\
                                status != user.status):
462
                        # display the message in the room
463
                        self.add_message_to_room(room, msg)
464 465 466 467 468
                    private_room = self.get_room_by_name(stanza.getFrom())
                    if private_room: # display the message in private
                        self.add_message_to_room(private_room, msg)
                    # finally, effectively change the user status
                    user.update(affiliation, show, status, role)
469 470
            if room == self.current_room():
                self.window.user_win.refresh(room.users)
471
        self.window.input.refresh()
472
        self.window.info_win.refresh(self.rooms, self.current_room())
473
        doupdate()
474

475
    def add_message_to_room(self, room, txt, time=None, nickname=None):
476
        """
477 478
        Add the message to the room and refresh the associated component
        of the interface
479
        """
480
        room.add_message(txt, time, nickname)
481
        if room == self.current_room():
482 483
            self.window.text_win.refresh(room)
        else:
484
            self.window.info_win.refresh(self.rooms, self.current_room())
485 486

    def execute(self):
487 488 489
        """
        Execute the /command or just send the line on the current room
        """
490 491
        line = self.window.input.get_text()
        self.window.input.clear_text()
492
        self.window.input.refresh()
493 494
        if line == "":
            return
495
        if line.startswith('/') and not line.startswith('/me '):
496 497 498
            command = line.strip()[:].split()[0][1:]
            args = line.strip()[:].split()[1:]
            if command in self.commands.keys():
499
                func = self.commands[command][0]
500
                func(args)
501
                return
502
            else:
503
                self.add_message_to_room(self.current_room(), _("Error: unknown command (%s)") % (command))
504
        elif self.current_room().name != 'Info':
505 506 507 508 509
            if self.current_room().jid is not None:
                self.muc.send_private_message(self.current_room().name, line)
                self.add_message_to_room(self.current_room(), line.decode('utf-8'), None, self.current_room().own_nick)
            else:
                self.muc.send_message(self.current_room().name, line)
510
        self.window.input.refresh()
511
        doupdate()
512

513
    def command_help(self, args):
514 515 516
        """
        /help <command_name>
        """
517 518
        room = self.current_room()
        if len(args) == 0:
519
            msg = _('Available commands are: ')
520 521
            for command in self.commands.keys():
                msg += "%s " % command
522
            msg += _("\nType /help <command_name> to know what each command does")
523 524 525 526
        if len(args) == 1:
            if args[0] in self.commands.keys():
                msg = self.commands[args[0]][1]
            else:
527
                msg = _('Unknown command: %s') % args[0]
528
        self.add_message_to_room(room, msg)
529

530
    def command_win(self, args):
531 532 533
        """
        /win <number>
        """
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
        if len(args) != 1:
            self.command_help(['win'])
            return
        try:
            nb = int(args[0])
        except ValueError:
            self.command_help(['win'])
            return
        if self.current_room().nb == nb:
            return
        self.current_room().set_color_state(11)
        start = self.current_room()
        self.rooms.append(self.rooms.pop(0))
        while self.current_room().nb != nb:
            self.rooms.append(self.rooms.pop(0))
            if self.current_room() == start:
                self.window.refresh(self.rooms)
                return
        self.window.refresh(self.rooms)

554
    def command_kick(self, args):
555 556 557
        """
        /kick <nick> [reason]
        """
558 559 560 561 562 563 564 565 566 567 568 569 570
        if len(args) < 1:
            self.command_help(['kick'])
            return
        nick = args[0]
        if len(args) >= 2:
            reason = ' '.join(args[1:])
        else:
            reason = ''
        if self.current_room().name == 'Info' or not self.current_room().joined:
            return
        roomname = self.current_room().name
        self.muc.eject_user(roomname, 'kick', nick, reason)

571
    def command_join(self, args):
572 573 574
        """
        /join [room][/nick] [password]
        """
575
        password = None
576 577 578 579 580 581
        if len(args) == 0:
            r = self.current_room()
            if r.name == 'Info':
                return
            room = r.name
            nick = r.own_nick
582
        else:
583 584 585 586 587
            info = args[0].split('/')
            if len(info) == 1:
                nick = config.get('default_nick', 'Poezio')
            else:
                nick = info[1]
588
            if info[0] == '':   # happens with /join /nickname, which is OK
589 590 591 592
                r = self.current_room()
                if r.name == 'Info':
                    return
                room = r.name
593 594
                if nick == '':
                    nick = r.own_nick
595 596 597
            else:
                room = info[0]
            r = self.get_room_by_name(room)
598 599
        if len(args) == 2:       # a password is provided
            password = args[1]
600
        if r and r.joined:                   # if we are already in the room
601
            self.information(_("already in room [%s]") % room)
602
            return
603
        self.muc.join_room(room, nick, password)
604
        if not r:   # if the room window exists, we don't recreate it.
605
            self.join_room(room, nick)
606
        else:
607
            # r.own_nick = nick
608
            r.users = []
609

610
    def command_bookmark(self, args):
611 612 613
        """
        /bookmark [room][/nick]
        """
614 615 616 617 618 619 620 621 622 623 624 625 626
        nick = None
        if len(args) == 0:
            room = self.current_room()
            if room.name == 'Info':
                return
            roomname = room.name
            if room.joined:
                nick = room.own_nick
        else:
            info = args[0].split('/')
            if len(info) == 2:
                nick = info[1]
            roomname = info[0]
627 628
            if roomname == '':
                roomname = self.current_room().name
629 630 631 632
        if nick:
            res = roomname+'/'+nick
        else:
            res = roomname
633 634 635 636 637 638 639 640 641
        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 room.split('/')[0] == roomname:
                bookmarked.remove(room)
                break
        bookmarked = ':'.join(bookmarked)
642
        config.set_and_save('rooms', bookmarked+':'+res)
643

644
    def command_set(self, args):
645 646 647
        """
        /set <option> [value]
        """
648
        if len(args) != 2 and len(args) != 1:
649 650 651
            self.command_help(['set'])
            return
        option = args[0]
652 653 654 655
        if len(args) == 2:
            value = args[1]
        else:
            value = ''
656
        config.set_and_save(option, value)
657 658
        msg = "%s=%s" % (option, value)
        room = self.current_room()
659
        self.add_message_to_room(room, msg)
660

661
    def command_show(self, args):
662 663 664
        """
        /show <status> [msg]
        """
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
        possible_show = {'avail':'None',
                         'available':'None',
                         'ok':'None',
                         'here':'None',
                         'chat':'chat',
                         'away':'away',
                         'afk':'away',
                         'dnd':'dnd',
                         'busy':'dnd',
                         'xa':'xa'
                         }
        if len(args) < 1:
            return
        if not args[0] in possible_show.keys():
            self.command_help(['show'])
            return
        show = possible_show[args[0]]
        if len(args) > 1:
            msg = ' '.join(args[1:])
        else:
            msg = None
        for room in self.rooms:
            if room.joined:
                self.muc.change_show(room.name, room.own_nick, show, msg)

690 691 692 693 694
    def command_ignore(self, args):
        """
        /ignore <nick>
        """
        if len(args) != 1:
695 696 697
            self.command_help(['ignore'])
            return
        if self.current_room().name == 'Info' or not self.current_room().joined:
698
            return
699 700 701 702 703 704 705 706 707
        roomname = self.current_room().name
        nick = args[0]
        if not self.ignores.has_key(roomname):
            self.ignores[roomname] = set() # no need for any order
        if nick not in self.ignores[roomname]:
            self.ignores[roomname].add(nick)
            self.add_message_to_room(self.current_room(), _("%s is now ignored") % nick)
        else:
            self.add_message_to_room(self.current_room(), _("%s is already ignored") % nick)
708 709 710 711 712 713

    def command_unignore(self, args):
        """
        /unignore <nick>
        """
        if len(args) != 1:
714 715 716 717 718 719 720 721
            self.command_help(['unignore'])
            return
        if self.current_room().name == 'Info' or not self.current_room().joined:
            return
        roomname = self.current_room().name
        nick = args[0]
        if not self.ignores.has_key(roomname) or (nick not in self.ignores[roomname]):
            self.add_message_to_room(self.current_room(), _("%s was not ignored") % nick)
722
            return
723 724 725 726
        self.ignores[roomname].remove(nick)
        if self.ignores[roomname] == set():
            del self.ignores[roomname]
        self.add_message_to_room(self.current_room(), _("%s is now unignored") % nick)
727

728
    def command_away(self, args):
729 730 731
        """
        /away [msg]
        """
732 733 734 735
        args.insert(0, 'away')
        self.command_show(args)

    def command_busy(self, args):
736 737 738
        """
        /busy [msg]
        """
739 740 741 742
        args.insert(0, 'busy')
        self.command_show(args)

    def command_avail(self, args):
743 744 745
        """
        /avail [msg]
        """
746 747
        args.insert(0, 'available')
        self.command_show(args)
748

749
    def command_part(self, args):
750 751 752
        """
        /part [msg]
        """
753 754 755 756 757 758 759 760
        reason = None
        room = self.current_room()
        if room.name == 'Info':
            return
        if len(args):
            msg = ' '.join(args)
        else:
            msg = None
761 762
        if room.joined:
            self.muc.quit_room(room.name, room.own_nick, msg)
763
        self.rooms.remove(self.current_room())
764
        self.window.refresh(self.rooms)
765

766
    def command_query(self, args):
767 768 769
        """
        /query
        """
770 771 772 773 774 775 776 777 778 779
        if len(args) != 1:
            return
        nick = args[0]
        room = self.current_room()
        if room.name == "Info" or room.jid is not None:
            return
        for user in room.users:
            if user.nick == nick:
                self.open_private_window(room.name, user.nick)

780
    def command_topic(self, args):
781 782 783
        """
        /topic [new topic]
        """
784
        room = self.current_room()
785
        if len(args) == 0:
786
            self.add_message_to_room(room, _("The subject of the room is: %s") % room.topic.decode('utf-8'))
787
        subject = ' '.join(args)
788 789 790 791
        if not room.joined or room.name == "Info":
            return
        self.muc.change_subject(room.name, subject)

792
    def command_nick(self, args):
793 794 795
        """
        /nick <nickname>
        """
796 797 798 799 800 801 802 803
        if len(args) != 1:
            return
        nick = args[0]
        room = self.current_room()
        if not room.joined or room.name == "Info":
            return
        self.muc.change_nick(room.name, nick)

804
    def information(self, msg):
805 806 807
        """
        Displays an informational message in the "Info" room window
        """
808
        room = self.get_room_by_name("Info")
809
        self.add_message_to_room(room, msg)
810

811
    def command_quit(self, args):
812 813 814
        """
        /quit
        """
815 816 817 818 819 820 821
        if len(args):
            msg = ' '.join(args)
        else:
            msg = None
        if msg:
            self.muc.disconnect(self.rooms, msg)
            sleep(0.2)          # :(
822
	self.reset_curses()
823
        sys.exit()