| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. |  | 
| 2 # See LICENSE for details. |  | 
| 3 |  | 
| 4 |  | 
| 5 import string |  | 
| 6 import time |  | 
| 7 |  | 
| 8 import gtk |  | 
| 9 |  | 
| 10 from twisted.words.im.gtkcommon import GLADE_FILE, autoConnectMethods, InputOutp
     utWindow, openGlade |  | 
| 11 |  | 
| 12 class ContactsList: |  | 
| 13     def __init__(self, chatui, xml): |  | 
| 14         self.xml = xml# openGlade(GLADE_FILE, root="ContactsWidget") |  | 
| 15         # self.widget = self.xml.get_widget("ContactsWidget") |  | 
| 16         self.people = [] |  | 
| 17         self.onlinePeople = [] |  | 
| 18         self.countOnline = 0 |  | 
| 19         autoConnectMethods(self) |  | 
| 20         self.selectedPerson = None |  | 
| 21         self.xml.get_widget("OnlineCount").set_text("Online: 0") |  | 
| 22         self.chat = chatui |  | 
| 23 |  | 
| 24         # Construct Menu for Account Selection |  | 
| 25         self.optionMenu = self.xml.get_widget("AccountsListPopup") |  | 
| 26         self.accountMenuItems = [] |  | 
| 27         self.currentAccount = None |  | 
| 28 |  | 
| 29 |  | 
| 30     def registerAccountClient(self, account): |  | 
| 31         print 'registering account client', self, account |  | 
| 32         self.accountMenuItems.append(account) |  | 
| 33         self._updateAccountMenu() |  | 
| 34         self.chat._accountmanager.refreshAccounts() |  | 
| 35 |  | 
| 36     def _updateAccountMenu(self): |  | 
| 37         # This seems to be necessary -- I don't understand gtk's handling of |  | 
| 38         # GtkOptionMenus |  | 
| 39         print 'updating account menu', self.accountMenuItems |  | 
| 40         self.accountMenu = gtk.GtkMenu() |  | 
| 41         for account in self.accountMenuItems: |  | 
| 42             i = gtk.GtkMenuItem(account.accountName) |  | 
| 43             i.connect('activate', self.on_AccountsListPopup_activate, account) |  | 
| 44             self.accountMenu.append(i) |  | 
| 45         if self.accountMenuItems: |  | 
| 46             print "setting default account to", self.accountMenuItems[0] |  | 
| 47             self.currentAccount = self.accountMenuItems[0] |  | 
| 48         self.accountMenu.show_all() |  | 
| 49         self.optionMenu.set_menu(self.accountMenu) |  | 
| 50 |  | 
| 51     def on_AccountsListPopup_activate(self, w, account): |  | 
| 52         print 'setting current account', account |  | 
| 53         self.currentAccount = account |  | 
| 54 |  | 
| 55     def on_AddContactButton_clicked(self, b): |  | 
| 56         self.currentAccount.addContact( |  | 
| 57             self.xml.get_widget("ContactNameEntry").get_text()) |  | 
| 58 |  | 
| 59     def unregisterAccountClient(self,account): |  | 
| 60         print 'unregistering account client', self, account |  | 
| 61         self.accountMenuItems.remove(account) |  | 
| 62         self._updateAccountMenu() |  | 
| 63 |  | 
| 64     def setContactStatus(self, person): |  | 
| 65         if person not in self.people: |  | 
| 66             self.people.append(person) |  | 
| 67         self.refreshContactsLists() |  | 
| 68 |  | 
| 69     def on_OnlineContactsTree_select_row(self, w, row, column, event): |  | 
| 70         self.selectedPerson = self.onlinePeople[row] |  | 
| 71         entry = self.xml.get_widget("ContactNameEntry") |  | 
| 72         entry.set_text(self.selectedPerson.name) |  | 
| 73         self.currentAccount = self.selectedPerson.account.client |  | 
| 74         idx = self.accountMenuItems.index(self.currentAccount) |  | 
| 75         self.accountMenu.set_active(idx) |  | 
| 76         self.optionMenu.remove_menu() |  | 
| 77         self.optionMenu.set_menu(self.accountMenu) |  | 
| 78 |  | 
| 79     def on_PlainSendIM_clicked(self, b): |  | 
| 80         self.chat.getConversation( |  | 
| 81             self.currentAccount.getPerson( |  | 
| 82             self.xml.get_widget("ContactNameEntry").get_text())) |  | 
| 83 ##         if self.selectedPerson: |  | 
| 84 ##             c = self.chat.getConversation(self.selectedPerson) |  | 
| 85 |  | 
| 86     def on_PlainJoinChat_clicked(self, b): |  | 
| 87         ## GroupJoinWindow(self.chat) |  | 
| 88         name = self.xml.get_widget("ContactNameEntry").get_text() |  | 
| 89         self.currentAccount.joinGroup(name) |  | 
| 90 |  | 
| 91     def refreshContactsLists(self): |  | 
| 92         # HIDEOUSLY inefficient |  | 
| 93         online = self.xml.get_widget("OnlineContactsTree") |  | 
| 94         offline = self.xml.get_widget("OfflineContactsList") |  | 
| 95         online.freeze() |  | 
| 96         offline.freeze() |  | 
| 97         online.clear() |  | 
| 98         offline.clear() |  | 
| 99         self.countOnline = 0 |  | 
| 100         self.onlinePeople = [] |  | 
| 101         self.people.sort(lambda x, y: cmp(x.name, y.name)) |  | 
| 102         for person in self.people: |  | 
| 103             if person.isOnline(): |  | 
| 104                 self.onlinePeople.append(person) |  | 
| 105                 online.append([person.name, str(person.getStatus()), |  | 
| 106                                person.getIdleTime(), |  | 
| 107                                person.account.accountName]) |  | 
| 108                 self.countOnline = self.countOnline + 1 |  | 
| 109             offline.append([person.name, person.account.accountName, |  | 
| 110                             'Aliasing Not Implemented', 'Groups Not Implemented'
     ]) |  | 
