gui.py 8.36 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 72 73 74 75 76 77
        if not msg:
            logger.info('msg is None..., %s' % (nick))
            return
        lines = msg.split('\n')
        # first line has the nick and timestamp but others don't
        self.lines.append((datetime.now(), nick.encode('utf-8'), lines.pop(0).encode('utf-8')))
        if len(lines) > 0:
            for line in lines:
                self.lines.append((line.encode('utf-8')))
78

79 80 81
    def add_info(self, info):
        """ info, like join/quit/status messages"""
        self.lines.append((datetime.now(), info.encode('utf-8')))
82

83
    def on_presence(self, stanza, nick):
84 85
        """
        """
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
        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))
             if nick == self.own_nick:
                 self.joined = True
             self.add_info("%s is in the room" % (nick))
             return
        change_nick = stanza.getStatusCode() == '303'
        for user in self.users:
            if user.nick == nick:
                if change_nick:
                    user.change_nick(stanza.getNick())
                    self.add_info('%s is now known as %s' % (nick, stanza.getNick()))
                    return
103
                if status == 'offline' or role == 'none':
104 105 106 107 108 109 110 111
                    self.users.remove(user)
                    self.add_info('%s has left the room' % (nick))
                    return
                user.update(affiliation, show, status, role)
                self.add_info('%s, status : %s, %s, %s, %s' % (nick, affiliation, role, show, status))
                return
        self.users.append(User(nick, affiliation, show, status, role))
        self.add_info('%s joined the room %s' % (nick, self.name))
112

113 114 115 116
class Gui(object):
    """
    Graphical user interface using ncurses
    """
117 118 119 120 121 122 123 124 125 126
    def __init__(self, stdscr=None, muc=None):

        self.init_curses(stdscr)
        self.stdscr = stdscr
        self.stdscr.leaveok(True)
        self.rooms = [Room('Info', '')]         # current_room is self.rooms[0]
        self.window = Window(stdscr)
        self.window.refresh(self.rooms[0])

        self.muc = muc
127

128 129 130
        self.commands = {
            'join': self.command_join,
            'quit': self.command_quit,
131 132
            'next': self.rotate_rooms_left,
            'prev': self.rotate_rooms_right,
133 134
            }

135 136 137 138 139 140
        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)

141 142 143 144 145 146 147 148 149
    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

150 151 152 153 154
    def init_curses(self, stdscr):
        curses.start_color()
        curses.noecho()
        curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
        curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK)
155 156 157
        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
158 159 160 161
        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)
162

163 164 165
    def reset_curses(self):
	curses.echo()

166 167 168 169 170
    def on_connected(self):
        pass

    def join_room(self, room, nick):
        self.rooms.insert(0, Room(room, nick))
171
        self.window.refresh(self.current_room())
172 173 174

    def rotate_rooms_left(self, args):
        self.rooms.append(self.rooms.pop(0))
175
        self.window.refresh(self.current_room())
176 177 178

    def rotate_rooms_right(self, args):
        self.rooms.insert(0, self.rooms.pop())
179
        self.window.refresh(self.current_room())
180 181 182 183 184 185

    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()
186 187 188
        if not nick_from:
            nick_from = ''
	room = self.get_room_by_name(room_from)
189 190
	if not room:
	    return logger.warning("message received for a non-existing room: %s" % (name))
191 192 193 194 195 196
        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)
197
        if room == self.current_room():
198
            self.window.text_win.refresh(room.lines, room.users)
199 200 201
            self.window.user_win.refresh(room.users)
            self.window.input.refresh()
            curses.doupdate()
202 203 204 205

    def room_presence(self, stanza):
        from_nick = stanza.getFrom().getResource()
        from_room = stanza.getFrom().getStripped()
206
	room = self.get_room_by_name(from_room)
207 208 209
	if not room:
	    return logger.warning("presence received for a non-existing room: %s" % (name))
        room.on_presence(stanza, from_nick)
210 211
        if room == self.current_room():
	        self.window.text_win.refresh(room.lines, room.users)
212 213
                self.window.user_win.refresh(room.users)
                curses.doupdate()
214 215

    def execute(self):
216 217 218 219
        line = self.window.input.get_text()
        self.window.input.clear_text()
        if line == "":
            return
220 221 222 223 224 225 226
        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)
            return
227 228 229
        if self.current_room().name != 'Info':
            self.muc.send_message(self.current_room().name, line)
	self.window.input.refresh()
230 231 232

    def command_join(self, args):
        room = args[0]
233 234
        self.muc.join_room(room, "poezio")
        self.join_room(room, 'poezio')
235 236

    def command_quit(self, args):
237
	self.reset_curses()
238
        sys.exit()
239

240 241
    def main_loop(self, stdscr):
        while 1:
242
            curses.doupdate()
243 244
            key = stdscr.getch()
            if key == curses.KEY_RESIZE:
245
                self.window.resize(stdscr)
246
            elif key == 10:
247 248
                self.execute()
            else:
249
                self.window.do_command(key)