gui.py 11.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
#!/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/>.

from handler import Handler
import curses
from curses import textpad

24
import locale
25 26
from datetime import datetime

27 28
from logging import logger

29 30
from random import randrange

31 32 33
locale.setlocale(locale.LC_ALL, '')
code = locale.getpreferredencoding()

34 35
import sys

36
from connection import *
37
from window import Window
38

39
class User(object):
40
    """
41
    keep trace of an user in a Room
42
    """
43 44 45
    def __init__(self, nick, affiliation, show, status, role):
        self.update(affiliation, show, status, role)
        self.change_nick(nick)
46
        self.color = randrange(2, 10)
47

48 49 50 51 52
    def update(self, affiliation, show, status, role):
        self.affiliation = None
        self.show = None
        self.status = status
        self.role = role
53

54
    def change_nick(self, nick):
55
        self.nick = nick.encode('utf-8')
56

57
class Room(object):
58 59
    """
    """
60
    def __init__(self, name, nick):
61
        self.name = name
62 63 64 65 66
        self.own_nick = nick
        self.joined = False     # false until self presence is received
        self.users = []
        self.lines = []         # (time, nick, msg) or (time, info)
        self.topic = ''
67

68
    def add_message(self, nick, msg):
69 70 71
        if not msg:
            logger.info('msg is None..., %s' % (nick))
            return
72
        self.lines.append((datetime.now(), nick.encode('utf-8'), msg.encode('utf-8')))
73

74 75 76
    def add_info(self, info):
        """ info, like join/quit/status messages"""
        self.lines.append((datetime.now(), info.encode('utf-8')))
77
        return info.encode('utf-8')
78

79
    def on_presence(self, stanza, nick):
80 81
        """
        """
82 83 84 85 86 87
        affiliation = stanza.getAffiliation()
        show = stanza.getShow()
        status = stanza.getStatus()
        role = stanza.getRole()
        if not self.joined:
             self.users.append(User(nick, affiliation, show, status, role))
88
             if nick.encode('utf-8') == self.own_nick:
89
                 self.joined = True
90
             return self.add_info("%s is in the room" % (nick))
91
        change_nick = stanza.getStatusCode() == '303'
92
        kick = stanza.getStatusCode() == '307'
93
        for user in self.users:
94
            if user.nick == nick:
95 96
                if change_nick:
                    user.change_nick(stanza.getNick())
97
                    return self.add_info('%s is now known as %s' % (nick, stanza.getNick()))
98
                if kick:
99
                    self.users.remove(user)
100
                    reason = stanza.getReason().encode('utf-8') or ''
101 102 103 104
                    try:
                        by = stanza.getActor().encode('utf-8')
                    except:
                        by = None
105 106
                    if nick == self.own_nick:
                        self.joined = False
107 108 109 110
                        if by:
                            return self.add_info('You have been kicked by %s. Reason: %s' % (by, reason))
                        else:
                            return self.add_info('You have been kicked. Reason: %s' % (reason))
111
                    else:
112 113 114 115
                        if by:
                            return self.add_info('%s has been kicked by %s. Reason: %s' % (nick, by, reason))
                        else:
                            return self.add_info('%s has been kicked. Reason: %s' % (nick, reason))
116
                if status == 'offline' or role == 'none':
117
                    self.users.remove(user)
118
                    return self.add_info('%s has left the room' % (nick))
119
                user.update(affiliation, show, status, role)
120
                return self.add_info('%s, status : %s, %s, %s, %s' % (nick, affiliation, role, show, status))
121
        self.users.append(User(nick, affiliation, show, status, role))
122
        return self.add_info('%s joined the room %s' % (nick, self.name))
123

124 125 126 127
class Gui(object):
    """
    Graphical user interface using ncurses
    """
128 129 130 131
    def __init__(self, stdscr=None, muc=None):

        self.init_curses(stdscr)
        self.stdscr = stdscr
132
        self.stdscr.leaveok(1)
133 134
        self.rooms = [Room('Info', '')]         # current_room is self.rooms[0]
        self.window = Window(stdscr)
135
        self.window.text_win.new_win('Info')
136 137 138
        self.window.refresh(self.rooms[0])

        self.muc = muc
139

140 141 142
        self.commands = {
            'join': self.command_join,
            'quit': self.command_quit,
143 144
            'next': self.rotate_rooms_left,
            'prev': self.rotate_rooms_right,
145 146
            }

147 148 149 150 151 152 153
        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,
154
            "KEY_DC": self.window.input.key_dc,
155 156 157 158
            "KEY_F(5)": self.rotate_rooms_left,
            "KEY_F(6)": self.rotate_rooms_right,
            "kLFT5": self.rotate_rooms_left,
            "kRIT5": self.rotate_rooms_right,
159 160 161
            "KEY_BACKSPACE": self.window.input.key_backspace
            }

162 163 164 165 166
        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)
167
        self.handler.connect('room-iq', self.room_iq)
168

169 170 171 172
    def main_loop(self, stdscr):
        while 1:
            curses.doupdate()
            key = stdscr.getkey()
173 174
            # print key
            # sys.exit()
175
            if str(key) in self.key_func.keys():
176
                self.key_func[key]()
177 178
            elif len(key) >= 4:
                continue
179 180
            elif ord(key) == 10:
                self.execute()
181
            elif ord(key) == 8 or ord(key) == 127:
182
                self.window.input.key_backspace()
183 184
            elif ord(key) < 32:
                continue
185
            else:
