gui.py 9.37 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 25 26 27
import locale
locale.setlocale(locale.LC_ALL, '')
code = locale.getpreferredencoding()

28 29
import sys

30 31
from connection import *

32 33
class Win(object):
    def __init__(self, height, width, y, x, parent_win):
34 35 36
        self._resize(height, width, y, x, parent_win)

    def _resize(self, height, width, y, x, parent_win):
37
        self.height, self.width, self.x, self.y = height, width, x, y
38 39 40 41
        try:
            self.win = parent_win.subwin(height, width, y, x)
        except:
            pass
42 43 44 45

class UserList(Win):
    def __init__(self, height, width, y, x, parent_win):
        Win.__init__(self, height, width, y, x, parent_win)
46 47 48
        self.win.attron(curses.color_pair(2))
        self.win.vline(0, 0, curses.ACS_VLINE, self.height)
        self.win.attroff(curses.color_pair(2))
49 50 51 52 53 54 55 56
        self.list = []

    def add_user(self, name):
        """
        add an user to the list
        """
        self.list.append(name)

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
    def refresh(self):
        self.win.clear()
        self.win.attron(curses.color_pair(2))
        self.win.vline(0, 0, curses.ACS_VLINE, self.height)
        self.win.attroff(curses.color_pair(2))
        y = 0
        for name in self.list:
            self.win.addstr(y, 1, name)
            y += 1
        self.win.refresh()

    def resize(self, height, width, y, x, stdscr):
        self._resize(height, width, y, x, stdscr)
        self.refresh()

72 73 74
class Info(Win):
    def __init__(self, height, width, y, x, parent_win):
        Win.__init__(self, height, width, y, x, parent_win)
75
        self.txt = ""
76 77 78
#        self.win.bkgd(ord('p'), curses.COLOR_BLUE)

    def set_info(self, text):
79 80 81 82 83 84 85 86
        self.txt = text
        self.refresh()

    def resize(self, height, width, y, x, stdscr):
        self._resize(height, width, y, x, stdscr)
        self.refresh()

    def refresh(self):
87
        self.win.clear()
88 89
        try:
            self.win.addstr(0, 0, self.txt + " "*(self.width-len(self.txt)-1)
90
                        , curses.color_pair(1))
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
        except:
            pass

class TextWin(Win):
    def __init__(self, height, width, y, x, parent_win):
        Win.__init__(self, height, width, y, x, parent_win)
        self.lines = []

    def add_line(self, time, nick, text):
        self.lines.append((time, nick, text))
        self.refresh()

    def refresh(self):
        self.win.clear()
        y = 0
        for line in self.lines[-self.height:]:
            self.win.addstr(y, 0, line[0] + " : " + line[1] + ": " + line[2])
            y += 1
        self.win.refresh()

    def resize(self, height, width, y, x, stdscr):
        self._resize(height, width, y, x, stdscr)
        self.refresh()

class Input(Win):
    """
    """
    def __init__(self, height, width, y, x, stdscr):
        Win.__init__(self, height, width, y, x, stdscr)
        self.input = curses.textpad.Textbox(self.win)
121 122 123
        self.input.stripspaces = False
        self.input.insert_mode = True
        self.txt = ''
124 125 126 127

    def resize(self, height, width, y, x, stdscr):
        self._resize(height, width, y, x, stdscr)
        self.input = curses.textpad.Textbox(self.win)
128 129
        self.input.insert_mode = True
        self.input.stripspaces = False
130
        self.win.clear()
131
        self.win.addstr(self.txt)
132 133 134

    def do_command(self, key):
        self.input.do_command(key)
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
#        self.win.refresh()
#        self.text = self.input.gather()

    # def insert_char(self, key):
    #     if self.insert:
    #         self.text = self.text[:self.pos]+key+self.text[self.pos:]
    #     else:
    #         self.text = self.text[:self.pos]+key+self.text[self.pos+1:]
    #     self.pos += 1
    #     pass

    def get_text(self):
        return self.input.gather()

    def save_text(self):
        self.txt = self.input.gather()
#        self.win.clear()
#        self.win.addstr(self.txt)

    def refresh(self):
#        self.win.clear()
#        self.win.addstr(self.text)
#        self.win.move(0, len(self.text)-1)
158 159
        self.win.refresh()

160 161 162 163 164
    def clear_text(self):
        self.win.clear()
        self.txt = ''
        self.pos = 0
        self.refresh()
165

166
class Tab(object):
167 168 169 170 171
    """
    The whole "screen" that can be seen at once in the terminal.
    It contains an userlist, an input zone and a chat zone, all
    related to one single chat room.
    """
