diff frontends/src/wix/main_window.py @ 223:86d249b6d9b7

Files reorganisation
author Goffi <goffi@goffi.org>
date Wed, 29 Dec 2010 01:06:29 +0100
parents frontends/wix/main_window.py@782319a64ac6
children fd9b7834d98a
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/frontends/src/wix/main_window.py	Wed Dec 29 01:06:29 2010 +0100
@@ -0,0 +1,498 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+"""
+wix: 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_chat_list import QuickChatList
+from quick_frontend.quick_app import QuickApp
+from quick_frontend.quick_contact_management import QuickContactManagement
+import wx
+from contact_list import ContactList
+from chat import Chat
+from param import Param
+from xmlui import XMLUI
+from gateways import GatewaysManager
+from profile import Profile
+from profile_manager import ProfileManager
+import gobject
+import os.path
+import pdb
+from tools.jid  import JID
+from logging import debug, info, warning, error
+import constants
+
+idCONNECT,\
+idDISCONNECT,\
+idEXIT,\
+idABOUT,\
+idPARAM,\
+idADD_CONTACT,\
+idREMOVE_CONTACT,\
+idSHOW_PROFILE,\
+idJOIN_ROOM,\
+idFIND_GATEWAYS = range(10)
+
+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 MainWindow(wx.Frame, QuickApp):
+    """main app window"""
+
+    def __init__(self):
+        wx.Frame.__init__(self,None, title="SàT Wix", size=(300,500))
+        self.CM = QuickContactManagement() #FIXME: not the best place
+
+        #sizer
+        self.sizer = wx.BoxSizer(wx.VERTICAL)
+        self.SetSizer(self.sizer)
+        
+        #Frame elements
+        self.contactList = ContactList(self, self)
+        self.contactList.registerActivatedCB(self.onContactActivated)
+        self.contactList.Hide()
+        self.sizer.Add(self.contactList, 1, flag=wx.EXPAND)
+        
+        self.chat_wins=ChatList(self)
+        self.CreateStatusBar()
+
+        #ToolBar
+        self.tools=self.CreateToolBar()
+        self.statusBox =  wx.ComboBox(self.tools, -1, "Online", choices=[status[1] for status in const_STATUS],
+                                      style=wx.CB_DROPDOWN | wx.CB_READONLY)
+        self.tools.AddControl(self.statusBox)
+        self.tools.AddSeparator()
+        self.statusTxt=wx.TextCtrl(self.tools, -1, style = wx.TE_PROCESS_ENTER)
+        self.tools.AddControl(self.statusTxt)
+        self.Bind(wx.EVT_COMBOBOX, self.onStatusChange, self.statusBox) 
+        self.Bind(wx.EVT_TEXT_ENTER, self.onStatusChange, self.statusTxt)
+        self.tools.Disable()
+        
+        #tray icon
+        ticon = wx.Icon(IMAGE_DIR+'/crystal/tray_icon.xpm', wx.BITMAP_TYPE_XPM)
+        self.tray_icon = wx.TaskBarIcon()
+        self.tray_icon.SetIcon(ticon, _("Wix jabber client"))
+        wx.EVT_TASKBAR_LEFT_UP(self.tray_icon, self.onTrayClick)
+
+
+        #events
+        self.Bind(wx.EVT_CLOSE, self.onClose, self)
+
+        
+        QuickApp.__init__(self)
+        
+        #menus
+        self.createMenus()
+        for i in range(self.menuBar.GetMenuCount()):
+            self.menuBar.EnableTop(i, False)
+        
+        #profile panel
+        self.profile_pan = ProfileManager(self) 
+        self.sizer.Add(self.profile_pan, 1, flag=wx.EXPAND)
+      
+        self.postInit()
+
+        self.Show()
+
+    def plug_profile(self, profile_key='@DEFAULT@'):
+        """Hide profile panel then plug profile"""
+        debug (_('plugin profile %s' % profile_key))
+        self.profile_pan.Hide()
+        self.contactList.Show()
+        self.sizer.Layout()
+        for i in range(self.menuBar.GetMenuCount()):
+            self.menuBar.EnableTop(i, True)
+        super(MainWindow, self).plug_profile(profile_key)
+
+    def createMenus(self):
+        info(_("Creating menus"))
+        connectMenu = wx.Menu()
+        connectMenu.Append(idCONNECT, _("&Connect	CTRL-c"),_(" Connect to the server"))
+        connectMenu.Append(idDISCONNECT, _("&Disconnect	CTRL-d"),_(" Disconnect from the server"))
+        connectMenu.Append(idPARAM,_("&Parameters"),_(" Configure the program"))
+        connectMenu.AppendSeparator()
+        connectMenu.Append(idABOUT,_("A&bout"),_(" About %s") % APP_NAME)
+        connectMenu.Append(idEXIT,_("E&xit"),_(" Terminate the program"))
+        contactMenu = wx.Menu()
+        contactMenu.Append(idADD_CONTACT, _("&Add contact"),_(" Add a contact to your list"))
+        contactMenu.Append(idREMOVE_CONTACT, _("&Remove contact"),_(" Remove the selected contact from your list"))
+        contactMenu.AppendSeparator()
+        contactMenu.Append(idSHOW_PROFILE, _("&Show profile"), _(" Show contact's profile"))
+        communicationMenu = wx.Menu()
+        communicationMenu.Append(idJOIN_ROOM, _("&Join Room"),_(" Join a Multi-User Chat room"))
+        communicationMenu.Append(idFIND_GATEWAYS, _("&Find Gateways"),_(" Find gateways to legacy IM"))
+        self.menuBar = wx.MenuBar()
+        self.menuBar.Append(connectMenu,_("&General"))
+        self.menuBar.Append(contactMenu,_("&Contacts"))
+        self.menuBar.Append(communicationMenu,_("&Communication"))
+        self.SetMenuBar(self.menuBar)
+
+        #additionals menus
+        #FIXME: do this in a more generic way (in quickapp)
+        add_menus = self.bridge.getMenus()
+        for menu in add_menus:
+            category,item,type = menu
+            assert(type=="NORMAL") #TODO: manage other types
+            menu_idx = self.menuBar.FindMenu(category)
+            current_menu = None
+            if menu_idx == wx.NOT_FOUND:
+                #the menu is new, we create it
+                current_menu = wx.Menu()
+                self.menuBar.Append(current_menu, category)
+            else:
+                current_menu = self.menuBar.GetMenu(menu_idx)
+            assert(current_menu != None)
+            item_id = wx.NewId()
+            help_string = self.bridge.getMenuHelp(category, item, type)
+            current_menu.Append(item_id, item, help = help_string)
+            #now we register the event
+            def event_answer(e):
+                id = self.bridge.callMenu(category, item, type, self.profile)
+                self.current_action_ids.add(id)
+            wx.EVT_MENU(self, item_id, event_answer)
+
+
+        #events
+        wx.EVT_MENU(self, idCONNECT, self.onConnectRequest)
+        wx.EVT_MENU(self, idDISCONNECT, self.onDisconnectRequest)
+        wx.EVT_MENU(self, idPARAM, self.onParam)
+        wx.EVT_MENU(self, idABOUT, self.onAbout)
+        wx.EVT_MENU(self, idEXIT, self.onExit)
+        wx.EVT_MENU(self, idADD_CONTACT, self.onAddContact)
+        wx.EVT_MENU(self, idREMOVE_CONTACT, self.onRemoveContact)
+        wx.EVT_MENU(self, idSHOW_PROFILE, self.onShowProfile)
+        wx.EVT_MENU(self, idJOIN_ROOM, self.onJoinRoom)
+        wx.EVT_MENU(self, idFIND_GATEWAYS, self.onFindGateways)
+
+
+    def newMessage(self, from_jid, msg, type, to_jid, profile):
+        QuickApp.newMessage(self, from_jid, msg, type, to_jid, profile)
+
+    def roomJoined(self, room_id, room_service, room_nicks, user_nick, profile):
+        super(MainWindow, self).roomJoined(room_id, room_service, room_nicks, user_nick, profile)
+
+    def showAlert(self, message):
+        # TODO: place this in a separate class
+        popup=wx.PopupWindow(self)
+        ### following code come from wxpython demo
+        popup.SetBackgroundColour("CADET BLUE")
+        st = wx.StaticText(popup, -1, message, pos=(10,10))
+        sz = st.GetBestSize()
+        popup.SetSize( (sz.width+20, sz.height+20) )
+        x=(wx.DisplaySize()[0]-popup.GetSize()[0])/2
+        popup.SetPosition((x,0))
+        popup.Show()
+        wx.CallLater(5000,popup.Destroy)
+    
+    def showDialog(self, message, title="", type="info", answer_cb = None, answer_data = None):
+        if type == 'info':
+            flags = wx.OK | wx.ICON_INFORMATION
+        elif type == 'error':
+            flags = wx.OK | wx.ICON_ERROR
+        elif type == 'yes/no':
+            flags = wx.YES_NO | wx.ICON_QUESTION
+        else:
+            flags = wx.OK | wx.ICON_INFORMATION
+            error(_('unmanaged dialog type: %s'), type)
+        dlg = wx.MessageDialog(self, message, title, flags)
+        answer = dlg.ShowModal()
+        dlg.Destroy()
+        if answer_cb:
+            data = [answer_data] if answer_data else []
+            answer_cb(True if (answer == wx.ID_YES or answer == wx.ID_OK) else False, *data)
+       
+    def setStatusOnline(self, online=True):
+        """enable/disable controls, must be called when local user online status change"""
+        if online:
+            self.SetStatusText(msgONLINE)
+            self.tools.Enable()
+        else:
+            self.SetStatusText(msgOFFLINE)
+            self.tools.Disable()
+        return
+
+    def askConfirmation(self, type, id, data):
+        #TODO: refactor this in QuickApp
+        debug (_("Confirmation asked"))
+        answer_data={}
+        if type == "FILE_TRANSFERT":
+            debug (_("File transfert confirmation asked"))
+            dlg = wx.MessageDialog(self, _("The contact %(jid)s wants to send you the file %(filename)s\nDo you accept ?") % {'jid':data["from"], 'filename':data["filename"]},
+                                   _('File Request'),
+                                   wx.YES_NO | wx.ICON_QUESTION
+                                  )
+            answer=dlg.ShowModal()
+            if answer==wx.ID_YES:
+                filename = wx.FileSelector(_("Where do you want to save the file ?"), flags = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
+                if filename:
+                    answer_data["dest_path"] = filename
+                    self.bridge.confirmationAnswer(id, True, answer_data)
+                    self.waitProgress(id, _("File Transfer"), _("Copying %s") % os.path.basename(filename)) 
+                else:
+                    answer = wx.ID_NO
+            if answer==wx.ID_NO:
+                    self.bridge.confirmationAnswer(id, False, answer_data)
+            
+            dlg.Destroy()
+
+        elif type == "YES/NO":
+            debug (_("Yes/No confirmation asked"))
+            dlg = wx.MessageDialog(self, data["message"],
+                                   _('Confirmation'),
+                                   wx.YES_NO | wx.ICON_QUESTION
+                                  )
+            answer=dlg.ShowModal()
+            if answer==wx.ID_YES:
+                self.bridge.confirmationAnswer(id, True, {})
+            if answer==wx.ID_NO:
+                self.bridge.confirmationAnswer(id, False, {})
+
+            dlg.Destroy()
+
+    def actionResult(self, type, id, data):
+        debug (_("actionResult: type = [%(type)s] id = [%(id)s] data = [%(data)s]") % {'type':type, 'id':id, 'data':data})
+        if not id in self.current_action_ids:
+            debug (_('unknown id, ignoring'))
+            return
+        if type == "SUPPRESS":
+            self.current_action_ids.remove(id)
+        elif type == "SUCCESS":
+            self.current_action_ids.remove(id)
+            dlg = wx.MessageDialog(self, data["message"],
+                                   _('Success'),
+                                   wx.OK | wx.ICON_INFORMATION
+                                  )
+            dlg.ShowModal()
+            dlg.Destroy()
+        elif type == "ERROR":
+            self.current_action_ids.remove(id)
+            dlg = wx.MessageDialog(self, data["message"],
+                                   _('Error'),
+                                   wx.OK | wx.ICON_ERROR
+                                  )
+            dlg.ShowModal()
+            dlg.Destroy()
+        elif type == "XMLUI":
+            self.current_action_ids.remove(id)
+            debug (_("XML user interface received"))
+            misc = {}
+            #FIXME FIXME FIXME: must clean all this crap !
+            title = _('Form')
+            if data['type'] == _('registration'):
+                title = _('Registration')
+                misc['target'] = data['target']
+                misc['action_back'] = self.bridge.gatewayRegister
+            XMLUI(self, title=title, xml_data = data['xml'], misc = misc)
+        elif type == "RESULT":
+            self.current_action_ids.remove(id)
+            if self.current_action_ids_cb.has_key(id):
+                callback = self.current_action_ids_cb[id]
+                del self.current_action_ids_cb[id]
+                callback(data)
+        elif type == "DICT_DICT":
+            self.current_action_ids.remove(id)
+            if self.current_action_ids_cb.has_key(id):
+                callback = self.current_action_ids_cb[id]
+                del self.current_action_ids_cb[id]
+                callback(data)
+        else:
+            error (_("FIXME FIXME FIXME: type [%s] not implemented") % type)
+            raise NotImplementedError
+
+
+
+    def progressCB(self, id, title, message):
+        data = self.bridge.getProgress(id)
+        if data:
+            if not self.pbar:
+                #first answer, we must construct the bar
+                self.pbar = wx.ProgressDialog(title, message, float(data['size']), None,
+                    wx.PD_SMOOTH | wx.PD_ELAPSED_TIME | wx.PD_ESTIMATED_TIME | wx.PD_REMAINING_TIME) 
+                self.pbar.finish_value = float(data['size'])
+                
+            self.pbar.Update(int(data['position']))
+        elif self.pbar:
+            self.pbar.Update(self.pbar.finish_value)
+            return
+
+        wx.CallLater(10, self.progressCB, id, title, message)
+        
+    def waitProgress (self, id, title, message):
+        self.pbar = None
+        wx.CallLater(10, self.progressCB, id, title, message)
+        
+        
+
+    ### events ###
+
+    def onContactActivated(self, jid):
+        debug (_("onContactActivated: %s"), jid)
+        if self.chat_wins[jid.short].IsShown():
+            self.chat_wins[jid.short].Hide()
+        else:
+            self.chat_wins[jid.short].Show()
+
+    def onConnectRequest(self, e):
+        self.bridge.connect(self.profile)
+
+    def onDisconnectRequest(self, e):
+        self.bridge.disconnect(self.profile)
+
+    def __updateStatus(self):
+        show = filter(lambda x:x[1] == self.statusBox.GetValue(), const_STATUS)[0][0]
+        status =  self.statusTxt.GetValue()
+        self.bridge.setPresence(show=show, statuses={'default':status}, profile_key=self.profile)  #FIXME: manage multilingual statuses
+
+    def onStatusChange(self, e):
+        debug(_("Status change request"))
+        self.__updateStatus()
+
+    def onParam(self, e):
+        debug(_("Param request"))
+        #xmlui = self.bridge.getParamsUI(self.profile)
+        #XMLUI(self, xml_data = xmlui)
+        param=Param(self)
+
+    def onAbout(self, e):
+        about = wx.AboutDialogInfo()
+        about.SetName(APP_NAME)
+        about.SetVersion (unicode(self.bridge.getVersion()))
+        about.SetCopyright(u"(C) 2009,2010 Jérôme Poisson aka Goffi")
+        about.SetDescription( _(u"%(name)s is a SàT (Salut à Toi) frontend\n"+
+        u"%(name)s is based on WxPython, and is the standard graphic interface of SàT") % {'name':APP_NAME})
+        about.SetWebSite(("http://www.goffi.org", "Goffi's non-hebdo (french)"))
+        about.SetDevelopers([ "Goffi (Jérôme Poisson)"])
+        try:
+            with open(LICENCE_PATH,"r") as licence:
+                about.SetLicence(''.join(licence.readlines()))
+        except:
+            pass
+
+        wx.AboutBox(about)
+
+    def onExit(self, e):
+        self.Close()
+    
+    def onAddContact(self, e):
+        debug(_("Add contact request"))
+        dlg = wx.TextEntryDialog(
+                self, _('Please enter new contact JID'),
+                _('Adding a contact'), _('name@server.tld'))
+
+        if dlg.ShowModal() == wx.ID_OK:
+            jid=JID(dlg.GetValue())
+            if jid.is_valid():
+                self.bridge.addContact(jid.short, profile_key=self.profile)
+            else:
+                error (_("'%s' is an invalid JID !"), jid)
+                #TODO: notice the user
+
+        dlg.Destroy()
+
+    def onRemoveContact(self, e):
+        debug(_("Remove contact request"))
+        target = self.contactList.getSelection()
+        if not target:
+            dlg = wx.MessageDialog(self, _("You haven't selected any contact !"),
+                                   _('Error'),
+                                   wx.OK | wx.ICON_ERROR
+                                  )
+            dlg.ShowModal()
+            dlg.Destroy()
+            return
+
+        dlg = wx.MessageDialog(self, _("Are you sure you want to delete %s from your roster list ?") % target.short,
+                               _('Contact suppression'),
+                               wx.YES_NO | wx.ICON_QUESTION
+                              )
+
+        if dlg.ShowModal() == wx.ID_YES:
+            info(_("Unsubscribing %s presence"), target.short)
+            self.bridge.delContact(target.short, profile_key=self.profile)
+
+        dlg.Destroy()
+
+    def onShowProfile(self, e):
+        debug(_("Show contact's profile request"))
+        target = self.contactList.getSelection()
+        if not target:
+            dlg = wx.MessageDialog(self, _("You haven't selected any contact !"),
+                                   _('Error'),
+                                   wx.OK | wx.ICON_ERROR
+                                  )
+            dlg.ShowModal()
+            dlg.Destroy()
+            return
+        id = self.bridge.getCard(target.short, profile_key=self.profile) 
+        self.current_action_ids.add(id)
+        self.current_action_ids_cb[id] = self.onProfileReceived
+   
+    def onProfileReceived(self, data):
+        """Called when a profile is received"""
+        debug (_('Profile received: [%s]') % data)
+        profile=Profile(self, data)
+        
+    def onJoinRoom(self, e):
+        warning('FIXME: temporary menu, must be improved')
+        #TODO: a proper MUC room joining dialog with nickname etc
+        dlg = wx.TextEntryDialog(
+                self, _("Please enter MUC's JID"),
+                #_('Entering a MUC room'), 'test@conference.necton2.int')
+                _('Entering a MUC room'), 'room@muc_service.server.tld')
+        if dlg.ShowModal() == wx.ID_OK:
+            room_jid=JID(dlg.GetValue())
+            if room_jid.is_valid():
+                self.bridge.joinMUC(room_jid.domain, room_jid.node, self.profiles[self.profile]['whoami'].node, self.profile)
+            else:
+                error (_("'%s' is an invalid JID !"), room_jid)
+
+    def onFindGateways(self, e):
+        debug(_("Find Gateways request"))
+        id = self.bridge.findGateways(self.profiles[self.profile]['whoami'].domain, self.profile)
+        self.current_action_ids.add(id)
+        self.current_action_ids_cb[id] = self.onGatewaysFound
+
+    def onGatewaysFound(self, data):
+        """Called when SàT has found the server gateways"""
+        target = data['__private__']['target']
+        del data['__private__']
+        gatewayManager = GatewaysManager(self, data, server=target)
+    
+    def onClose(self, e):
+        QuickApp.onExit(self)
+        info(_("Exiting..."))
+        for win in self.chat_wins:
+            self.chat_wins[win].Destroy()
+        e.Skip()
+
+    def onTrayClick(self, e):
+        debug(_("Tray Click"))
+        if self.IsShown():
+            self.Hide()
+        else:
+            self.Show()
+            self.Raise()
+        e.Skip()
+