| 111         self.xml.get_widget("OnlineCount").set_text("Online: %d" % self.countOnl
     ine) |  | 
| 112         online.thaw() |  | 
| 113         offline.thaw() |  | 
| 114 |  | 
| 115 |  | 
| 116 |  | 
| 117 def colorhash(name): |  | 
| 118     h = hash(name) |  | 
| 119     l = [0x5555ff, |  | 
| 120          0x55aa55, |  | 
| 121          0x55aaff, |  | 
| 122          0xff5555, |  | 
| 123          0xff55ff, |  | 
| 124          0xffaa55] |  | 
| 125     index = l[h % len(l)] |  | 
| 126     return '%06.x' % (abs(hash(name)) & index) |  | 
| 127 |  | 
| 128 |  | 
| 129 def _msgDisplay(output, name, text, color, isEmote): |  | 
| 130     text = string.replace(text, '\n', '\n\t') |  | 
| 131     ins = output.insert |  | 
| 132     ins(None, color, None, "[ %s ] " % time.strftime("%H:%M:%S")) |  | 
| 133     if isEmote: |  | 
| 134         ins(None, color, None, "* %s " % name) |  | 
| 135         ins(None, None, None, "%s\n" % text) |  | 
| 136     else: |  | 
| 137         ins(None, color, None, "<%s> " % name) |  | 
| 138         ins(None, None, None, "%s\n" % text) |  | 
| 139 |  | 
| 140 |  | 
| 141 class Conversation(InputOutputWindow): |  | 
| 142     """GUI representation of a conversation. |  | 
| 143     """ |  | 
| 144     def __init__(self, person): |  | 
| 145         InputOutputWindow.__init__(self, |  | 
| 146                                    "ConversationWidget", |  | 
| 147                                    "ConversationMessageEntry", |  | 
| 148                                    "ConversationOutput") |  | 
| 149         self.person = person |  | 
| 150         alloc_color = self.output.get_colormap().alloc |  | 
| 151         self.personColor = alloc_color("#%s" % colorhash(person.name)) |  | 
| 152         self.myColor = alloc_color("#0000ff") |  | 
| 153         print "allocated my color %s and person color %s" % ( |  | 
| 154             self.myColor, self.personColor) |  | 
| 155 |  | 
| 156     def getTitle(self): |  | 
| 157         return "Conversation - %s (%s)" % (self.person.name, |  | 
| 158                                            self.person.account.accountName) |  | 
| 159 |  | 
| 160     # Chat Interface Implementation |  | 
| 161 |  | 
| 162     def sendText(self, text): |  | 
| 163         metadata = None |  | 
| 164         if text[:4] == "/me ": |  | 
| 165             text = text[4:] |  | 
| 166             metadata = {"style": "emote"} |  | 
| 167         self.person.sendMessage(text, metadata).addCallback(self._cbTextSent, te
     xt, metadata) |  | 
