Mercurial > libervia-backend
comparison frontends/wix/main_window.py @ 72:f271fff3a713
MUC implementation: first draft
/!\ the experimental muc branche of wokkel must be used
- bridge: new roomJoined signal
- wix: contact list widget is now in a separate file, and manage different kinds of presentation
- wix: chat window now manage group chat (first draft, not working yet)
- wix: constants are now in a separate class, so then can be accessible from everywhere
- wix: new menu to join room (do nothing yet, except entering in a test room)
- new plugin for xep 0045 (MUC), use wokkel experimental MUC branch
- plugins: the profile is now given for get_handler, cause it can be used internally by a plugin (e.g.: xep-0045 plugin)
author | Goffi <goffi@goffi.org> |
---|---|
date | Sun, 21 Mar 2010 10:28:55 +1100 |
parents | 8f2ed279784b |
children | 7322a41f8a8e |
comparison
equal
deleted
inserted
replaced
71:efe81b61673c | 72:f271fff3a713 |
---|---|
20 """ | 20 """ |
21 | 21 |
22 | 22 |
23 from quick_frontend.quick_chat_list import QuickChatList | 23 from quick_frontend.quick_chat_list import QuickChatList |
24 from quick_frontend.quick_app import QuickApp | 24 from quick_frontend.quick_app import QuickApp |
25 from quick_frontend.quick_contact_list import QuickContactList | |
26 from quick_frontend.quick_contact_management import QuickContactManagement | 25 from quick_frontend.quick_contact_management import QuickContactManagement |
27 import wx | 26 import wx |
27 from contact_list import ContactList | |
28 from chat import Chat | 28 from chat import Chat |
29 from param import Param | 29 from param import Param |
30 from form import Form | 30 from form import Form |
31 from gateways import GatewaysManager | 31 from gateways import GatewaysManager |
32 from profile import Profile | 32 from profile import Profile |
33 from profile_manager import ProfileManager | 33 from profile_manager import ProfileManager |
34 import gobject | 34 import gobject |
35 import os.path | 35 import os.path |
36 import pdb | 36 import pdb |
37 from tools.jid import JID | 37 from tools.jid import JID |
38 from logging import debug, info, error | 38 from logging import debug, info, warning, error |
39 from cgi import escape | 39 import constants |
40 import sys | 40 |
41 | |
42 IMAGE_DIR = sys.path[0]+'/images' | |
43 | |
44 msgOFFLINE = "offline" | |
45 msgONLINE = "online" | |
46 idCONNECT,\ | 41 idCONNECT,\ |
47 idDISCONNECT,\ | 42 idDISCONNECT,\ |
48 idEXIT,\ | 43 idEXIT,\ |
49 idPARAM,\ | 44 idPARAM,\ |
50 idADD_CONTACT,\ | 45 idADD_CONTACT,\ |
51 idREMOVE_CONTACT,\ | 46 idREMOVE_CONTACT,\ |
52 idSHOW_PROFILE,\ | 47 idSHOW_PROFILE,\ |
53 idFIND_GATEWAYS = range(8) | 48 idJOIN_ROOM,\ |
54 const_DEFAULT_GROUP = "Unclassed" | 49 idFIND_GATEWAYS = range(9) |
55 const_STATUS = [("", _("Online"), None), | |
56 ("chat", _("Free for chat"), "green"), | |
57 ("away", _("AFK"), "brown"), | |
58 ("dnd", _("DND"), "red"), | |
59 ("xa", _("Away"), "red")] | |
60 | 50 |
61 class ChatList(QuickChatList): | 51 class ChatList(QuickChatList): |
62 """This class manage the list of chat windows""" | 52 """This class manage the list of chat windows""" |
63 | 53 |
64 def __init__(self, host): | 54 def __init__(self, host): |
65 QuickChatList.__init__(self, host) | 55 QuickChatList.__init__(self, host) |
66 | 56 |
67 def createChat(self, target): | 57 def createChat(self, target): |
68 return Chat(target, self.host) | 58 return Chat(target, self.host) |
69 | 59 |
70 | |
71 class ContactList(wx.SimpleHtmlListBox, QuickContactList): | |
72 """Customized control to manage contacts.""" | |
73 | |
74 def __init__(self, parent, CM): | |
75 wx.SimpleHtmlListBox.__init__(self, parent, -1) | |
76 QuickContactList.__init__(self, CM) | |
77 self.host = parent | |
78 self.groups = {} #list contacts in each groups, key = group | |
79 self.Bind(wx.EVT_LISTBOX, self.onSelected) | |
80 self.Bind(wx.EVT_LISTBOX_DCLICK, self.onActivated) | |
81 | |
82 def __find_idx(self, entity, reverse=False): | |
83 """Find indexes of given jid (or groups) in contact list | |
84 @return: list of indexes""" | |
85 result=[] | |
86 for i in range(self.GetCount()): | |
87 if (type(entity) == JID and type(self.GetClientData(i)) == JID and self.GetClientData(i).short == entity.short) or\ | |
88 self.GetClientData(i) == entity: | |
89 result.append(i) | |
90 return result | |
91 | |
92 def replace(self, jid): | |
93 debug(_("update %s") % jid) | |
94 if not self.__find_idx(jid): | |
95 self.add(jid) | |
96 else: | |
97 for i in self.__find_idx(jid): | |
98 self.SetString(i, self.__presentItem(jid)) | |
99 | |
100 def disconnect(self, jid): | |
101 self.remove(jid) #for now, we only show online contacts | |
102 | |
103 def __eraseGroup(self, group): | |
104 """Erase all contacts in group | |
105 @param group: group to erase | |
106 @return: True if something as been erased""" | |
107 erased = False | |
108 indexes = self.__find_idx(group) | |
109 for idx in indexes: | |
110 while idx<self.GetCount()-1 and type(self.GetClientData(idx+1)) == JID: | |
111 erased = True | |
112 self.Delete(idx+1) | |
113 return erased | |
114 | |
115 | |
116 def __presentGroup(self, group): | |
117 """Make a nice presentation for the contact groups""" | |
118 html = """-- [%s] --""" % group | |
119 | |
120 return html | |
121 | |
122 def __presentItem(self, jid): | |
123 """Make a nice presentation of the contact in the list.""" | |
124 name = self.CM.getAttr(jid,'name') | |
125 nick = self.CM.getAttr(jid,'nick') | |
126 show = filter(lambda x:x[0]==self.CM.getAttr(jid,'show'), const_STATUS)[0] | |
127 #show[0]==shortcut | |
128 #show[1]==human readable | |
129 #show[2]==color (or None) | |
130 show_html = "<font color='%s'>[%s]</font>" % (show[2], show[1]) if show[2] else "" | |
131 status = self.CM.getAttr(jid,'status') or '' | |
132 avatar = self.CM.getAttr(jid,'avatar') or IMAGE_DIR+'/empty_avatar.png' | |
133 | |
134 #XXX: yes table I know :) but wxHTML* doesn't support CSS | |
135 html = """ | |
136 <table border='0'> | |
137 <td> | |
138 <img height='64' width='64' src='%s' /> | |
139 </td> | |
140 <td> | |
141 <b>%s</b> %s<br /> | |
142 <i>%s</i> | |
143 </td> | |
144 </table> | |
145 """ % (avatar, | |
146 escape(nick or name or jid.node or jid.short), | |
147 show_html, | |
148 escape(status)) | |
149 | |
150 return html | |
151 | |
152 def clear_contacts(self): | |
153 """Clear all the contact list""" | |
154 self.Clear() | |
155 | |
156 def add(self, jid): | |
157 """add a contact to the list""" | |
158 debug (_("adding %s"),jid) | |
159 groups = self.CM.getAttr(jid, 'groups') | |
160 if not groups: | |
161 idx = self.Append(self.__presentItem(jid), jid) | |
162 else: | |
163 for group in groups: | |
164 indexes = self.__find_idx(group) | |
165 gp_idx = 0 | |
166 if not indexes: #this is a new group, we have to create it | |
167 gp_idx = self.Append(self.__presentGroup(group), group) | |
168 else: | |
169 gp_idx = indexes[0] | |
170 | |
171 self.Insert(self.__presentItem(jid), gp_idx+1, jid) | |
172 | |
173 | |
174 | |
175 def remove(self, jid): | |
176 """remove a contact from the list""" | |
177 debug (_("removing %s"),jid) | |
178 list_idx = self.__find_idx(jid) | |
179 list_idx.reverse() #we me make some deletions, we have to reverse the order | |
180 for i in list_idx: | |
181 self.Delete(i) | |
182 | |
183 def onSelected(self, event): | |
184 """Called when a contact is selected.""" | |
185 data = self.getSelection() | |
186 if type(data) == JID: | |
187 event.Skip() | |
188 else: | |
189 group = self.GetClientData(self.GetSelection()) | |
190 erased = self.__eraseGroup(group) | |
191 if not erased: #the group was already erased, we can add again the contacts | |
192 contacts = self.CM.getContFromGroup(group) | |
193 contacts.sort() | |
194 id_insert = self.GetSelection()+1 | |
195 for contact in contacts: | |
196 self.Insert(self.__presentItem(contact), id_insert, contact) | |
197 self.SetSelection(wx.NOT_FOUND) | |
198 event.Skip(False) | |
199 | |
200 def onActivated(self, event): | |
201 """Called when a contact is clicked or activated with keyboard.""" | |
202 data = self.getSelection() | |
203 self.onActivatedCB(data) | |
204 event.Skip() | |
205 | |
206 def getSelection(self): | |
207 """Return the selected contact, or an empty string if there is not""" | |
208 if self.GetSelection() == wx.NOT_FOUND: | |
209 return None | |
210 data = self.GetClientData(self.GetSelection()) | |
211 if type(data) != JID: | |
212 return None | |
213 return data | |
214 | |
215 def registerActivatedCB(self, cb): | |
216 """Register a callback with manage contact activation.""" | |
217 self.onActivatedCB=cb | |
218 | |
219 class MainWindow(wx.Frame, QuickApp): | 60 class MainWindow(wx.Frame, QuickApp): |
220 """main app window""" | 61 """main app window""" |
221 | 62 |
222 def __init__(self): | 63 def __init__(self): |
223 wx.Frame.__init__(self,None, title="SàT Wix", size=(300,500)) | 64 wx.Frame.__init__(self,None, title="SàT Wix", size=(300,500)) |
226 #sizer | 67 #sizer |
227 self.sizer = wx.BoxSizer(wx.VERTICAL) | 68 self.sizer = wx.BoxSizer(wx.VERTICAL) |
228 self.SetSizer(self.sizer) | 69 self.SetSizer(self.sizer) |
229 | 70 |
230 #Frame elements | 71 #Frame elements |
231 self.contactList = ContactList(self, self.CM) | 72 self.contactList = ContactList(self, self) |
232 self.contactList.registerActivatedCB(self.onContactActivated) | 73 self.contactList.registerActivatedCB(self.onContactActivated) |
233 self.contactList.Hide() | 74 self.contactList.Hide() |
234 self.sizer.Add(self.contactList, 1, flag=wx.EXPAND) | 75 self.sizer.Add(self.contactList, 1, flag=wx.EXPAND) |
235 | 76 |
236 self.chat_wins=ChatList(self) | 77 self.chat_wins=ChatList(self) |
294 contactMenu.Append(idADD_CONTACT, _("&Add contact"),_(" Add a contact to your list")) | 135 contactMenu.Append(idADD_CONTACT, _("&Add contact"),_(" Add a contact to your list")) |
295 contactMenu.Append(idREMOVE_CONTACT, _("&Remove contact"),_(" Remove the selected contact from your list")) | 136 contactMenu.Append(idREMOVE_CONTACT, _("&Remove contact"),_(" Remove the selected contact from your list")) |
296 contactMenu.AppendSeparator() | 137 contactMenu.AppendSeparator() |
297 contactMenu.Append(idSHOW_PROFILE, _("&Show profile"), _(" Show contact's profile")) | 138 contactMenu.Append(idSHOW_PROFILE, _("&Show profile"), _(" Show contact's profile")) |
298 communicationMenu = wx.Menu() | 139 communicationMenu = wx.Menu() |
140 communicationMenu.Append(idJOIN_ROOM, _("&Join Room"),_(" Join a Multi-User Chat room")) | |
299 communicationMenu.Append(idFIND_GATEWAYS, _("&Find Gateways"),_(" Find gateways to legacy IM")) | 141 communicationMenu.Append(idFIND_GATEWAYS, _("&Find Gateways"),_(" Find gateways to legacy IM")) |
300 self.menuBar = wx.MenuBar() | 142 self.menuBar = wx.MenuBar() |
301 self.menuBar.Append(connectMenu,_("&General")) | 143 self.menuBar.Append(connectMenu,_("&General")) |
302 self.menuBar.Append(contactMenu,_("&Contacts")) | 144 self.menuBar.Append(contactMenu,_("&Contacts")) |
303 self.menuBar.Append(communicationMenu,_("&Communication")) | 145 self.menuBar.Append(communicationMenu,_("&Communication")) |
309 wx.EVT_MENU(self, idPARAM, self.onParam) | 151 wx.EVT_MENU(self, idPARAM, self.onParam) |
310 wx.EVT_MENU(self, idEXIT, self.onExit) | 152 wx.EVT_MENU(self, idEXIT, self.onExit) |
311 wx.EVT_MENU(self, idADD_CONTACT, self.onAddContact) | 153 wx.EVT_MENU(self, idADD_CONTACT, self.onAddContact) |
312 wx.EVT_MENU(self, idREMOVE_CONTACT, self.onRemoveContact) | 154 wx.EVT_MENU(self, idREMOVE_CONTACT, self.onRemoveContact) |
313 wx.EVT_MENU(self, idSHOW_PROFILE, self.onShowProfile) | 155 wx.EVT_MENU(self, idSHOW_PROFILE, self.onShowProfile) |
156 wx.EVT_MENU(self, idJOIN_ROOM, self.onJoinRoom) | |
314 wx.EVT_MENU(self, idFIND_GATEWAYS, self.onFindGateways) | 157 wx.EVT_MENU(self, idFIND_GATEWAYS, self.onFindGateways) |
315 | 158 |
316 | 159 |
317 def newMessage(self, from_jid, msg, type, to_jid, profile): | 160 def newMessage(self, from_jid, msg, type, to_jid, profile): |
318 QuickApp.newMessage(self, from_jid, msg, type, to_jid, profile) | 161 QuickApp.newMessage(self, from_jid, msg, type, to_jid, profile) |
162 | |
163 def roomJoined(self, room_id, room_service, room_nicks, user_nick, profile): | |
164 super(MainWindow, self).roomJoined(room_id, room_service, room_nicks, user_nick, profile) | |
165 self.chat_wins[room_id+'@'+room_service].setType("group") | |
166 self.chat_wins[room_id+'@'+room_service].setPresents([user_nick]+room_nicks) | |
319 | 167 |
320 def showAlert(self, message): | 168 def showAlert(self, message): |
321 # TODO: place this in a separate class | 169 # TODO: place this in a separate class |
322 popup=wx.PopupWindow(self) | 170 popup=wx.PopupWindow(self) |
323 ### following code come from wxpython demo | 171 ### following code come from wxpython demo |
550 def onProfileReceived(self, data): | 398 def onProfileReceived(self, data): |
551 """Called when a profile is received""" | 399 """Called when a profile is received""" |
552 debug (_('Profile received: [%s]') % data) | 400 debug (_('Profile received: [%s]') % data) |
553 profile=Profile(self, data) | 401 profile=Profile(self, data) |
554 | 402 |
403 def onJoinRoom(self, e): | |
404 warning('FIXME: temporary menu, must be improved') | |
405 self.bridge.joinMUC("conference.necton2.int", "test", "Goffi \o/", self.profile) | |
555 | 406 |
556 def onFindGateways(self, e): | 407 def onFindGateways(self, e): |
557 debug(_("Find Gateways request")) | 408 debug(_("Find Gateways request")) |
558 id = self.bridge.findGateways(self.profiles[self.profile]['whoami'].domain) | 409 id = self.bridge.findGateways(self.profiles[self.profile]['whoami'].domain) |
559 self.current_action_ids.add(id) | 410 self.current_action_ids.add(id) |