gui.py 9.47 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 92
        change_nick = stanza.getStatusCode() == '303'
        for user in self.users:
93
            if user.nick == nick:
94 95
                if change_nick:
                    user.change_nick(stanza.getNick())
96
                    return self.add_info('%s is now known as %s' % (nick, stanza.getNick()))
97
                if status == 'offline' or role == 'none':
98
                    self.users.remove(user)
99
                    return self.add_info('%s has left the room' % (nick))
100
                user.update(affiliation, show, status, role)
101
                return self.add_info('%s, status : %s, %s, %s, %s' % (nick, affiliation, role, show, status))
102
        self.users.append(User(nick, affiliation, show, status, role))
103
        return self.add_info('%s joined the room %s' % (nick, self.name))
104

105 106 107 108
class Gui(object):
    """
    Graphical user interface using ncurses
    """
109 110 111 112
    def __init__(self, stdscr=None, muc=None):

        self.init_curses(stdscr)
        self.stdscr = stdscr
113
        self.stdscr.leaveok(1)
114 115
        self.rooms = [Room('Info', '')]         # current_room is self.rooms[0]
        self.window = Window(stdscr)
116
        self.window.text_win.new_win('Info')
117 118 119
        self.window.refresh(self.rooms[0])

        self.muc = muc
120

121 122 123
        self.commands = {
            'join': self.command_join,
            'quit': self.command_quit,
124 125
            'next': self.rotate_rooms_left,
            'prev': self.rotate_rooms_right,
126 127
            }

128 129 130 131 132 133 134
        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,
135
            "KEY_DC": self.window.input.key_dc,
136 137 138
            "KEY_BACKSPACE": self.window.input.key_backspace
            }

139 140 141 142 143 144
        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)

145 146 147 148
    def main_loop(self, stdscr):
        while 1:
            curses.doupdate()
            key = stdscr.getkey()
149
            if str(key) in self.key_func.keys():
150
                self.key_func[key]()
151 152
            elif len(key) >= 4:
                continue
153 154
            elif ord(key) == 10:
                self.execute()
155
            elif ord(key) == 8 or ord(key) == 127:
156
                self.window.input.key_backspace()
157 158
            elif ord(key) < 32:
                continue
159
            else:
160 161 162 163
                if ord(key) == 27 and ord(stdscr.getkey()) == 91 \
                        and ord(stdscr.getkey()) == 51: # FIXME: ugly ugly workaroung.
                    self.window.input.key_dc()
                elif ord(key) > 190 and ord(key) < 225:
164 165 166 167 168 169
                    key = key+stdscr.getkey()
                elif ord(key) == 226:
                    key = key+stdscr.getkey()
                    key = key+stdscr.getkey()
                self.window.do_command(key)

170 171 172 173 174 175 176 177 178
    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

179 180 181
    def init_curses(self, stdscr):
        curses.start_color()
        curses.noecho()
182
        stdscr.keypad(True)
183 184
        curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
        curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK)
185 186 187
        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
188 189 190 191
        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)
192

193 194
    def reset_curses(self):
	curses.echo()
195
        curses.endwin()
196

197 198 199 200
    def on_connected(self):
        pass

    def join_room(self, room, nick):
201
        self.window.text_win.new_win(room)
202
        self.rooms.insert(0, Room(room, nick))
203
        self.window.refresh(self.current_room())
204 205 206

    def rotate_rooms_left(self, args):
        self.rooms.append(self.rooms.pop(0))
207
        self.window.refresh(self.current_room())
208 209 210

    def rotate_rooms_right(self, args):
        self.rooms.insert(0, self.rooms.pop())
211
        self.window.refresh(self.current_room())
212 213 214 215 216 217

    def room_message(self, stanza):
        if stanza.getType() != 'groupchat':
            return  # ignore all messages not comming from a MUC
        room_from = stanza.getFrom().getStripped()
        nick_from = stanza.getFrom().getResource()
218 219 220
        if not nick_from:
            nick_from = ''
	room = self.get_room_by_name(room_from)
221 222
	if not room:
	    return logger.warning("message received for a non-existing room: %s" % (name))
223 224 225 226 227 228
        body = stanza.getBody()
        if not body:
            body = stanza.getSubject()
            room.add_info("%s changed the subject to: %s" % (nick_from, stanza.getSubject()))
        else:
            room.add_message(nick_from, body)
229
            self.window.text_win.add_line(room, (datetime.now(), nick_from.encode('utf-8'), body.encode('utf-8')))
230
        if room == self.current_room():
231
            self.window.text_win.refresh(room.name)
232 233
            self.window.input.refresh()
            curses.doupdate()
234 235 236 237

    def room_presence(self, stanza):
        from_nick = stanza.getFrom().getResource()
        from_room = stanza.getFrom().getStripped()
238
	room = self.get_room_by_name(from_room)
239 240
	if not room:
	    return logger.warning("presence received for a non-existing room: %s" % (name))
241
        msg = room.on_presence(stanza, from_nick)
242
        if room == self.current_room():
243 244 245 246
            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()
247 248

    def execute(self):
249 250 251 252
        line = self.window.input.get_text()
        self.window.input.clear_text()
        if line == "":
            return
253 254 255 256 257 258
        if line.strip().startswith('/'):
            command = line.strip()[:].split()[0][1:]
            args = line.strip()[:].split()[1:]
            if command in self.commands.keys():
                func = self.commands[command]
                func(args)
259
                return
260 261 262
        if self.current_room().name != 'Info':
            self.muc.send_message(self.current_room().name, line)
	self.window.input.refresh()
263 264 265

    def command_join(self, args):
        room = args[0]
266 267
        self.muc.join_room(room, "poezio")
        self.join_room(room, 'poezio')
268 269

    def command_quit(self, args):
270
	self.reset_curses()
271
        sys.exit()