view frontends/primitivus/primitivus @ 132:a86607e5cf38

quick_app: self.occupants for group chat are now managed by quick_chat. self.options.profile now support unicode
author Goffi <goffi@goffi.org>
date Fri, 16 Jul 2010 20:12:54 +0800
parents 6cad483a6d84
children 227394eb080c
line wrap: on
line source

#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
Primitivus: a SAT frontend
Copyright (C) 2009, 2010  Jérôme Poisson (goffi@goffi.org)

This program 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, either version 3 of the License, or
(at your option) any later version.

This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
"""


from quick_frontend.quick_app import QuickApp
from quick_frontend.quick_chat_list import QuickChatList
from quick_frontend.quick_contact_list import QuickContactList
from quick_frontend.quick_contact_management import QuickContactManagement
import urwid
from profile_manager import ProfileManager
from contact_list import ContactList
from chat import Chat
import custom_widgets
import pdb
"""from window import Window
from editbox import EditBox
from statusbar import StatusBar
from chat import Chat
from tools.jid  import JID"""
import logging
from logging import debug, info, error
#import locale
import sys, os
from tools.jid  import JID
#from curses import ascii
#import locale
#from signal import signal, SIGWINCH 
#import fcntl
#import struct
#import termios
#from boxsizer import BoxSizer


### logging configuration FIXME: put this elsewhere ###
logging.basicConfig(level=logging.CRITICAL,  #TODO: configure it to put messages in a log file
                    format='%(message)s')
###

const_APP_NAME      = "Primitivus"
const_PALETTE = [('title', 'black', 'light gray', 'standout,underline'),
                 ('selected', 'default', 'dark red'),
                 ('selected_focus', 'default,bold', 'dark red'),
                 ('default', 'default', 'default'),
                 ('default_focus', 'default,bold', 'default'),
                 ('alert', 'default,underline', 'default'),
                 ('alert_focus', 'default,bold,underline', 'default'),
                 ('date', 'light gray', 'default'),
                 ('my_nick', 'dark red,bold', 'default'),
                 ('other_nick', 'dark cyan,bold', 'default'),
                 ('menubar', 'light gray,bold', 'dark red'),
                 ('selected_menu', 'light gray,bold', 'dark green'),
                 ('menuitem', 'light gray,bold', 'dark red'),
                 ('menuitem_focus', 'light gray,bold', 'dark green'),
                 ]
            
class ChatList(QuickChatList):
    """This class manage the list of chat windows"""
    
    def __init__(self, host):
        QuickChatList.__init__(self, host)
    
    def createChat(self, target):
        return Chat(target, self.host)

class PrimitivusApp(QuickApp):
    
    def __init__(self):
        self.CM = QuickContactManagement() #FIXME: not the best place
        QuickApp.__init__(self)  #XXX: yes it's an unusual place for the constructor of a parent class, but the init order is important
        
        ## main loop setup ##
        self.main_widget = ProfileManager(self)
        self.loop = urwid.MainLoop(self.main_widget, const_PALETTE, event_loop=urwid.GLibEventLoop(), input_filter=self.inputFilter, unhandled_input=self.keyHandler)

        ##misc setup##
        self.chat_wins=ChatList(self)
    
    def debug(self):
        """convenient method to reset screen and launch pdb"""
        import os
        os.system('reset')
        print 'Entered debug mode'
        pdb.set_trace()

    def write_log(self, log, file_name='/tmp/primitivus_log'):
        """method to write log in a temporary file, useful for debugging"""
        f=open(file_name, 'a')
        f.write(log+"\n")
        f.close()

    def redraw(self):
        """redraw the screen"""
        self.loop.draw_screen()

    def start(self):
        self.i = 0
        self.loop.set_alarm_in(0,lambda a,b: self.postInit())
        self.loop.run()

    def inputFilter(self, input, raw):
        for i in input:
            if i.__class__==tuple:
                if i[0] == 'mouse press':
                    if i[1] == 4: #Mouse wheel up
                        input[input.index(i)] = 'up'
                    if i[1] == 5: #Mouse wheel down
                        input[input.index(i)] = 'down'
        return input

    def keyHandler(self, input):
        if input == 'meta m':
            try:
                if self.main_widget.header == None:
                    self.main_widget.header = self.menu
                else:
                    self.main_widget.header = None
            except AttributeError:
                pass
        elif input == 'ctrl d' and 'D' in self.bridge.getVersion(): #Debug only for dev versions
            self.debug()
        elif input == 'f2': #user wants to (un)hide the contact_list
            try:
                center_widgets = self.center_part.widget_list
                if self.contactList in center_widgets:
                    self.center_part.set_focus(0) #necessary as the focus change to the next object, we can go out of range if we are on the last object of self.center_part
                    center_widgets.remove(self.contactList)
                    del self.center_part.column_types[0]
                else:
                    center_widgets.insert(0, self.contactList)
                    self.center_part.column_types.insert(0, ('weight', 2))
            except AttributeError:
                #The main widget is not built (probably in Profile Manager)
                pass
        elif input == 'window resize':
            width,height = self.loop.screen_size
            if height<=5 and width<=35:
                if not 'save_main_widget' in dir(self):
                    self.save_main_widget = self.loop.widget
                    self.loop.widget = urwid.Filler(urwid.Text(_("Pleeeeasse, I can't even breathe !")))
            else:
                if 'save_main_widget' in dir(self):
                    self.loop.widget = self.save_main_widget
                    del self.save_main_widget
        try:
            return self.menu.checkShortcuts(input)
        except AttributeError:
            return input

    def __buildMainMenu(self):
        menu = custom_widgets.Menu(self.loop)
        connect = _("Connect")
        menu.addMenu(connect, connect, self.onConnectRequest)
        menu.addMenu(connect, _("Disconnect"), self.onDisconnectRequest)
        menu.addMenu(connect, _("Exit"), self.onExitRequest, 'ctrl x')
        communication = _("Communication")
        menu.addMenu(communication, _("Join room"), self.onJoinRoomRequest, 'meta j')
        return menu 

    def __buildMainWidget(self):
        self.contactList = ContactList(self, self.CM, on_click = self.contactSelected, on_change=lambda w: self.redraw())
        #self.center_part = urwid.Columns([('weight',2,self.contactList),('weight',8,Chat('',self))])
        self.center_part = urwid.Columns([('weight',2,self.contactList), ('weight',8,urwid.Filler(urwid.Text('')))])
        editBar = custom_widgets.AdvancedEdit('> ')
        urwid.connect_signal(editBar,'click',self.onTextEntered)
        self.menu = self.__buildMainMenu()
        self.main_widget = custom_widgets.FocusFrame(self.center_part, header=self.menu, footer=editBar, focus_part='footer')
        return self.main_widget

    def plug_profile(self, profile_key='@DEFAULT@'):
        self.loop.widget = self.__buildMainWidget() 
        QuickApp.plug_profile(self, profile_key)
    
    def removePopUp(self, widget=None):
        self.loop.widget = self.main_widget

    def showPopUp(self, pop_up_widget):
        display_widget = urwid.Overlay(pop_up_widget, self.main_widget, 'center', ('relative', 40), 'middle', ('relative', 40))
        self.loop.widget = display_widget

    def contactSelected(self, contact_list):
        contact = contact_list.get_contact()
        if contact:
            assert(len(self.center_part.widget_list)==2)
            #self.center_part.widget_list.append(self.chat_wins[contact])
            #self.center_part.column_types.append(('weight',8))
            self.center_part.widget_list[1] = self.chat_wins[contact]

    def onTextEntered(self, editBar):
        """Called when text is entered in the main edit bar"""
        contact = self.contactList.get_contact() ###Based on the fact that there is currently only one contact selectableat once
        if contact:
            chat = self.chat_wins[contact]
            self.bridge.sendMessage(contact,
                                    editBar.get_edit_text(),
                                    type = "groupchat" if chat.type == 'group' else "chat",
                                    profile_key=self.profile)
            editBar.set_edit_text('')

    def newMessage(self, from_jid, msg, type, to_jid, profile):
        if not self.check_profile(profile):
            return
        QuickApp.newMessage(self, from_jid, msg, type, to_jid, profile)
        sender = JID(from_jid)
        if JID(self.contactList.selected).short != sender.short:
            self.contactList.putAlert(sender)

    def onJoinRoom(self, button, edit):
        self.removePopUp()
        room_jid = JID(edit.get_edit_text())
        if room_jid.is_valid():
            self.bridge.joinMUC(room_jid.domain, room_jid.node, self.profiles[self.profile]['whoami'].node, self.profile)
        else:
            message = _("'%s' is an invalid JID !") % room_jid
            error (message)
            custom_widgets.Alert(_("Error"), message, ok_cb=self.removePopUp)        

    #Menu events#
    def onConnectRequest(self, menu):
        self.bridge.connect(self.profile)

    def onDisconnectRequest(self, menu):
        self.bridge.disconnect(self.profile)

    def onExitRequest(self, menu):
        raise urwid.ExitMainLoop()

    def onJoinRoomRequest(self, menu):
        """User wants to join a MUC room"""
        pop_up_widget = custom_widgets.InputDialog(_("Entering a MUC room"), _("Please enter MUC's JID"), default_txt = 'test@conference.necton2.int', cancel_cb=self.removePopUp, ok_cb=self.onJoinRoom)
        self.showPopUp(pop_up_widget)

sat = PrimitivusApp()
sat.start()