libervia-backend/frontends/wix/main_window.py
Goffi 84cd3c6ea2 better presentation for register new account
- error message when no user, pass or server given
- server is now automatically updated when jid change in connection category
2009-12-01 06:07:20 +01:00

436 lines
15 KiB
Python

#!/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
idDISCONNECT = 2
idEXIT = 3
idPARAM = 4
idADD_CONTACT = 5
idREMOVE_CONTACT = 6
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()
#tray icon
ticon = wx.Icon("images/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)
self.Show()
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(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, idDISCONNECT, self.onDisconnectRequest)
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()
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 = [%s] id = [%s] data =[%s]" % (type, id, data))
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()
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 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 onDisconnectRequest(self, e):
self.bridge.disconnect()
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)
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()
def onTrayClick(self, e):
debug("Tray Click")
if self.IsShown():
self.Hide()
else:
self.Show()
self.Raise()
e.Skip()