home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 February / maximum-cd-2011-02.iso / DiscContents / digsby_setup85.exe / lib / gui / imwin / roomlist.pyo (.txt) < prev   
Encoding:
Python Compiled Bytecode  |  2010-11-24  |  22.5 KB  |  625 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.6)
  3.  
  4. if __name__ == '__main__':
  5.     import gettext
  6.     gettext.install('Digsby')
  7.  
  8. import wx
  9. from time import time
  10. from gui.textutil import default_font
  11. from util.primitives.mapping import Storage
  12. from util.primitives.error_handling import try_this
  13. from gui.buddylist.renderers import get_buddy_icon
  14. from util.observe import ObservableList
  15. from gui.skin.skinobjects import SkinColor
  16. from gui import skin
  17. from gui.toolbox import update_tooltip
  18. import gui.imwin as gui
  19. from util.Events import EventMixin
  20. from logging import getLogger
  21. log = getLogger('roomlist')
  22. S = Storage
  23.  
  24. class TextControl(wx.TextCtrl):
  25.     
  26.     def __init__(self, parent, value = None, empty_text = None):
  27.         wx.TextCtrl.__init__(self, parent)
  28.         self.EmptyText = empty_text
  29.         if not value:
  30.             pass
  31.         self.Value = ''
  32.         self.defaultcolor = self.GetForegroundColour()
  33.         self.emptycolor = wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT)
  34.         self.BBind(KILL_FOCUS = self.OnLoseFocus, SET_FOCUS = self.OnSetFocus)
  35.  
  36.     
  37.     def OnLoseFocus(self, e):
  38.         e.Skip()
  39.         if self.EmptyText and self.Value == '':
  40.             self.SetForegroundColour(self.emptycolor)
  41.             self.Value = self.EmptyText
  42.         
  43.  
  44.     
  45.     def OnSetFocus(self, e):
  46.         e.Skip()
  47.         if self.EmptyText and self.ForegroundColour == self.emptycolor:
  48.             self.SetForegroundColour(self.defaultcolor)
  49.             self.Value = ''
  50.         
  51.  
  52.  
  53.  
  54. class SkinVListBox(wx.VListBox):
  55.     
  56.     def __init__(self, *a, **k):
  57.         wx.VListBox.__init__(self, *a, **k)
  58.         syscol = wx.SystemSettings.GetColour
  59.         self.colors = Storage(textfg = Storage(selected = syscol(wx.SYS_COLOUR_HIGHLIGHTTEXT), normal = syscol(wx.SYS_COLOUR_WINDOWTEXT)))
  60.         self.bg = Storage(selected = SkinColor(syscol(wx.SYS_COLOUR_HIGHLIGHT)), normal = SkinColor(syscol(wx.SYS_COLOUR_LISTBOX)))
  61.         self.fonts = Storage(text = default_font())
  62.         self.BBind(LEFT_DOWN = self._OnLeftDown)
  63.  
  64.     
  65.     def GetSelections(self):
  66.         (item, cookie) = self.GetFirstSelected()
  67.         yield item
  68.         while item != -1:
  69.             (item, cookie) = self.GetNextSelected(cookie)
  70.             yield item
  71.  
  72.     
  73.     def IsSelected(self, n):
  74.         if self.HasMultipleSelection():
  75.             return n in self.GetSelections()
  76.         return self.Selection == n
  77.  
  78.     
  79.     def OnDrawItem(self, dc, rect, n):
  80.         selected = self.IsSelected(n)
  81.         textfg = None(getattr, self.colors.textfg if selected else 'normal')
  82.         dc.SetTextForeground(textfg)
  83.         dc.SetFont(self.fonts.text)
  84.         self._draw(dc, rect, n, selected)
  85.  
  86.     
  87.     def OnDrawBackground(self, dc, rect, n):
  88.         selected = self.IsSelected(n)
  89.         bg = None(getattr, self.bg if selected else 'normal')
  90.         bg.Draw(dc, rect, n)
  91.         
  92.         try:
  93.             self._drawbg(dc, rect, n, selected)
  94.         except AttributeError:
  95.             pass
  96.  
  97.  
  98.     
  99.     def OnMeasureItem(self, n):
  100.         
  101.         try:
  102.             measure = self._measure
  103.         except AttributeError:
  104.             return 20
  105.  
  106.         return measure(n)
  107.  
  108.     
  109.     def _OnLeftDown(self, e):
  110.         e.Skip()
  111.         i = self.HitTest((e.GetX(), e.GetY()))
  112.         if i == -1:
  113.             self.SetSelection(-1)
  114.         
  115.  
  116.  
  117.  
  118. class RoomListModel(EventMixin):
  119.     events = set(('contacts_changed',))
  120.     
  121.     def __init__(self, contacts):
  122.         EventMixin.__init__(self)
  123.         self.contacts = None
  124.         self.offline = False
  125.         self.set_contacts(contacts)
  126.  
  127.     
  128.     def _init_contacts(self):
  129.         self.contacts_view = []
  130.         self.contact_enter_times = { }
  131.         self.contact_leave_times = { }
  132.         self.pending_contacts = ObservableList()
  133.  
  134.     
  135.     def set_contacts(self, contacts):
  136.         if hasattr(self.contacts, 'remove_observer'):
  137.             self.contacts.remove_observer(self._on_contacts_changed)
  138.         
  139.         self.contacts = contacts
  140.         self._init_contacts()
  141.         for contact in contacts:
  142.             self.contact_enter_times[contact] = 0
  143.         
  144.         if hasattr(self.contacts, 'add_observer'):
  145.             self.contacts.add_observer(self._on_contacts_changed)
  146.         
  147.         self._on_contacts_changed()
  148.  
  149.     
  150.     def _on_contacts_changed(self, *a, **k):
  151.         self._update_pending()
  152.         self._update_view()
  153.         self.fire_contacts_changed()
  154.  
  155.     
  156.     def fire_contacts_changed(self):
  157.         self.event('contacts_changed')
  158.  
  159.     TIME_LEAVING = 4
  160.     
  161.     def _update_view(self):
  162.         self.leaving_contacts = view = []
  163.         now = time()
  164.         for gone_contact, t in list(self.contact_leave_times.items()):
  165.             if now - t > self.TIME_LEAVING:
  166.                 self.contact_leave_times.pop(gone_contact, None)
  167.                 continue
  168.             view.append(gone_contact)
  169.         
  170.         old_length = len(self.contacts_view)
  171.         self.contacts_view = sorted(list(self.contacts) + view)
  172.         return len(self.contacts_view) != old_length
  173.  
  174.     
  175.     def _update_pending(self):
  176.         for pending_contact in list(self.pending_contacts):
  177.             for contact in self.contacts:
  178.                 if pending_contact.equals_chat_buddy(contact):
  179.                     self.pending_contacts.remove(pending_contact)
  180.                     continue
  181.             
  182.         
  183.         contacts = set(self.contacts)
  184.         now = time()
  185.         for contact in list(self.contact_enter_times.keys()):
  186.             if contact not in contacts:
  187.                 self.contact_enter_times.pop(contact, None)
  188.                 self.contact_leave_times.setdefault(contact, now)
  189.                 continue
  190.         
  191.         for contact in contacts:
  192.             if contact not in self.contact_enter_times:
  193.                 self.contact_enter_times.setdefault(contact, now)
  194.                 continue
  195.         
  196.  
  197.     
  198.     def contact_is_new(self, contact):
  199.         time_joined = self.contact_enter_times.get(contact, None)
  200.         if time_joined is None:
  201.             return False
  202.         TIME_NEW = 2
  203.         now = time()
  204.         return now - time_joined < TIME_NEW
  205.  
  206.     
  207.     def contact_is_leaving(self, contact):
  208.         time_left = self.contact_leave_times.get(contact, None)
  209.         if time_left is not None:
  210.             pass
  211.         return time() - time_left < self.TIME_LEAVING
  212.  
  213.     
  214.     def add_pending_contact(self, contact):
  215.         if contact not in self.pending_contacts:
  216.             self.pending_contacts.append(contact)
  217.             self.event('contacts_changed')
  218.             return True
  219.  
  220.     
  221.     def remove_pending_contact(self, contact):
  222.         
  223.         try:
  224.             self.pending_contacts.remove(contact)
  225.         except ValueError:
  226.             return False
  227.  
  228.         self.event('contacts_changed')
  229.         return True
  230.  
  231.     
  232.     def length_including_pending(self):
  233.         return self.length + len(self.pending_contacts)
  234.  
  235.     length_including_pending = property(length_including_pending)
  236.     
  237.     def length(self):
  238.         return len(self.contacts_view)
  239.  
  240.     length = property(length)
  241.     
  242.     def get_contact(self, n):
  243.         return self.contacts_view[n]
  244.  
  245.     
  246.     def get_pending_contact(self, n):
  247.         return self.pending_contacts[n]
  248.  
  249.  
  250.  
  251. class ContactListCtrl(SkinVListBox):
  252.     click_raises_imwin = True
  253.     
  254.     def __init__(self, parent, model):
  255.         SkinVListBox.__init__(self, parent, style = wx.LB_MULTIPLE)
  256.         self.model = model
  257.         self.show_pending = True
  258.         self.model.bind_event(('contacts_changed',), (lambda : wx.CallAfter(self.update_count)))
  259.         self.Bind(wx.EVT_MOTION, self._on_motion)
  260.         self.Bind(wx.EVT_LEFT_DOWN, self._on_left_down)
  261.         self.Bind(wx.EVT_LEFT_DCLICK, self._on_left_dclick)
  262.         self.contact_timer = wx.PyTimer(self._on_contact_timer)
  263.         self.UpdateSkin()
  264.  
  265.     
  266.     def _on_contact_timer(self):
  267.         if wx.IsDestroyed(self):
  268.             return None
  269.         if self.model._update_view():
  270.             self.update_count()
  271.         
  272.         self.Refresh()
  273.  
  274.     
  275.     def UpdateSkin(self):
  276.         getattr(SkinVListBox, 'UpdateSkin', (lambda self: pass))(self)
  277.         self.x_icon = skin.get('appdefaults.removeicon').ResizedSmaller(16).WXB
  278.         self.pad_x = 2
  279.  
  280.     
  281.     def _on_left_down(self, e):
  282.         i = self.HitTest(e.Position)
  283.         if i != -1:
  284.             info = self._contact(i)
  285.             if info.pending:
  286.                 if e.Position.x > self.Size.width - self.x_icon.Width - self.pad_x * 2:
  287.                     self.model.remove_pending_contact(info.contact)
  288.                 
  289.             
  290.         
  291.         e.Skip()
  292.  
  293.     
  294.     def _on_left_dclick(self, e):
  295.         i = self.HitTest(e.Position)
  296.         if i == -1:
  297.             return e.Skip()
  298.         chat_buddy = self.model.get_contact(i)
  299.         if hasattr(chat_buddy, 'private_message_buddy'):
  300.             chat_buddy = chat_buddy.private_message_buddy()
  301.         
  302.         gui.imwin.begin_conversation(chat_buddy)
  303.  
  304.     
  305.     def _on_motion(self, e):
  306.         e.Skip()
  307.         i = self.HitTest(e.Position)
  308.         if i == -1:
  309.             tip = None
  310.         else:
  311.             tip = self._contact_str(i)
  312.         update_tooltip(self, tip)
  313.  
  314.     
  315.     def SetContacts(self, contacts):
  316.         self.model.set_contacts(contacts)
  317.  
  318.     
  319.     def update_count(self):
  320.         if wx.IsDestroyed(self):
  321.             print 'error: update_count still being called'
  322.         elif self.model.offline:
  323.             count = 0
  324.         elif self.show_pending:
  325.             count = self.model.length_including_pending
  326.         else:
  327.             count = self.model.length
  328.         self.SetItemCount(count)
  329.         self.RefreshAll()
  330.  
  331.     iconsize = 16
  332.     padding = 3
  333.     
  334.     def _contact(self, n):
  335.         
  336.         try:
  337.             contact = self.model.get_contact(n)
  338.             icon = get_buddy_icon(contact, self.iconsize, False)
  339.             pending = False
  340.         except IndexError:
  341.             contact = self.model.get_pending_contact(n - self.model.length)
  342.             icon = None
  343.             pending = True
  344.  
  345.         leaving = self.model.contact_is_leaving(contact)
  346.         return S(contact = contact, icon = icon, pending = pending, leaving = leaving)
  347.  
  348.     
  349.     def _contact_str(self, n):
  350.         return contact_display_str(self._contact(n).contact)
  351.  
  352.     
  353.     def _draw(self, dc, rect, n, selected):
  354.         _contact = self._contact(n)
  355.         contact = _contact.contact
  356.         icon = _contact.icon
  357.         contact_str = self._contact_str(n)
  358.         rect.Subtract(left = self.padding)
  359.         if icon:
  360.             dc.DrawBitmap(icon, rect.X, rect.Y + (rect.Height / 2 - icon.Size.height / 2))
  361.         
  362.         rect.Subtract(left = self.iconsize + self.padding)
  363.         if not (_contact.pending):
  364.             pass
  365.         bold = self.model.contact_is_new(contact)
  366.         if (bold or _contact.leaving) and not self.contact_timer.IsRunning():
  367.             self.contact_timer.StartOneShot(1000)
  368.         
  369.         f = dc.Font
  370.         None if not _contact.pending else rect(f.SetWeight if bold else wx.FONTWEIGHT_NORMAL)
  371.         dc.Font = f
  372.         dc.SetTextForeground(color)
  373.         dc.DrawTruncatedText(contact_str, rect, wx.ALIGN_CENTER_VERTICAL)
  374.         if _contact.pending:
  375.             dc.DrawBitmap(self.x_icon, rect.Right + self.pad_x, rect.Top + (rect.Height / 2 - self.x_icon.Height / 2))
  376.         
  377.  
  378.     
  379.     def _measure(self, n):
  380.         return 22
  381.  
  382.     
  383.     def OnGetItemImage(self, item):
  384.         return item
  385.  
  386.  
  387. from gui.uberwidgets.UberCombo import UberCombo
  388. from gui.uberwidgets.simplemenu import SimpleMenuItem
  389.  
  390. def item_for_contact(contact):
  391.     alias = contact.alias
  392.     name = contact.name
  393.     icon = skin.get('statusicons.' + contact.status_orb)
  394.     if alias == name:
  395.         smi = SimpleMenuItem([
  396.             icon,
  397.             name])
  398.     else:
  399.         smi = SimpleMenuItem([
  400.             icon,
  401.             '%s (%s)' % (alias, name)])
  402.     smi.buddy = contact
  403.     return smi
  404.  
  405.  
  406. class ContactCombo(UberCombo):
  407.     
  408.     def __init__(self, parent, skinkey = None, contacts = None, inviteCallback = None, accountCallback = None, model = None, use_confirm_dialog = True):
  409.         if skinkey is None:
  410.             skinkey = skin.get('RoomListComboSkin')
  411.         
  412.         UberCombo.__init__(self, parent, typeable = True, skinkey = skinkey, editmethod = self.EditField, selectioncallback = self.on_selection, maxmenuheight = 10, minmenuwidth = 230, empty_text = _('Invite Buddy'))
  413.         self.TextField.Bind(wx.EVT_KEY_DOWN, self.OnKey)
  414.         self.contacts = None if contacts is not None else { }
  415.         self.inviteCallback = inviteCallback
  416.         self.accountCallback = accountCallback
  417.         self.model = model
  418.         self.use_confirm_dialog = use_confirm_dialog
  419.         
  420.         def EditField():
  421.             if not self.display.txtfld.IsShown():
  422.                 self.display.TypeField()
  423.             
  424.  
  425.         (self.menu.BeforeDisplay,) += (lambda : self.update_menu(self.TextField.Value))
  426.         self.menu.BeforeDisplay += EditField
  427.  
  428.     
  429.     def Active(self, active):
  430.         pass
  431.  
  432.     
  433.     def buddy_sort(self, c):
  434.         return (-int(c.online), c.alias)
  435.  
  436.     
  437.     def OnKey(self, e):
  438.         k = e.GetKeyCode()
  439.         m = self.menu
  440.         if k == wx.WXK_DOWN:
  441.             m.Selection = (m.Selection + 1) % m.Count
  442.         elif k == wx.WXK_UP:
  443.             if m.Selection == -1:
  444.                 m.Selection = m.Count - 1
  445.             else:
  446.                 m.Selection = (m.Selection - 1) % m.Count
  447.         elif k == wx.WXK_RETURN:
  448.             if m.IsShown() and m.Selection >= 0:
  449.                 item = m.GetItem(m.Selection)
  450.                 m.Hide()
  451.                 m.Selection = -1
  452.                 self.on_selection(item)
  453.             else:
  454.                 self.on_selection(self.TextField.Value)
  455.                 self.GrandParent.SetFocus()
  456.         elif k == wx.WXK_ESCAPE:
  457.             self.TextField.Value = ''
  458.             m.Hide()
  459.         else:
  460.             e.Skip()
  461.  
  462.     
  463.     def update_menu(self, val = None):
  464.         m = self.menu
  465.         m.RemoveAll()
  466.         for item in self.get_dropdown_items(val):
  467.             m.AppendItem(item)
  468.         
  469.         if m.Count:
  470.             m.spine.CalcSize()
  471.             m.Selection = -1
  472.             m.Refresh()
  473.         else:
  474.             m.Selection = -1
  475.             m.Hide()
  476.  
  477.     
  478.     def get_dropdown_items(self, prefix = None):
  479.         return [ item_for_contact(c) for c in self.get_dropdown_contacts(prefix) ]
  480.  
  481.     
  482.     def get_dropdown_contacts(self, prefix = None):
  483.         val = None if prefix is not None else ''
  484.         filtered_contacts = []
  485.         contacts_inviting = set(self.model.contacts)
  486.         
  487.         is_self = lambda b: (try_this,)((lambda : b is b.protocol.self_buddy), False)
  488.  
  489.         for contact in sorted(self.contacts.itervalues(), key = self.buddy_sort):
  490.             if contact not in contacts_inviting and not is_self(contact):
  491.                 n = contact.name.lower()
  492.                 a = contact.alias.lower()
  493.                 if n.startswith(val) or a.startswith(val):
  494.                     filtered_contacts.append(contact)
  495.                 
  496.             a.startswith(val)
  497.         
  498.         return filtered_contacts
  499.  
  500.     
  501.     def ActiveConnection(self):
  502.         acct = self.accountCallback()
  503.         if acct is not None:
  504.             return getattr(acct, 'connection', None)
  505.  
  506.     ActiveConnection = property(ActiveConnection)
  507.     
  508.     def on_selection(self, item):
  509.         if self.model.offline:
  510.             return None
  511.         if isinstance(item, basestring):
  512.             buddy = None
  513.             if item.strip():
  514.                 connection = self.ActiveConnection
  515.                 if connection is not None:
  516.                     buddy = connection.get_buddy(item)
  517.                 
  518.             
  519.         else:
  520.             buddy = item.buddy
  521.         if buddy is None:
  522.             return None
  523.         if not (self.use_confirm_dialog) or wx.YES == wx.MessageBox(_('Do you want to invite %s to this chat?') % contact_display_str(buddy), _('Chat Invite'), wx.YES | wx.NO):
  524.             cb = self.inviteCallback
  525.             if cb is None:
  526.                 
  527.                 cb = lambda *a, **k: log.warning('inviteCallback(%r, %r)', a, k)
  528.             
  529.             if cb(buddy):
  530.                 self.SetValue('')
  531.             
  532.         
  533.  
  534.     
  535.     def EditField(self):
  536.         if self.display.txtfld.IsShown():
  537.             wx.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
  538.             self.OpenMenu()
  539.         
  540.  
  541.  
  542.  
  543. class RoomListPanel(wx.Panel):
  544.     
  545.     def __init__(self, parent, buddies = None, inviteCallback = None, accountCallback = None):
  546.         wx.Panel.__init__(self, parent)
  547.         self._obs_link = None
  548.         self.roomlist = None
  549.         self.model = RoomListModel([])
  550.         self.list = ContactListCtrl(self, model = self.model)
  551.         self.combo = ContactCombo(self, contacts = buddies, inviteCallback = inviteCallback, accountCallback = accountCallback, model = self.model)
  552.         s = self.Sizer = wx.BoxSizer(wx.VERTICAL)
  553.         s.Add(self.combo, 0, wx.EXPAND | wx.ALL)
  554.         s.Add(self.list, 1, wx.EXPAND | wx.ALL)
  555.         self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
  556.         self.Bind(wx.EVT_ERASE_BACKGROUND, (lambda e: pass))
  557.  
  558.     contact_attrs = ('status', 'alias')
  559.     
  560.     def UpdateSkin(self):
  561.         self.combo.SetSkinKey(skin.get('RoomListComboSkin'))
  562.  
  563.     
  564.     def SetConversation(self, convo):
  565.         self.model.offline = False
  566.         self._watch_protocol(convo)
  567.         self.SetRoomList(convo.room_list)
  568.         self.combo.contacts = convo.protocol.buddies
  569.         convo.pending_contacts_callbacks.add(self.on_pending_contacts)
  570.         self.list.show_pending = getattr(convo, 'contact_identities_known', True)
  571.  
  572.     proto = None
  573.     
  574.     def _watch_protocol(self, convo):
  575.         if self.proto is not None:
  576.             self.proto.remove_observer(self._on_state_change, 'state')
  577.         
  578.         self.proto = convo.protocol
  579.         self.proto.add_observer(self._on_state_change, 'state')
  580.  
  581.     
  582.     def _on_state_change(self, protocol, attr, old, new):
  583.         offline = self.model.offline
  584.         self.model.offline = new == protocol.Statuses.OFFLINE
  585.         if offline != self.model.offline:
  586.             self.list.update_count()
  587.         
  588.  
  589.     
  590.     def on_pending_contacts(self, contacts):
  591.         added = False
  592.         for c in contacts:
  593.             if c not in self.model.pending_contacts:
  594.                 self.model.pending_contacts.append(c)
  595.                 added = True
  596.                 continue
  597.         
  598.         if added:
  599.             self.model.event('contacts_changed')
  600.         
  601.  
  602.     
  603.     def SetRoomList(self, obslist):
  604.         self.roomlist = obslist
  605.         self.list.SetContacts(obslist)
  606.  
  607.     RoomList = property((lambda self: self.roomlist), SetRoomList, (lambda self: self.SetRoomList(None)), "Sets or gets this panel's roomlist.")
  608.     
  609.     def on_list_changed(self, src, attr, old, new):
  610.         pass
  611.  
  612.     
  613.     def on_contact_changed(self, contact, attr, old, new):
  614.         pass
  615.  
  616.  
  617.  
  618. def contact_display_str(contact):
  619.     alias = contact.alias
  620.     name = contact.name
  621.     if alias == name:
  622.         return alias
  623.     return u'%s (%s)' % (alias, name)
  624.  
  625.