| 168 |  | 
| 169     def showMessage(self, text, metadata=None): |  | 
| 170         _msgDisplay(self.output, self.person.name, text, self.personColor, |  | 
| 171                     (metadata and metadata.get("style", None) == "emote")) |  | 
| 172 |  | 
| 173     # Internal |  | 
| 174 |  | 
| 175     def _cbTextSent(self, result, text, metadata=None): |  | 
| 176         _msgDisplay(self.output, self.person.account.client.name, |  | 
| 177                     text, self.myColor, |  | 
| 178                     (metadata and metadata.get("style", None) == "emote")) |  | 
| 179 |  | 
| 180 class GroupConversation(InputOutputWindow): |  | 
| 181     def __init__(self, group): |  | 
| 182         InputOutputWindow.__init__(self, |  | 
| 183                                    "GroupChatBox", |  | 
| 184                                    "GroupInput", |  | 
| 185                                    "GroupOutput") |  | 
| 186         self.group = group |  | 
| 187         self.members = [] |  | 
| 188         self.membersHidden = 0 |  | 
| 189         self._colorcache = {} |  | 
| 190         alloc_color = self.output.get_colormap().alloc |  | 
| 191         self.myColor = alloc_color("#0000ff") |  | 
| 192         # TODO: we shouldn't be getting group conversations randomly without |  | 
| 193         # names, but irc autojoin appears broken. |  | 
| 194         self.xml.get_widget("NickLabel").set_text( |  | 
| 195             getattr(self.group.account.client,"name","(no name)")) |  | 
| 196         participantList = self.xml.get_widget("ParticipantList") |  | 
| 197         groupBox = self.xml.get_widget("GroupActionsBox") |  | 
| 198         for method in group.getGroupCommands(): |  | 
| 199             b = gtk.GtkButton(method.__name__) |  | 
| 200             b.connect("clicked", self._doGroupAction, method) |  | 
| 201             groupBox.add(b) |  | 
| 202         self.setTopic("No Topic Yet", "No Topic Author Yet") |  | 
| 203 |  | 
| 204     # GTK Event Handlers |  | 
| 205 |  | 
| 206     def on_HideButton_clicked(self, b): |  | 
| 207         self.membersHidden = not self.membersHidden |  | 
| 208         self.xml.get_widget("GroupHPaned").set_position(self.membersHidden and -
     1 or 20000) |  | 
| 209 |  | 
| 210     def on_LeaveButton_clicked(self, b): |  | 
| 211         self.win.destroy() |  | 
| 212         self.group.leave() |  | 
| 213 |  | 
| 214     def on_AddContactButton_clicked(self, b): |  | 
| 215         lw = self.xml.get_widget("ParticipantList") |  | 
| 216 |  | 
| 217         if lw.selection: |  | 
| 218             self.group.client.addContact(self.members[lw.selection[0]]) |  | 
| 219 |  | 
| 220     def on_TopicEntry_focus_out_event(self, w, e): |  | 
| 221         self.xml.get_widget("TopicEntry").set_text(self._topic) |  | 
| 222 |  | 
| 223     def on_TopicEntry_activate(self, e): |  | 
| 224         print "ACTIVATING TOPIC!!" |  | 
| 225         self.group.setTopic(e.get_text()) |  | 
| 226         # self.xml.get_widget("TopicEntry").set_editable(0) |  | 
| 227         self.xml.get_widget("GroupInput").grab_focus() |  | 
| 228 |  | 
| 229     def on_ParticipantList_unselect_row(self, w, row, column, event): |  | 
| 230         print 'row unselected' |  | 
| 231         personBox = self.xml.get_widget("PersonActionsBox") |  | 
| 232         for child in personBox.children(): |  | 
| 233             personBox.remove(child) |  | 
| 234 |  | 
| 235     def on_ParticipantList_select_row(self, w, row, column, event): |  | 
| 236         self.selectedPerson = self.group.account.client.getPerson(self.members[r
     ow]) |  | 