172
    def __init__(self, stdscr, name='info'):
173 174 175 176 177 178 179 180 181 182
        """
        name is the name of the Tab, and it's also
        the JID of the chatroom.
        A particular tab is the "Info" tab which has no
        name (None). This info tab should be unique.
        The stdscr should be passed to know the size of the
        terminal
        """
        self.name = name
        self.size = (self.height, self.width) = stdscr.getmaxyx()
183 184

        self.user_win = UserList(self.height-3, self.width/7, 1, 6*(self.width/7), stdscr)
185 186
        self.topic_win = Info(1, self.width, 0, 0, stdscr)
        self.info_win = Info(1, self.width, self.height-2, 0, stdscr)
187 188 189
        self.text_win = TextWin(self.height-3, (self.width/7)*6, 1, 0, stdscr)
        self.input = Input(1, self.width, self.height-1, 0, stdscr)

190
        self.info_win.set_info(name)
191 192
        # debug
        self.refresh()
193

194
    def resize(self, stdscr):
195 196 197
        """
        Resize the whole tabe. i.e. all its sub-windows
        """
198 199 200 201 202 203 204 205 206
        self.size = (self.height, self.width) = stdscr.getmaxyx()
        self.user_win.resize(self.height-3, self.width/7, 1, 6*(self.width/7), stdscr)
        self.topic_win.resize(1, self.width, 0, 0, stdscr)
        self.info_win.resize(1, self.width, self.height-2, 0, stdscr)
        self.text_win.resize(self.height-3, (self.width/7)*6, 1, 0, stdscr)
        self.input.resize(1, self.width, self.height-1, 0, stdscr)
        self.refresh()

    def refresh(self):
207
        self.text_win.add_line("fion", "fion", "refresh")
208 209 210 211
        self.text_win.refresh()
        self.user_win.refresh()
        self.topic_win.refresh()
        self.info_win.refresh()
212
        self.input.refresh()
213 214 215

    def do_command(self, key):
        self.input.do_command(key)
216 217
#        self.input.save_text()
        self.input.refresh()
218

219 220 221 222
class Gui(object):
    """
    Graphical user interface using ncurses
    """
223
    def __init__(self, stdscr):
224 225
        self.handler = Handler()

226 227 228 229 230
        self.commands = {
            'join': self.command_join,
            'quit': self.command_quit,
            }

231
        self.handler.connect('on-muc-message-received', self.on_message)
232
        self.handler.connect('gui-join-room', self.on_join_room)
233 234
        self.handler.connect('on-muc-presence-changed', self.on_presence)

235
        self.init_curses(stdscr)
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
        self.stdscr = stdscr

    def execute(self):
        line = self.current_tab.input.get_text()
        self.current_tab.input.clear_text()
        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
        self.current_tab.text_win.add_line("NOW", "louiz'", line)
        # TODO, send message to jabber

    def command_join(self, args):
        room = args[0]
        self.on_join_room(room, "poezio")

    def command_quit(self, args):
        sys.exit()
257

258
    def init_curses(self, stdscr):
259
        stdscr.leaveok(True)
260
        curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
261
        curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK)
262
        self.current_tab = Tab(stdscr)
263
        self.tabs = [self.current_tab]
264

265 266
    def main_loop(self, stdscr):
        while 1:
267
            stdscr.refresh()
268 269
            key = stdscr.getch()
            if key == curses.KEY_RESIZE:
270 271
                self.current_tab.resize(stdscr)
            elif key == 10:
272 273 274
                self.execute()
            else:
                self.current_tab.do_command(key)
275

276 277 278 279
    def on_message(self, jid, msg, subject, typ, stanza):
        print "on_message", jid, msg, subject, typ

    def on_join_room(self, room, nick):
280 281 282 283 284
        sys.stderr.write(room)
        self.current_tab = Tab(self.stdscr, room)
        self.tabs.append(self.current_tab)
#        self.current_tab.resize()
        self.current_tab.refresh()
285 286 287 288 289
        print "on_join_room", room, nick

    def on_presence(self, jid, priority, show, status, stanza):
        print "on presence", jid, priority, show, status

290
def main(stdscr):
291
    gui = Gui(stdscr)
292
    gui.main_loop(stdscr)
293

294
if __name__ == '__main__':
295 296 297 298
    resource = config.get('resource')
    server = config.get('server')
    connection = Connection(server, resource)
    connection.start()
299
    curses.wrapper(main)
300 301 302
    # rooms = config.get('rooms').split(':')
    # for room in rooms:
    #     connection.send_join_room(room.split('/')[0], room.split('/')[1])