186 187 188 189 190
                if ord(key) == 27 and ord(stdscr.getkey()) == 91:
                    last = ord(stdscr.getkey()) # FIXME: ugly ugly workaroung.
                    if last == 51:
                        self.window.input.key_dc()
                    continue
191
                elif ord(key) > 190 and ord(key) < 225:
192 193 194 195 196 197
                    key = key+stdscr.getkey()
                elif ord(key) == 226:
                    key = key+stdscr.getkey()
                    key = key+stdscr.getkey()
                self.window.do_command(key)

198 199 200 201 202 203 204 205 206
    def current_room(self):
	return self.rooms[0]

    def get_room_by_name(self, name):
	for room in self.rooms:
	    if room.name == name:
		return room
	return None

207 208 209
    def init_curses(self, stdscr):
        curses.start_color()
        curses.noecho()
210
        stdscr.keypad(True)
211 212
        curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
        curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK)
213 214 215
        curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK) # Admin
        curses.init_pair(4, curses.COLOR_BLUE, curses.COLOR_BLACK) # Participant
        curses.init_pair(5, curses.COLOR_WHITE, curses.COLOR_BLACK) # Visitor
216 217 218 219
        curses.init_pair(6, curses.COLOR_CYAN, curses.COLOR_BLACK)
        curses.init_pair(7, curses.COLOR_GREEN, curses.COLOR_BLACK)
        curses.init_pair(8, curses.COLOR_MAGENTA, curses.COLOR_BLACK)
        curses.init_pair(9, curses.COLOR_YELLOW, curses.COLOR_BLACK)
220

221 222
    def reset_curses(self):
	curses.echo()
223
        curses.endwin()
224

225
    def on_connected(self, jid):
226
        self.information("Welcome on Poezio \o/ !")
227
        self.information("Your JID is %s" % jid)
228 229 230
        pass

    def join_room(self, room, nick):
231
        self.window.text_win.new_win(room)
232
        self.rooms.insert(0, Room(room, nick))
233
        self.window.refresh(self.current_room())
234

235
    def rotate_rooms_left(self, args=None):
236
        self.rooms.append(self.rooms.pop(0))
237
        self.window.refresh(self.current_room())
238

239
    def rotate_rooms_right(self, args=None):
240
        self.rooms.insert(0, self.rooms.pop())
241
        self.window.refresh(self.current_room())
242 243

    def room_message(self, stanza):
244 245
        if len(sys.argv) > 1:
            self.information(str(stanza))
246 247 248 249
        if stanza.getType() != 'groupchat':
            return  # ignore all messages not comming from a MUC
        room_from = stanza.getFrom().getStripped()
        nick_from = stanza.getFrom().getResource()
250 251 252
        if not nick_from:
            nick_from = ''
	room = self.get_room_by_name(room_from)
253
	if not room:
254 255
	    self.information("message received for a non-existing room: %s" % (name))
            return
256 257 258
        body = stanza.getBody()
        if not body:
            body = stanza.getSubject()
259 260 261 262 263 264
            info = room.add_info("%s changed the subject to: %s" % (nick_from, stanza.getSubject()))
            self.window.text_win.add_line(room, (datetime.now(), info))
            room.topic = stanza.getSubject().encode('utf-8').replace('\n', '|')
            if room == self.current_room():
                self.window.topic_win.refresh(room.topic)
            curses.doupdate()
265 266
        else:
            room.add_message(nick_from, body)
267
            self.window.text_win.add_line(room, (datetime.now(), nick_from.encode('utf-8'), body.encode('utf-8')))
268
        if room == self.current_room():
269
            self.window.text_win.refresh(room.name)
270
            self.window.input.refresh()
271
        curses.doupdate()
272 273

    def room_presence(self, stanza):
274 275
        if len(sys.argv) > 1:
            self.information(str(stanza))
276 277
        from_nick = stanza.getFrom().getResource()
        from_room = stanza.getFrom().getStripped()
278
	room = self.get_room_by_name(from_room)
279
	if not room:
280
	    self.information("presence received for a non-existing room: %s" % (name))
281
        msg = room.on_presence(stanza, from_nick)
282
        if room == self.current_room():
283 284 285 286
            self.window.text_win.add_line(room, (datetime.now(), msg))
            self.window.text_win.refresh(room.name)
            self.window.user_win.refresh(room.users)
            curses.doupdate()
287

288 289 290 291
    def room_iq(self, iq):
        if len(sys.argv) > 1:
            self.information(str(iq))

292
    def execute(self):
293 294 295 296
        line = self.window.input.get_text()
        self.window.input.clear_text()
        if line == "":
            return
297
        if line.startswith('/'):
298 299 300 301 302
            command = line.strip()[:].split()[0][1:]
            args = line.strip()[:].split()[1:]
            if command in self.commands.keys():
                func = self.commands[command]
                func(args)
303
                return
304 305 306
        if self.current_room().name != 'Info':
            self.muc.send_message(self.current_room().name, line)
	self.window.input.refresh()
307 308 309

    def command_join(self, args):
        room = args[0]
310 311 312 313
        r = self.get_room_by_name(room)
        if r:                   # if we are already in the room
            self.information("already in room [%s]" % room)
            return
314 315
        self.muc.join_room(room, "poezio")
        self.join_room(room, 'poezio')
316

317 318 319
    def information(self, msg):
        room = self.get_room_by_name("Info")
        info = room.add_info(msg)
320 321
        if self.current_room() == room:
            self.window.text_win.add_line(room, (datetime.now(), info))
322 323
            self.window.text_win.refresh(room.name)
            curses.doupdate()
324

325
    def command_quit(self, args):
326
	self.reset_curses()
327
        sys.exit()