| 237         print 'selected', self.selectedPerson |  | 
| 238         personBox = self.xml.get_widget("PersonActionsBox") |  | 
| 239         personFrame = self.xml.get_widget("PersonFrame") |  | 
| 240         # clear out old buttons |  | 
| 241         for child in personBox.children(): |  | 
| 242             personBox.remove(child) |  | 
| 243         personFrame.set_label("Person: %s" % self.selectedPerson.name) |  | 
| 244         for method in self.selectedPerson.getPersonCommands(): |  | 
| 245             b = gtk.GtkButton(method.__name__) |  | 
| 246             b.connect("clicked", self._doPersonAction, method) |  | 
| 247             personBox.add(b) |  | 
| 248             b.show() |  | 
| 249         for method in self.group.getTargetCommands(self.selectedPerson): |  | 
| 250             b = gtk.GtkButton(method.__name__) |  | 
| 251             b.connect("clicked", self._doTargetAction, method, |  | 
| 252                       self.selectedPerson) |  | 
| 253             personBox.add(b) |  | 
| 254             b.show() |  | 
| 255 |  | 
| 256     def _doGroupAction(self, evt, method): |  | 
| 257         method() |  | 
| 258 |  | 
| 259     def _doPersonAction(self, evt, method): |  | 
| 260         method() |  | 
| 261 |  | 
| 262     def _doTargetAction(self, evt, method, person): |  | 
| 263         method(person) |  | 
| 264 |  | 
| 265     def hidden(self, w): |  | 
| 266         InputOutputWindow.hidden(self, w) |  | 
| 267         self.group.leave() |  | 
| 268 |  | 
| 269     def getTitle(self): |  | 
| 270         return "Group Conversation - " + self.group.name |  | 
| 271 |  | 
| 272     # Chat Interface Implementation |  | 
| 273     def sendText(self, text): |  | 
| 274         metadata = None |  | 
| 275         if text[:4] == "/me ": |  | 
| 276             text = text[4:] |  | 
| 277             metadata = {"style": "emote"} |  | 
| 278         self.group.sendGroupMessage(text, metadata).addCallback(self._cbTextSent
     , text, metadata=metadata) |  | 
| 279 |  | 
| 280     def showGroupMessage(self, sender, text, metadata=None): |  | 
| 281         _msgDisplay(self.output, sender, text, self._cacheColorHash(sender), |  | 
| 282                     (metadata and metadata.get("style", None) == "emote")) |  | 
| 283 |  | 
| 284 |  | 
| 285     def setGroupMembers(self, members): |  | 
| 286         self.members = members |  | 
| 287         self.refreshMemberList() |  | 
| 288 |  | 
| 289     def setTopic(self, topic, author): |  | 
| 290         self._topic = topic |  | 
| 291         self._topicAuthor = author |  | 
| 292         self.xml.get_widget("TopicEntry").set_text(topic) |  | 
| 293         self.xml.get_widget("AuthorLabel").set_text(author) |  | 
| 294 |  | 
| 295     def memberJoined(self, member): |  | 
| 296         self.members.append(member) |  | 
| 297         self.output.insert_defaults("> %s joined <\n" % member) |  | 
| 298         self.refreshMemberList() |  | 
| 299 |  | 
| 300     def memberChangedNick(self, member, newnick): |  | 
| 301         self.members.remove(member) |  | 
| 302         self.members.append(newnick) |  | 
| 303         self.output.insert_defaults("> %s becomes %s <\n" % (member, newnick)) |  | 
| 304         self.refreshMemberList() |  | 
| 305 |  | 
| 306     def memberLeft(self, member): |  | 
| 307         self.members.remove(member) |  | 
| 308         self.output.insert_defaults("> %s left <\n" % member) |  | 
| 309         self.refreshMemberList() |  | 
| 310 |  | 
| 311 |  | 
| 312 |  | 
| 313     # Tab Completion |  | 
| 314 |  | 
| 315     def tabComplete(self, word): |  | 
| 316         """InputOutputWindow calls me when tab is pressed.""" |  | 
| 317         if not word: |  | 
| 318             return [] |  | 
| 319         potentialMatches = [] |  | 
| 320         for nick in self.members: |  | 
| 321             if string.lower(nick[:len(word)]) == string.lower(word): |  | 
| 322                 potentialMatches.append(nick + ": ") #colon is a nick-specific t
     hing |  | 
