some minor fixes
[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 #        print"<%s> %s" % (self.nickname, text)
115
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()
121                 self.person.bye()
122             else:
123                 print "sending chat : %s" % str(self.pipe.getOutText())
124                 self.sendText(str(self.pipe.getOutText()))
125                 self.pipe.clearOutText()
126
127     def showMessage(self, text, metadata=None):
128         """Display a message sent from the person with whom she is conversing
129
130         @type text: string
131         @type metadata: dict
132         """
133         self.pipe.add("<%s> %s" % (self.person.name, text))
134 #        raise NotImplementedError("Subclasses must implement this method")
135
136     def contactChangedNick(self, person, newnick):
137         """Change a person's name.
138
139         @type person: L{Person<interfaces.IPerson>}
140         @type newnick: string
141         """
142         self.person.name = newnick
143         self.pipe.add("-!- %s is now known as %s" % (person.name, newnick))
144 #        self.blist.updateBuddyWindow()
145
146     def serverMsg(self, message):
147         """Displays a serverMsg in the group conversation window
148
149         @type message: string
150         """
151         self.pipe.add("-!- %s " % (message))
152 #        self.blist.updateBuddyWindow()
153
154 class GroupConversation:
155     """A conversation with a group of people."""
156     def __init__(self, group, chatui):
157         """
158         @type group: L{Group<interfaces.IGroup>}
159         @param chatui: ???
160         @type chatui: L{ChatUI}
161         """
162         self.chatui = chatui
163         self.group = group
164         self.members = []
165         self.pipe = MessagePipe()
166         self.timer=eTimer()
167         self.timer.timeout.get().append(self.sendOutPipe)
168         self.timer.start(100)
169         
170     def show(self):
171         """Displays the GroupConversationWindow."""
172 #        raise NotImplementedError("Subclasses must implement this method")
173
174     def hide(self):
175         """Hides the GroupConversationWindow."""
176 #        raise NotImplementedError("Subclasses must implement this method")
177
178     def sendText(self, text):
179         """Sends text to the group.
180         @type text: string
181         @returntype: L{Deferred<twisted.internet.defer.Deferred>}
182         """
183         self.group.sendGroupMessage(text, None)
184         self.pipe.add("%s" % text)
185         self.pipe.clearOutText()
186     
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()
192                         self.group.bye()
193                 else:
194                         print "sending group chat : %s" % str(self.pipe.getOutText())
195                         self.sendText(str(self.pipe.getOutText()))
196                         self.pipe.clearOutText()
197
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?)
201         @type text: string
202         @type metadata: dict
203         """
204         self.pipe.add("<%s/%s> %s" % (sender, self.group.name, text))
205
206     def setGroupMembers(self, members):
207         """Sets the list of members in the group and displays it to the user
208         """
209         self.members = members
210         self.refreshMemberList()
211
212     def setTopic(self, topic, author):
213         """Displays the topic (from the server) for the group conversation window
214
215         @type topic: string
216         @type author: string (XXX: Not Person?)
217         """
218         self.pipe.add("-!- %s set the topic of %s to: %s" % (author, self.group.name, topic))
219
220     def serverMsg(self, message):
221         """Displays a serverMsg in the group conversation window
222
223         @type message: string
224         """
225         self.pipe.add("-!- %s " % (message))
226
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
230
231         @type member: string (XXX: Not Person?)
232         """
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()
237
238     def memberChangedNick(self, oldnick, newnick):
239         """Changes the oldnick in the list of members to newnick and displays this
240         change to the user
241
242         @type oldnick: string
243         @type newnick: string
244         """
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()
251
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
255
256         @type member: string
257         """
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()
262         
263         
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()
275         
276 class ChatUI:
277     """A GUI chat client"""
278     def __init__(self):
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()
286         self.helper = ""
287
288     def registerAccountClient(self, client):
289         """Notifies user that an account has been signed on to.
290
291         @type client: L{Client<interfaces.IClient>}
292         @returns: client, so that I may be used in a callback chain
293         """
294         self.pipe.debug("signing onto %s" % client.accountName)
295         self.onlineClients.append(client)
296         self.contactsList.registerAccountClient(client)
297         self.helper=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)
301         return client
302
303     def unregisterAccountClient(self, client):
304         """Notifies user that an account has been signed off or disconnected
305
306         @type client: L{Client<interfaces.IClient>}
307         """
308         self.pipe.debug("signing off from %s" % client.accountName)
309         self.onlineClients.remove(client)
310         self.contactsList.unregisterAccountClient(client)
311
312     def remClient(self):
313         """Notifies user that an account has been signed off or disconnected
314
315         @type client: L{Client<interfaces.IClient>}
316         """
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)
323
324     def getContactsList(self):
325         """
326         @returntype: L{ContactsList}
327         """
328         self.pipe.debug("contactlist = %s" % self.contactsList)
329         return self.contactsList
330
331
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.
335
336         @type person: L{Person<interfaces.IPerson>}
337         @type Class: L{Conversation<interfaces.IConversation>} class
338         @type stayHidden: boolean
339
340         @returntype: L{Conversation<interfaces.IConversation>}
341         """
342         conv = self.conversations.get(person)
343         if not conv:
344             conv = Class(person, self)
345             self.conversations[person] = conv
346         if stayHidden:
347             conv.hide()
348         else:
349             conv.show()
350         return conv
351
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
355
356         @type group: L{Group<interfaces.IGroup>}
357         @type Class: L{Conversation<interfaces.IConversation>} class
358         @type stayHidden: boolean
359
360         @returntype: L{GroupConversation<interfaces.IGroupConversation>}
361         """
362         conv = self.groupConversations.get(group)
363         if not conv:
364             conv = Class(group, self)
365             self.groupConversations[group] = conv
366         if stayHidden:
367             conv.hide()
368         else:
369             conv.show()
370 #        print "[dreamIRC] : " , conv
371         return conv
372
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
377
378         @type name: string
379         @type client: L{Client<interfaces.IClient>}
380
381         @returntype: L{Person<interfaces.IPerson>}
382         """
383         account = client.account
384         p = self.persons.get((name, account))
385         if not p:
386             p = account.getPerson(name)
387             self.persons[name, account] = p
388         return p
389
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
394
395         @type name: string
396         @type client: L{Client<interfaces.IClient>}
397
398         @returntype: L{Group<interfaces.IGroup>}
399         """
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))
405         if not g:
406             g = account.getGroup(name)
407             self.groups[name, account] = g
408 #        self.pipe.add("joined %s" % g)
409         return g
410
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
414         to change as well.
415
416         @type oldnick: string
417         @type newnick: string
418         """
419         if self.persons.has_key((person.name, person.account)):
420             conv = self.conversations.get(person)
421             if conv:
422                 conv.contactChangedNick(person, newnick)
423
424             self.contactsList.contactChangedNick(person, newnick)
425
426             del self.persons[person.name, person.account]
427             person.name = newnick
428             self.persons[person.name, person.account] = person
429
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()