gui.py 9.2 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
        # lines = msg.split('\n')
73
        # first line has the nick and timestamp but others don't
74 75 76 77
        self.lines.append((datetime.now(), nick.encode('utf-8'), msg.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
        return info.encode('utf-8')
83

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

110 111 112 113
class Gui(object):
    """
    Graphical user interface using ncurses
    """
114 115 116 117
    def __init__(self, stdscr=None, muc=None):

        self.init_curses(stdscr)
        self.stdscr = stdscr
118
        self.stdscr.leaveok(1)
119 120
        self.rooms = [Room('Info', '')]         # current_room is self.rooms[0]
        self.window = Window(stdscr)
121
        self.window.text_win.new_win('Info')
122 123 124
        self.window.refresh(self.rooms[0])

        self.muc = muc
125

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

133 134 135 136 137 138
        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)

139 140 141 142 143 144 145 146 147
    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

148 149 150 151 152
    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)
153 154 155
        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
156 157 158 159
        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)
160

161 162
    def reset_curses(self):
	curses.echo()
163
        curses.endwin()
164

165 166 167 168
    def on_connected(self):
        pass

    def join_room(self, room, nick):
169
        self.window.text_win.new_win(room)
170
        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.stdscr.touchwin()
176
        self.window.refresh(self.current_room())
177 178 179

    def rotate_rooms_right(self, args):
        self.rooms.insert(0, self.rooms.pop())
180
#        self.stdscr.touchwin()
181
        self.window.refresh(self.current_room())
182 183 184 185 186 187

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

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

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

    def command_join(self, args):
        room = args[0]
236 237
        self.muc.join_room(room, "poezio")
        self.join_room(room, 'poezio')
238 239

    def command_quit(self, args):
240
	self.reset_curses()
241
        sys.exit()
242

243 244
    def main_loop(self, stdscr):
        while 1:
245
            curses.doupdate()
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
            key = stdscr.getkey()
            if ord(key) == 195:
                n = stdscr.getkey()
                key = key+n
                self.window.input.win.addstr(key)
                self.window.input.add_char(key)
                self.window.input.win.refresh()
            elif ord(key) == 226:
                n = stdscr.getkey()
                m = stdscr.getkey()
                key = key+n+m
                self.window.input.win.addstr(key)
                self.window.input.add_char(key)
                self.window.input.win.refresh()

            elif key == curses.KEY_RESIZE:
262
                self.window.resize(stdscr)
263
            elif ord(key) == 10:
264 265
                self.execute()
            else:
266
                self.window.do_command(ord(key))