| 323         return potentialMatches |  | 
| 324 |  | 
| 325     # Internal |  | 
| 326 |  | 
| 327     def _cbTextSent(self, result, text, metadata=None): |  | 
| 328         _msgDisplay(self.output, self.group.account.client.name, |  | 
| 329                     text, self.myColor, |  | 
| 330                     (metadata and metadata.get("style", None) == "emote")) |  | 
| 331 |  | 
| 332     def refreshMemberList(self): |  | 
| 333         pl = self.xml.get_widget("ParticipantList") |  | 
| 334         pl.freeze() |  | 
| 335         pl.clear() |  | 
| 336         self.members.sort(lambda x,y: cmp(string.lower(x), string.lower(y))) |  | 
| 337         for member in self.members: |  | 
| 338             pl.append([member]) |  | 
| 339         pl.thaw() |  | 
| 340 |  | 
| 341     def _cacheColorHash(self, name): |  | 
| 342         if self._colorcache.has_key(name): |  | 
| 343             return self._colorcache[name] |  | 
| 344         else: |  | 
| 345             alloc_color = self.output.get_colormap().alloc |  | 
| 346             c = alloc_color('#%s' % colorhash(name)) |  | 
| 347             self._colorcache[name] = c |  | 
| 348             return c |  | 
| 349 |  | 
| 350 |  | 
| 351 |  | 
| 352 |  | 
| 353 class GtkChatClientUI: |  | 
| 354     def __init__(self,xml): |  | 
| 355         self.conversations = {}         # cache of all direct windows |  | 
| 356         self.groupConversations = {}    # cache of all group windows |  | 
| 357         self.personCache = {}           # keys are (name, account) |  | 
| 358         self.groupCache = {}            # cache of all groups |  | 
| 359         self.theContactsList = ContactsList(self,xml) |  | 
| 360         self.onlineAccounts = []     # list of message sources currently online |  | 
| 361 |  | 
| 362     def registerAccountClient(self,account): |  | 
| 363         print 'registering account client' |  | 
| 364         self.onlineAccounts.append(account) |  | 
| 365         self.getContactsList().registerAccountClient(account) |  | 
| 366 |  | 
| 367     def unregisterAccountClient(self,account): |  | 
| 368         print 'unregistering account client' |  | 
| 369         self.onlineAccounts.remove(account) |  | 
| 370         self.getContactsList().unregisterAccountClient(account) |  | 
| 371 |  | 
| 372     def contactsListClose(self, w, evt): |  | 
| 373         return gtk.TRUE |  | 
| 374 |  | 
| 375     def getContactsList(self): |  | 
| 376         return self.theContactsList |  | 
| 377 |  | 
| 378     def getConversation(self, person): |  | 
| 379         conv = self.conversations.get(person) |  | 
| 380         if not conv: |  | 
| 381             conv = Conversation(person) |  | 
| 382             self.conversations[person] = conv |  | 
| 383         conv.show() |  | 
| 384         return conv |  | 
| 385 |  | 
| 386     def getGroupConversation(self, group, stayHidden=0): |  | 
| 387         conv = self.groupConversations.get(group) |  | 
| 388         if not conv: |  | 
| 389             conv = GroupConversation(group) |  | 
| 390             self.groupConversations[group] = conv |  | 
| 391         if not stayHidden: |  | 
| 392             conv.show() |  | 
| 393         else: |  | 
| 394             conv.hide() |  | 
| 395         return conv |  | 
| 396 |  | 
| 397     # ??? Why doesn't this inherit the basechat.ChatUI implementation? |  | 
| 398 |  | 
| 399     def getPerson(self, name, client): |  | 
| 400         account = client.account |  | 
| 401         p = self.personCache.get((name, account)) |  | 
| 402         if not p: |  | 
| 403             p = account.getPerson(name) |  | 
| 404             self.personCache[name, account] = p |  | 
| 405         return p |  | 
| 406 |  | 
| 407     def getGroup(self, name, client): |  | 
| 408         account = client.account |  | 
| 409         g = self.groupCache.get((name, account)) |  | 
| 410         if g is None: |  | 
| 411             g = account.getGroup(name) |  | 
| 412             self.groupCache[name, account] = g |  | 
| 413         return g |  | 
| OLD | NEW | 
|---|