ecasa: add search functionality
[enigma2-plugins.git] / dreamirc / src / e2chat.py
1 # Copyright (c) 2001-2004 Twisted Matrix Laboratories.
2 # See LICENSE for details.
3
4 #
5
6 """Base classes for Instance Messenger clients."""
7 from enigma import *
8 from Screens.Screen import Screen
9
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 *
23
24 from locals import OFFLINE, ONLINE, AWAY
25 import dreamIRCTools
26 from enigma import *
27 from dreamIRCTools import *
28 #from myScrollLabel import *
29 #from dreamIRCMainMenu import *
30
31 class ContactsList:
32     """A GUI object that displays a contacts list"""
33     def __init__(self, chatui):
34         """
35         @param chatui: ???
36         @type chatui: L{ChatUI}
37         """
38         self.chatui = chatui
39         self.contacts = {}
40         self.onlineContacts = {}
41         self.clients = []
42         
43     def setContactStatus(self, person):
44         """Inform the user that a person's status has changed.
45
46         @type person: L{Person<interfaces.IPerson>}
47         """
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]
56
57     def registerAccountClient(self, client):
58         """Notify the user that an account client has been signed on to.
59
60         @type client: L{Client<interfaces.IClient>}
61         """
62         if not client in self.clients:
63             self.clients.append(client)
64
65     def unregisterAccountClient(self, client):
66         """Notify the user that an account client has been signed off
67         or disconnected from.
68
69         @type client: L{Client<interfaces.IClient>}
70          """
71         if client in self.clients:
72             self.clients.remove(client)
73
74     def contactChangedNick(self, person, newnick):
75         oldname = person.name
76         if self.contacts.has_key(oldname):
77             del self.contacts[oldname]
78             person.name = newnick
79             self.contacts[newnick] = person
80             if self.onlineContacts.has_key(oldname):
81                 del self.onlineContacts[oldname]
82                 self.onlineContacts[newnick] = person
83
84
85 class Conversation:
86     """A GUI window of a conversation with a specific person"""
87     def __init__(self, person, chatui):
88         """
89         @type person: L{Person<interfaces.IPerson>}
90         @type chatui: L{ChatUI}
91         """
92         self.chatui = chatui
93         self.person = person
94         self.pipe = MessagePipe()
95         self.timer=eTimer()
96         self.timer.timeout.get().append(self.sendOutPipe)
97         self.timer.start(100)
98
99     def show(self):
100         """Displays the ConversationWindow"""
101  #       raise NotImplementedError("Subclasses must implement this method")
102
103     def hide(self):
104         """Hides the ConversationWindow"""
105 #        raise NotImplementedError("Subclasses must implement this method")
106
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>}
110         """
111         self.person.sendMessage(text, None)
112         self.pipe.add("%s" % text)
113         self.pipe.clearOutText()
114
115     def sendOutPipe(self):
116         if len(str(self.pipe.getOutText())) > 0:
117             if (self.pipe.getOutText()=="/QUIT"):
118                 self.pipe.debug("/quit detected....")
119                 self.pipe.clearOutText()
120                 self.person.bye()
121             else:
122                 self.pipe.debug("sending chat : %s" % str(self.pipe.getOutText()))
123                 self.sendText(str(self.pipe.getOutText()))
124                 self.pipe.clearOutText()
125
126     def showMessage(self, text, metadata=None):
127         """Display a message sent from the person with whom she is conversing
128
129         @type text: string
130         @type metadata: dict
131         """
132         self.pipe.add("<%s> %s" % (self.person.name, text))
133
134     def contactChangedNick(self, person, newnick):
135         """Change a person's name.
136
137         @type person: L{Person<interfaces.IPerson>}
138         @type newnick: string
139         """
140         self.person.name = newnick
141         self.pipe.add("-!- %s is now known as %s" % (person.name, newnick))
142
143     def serverMsg(self, message):
144         """Displays a serverMsg in the group conversation window
145
146         @type message: string
147         """
148         self.pipe.add("-!- %s " % (message))
149
150 class GroupConversation:
151     """A conversation with a group of people."""
152     def __init__(self, group, chatui):
153         """
154         @type group: L{Group<interfaces.IGroup>}
155         @param chatui: ???
156         @type chatui: L{ChatUI}
157         """
158         self.chatui = chatui
159         self.group = group
160         self.members = []
161         self.pipe = MessagePipe()
162         self.timer=eTimer()
163         self.timer.timeout.get().append(self.sendOutPipe)
164         self.timer.start(100)
165         
166     def show(self):
167         """Displays the GroupConversationWindow."""
168 #        raise NotImplementedError("Subclasses must implement this method")
169
170     def hide(self):
171         """Hides the GroupConversationWindow."""
172 #        raise NotImplementedError("Subclasses must implement this method")
173
174     def sendText(self, text):
175         """Sends text to the group.
176         @type text: string
177         @returntype: L{Deferred<twisted.internet.defer.Deferred>}
178         """
179         self.group.sendGroupMessage(text, None)
180         self.pipe.add("%s" % text)
181         self.pipe.clearOutText()
182     
183     def sendOutPipe(self):
184         if len(str(self.pipe.getOutText())) > 0:
185                 if (self.pipe.getOutText()=="/QUIT"):
186                         self.pipe.debug("/quit detected....")
187                         self.pipe.clearOutText()
188                         self.group.bye()
189                 else:
190                         self.pipe.debug("sending group chat : %s" % str(self.pipe.getOutText()))
191                         self.sendText(str(self.pipe.getOutText()))
192                         self.pipe.clearOutText()
193
194     def showGroupMessage(self, sender, text, metadata=None):
195         """Displays to the user a message sent to this group from the given sender
196         @type sender: string (XXX: Not Person?)
197         @type text: string
198         @type metadata: dict
199         """
200         self.pipe.add("<%s/%s> %s" % (sender, self.group.name, text))
201
202     def setGroupMembers(self, members):
203         """Sets the list of members in the group and displays it to the user
204         """
205         self.members = members
206         self.refreshMemberList()
207
208     def setTopic(self, topic, author):
209         """Displays the topic (from the server) for the group conversation window
210
211         @type topic: string
212         @type author: string (XXX: Not Person?)
213         """
214         self.pipe.add("-!- %s set the topic of %s to: %s" % (author, self.group.name, topic))
215
216     def serverMsg(self, message):
217         """Displays a serverMsg in the group conversation window
218
219         @type message: string
220         """
221         self.pipe.add("-!- %s " % (message))
222
223     def memberJoined(self, member):
224         """Adds the given member to the list of members in the group conversation
225         and displays this to the user
226
227         @type member: string (XXX: Not Person?)
228         """
229         if not member in self.members:
230             self.members.append(member)
231         self.pipe.add("-!- %s joined %s" % (member, self.group.name))
232         self.refreshMemberList()
233
234     def memberChangedNick(self, oldnick, newnick):
235         """Changes the oldnick in the list of members to newnick and displays this
236         change to the user
237
238         @type oldnick: string
239         @type newnick: string
240         """
241         if oldnick in self.members:
242             self.members.remove(oldnick)
243             self.members.append(newnick)
244             #self.chatui.contactChangedNick(oldnick, newnick)
245         self.pipe.add("-!- %s is now known as %s in %s" % (oldnick, newnick, self.group.name))
246         self.refreshMemberList()
247
248     def memberLeft(self, member):
249         """Deletes the given member from the list of members in the group
250         conversation and displays the change to the user
251
252         @type member: string
253         """
254         if member in self.members:
255             self.members.remove(member)
256         self.pipe.add("-!- %s left %s" % (member, self.group.name))
257         self.refreshMemberList()
258         
259         
260     def refreshMemberList(self):
261         self.pipe.clearBuddyList()
262         self.members.sort(lambda x,y: cmp(string.lower(x), string.lower(y)))
263         self.pipe.getCannelName(self.group.name)
264         for member in self.members:
265             self.pipe.buildBuddyList(str(member))
266         print "Buddylist of #%s : \n%s" % (self.group.name, self.pipe.showBuddyList())
267         self.pipe.updateBuddyWindow()
268         
269 class ChatUI:
270     """A GUI chat client"""
271     def __init__(self):
272         self.conversations = {}      # cache of all direct windows
273         self.groupConversations = {} # cache of all group windows
274         self.persons = {}            # keys are (name, client)
275         self.groups = {}             # cache of all groups
276         self.onlineClients = []      # list of message sources currently online
277         self.contactsList = ContactsList(self)
278         self.pipe = MessagePipe()
279         self.helper = ""
280
281     def registerAccountClient(self, client):
282         """Notifies user that an account has been signed on to.
283
284         @type client: L{Client<interfaces.IClient>}
285         @returns: client, so that I may be used in a callback chain
286         """
287         self.pipe.debug("signing onto %s" % client.accountName)
288         self.onlineClients.append(client)
289         self.contactsList.registerAccountClient(client)
290         self.helper=client
291         self.pipe.debug(" --- %s ---" % self.helper)
292         self.pipe.add("signing onto %s" % client)
293         self.pipe.add("signing onto %s" % client.accountName)
294         return client
295
296     def unregisterAccountClient(self, client):
297         """Notifies user that an account has been signed off or disconnected
298
299         @type client: L{Client<interfaces.IClient>}
300         """
301         self.pipe.debug("signing off from %s" % client.accountName)
302         self.onlineClients.remove(client)
303         self.contactsList.unregisterAccountClient(client)
304
305     def remClient(self):
306         """Notifies user that an account has been signed off or disconnected
307
308         @type client: L{Client<interfaces.IClient>}
309         """
310         self.pipe.debug(" --- %s ---" % self.helper)
311         self.pipe.debug("signing off from %s"  % self.helper.accountName)
312         self.pipe.add("signing off %s" % helper)
313         self.pipe.add("signing off %s" % helper.accountName)        
314         self.onlineClients.remove(helper)
315         self.contactsList.unregisterAccountClient(helper)
316
317     def getContactsList(self):
318         """
319         @returntype: L{ContactsList}
320         """
321         self.pipe.debug("contactlist = %s" % self.contactsList)
322         return self.contactsList
323
324
325     def getConversation(self, person, Class=Conversation, stayHidden=0):
326         """For the given person object, returns the conversation window
327         or creates and returns a new conversation window if one does not exist.
328
329         @type person: L{Person<interfaces.IPerson>}
330         @type Class: L{Conversation<interfaces.IConversation>} class
331         @type stayHidden: boolean
332
333         @returntype: L{Conversation<interfaces.IConversation>}
334         """
335         conv = self.conversations.get(person)
336         if not conv:
337             conv = Class(person, self)
338             self.conversations[person] = conv
339         if stayHidden:
340             conv.hide()
341         else:
342             conv.show()
343         return conv
344
345     def getGroupConversation(self,group,Class=GroupConversation,stayHidden=0):
346         """For the given group object, returns the group conversation window or
347         creates and returns a new group conversation window if it doesn't exist
348
349         @type group: L{Group<interfaces.IGroup>}
350         @type Class: L{Conversation<interfaces.IConversation>} class
351         @type stayHidden: boolean
352
353         @returntype: L{GroupConversation<interfaces.IGroupConversation>}
354         """
355         conv = self.groupConversations.get(group)
356         if not conv:
357             conv = Class(group, self)
358             self.groupConversations[group] = conv
359         if stayHidden:
360             conv.hide()
361         else:
362             conv.show()
363 #        print "[dreamIRC] : " , conv
364         return conv
365
366     def getPerson(self, name, client):
367         """For the given name and account client, returns the instance of the
368         AbstractPerson subclass, or creates and returns a new AbstractPerson
369         subclass of the type Class
370
371         @type name: string
372         @type client: L{Client<interfaces.IClient>}
373
374         @returntype: L{Person<interfaces.IPerson>}
375         """
376         account = client.account
377         p = self.persons.get((name, account))
378         if not p:
379             p = account.getPerson(name)
380             self.persons[name, account] = p
381         return p
382
383     def getGroup(self, name, client):
384         """For the given name and account client, returns the instance of the
385         AbstractGroup subclass, or creates and returns a new AbstractGroup
386         subclass of the type Class
387
388         @type name: string
389         @type client: L{Client<interfaces.IClient>}
390
391         @returntype: L{Group<interfaces.IGroup>}
392         """
393         # I accept 'client' instead of 'account' in my signature for
394         # backwards compatibility.  (Groups changed to be Account-oriented
395         # in CVS revision 1.8.)
396         account = client.account
397         g = self.groups.get((name, account))
398         if not g:
399             g = account.getGroup(name)
400             self.groups[name, account] = g
401 #        self.pipe.add("joined %s" % g)
402         return g
403
404     def contactChangedNick(self, oldnick, newnick):
405         """For the given person, changes the person's name to newnick, and
406         tells the contact list and any conversation windows with that person
407         to change as well.
408
409         @type oldnick: string
410         @type newnick: string
411         """
412         if self.persons.has_key((person.name, person.account)):
413             conv = self.conversations.get(person)
414             if conv:
415                 conv.contactChangedNick(person, newnick)
416
417             self.contactsList.contactChangedNick(person, newnick)
418
419             del self.persons[person.name, person.account]
420             person.name = newnick
421             self.persons[person.name, person.account] = person
422
423     def sendOutPipe(self):
424         print "groupchat %s" % self.pipe.OutText
425         if len(self.pipe.OutText()) > 0:
426             self.sendText(self.pipe.OutText())
427             self.pipe.clearOutText()