diff frontends/wix/main_window.py @ 0:c4bc297b82f0

sat: - first public release, initial commit
author goffi@necton2
date Sat, 29 Aug 2009 13:34:59 +0200
parents
children a06a151fc31f
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/frontends/wix/main_window.py	Sat Aug 29 13:34:59 2009 +0200
@@ -0,0 +1,377 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+"""
+wix: a SAT frontend
+Copyright (C) 2009  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/>.
+"""
+
+
+import wx
+from chat import Chat
+from param import Param
+import gobject
+import os.path
+import pdb
+from tools.jid  import JID
+from logging import debug, info, error
+from quick_frontend.quick_chat_list import QuickChatList
+from quick_frontend.quick_contact_list import QuickContactList
+from quick_frontend.quick_app import QuickApp
+
+
+msgOFFLINE          = "offline"
+msgONLINE           = "online"
+idCONNECT           = 1
+idEXIT              = 2
+idPARAM             = 3
+idADD_CONTACT       = 4
+idREMOVE_CONTACT    = 5
+const_DEFAULT_GROUP = "Unclassed"
+const_STATUS        = {"Online":"",
+                      "Want to discuss":"chat",
+                      "AFK":"away",
+                      "Do Not Disturb":"dnd",
+                      "Away":"xa"}
+
+class ChatList(QuickChatList):
+    """This class manage the list of chat windows"""
+    
+    def __init__(self, host):
+        QuickChatList.__init__(self, host)
+    
+    def createChat(self, name):
+        return Chat(name, self.host)
+    
+
+
+class ContactList(wx.TreeCtrl, QuickContactList):
+    """Customized control to manage contacts."""
+
+    def __init__(self, parent):
+        wx.TreeCtrl.__init__(self, parent, style = wx.TR_HIDE_ROOT | wx.TR_HAS_BUTTONS)
+        QuickContactList.__init__(self)
+        self.jid_ids={}
+        self.groups={}
+        self.root=self.AddRoot("")
+        self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.onActivated, self)
+
+        #icons
+        isz = (16,16)
+        il = wx.ImageList(isz[0], isz[1])
+        self.icon_online = il.Add(wx.ArtProvider_GetBitmap(wx.ART_TICK_MARK, wx.ART_OTHER, isz))
+        self.icon_unavailable = il.Add(wx.ArtProvider_GetBitmap(wx.ART_CROSS_MARK, wx.ART_OTHER, isz))
+        self.AssignImageList(il)
+
+        self.__addNode(const_DEFAULT_GROUP)
+
+    def __addNode(self, label):
+        """Add an item container"""
+        ret=self.AppendItem(self.root, label)
+        self.SetPyData(ret, "[node]")
+        self.SetItemBold(ret)
+        self.groups[label]=ret
+
+    def replace(self, jid, name="", show="", status="", group=""):
+        debug("status = %s show = %s",status, show)
+        if not self.jid_ids.has_key(jid):
+            self.add(jid, name, show, status, group)
+        else:
+            debug ("updating %s",jid)
+            self.__presentItem(jid, name, show, status, group)
+
+    def __presentItem(self, jid, name, show, status, group):
+        """Make a nice presentation of the contact in the list."""
+        id=self.jid_ids[jid]
+        label= "%s [%s] \n %s" % ((name or jid), (show or "online"), status)
+        self.SetItemText(id, label)
+
+        # icon
+        if not show or show=="chat":
+            self.SetItemImage(id, self.icon_online)
+        else:
+            self.SetItemImage(id, self.icon_unavailable)
+
+        #colour
+        if not show:
+            self.SetItemTextColour(id, wx.BLACK)
+        elif show=="chat":
+            self.SetItemTextColour(id, wx.GREEN)
+        elif show=="away":
+            self.SetItemTextColour(id, wx.BLUE)
+        else:
+            self.SetItemTextColour(id, wx.RED)
+
+    def add(self, jid, name="", show="", status="", group=""):
+        """add a contact to the list"""
+        debug ("adding %s",jid)
+        dest_group=group or const_DEFAULT_GROUP
+        if not self.groups.has_key(dest_group):
+            self.__addNode(dest_group)
+        self.jid_ids[jid]=self.AppendItem(self.groups[dest_group], "")
+        self.__presentItem(jid, name, show, status, group)
+        self.SetPyData(self.jid_ids[jid], "[contact]"+jid)
+        self.EnsureVisible(self.jid_ids[jid])
+        self.Refresh() #FIXME: Best way ?
+
+    def remove(self, jid):
+        """remove a contact from the list"""
+        debug ("removing %s",jid)
+        self.Delete(self.jid_ids[jid])
+        del self.jid_ids[jid]
+        self.Refresh() #FIXME: Best way ?
+
+    def onActivated(self, event):
+        """Called when a contact is clicked or activated with keyboard."""
+        if self.GetPyData(event.GetItem()).startswith("[contact]"):
+            self.onActivatedCB(self.GetPyData(event.GetItem())[9:])
+        else:
+            event.Skip()
+
+    def getSelection(self):
+        """Return the selected contact, or an empty string if there is not"""
+        data = self.GetPyData(self.GetSelection())
+        if not data or not data.startswith("[contact]"):
+            return ""
+        return JID(data[9:])
+
+    def registerActivatedCB(self, cb):
+        """Register a callback with manage contact activation."""
+        self.onActivatedCB=cb
+
+class MainWindow(wx.Frame, QuickApp):
+    """main app window"""
+
+    def __init__(self):
+        wx.Frame.__init__(self,None, title="SAT Wix", size=(400,200))
+
+
+        #Frame elements
+        self.contactList = ContactList(self)
+        self.contactList.registerActivatedCB(self.onContactActivated)
+        self.chat_wins=ChatList(self)
+        self.CreateStatusBar()
+        self.SetStatusText(msgOFFLINE)
+        self.createMenus()
+
+        #ToolBar
+        self.tools=self.CreateToolBar()
+        self.statusBox =  wx.ComboBox(self.tools, -1, "Online", choices=const_STATUS.keys(),
+                                      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()
+
+        #events
+        self.Bind(wx.EVT_CLOSE, self.onClose, self)
+
+        QuickApp.__init__(self)
+
+        self.Show()
+
+    def createMenus(self):
+        info("Creating menus")
+        connectMenu = wx.Menu()
+        connectMenu.Append(idCONNECT, "&Connect	CTRL-c"," Connect to the server")
+        connectMenu.Append(idPARAM,"&Parameters"," Configure the program")
+        connectMenu.AppendSeparator()
+        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")
+        menuBar = wx.MenuBar()
+        menuBar.Append(connectMenu,"&General")
+        menuBar.Append(contactMenu,"&Contacts")
+        self.SetMenuBar(menuBar)
+
+        #events
+        wx.EVT_MENU(self, idCONNECT, self.onConnectRequest)
+        wx.EVT_MENU(self, idPARAM, self.onParam)
+        wx.EVT_MENU(self, idEXIT, self.onExit)
+        wx.EVT_MENU(self, idADD_CONTACT, self.onAddContact)
+        wx.EVT_MENU(self, idREMOVE_CONTACT, self.onRemoveContact)
+
+
+    def newMessage(self, from_jid, msg, type, to_jid):
+        QuickApp.newMessage(self, from_jid, msg, type, to_jid)
+
+    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"):
+        if type == 'info':
+            flags = wx.OK | wx.ICON_INFORMATION
+        elif type == 'error':
+            flags = wx.OK | wx.ICON_ERROR
+        elif type == 'question':
+            flags = wx.OK | wx.ICON_QUESTION
+        else:
+            flags = wx.OK | wx.ICON_INFORMATION
+        dlg = wx.MessageDialog(self, message, title, flags)
+        answer = dlg.ShowModal()
+        dlg.Destroy()
+        return True if (answer == wx.ID_YES or answer == wx.ID_OK) else False
+       
+    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 presenceUpdate(self, jabber_id, type, show, status, priority):
+        QuickApp.presenceUpdate(self, jabber_id, type, show, status, priority)
+
+    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 %s wants to send you the file %s\nDo you accept ?" % (data["from"], 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()
+
+        
+        
+
+    def progressCB(self, id, title, message):
+        data = self.bridge.getProgress(id)
+        if data:
+            if not data['position']:
+                data['position'] = '0'
+            if not self.pbar:
+                #first answer, we must construct the bar
+                self.pbar = wx.ProgressDialog(title, message, int(data['size']), None,
+                    wx.PD_SMOOTH | wx.PD_ELAPSED_TIME | wx.PD_ESTIMATED_TIME | wx.PD_REMAINING_TIME) 
+                self.pbar.finish_value = int(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].IsShown():
+            self.chat_wins[jid].Hide()
+        else:
+            self.chat_wins[jid].Show()
+
+    def onConnectRequest(self, e):
+        self.bridge.connect()
+
+    def __updateStatus(self):
+        show = const_STATUS[self.statusBox.GetValue()]
+        status =  self.statusTxt.GetValue()
+        self.bridge.setPresence(show=show, status=status)
+
+    def onStatusChange(self, e):
+        debug("Status change request")
+        self.__updateStatus()
+
+    def onParam(self, e):
+        debug("Param request")
+        param=Param(self.bridge.setParam, self.bridge.getParam, self.bridge.getParams, self.bridge.getParamsCategories)
+
+    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.ext')
+
+        if dlg.ShowModal() == wx.ID_OK:
+            jid=JID(dlg.GetValue())
+            if jid.is_valid():
+                self.bridge.addContact(jid.short)
+            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("Unsubsribing %s presence", target.short)
+            self.bridge.delContact(target.short)
+
+        dlg.Destroy()
+
+    def onClose(self, e):
+        info("Exiting...")
+        e.Skip()
+