1 # Copyright (c) 2001-2004 Twisted Matrix Laboratories.
2 # See LICENSE for details.
6 """Base classes for Instance Messenger clients."""
8 from Screens.Screen import Screen
10 from Components.Pixmap import *
11 from Components.Pixmap import Pixmap
12 from Components.ActionMap import ActionMap, NumberActionMap
13 from Components.ScrollLabel import ScrollLabel
14 from Components.GUIComponent import *
15 from Components.MenuList import MenuList
16 from Components.Input import Input
17 from Components.Label import Label
18 from Components.config import *
19 from Components.ConfigList import ConfigList
20 from Plugins.Plugin import PluginDescriptor
21 from Tools.NumericalTextInput import *
22 from Tools.Directories import *
24 from locals import OFFLINE, ONLINE, AWAY
27 from dreamIRCTools import *
28 #from myScrollLabel import *
29 #from dreamIRCMainMenu import *
32 """A GUI object that displays a contacts list"""
33 def __init__(self, chatui):
36 @type chatui: L{ChatUI}
40 self.onlineContacts = {}
43 def setContactStatus(self, person):
44 """Inform the user that a person's status has changed.
46 @type person: L{Person<interfaces.IPerson>}
48 if not self.contacts.has_key(person.name):
49 self.contacts[person.name] = person
50 if not self.onlineContacts.has_key(person.name) and \
51 (person.status == ONLINE or person.status == AWAY):
52 self.onlineContacts[person.name] = person
53 if self.onlineContacts.has_key(person.name) and \
54 person.status == OFFLINE:
55 del self.onlineContacts[person.name]
57 def registerAccountClient(self, client):
58 """Notify the user that an account client has been signed on to.
60 @type client: L{Client<interfaces.IClient>}
62 if not client in self.clients:
63 self.clients.append(client)
65 def unregisterAccountClient(self, client):
66 """Notify the user that an account client has been signed off
69 @type client: L{Client<interfaces.IClient>}
71 if client in self.clients:
72 self.clients.remove(client)
74 def contactChangedNick(self, person, newnick):
76 if self.contacts.has_key(oldname):
77 del self.contacts[oldname]
79 self.contacts[newnick] = person
80 if self.onlineContacts.has_key(oldname):
81 del self.onlineContacts[oldname]
82 self.onlineContacts[newnick] = person
86 """A GUI window of a conversation with a specific person"""
87 def __init__(self, person, chatui):
89 @type person: L{Person<interfaces.IPerson>}
90 @type chatui: L{ChatUI}
94 self.pipe = MessagePipe()
96 self.timer.timeout.get().append(self.sendOutPipe)
100 """Displays the ConversationWindow"""
101 # raise NotImplementedError("Subclasses must implement this method")
104 """Hides the ConversationWindow"""
105 # raise NotImplementedError("Subclasses must implement this method")
107 def sendText(self, text):
108 """Sends text to the person with whom the user is conversing.
109 @returntype: L{Deferred<twisted.internet.defer.Deferred>}
111 self.person.sendMessage(text, None)
112 self.pipe.add("%s" % text)
113 self.pipe.clearOutText()
114 # print"<%s> %s" % (self.nickname, text)
116 def sendOutPipe(self):
117 if len(str(self.pipe.getOutText())) > 0:
118 if (self.pipe.getOutText()=="/QUIT"):
119 print "/quit detected...."
120 self.pipe.clearOutText()
123 print "sending chat : %s" % str(self.pipe.getOutText())
124 self.sendText(str(self.pipe.getOutText()))
125 self.pipe.clearOutText()
127 def showMessage(self, text, metadata=None):
128 """Display a message sent from the person with whom she is conversing
133 self.pipe.add("<%s> %s" % (self.person.name, text))
134 # raise NotImplementedError("Subclasses must implement this method")
136 def contactChangedNick(self, person, newnick):
137 """Change a person's name.
139 @type person: L{Person<interfaces.IPerson>}
140 @type newnick: string
142 self.person.name = newnick
143 self.pipe.add("-!- %s is now known as %s" % (person.name, newnick))
144 # self.blist.updateBuddyWindow()
146 def serverMsg(self, message):
147 """Displays a serverMsg in the group conversation window
149 @type message: string
151 self.pipe.add("-!- %s " % (message))
152 # self.blist.updateBuddyWindow()
154 class GroupConversation:
155 """A conversation with a group of people."""
156 def __init__(self, group, chatui):
158 @type group: L{Group<interfaces.IGroup>}
160 @type chatui: L{ChatUI}
165 self.pipe = MessagePipe()
167 self.timer.timeout.get().append(self.sendOutPipe)
168 self.timer.start(100)
171 """Displays the GroupConversationWindow."""
172 # raise NotImplementedError("Subclasses must implement this method")
175 """Hides the GroupConversationWindow."""
176 # raise NotImplementedError("Subclasses must implement this method")
178 def sendText(self, text):
179 """Sends text to the group.
181 @returntype: L{Deferred<twisted.internet.defer.Deferred>}
183 self.group.sendGroupMessage(text, None)
184 self.pipe.add("%s" % text)
185 self.pipe.clearOutText()
187 def sendOutPipe(self):
188 if len(str(self.pipe.getOutText())) > 0:
189 if (self.pipe.getOutText()=="/QUIT"):
190 # print "/quit detected...."
191 self.pipe.clearOutText()
194 print "sending group chat : %s" % str(self.pipe.getOutText())
195 self.sendText(str(self.pipe.getOutText()))
196 self.pipe.clearOutText()
198 def showGroupMessage(self, sender, text, metadata=None):
199 """Displays to the user a message sent to this group from the given sender
200 @type sender: string (XXX: Not Person?)
204 self.pipe.add("<%s/%s> %s" % (sender, self.group.name, text))
206 def setGroupMembers(self, members):
207 """Sets the list of members in the group and displays it to the user
209 self.members = members
210 self.refreshMemberList()
212 def setTopic(self, topic, author):
213 """Displays the topic (from the server) for the group conversation window
216 @type author: string (XXX: Not Person?)
218 self.pipe.add("-!- %s set the topic of %s to: %s" % (author, self.group.name, topic))
220 def serverMsg(self, message):
221 """Displays a serverMsg in the group conversation window
223 @type message: string
225 self.pipe.add("-!- %s " % (message))
227 def memberJoined(self, member):
228 """Adds the given member to the list of members in the group conversation
229 and displays this to the user
231 @type member: string (XXX: Not Person?)
233 if not member in self.members:
234 self.members.append(member)
235 self.pipe.add("-!- %s joined %s" % (member, self.group.name))
236 self.refreshMemberList()
238 def memberChangedNick(self, oldnick, newnick):
239 """Changes the oldnick in the list of members to newnick and displays this
242 @type oldnick: string
243 @type newnick: string
245 if oldnick in self.members:
246 self.members.remove(oldnick)
247 self.members.append(newnick)
248 #self.chatui.contactChangedNick(oldnick, newnick)
249 self.pipe.add("-!- %s is now known as %s in %s" % (oldnick, newnick, self.group.name))
250 self.refreshMemberList()
252 def memberLeft(self, member):
253 """Deletes the given member from the list of members in the group
254 conversation and displays the change to the user
258 if member in self.members:
259 self.members.remove(member)
260 self.pipe.add("-!- %s left %s" % (member, self.group.name))
261 self.refreshMemberList()
264 def refreshMemberList(self):
265 self.pipe.clearBuddyList()
266 self.members.sort(lambda x,y: cmp(string.lower(x), string.lower(y)))
267 self.pipe.getCannelName(self.group.name)
268 for member in self.members:
269 self.pipe.buildBuddyList(str(member))
270 # print "Channel : #%s" % self.group.name
271 print "Buddylist of #%s : \n%s" % (self.group.name, self.pipe.showBuddyList())
272 # self.pipe.showBuddyList()
273 # self.pipe.updateBuddyWindow(self.pipe.showBuddyList())
274 self.pipe.updateBuddyWindow()
277 """A GUI chat client"""
279 self.conversations = {} # cache of all direct windows
280 self.groupConversations = {} # cache of all group windows
281 self.persons = {} # keys are (name, client)
282 self.groups = {} # cache of all groups
283 self.onlineClients = [] # list of message sources currently online
284 self.contactsList = ContactsList(self)
285 self.pipe = MessagePipe()
288 def registerAccountClient(self, client):
289 """Notifies user that an account has been signed on to.
291 @type client: L{Client<interfaces.IClient>}
292 @returns: client, so that I may be used in a callback chain
294 self.pipe.debug("signing onto %s" % client.accountName)
295 self.onlineClients.append(client)
296 self.contactsList.registerAccountClient(client)
298 self.pipe.debug(" --- %s ---" % self.helper)
299 self.pipe.add("signing onto %s" % client)
300 self.pipe.add("signing onto %s" % client.accountName)
303 def unregisterAccountClient(self, client):
304 """Notifies user that an account has been signed off or disconnected
306 @type client: L{Client<interfaces.IClient>}
308 self.pipe.debug("signing off from %s" % client.accountName)
309 self.onlineClients.remove(client)
310 self.contactsList.unregisterAccountClient(client)
313 """Notifies user that an account has been signed off or disconnected
315 @type client: L{Client<interfaces.IClient>}
317 self.pipe.debug(" --- %s ---" % self.helper)
318 self.pipe.debug("signing off from %s" % self.helper.accountName)
319 self.pipe.add("signing off %s" % helper)
320 self.pipe.add("signing off %s" % helper.accountName)
321 self.onlineClients.remove(helper)
322 self.contactsList.unregisterAccountClient(helper)
324 def getContactsList(self):
326 @returntype: L{ContactsList}
328 self.pipe.debug("contactlist = %s" % self.contactsList)
329 return self.contactsList
332 def getConversation(self, person, Class=Conversation, stayHidden=0):
333 """For the given person object, returns the conversation window
334 or creates and returns a new conversation window if one does not exist.
336 @type person: L{Person<interfaces.IPerson>}
337 @type Class: L{Conversation<interfaces.IConversation>} class
338 @type stayHidden: boolean
340 @returntype: L{Conversation<interfaces.IConversation>}
342 conv = self.conversations.get(person)
344 conv = Class(person, self)
345 self.conversations[person] = conv
352 def getGroupConversation(self,group,Class=GroupConversation,stayHidden=0):
353 """For the given group object, returns the group conversation window or
354 creates and returns a new group conversation window if it doesn't exist
356 @type group: L{Group<interfaces.IGroup>}
357 @type Class: L{Conversation<interfaces.IConversation>} class
358 @type stayHidden: boolean
360 @returntype: L{GroupConversation<interfaces.IGroupConversation>}
362 conv = self.groupConversations.get(group)
364 conv = Class(group, self)
365 self.groupConversations[group] = conv
370 # print "[dreamIRC] : " , conv
373 def getPerson(self, name, client):
374 """For the given name and account client, returns the instance of the
375 AbstractPerson subclass, or creates and returns a new AbstractPerson
376 subclass of the type Class
379 @type client: L{Client<interfaces.IClient>}
381 @returntype: L{Person<interfaces.IPerson>}
383 account = client.account
384 p = self.persons.get((name, account))
386 p = account.getPerson(name)
387 self.persons[name, account] = p
390 def getGroup(self, name, client):
391 """For the given name and account client, returns the instance of the
392 AbstractGroup subclass, or creates and returns a new AbstractGroup
393 subclass of the type Class
396 @type client: L{Client<interfaces.IClient>}
398 @returntype: L{Group<interfaces.IGroup>}
400 # I accept 'client' instead of 'account' in my signature for
401 # backwards compatibility. (Groups changed to be Account-oriented
402 # in CVS revision 1.8.)
403 account = client.account
404 g = self.groups.get((name, account))
406 g = account.getGroup(name)
407 self.groups[name, account] = g
408 # self.pipe.add("joined %s" % g)
411 def contactChangedNick(self, oldnick, newnick):
412 """For the given person, changes the person's name to newnick, and
413 tells the contact list and any conversation windows with that person
416 @type oldnick: string
417 @type newnick: string
419 if self.persons.has_key((person.name, person.account)):
420 conv = self.conversations.get(person)
422 conv.contactChangedNick(person, newnick)
424 self.contactsList.contactChangedNick(person, newnick)
426 del self.persons[person.name, person.account]
427 person.name = newnick
428 self.persons[person.name, person.account] = person
430 def sendOutPipe(self):
431 print "groupchat %s" % self.pipe.OutText
432 if len(self.pipe.OutText()) > 0:
433 self.sendText(self.pipe.OutText())
434 self.pipe.clearOutText()