Ejemplo n.º 1
0
class FormattingBar(ToolBar, NewSkinModule):

    initover = False

    def __init__(self, parent, textctrl, skinkey, formatOptions):

        ToolBar.__init__(self, parent, skinkey = None, alignment = wx.ALIGN_LEFT)

        self.SetSkinKey(skinkey, FormattingBarSkinDefaults)

        self.textctrl = textctrl
        if sys.platform.startswith("win"):
            textctrl.Bind(EVT_SELECTION_CHANGED, self.OnCursorMove)
        textctrl.Bind(EVT_TEXT_FORMAT_CHANGED, self.OnCursorMove)

        self.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu)

        self.fontdd = FontDropDown(self, skinkey = self.skinTB['buttonskin'])
        self.fontdd.SetMenuSkinKey(self.skinTB["menuskin"])
        self.fontdd.Bind(wx.EVT_COMMAND_CHOICE_SELECTED, self.OnFontSelected)

        icons = self.icons

        self.msize = SimpleMenu(self, self.skinTB['menuskin'], maxheight = 10)
        self.msize.SetItems(self.GenSizeItems(DEFAULT_SIZES)) #TODO: None default sizes
#        self.msize.Bind(wx.EVT_COMMAND_CHOICE_SELECTED, self.OnSizeSelected)

        self.bsize = UberButton(self, -1, '10', menu = self.msize, type = 'menu')
        self.bsize.SetStaticWidth(self.skinFB['sizedropdownwidth'])
        self.msize.SetWidth(self.skinFB['sizedropdownwidth'])

        self.bbold = UberButton(self, -1, icon = icons['bold'], type = 'toggle')
        self.bbold.Bind(wx.EVT_TOGGLEBUTTON, self.OnBoldButton)

        self.bitalic = UberButton(self, -1, icon = icons['italic'], type="toggle")
        self.bitalic.Bind(wx.EVT_TOGGLEBUTTON, self.OnItalicButton)

        self.bunderline = UberButton(self, -1, icon = icons['underline'],  type="toggle")
        self.bunderline.Bind(wx.EVT_TOGGLEBUTTON, self.OnUnderlineButton)

        self.bcolor = UberButton(self, -1, icon = icons['foregroundcolor'] )
        self.bcolor.Bind(wx.EVT_BUTTON, self.OnColorButton)

        self.bbgcolor = UberButton(self,-1, icon = icons['backgroundcolor'])
        self.bbgcolor.Bind(wx.EVT_BUTTON, self.OnBGColorButton)

        self.bemote = UberButton(self, -1, icon = icons['emote'])
        self.bemote.Bind(wx.EVT_BUTTON, self.OnEmoteButton)

#        import pdb
#        pdb.set_trace()
        self.AddMany([self.fontdd,
                      self.bsize,
                      self.bbold,
                      self.bitalic,
                      self.bunderline,
                      self.bcolor,
                      self.bbgcolor,
                      self.bemote])


        self.initover = True
        self.EnableFormattingButtons(formatOptions)
        self.UpdateDisplay()

    def OnContextMenu(self, event):
        from gui.uberwidgets.umenu import UMenu
        m = UMenu(self)
        m.AddItem(_('Hide Formatting Bar'), callback = lambda: wx.CallAfter(setpref, 'messaging.show_formatting_bar', False))
        m.PopupMenu()

    def OnFontSelected(self, event):
        """
            Updates the button to the new font and applies it to the selection
            or calls ApplyStlye
        """
        self.textctrl.ApplyStyle(facename = self.fontdd.GetClientData(self.fontdd.GetSelection()).GetFaceName())


    def OnSizeSelected(self, item):
        """
            Updates the Size button to the new size and applies it to the selection
            or calls ApplyStyle
        """
        self.bsize.label = str(item.id)
        self.textctrl.ApplyStyle(pointsize = item.id)

    def OnBoldButton(self, event):
        self.textctrl.ApplyStyle(bold = event.EventObject.IsActive())

    def OnItalicButton(self, event):
        self.textctrl.ApplyStyle(italic = event.EventObject.IsActive())

    def OnUnderlineButton(self, event):
        self.textctrl.ApplyStyle(underline = event.EventObject.IsActive())

    def OnColorButton(self, event):
        oldtextcolor = self.textctrl.GetFormat().GetTextColour()
        self.textctrl.ApplyStyle(textcolor = wx.GetColourFromUser(self, oldtextcolor, _('Choose a foreground color')))

    def OnBGColorButton(self, event):
        oldbgcolor = self.textctrl.GetFormat().GetTextColour()
        self.textctrl.ApplyStyle(bgcolor = wx.GetColourFromUser(self, oldbgcolor, _('Choose a background color')))

    def OnEmoteButton(self, event):
        self.DisplayEmotibox(self.bemote.ScreenRect)

    def DisplayEmotibox(self, rect):
        import hooks
        hooks.notify('digsby.statistics.emoticons.box_viewed')
        ebox = self.GetEmotibox()
        # position and display the emotibox
        ebox.Display(rect)

    def GetEmotibox(self):
        'Shares the emoticon box between all instances of this class.'

        b = None
        old_name, new_name = getattr(self, '_emotipack_name', None), pref('appearance.conversations.emoticons.pack', type = unicode, default = u'default')
        self._emotipack_name = new_name

        try:
            b = self.__class__.emotibox
            if not wx.IsDestroyed(b):
                if old_name != new_name:
                    b.Destroy()
                elif b.Parent is not self:
                    b.Reparent(self)

        except AttributeError:
            pass

        if b is None or wx.IsDestroyed(b):
            from gui.imwin.emoticons import get_emoticon_bitmaps
            b = self.__class__.emotibox = UberEmotiBox(self, get_emoticon_bitmaps(self._emotipack_name), self.textctrl, maxwidth = 12)
        else:
            b.SetTextCtrl(self.textctrl)

        return b

    def OnCursorMove(self, event):

        event.Skip()
        wx.CallAfter(self.UpdateDisplay)

    def UpdateDisplay(self):
        if wx.IsDestroyed(self.textctrl):
            return

        selection = self.textctrl.GetSelection()
        if selection[0] != selection[1]:
            return

        textattr = self.textctrl.GetFormat()
        font = textattr.GetFont()

        facename = font.GetFaceName()
        self.fontdd.SetSelection(self.fontdd.FindString(facename, False))
        self.bsize.SetLabel(str(font.GetPointSize()))

        self.bbold.Active(font.GetWeight() == wx.FONTWEIGHT_BOLD)
        self.bitalic.Active(font.GetStyle() == wx.FONTSTYLE_ITALIC)
        self.bunderline.Active(font.GetUnderlined())


    def EnableFormattingButtons(self, enabledict):

        if enabledict is None:
            return

        default = enabledict['default'] if 'default' in enabledict else True

        #TODO: fontdd should be disableable
        buttons = [#('font', self.fontdd),
                   ('size',      self.bsize),
                   ('bold',      self.bbold),
                   ('italic',    self.bitalic),
                   ('underline', self.bunderline),
                   ('color',     self.bcolor),
                   ('bgcolor',   self.bbgcolor),
                   ('emote',     self.bemote)]

        for key, button in buttons:
            button.Enable(enabledict[key] if key in enabledict else default)

    def GenSizeItems(self, sizes = DEFAULT_SIZES):
        """
            Sets the list of selectable sizes
            If not set sizes default to ['8', '10', '12', '14', '18', '24', '36']
        """
        return [SimpleMenuItem([str(size)], id=size, method = self.OnSizeSelected) for size in sizes]

    def DoUpdateSkin(self, skin):

        self.skinFB = skin

        ToolBar.DoUpdateSkin(self, SkinProxy(skin['toolbarskin'], ToolBarSkinDefaults))

        icons = self.icons = {}
        icons['bold'] = skin['icons.bold']
        icons['italic'] = skin['icons.italic']
        icons['underline'] = skin['icons.underline']
        icons['foregroundcolor'] = skin['icons.foregroundcolor']
        icons['backgroundcolor'] = skin['icons.backgroundcolor']
        icons['emote'] = skin['icons.emote']

        iconsize = skin['iconsize']
        for key in icons:
            if icons[key] is not None:
                icons[key] = icons[key].Resized(iconsize)

        if self.initover:
            self.bsize.SetStaticWidth(skin['sizedropdownwidth'])
            self.msize.SetWidth(skin['sizedropdownwidth'])

            self.bbold.SetIcon(icons['bold'])
            self.bitalic.SetIcon(icons['italic'])
            self.bunderline.SetIcon(icons['underline'])
            self.bcolor.SetIcon(icons['foregroundcolor'])
            self.bbgcolor.SetIcon(icons['backgroundcolor'])
            self.bemote.SetIcon(icons['emote'])

    def GetSkinProxy(self):
        return self.skinFB if hasattr(self, 'skinFB') else None
Ejemplo n.º 2
0
class P(wx.Panel):
    def __init__(self,parent):
        wx.Panel.__init__(self,parent,-1)#,style = wx.CLIP_CHILDREN|wx.CLIP_SIBLINGS

        self.Bind(wx.EVT_BUTTON,self.onButton),
        self.Bind(wx.EVT_LEFT_UP,self.OnMouse),
        self.Bind(wx.EVT_LEFT_DOWN,self.OnMouse)

        self.skin = 'button'
#        print self.skin
#        self.menu=UMenu(self)
#        self.menu.Append(wx.NewId(),'item 1')
#        self.menu.Append(wx.NewId(),'item 2')
#        self.menu.Append(wx.NewId(),'item 3')

        content=wx.BoxSizer(wx.VERTICAL)

        size=None#(200,50)#
        type=None#'menu'#'toggle'#'toggle'#
        menu=None#self.menu#
        icon=wx.Bitmap('../../../../res/skins/default/statusicons/mobile.png',wx.BITMAP_TYPE_PNG)#wx.Bitmap('../../res/skins/default/tinydigsby.png',wx.BITMAP_TYPE_PNG)
        #label= "button"#"Super wide Button Name of Impending doooooooooooom!!!"#"button"#
        skin=self.skin#None#

        self.b1=UberButton(self,wx.NewId(),'&One',skin,icon=icon,style=wx.HORIZONTAL,size=size,type=type,menu=menu)
#        self.b1.SetStaticWidth(60)
        self.b2=UberButton(self,wx.NewId(),'&Two',icon=icon,style=wx.HORIZONTAL,size=size,type=type,menu=menu)
        self.b3=UberButton(self,wx.NewId(),'T&hree',skin,icon=icon,style=wx.VERTICAL,size=size,type=type,menu=menu)
        self.b4=UberButton(self,wx.NewId(),'&Four',icon=icon,style=wx.VERTICAL,size=size,type=type,menu=menu)
        self.b5=UberButton(self,wx.NewId(),"",skin,icon=icon,size=size,type=type,menu=menu)
        self.b6=UberButton(self,wx.NewId(),"",icon=icon,size=size,type=type,menu=menu)
        self.b7=UberButton(self,wx.NewId(),'Fi&ve',skin,size=size,type=type,menu=menu)
        self.b8=UberButton(self,wx.NewId(),'&Six',size=size,type=type,menu=menu)
        self.b9=UberButton(self,wx.NewId(),"",skin,size=size,type=type,menu=menu)
        self.b10=UberButton(self,wx.NewId(),"",size=size,type=type,menu=menu)
        self.b11 = wx.Button(self, wx.NewId(), 'Native')
        self.b12= wx.Button(self, wx.NewId(), ' ')
        self.b12.Bind(wx.EVT_BUTTON,self.ToggleAlignment)

        self.b13 = UberButton(self, wx.NewId(), 'active?', self.skin)
        self.b13.Bind(wx.EVT_BUTTON,self.ToggleSkin)

        wexp = 0#wx.EXPAND#wx.ALL|
        hrat = 0#1#
        pad = 0

        content.Add(self.b1,hrat,wexp,pad)
        content.Add(self.b2,hrat,wexp,pad)
        content.Add(self.b3,hrat,wexp,pad)
        content.Add(self.b4,hrat,wexp,pad)
        content.Add(self.b5,hrat,wexp,pad)
        content.Add(self.b6,hrat,wexp,pad)
        content.Add(self.b7,hrat,wexp,pad)
        content.Add(self.b8,hrat,wexp,pad)
        content.Add(self.b9,hrat,wexp,pad)
        content.Add(self.b10,hrat,wexp,pad)
        content.Add(self.b11,hrat,wexp,pad)
        content.Add(self.b12,hrat,wexp,pad)
        content.Add(self.b13, hrat, wexp, pad)


        self.SetSizer(content)

    def onButton(self,event):
        print event.GetEventObject().Label
#        print "..."
#        if type(event.EventObject)==wx.Button:
#            print event.EventObject.Label
#        else:
#            print event.EventObject.label
#        switch= not self.b1.IsEnabled()
#        self.b1.Enable(switch)
#        self.b2.Enable(switch)
#        self.b3.Enable(switch)
#        self.b4.Enable(switch)
#        self.b5.Enable(switch)
#        self.b6.Enable(switch)
#        self.b7.Enable(switch)
#        self.b8.Enable(switch)
#        self.b9.Enable(switch)
#        self.b10.Enable(switch)
#        self.b11.Enable(switch)

    def ToggleSkin(self,event = None):
        key = None if self.b1.skinkey else 'button'

        print 'toggleskin to',key
        self.b1.SetSkinKey(key,1)
        self.b3.SetSkinKey(key,1)
        self.b5.SetSkinKey(key,1)
        self.b7.SetSkinKey(key,1)
        self.b9.SetSkinKey(key,1)

    def ToggleAlignment(self,event = None):
        print "ToggleAlignment DISABLED!!! It can not be!!!"
#        align = wx.VERTICAL if self.b1.Alignment == wx.HORIZONTAL else wx.HORIZONTAL
#        self.b1.Alignment = align
#        self.b3.Alignment = align
#        self.b5.Alignment = align
#        self.b7.Alignment = align
#        self.b9.Alignment = align
#        self.Layout()


    def OnMouse(self,event):
        print "window caught mouse"
Ejemplo n.º 3
0
class StatusCombo(UberCombo):

    # number of milliseconds to wait after clicking the status button before the
    # status is set (if the user hasn't entered any text)
    set_delay = 3000

    def __init__(self,
                 parent,
                 buddylist,
                 statuses,
                 get_status_method=get_profile_status,
                 set_status_method=set_profile_status):
        '''
        StatusCombo constructor.

        parent   - a wx.Window parent window
        statuses - an observable list of StatusMessage objects
        '''

        self.buddylist = buddylist
        self.buddylist.Bind(wx.EVT_KEY_DOWN, self.on_buddylist_key)
        self.searching = False
        self.searchHintShown = False

        if not getattr(StatusCombo, 'searchThresholdRegistered',
                       False) and pref('search.buddylist.show_hint', True):

            def SearchThresholdReached(*a, **k):
                if pref('search.buddylist.show_hint', True):
                    setpref('search.buddylist.show_hint', False)

            Hook('digsby.achievements.threshold',
                 'buddylist.search').register(SearchThresholdReached)
            StatusCombo.searchThresholdRegistered = True

        self.offline_item = None
        self.get_profile_status = get_status_method
        self.set_profile_status = set_status_method

        status = self.get_profile_status()

        UberCombo.__init__(self,
                           parent,
                           skinkey='combobox',
                           typeable=True,
                           valuecallback=self.on_text_lose_focus,
                           empty_text=getattr(status, 'hint',
                                              status.title.title()),
                           maxmenuheight=15)

        self.buttoncallback = self.on_status_button
        self.cbutton = UberButton(self, -1, skin=self.cbuttonskin)
        self.cbutton.Bind(wx.EVT_BUTTON, self._on_left_button)
        self.content.Insert(0, self.cbutton, 0, wx.EXPAND)

        self.cbutton.BBind(RIGHT_UP=self.on_status_button_right_click,
                           LEFT_DOWN=self.on_status_button_left_click,
                           LEFT_UP=self.on_status_button_left_up)

        self.display.Bind(
            wx.EVT_LEFT_DOWN, lambda e:
            (e.Skip(), setattr(self, 'oldValue', self.Value)))

        # the on_allow_status_changes method is called when the list of connected
        # im accounts changes size. if all accounts are offline this control
        # becomes disabled..

        #profile.account_manager.connected_accounts.add_observer(self.on_allow_status_changes)
        profile.account_manager.connected_accounts.add_observer(
            self.on_offline_allowed, obj=self)

        # Listen on status messages (changes, additions, deletes).
        _obs_link = statuses.add_list_observer(self.on_status_messages_changed,
                                               self.on_status_messages_changed)
        self.Bind(
            wx.EVT_WINDOW_DESTROY, lambda e:
            (log.info('status combo removing observers'), e.Skip(),
             _obs_link.disconnect()))

        self.on_status_messages_changed(statuses)

        # when the profile's status changes, update to reflect it
        profile.add_observer(self.on_profile_status_changed, 'status')

        # Display the current status.
        self.show_status(self.get_profile_status())

        # Timer for committing status messages after a delay.
        self.timer = wx.PyTimer(self.SetFocus)
        self.Bind(wx.EVT_TEXT, self.on_typing)

        self.button_timer = wx.PyTimer(self.on_status_button_right_click)

        textbind = self.TextField.Bind
        textbind(wx.EVT_SET_FOCUS, lambda e: setattr(self, 'skipenter', False))
        textbind(wx.EVT_KEY_DOWN, self._on_key_down)
        textbind(wx.EVT_TEXT_ENTER, self._on_enter)

        self.DropDownButton.Bind(wx.EVT_LEFT_DOWN, self._dbutton_left)

        self.OnActivateSearch = Delegate()
        self.OnDeactivateSearch = Delegate()

    def UpdateSkin(self):
        key = 'statuspanel'

        if not skin.get(key, False) or skin.get(key + '.mode', '') == 'native':
            s = lambda k, d: None
        else:
            s = lambda k, default: skin.get('%s.%s' % (key, k), default)

        comboskinkey = s('comboboxskin', None)
        self.cbuttonskin = cbskinkey = s('statusbuttonskin', None)

        self.SetSkinKey(comboskinkey)
        UberCombo.UpdateSkin(self)

        if hasattr(self, 'cbutton'):
            self.cbutton.SetSkinKey(cbskinkey, True)
            self.SetButtonIcon(StatusMessage.icon_for(self.status_state))

        if hasattr(self, 'menu') and self.menu:
            self.on_status_messages_changed()

    def SetButtonIcon(self, icon):
        """set the icon for the cycle button"""
        self.cbutton.SetIcon(icon)
        self._button_icon = icon
        self.Layout()

    def SetCallbacks(self,
                     selection=sentinel,
                     value=sentinel,
                     button=sentinel):
        'Sets callbacks for this combobox.'

        UberCombo.SetCallbacks(self, selection, value)
        if button is not sentinel: self.buttoncallback = button

    def on_allow_status_changes(self, *a, **k):
        if self.Show(profile.allow_status_changes):
            self.Parent.gui_layout()

    def setandshow(self, statusmsg):
        'Immediately sets the status message and shows it.'

        log.info('setandshow %r', statusmsg)
        self.oldValue = None
        self.show_status(statusmsg)
        self.set_profile_status(statusmsg)

    def show_status(self, status, force=False):
        'Displays the specified status message.'

        if not force and status is getattr(self, '_shown_status', None):
            return

        # make the text area not editable for statuses like "Invisble" and
        # "Offline", which have the "editable" attribute set to False
        self.Editable = status.editable

        self.display.empty_text = getattr(status, 'hint', '')
        self.ChangeValue(status.message)  # change text
        self.SetButtonIcon(StatusMessage.icon_for(status))  # change icon
        self.status_state = status.status  # store the state
        self._shown_status = status

    #
    # events
    #

    def on_typing(self, e):
        'Invoked when the user is typing in the textfield.'

        if self.searching:
            search.link_prefs(profile.prefs)
            e.Skip()
            self.buddylist.search(e.EventObject.Value)
        else:
            self.cancel_timer()

    def on_status_button(self, button):
        '''
        Invoked when the user clicks the state button to the left of the
        dropdown.
        '''
        # toggle the control's status state
        isavail = StatusMessage.is_available_state(self.status_state)

        # do we need to change the shown text?
        needs_change = self._shown_status in StatusMessage.SpecialStatuses or not self._shown_status.editable

        self.oldValue = None

        self.change_state(state='Away' if isavail else 'Available', )
        #change_text = needs_change)

    def change_state(self, state, change_text=False):
        if not isinstance(state, basestring):
            raise TypeError('change_state takes a string got a %s' %
                            type(state))
        self.status_state = state

        if change_text:
            self.ChangeValue(self.status_state, state.title())
        else:
            self.Default = state.title()

        edit_toggle = getattr(profile.status, 'edit_toggle', True)
        if getattr(profile.status, 'edit_toggle', True):
            # update the icon
            self.SetButtonIcon(StatusMessage.icon_for(self.status_state))

            self.cancel_timer()
            self.timer.StartOneShot(self.set_delay)

            # select all text in the textfield
            disp = self.display
            disp.TypeField()
            wx.CallAfter(disp.txtfld.SetSelection, -1, -1)
        else:
            self.setandshow(
                profile.status.copy(status=self.status_state,
                                    editable=None,
                                    edit_toggle=None))

    def on_status_button_left_click(self, e=None):
        if self.searching:
            return self.TextField.SetFocus()
        self.skipenter = True
        self.button_timer.Start(BUTTON_HOLD_TIME, True)
        if e: e.Skip(True)

    def on_status_button_left_up(self, e=None):
        if not self.searching:
            self.button_timer.Stop()
        if e: e.Skip(True)

    def on_status_button_right_click(self, e=None):
        if not self.searching:
            self.show_extended_status_menu()

    def show_extended_status_menu(self):
        from gui.status import get_state_choices

        m = SimpleMenu(self, skinkey=skin.get('%s.MenuSkin' % self.skinkey))

        for status in get_state_choices():
            statusname, statuslabel = status

            def onclick(item, state=statusname):
                self.change_state(
                    state
                )  #, change_text = self.status_state == self.GetValue())

            m.AppendItem(
                SimpleMenuItem(
                    [StatusMessage.icon_for(statusname), statuslabel],
                    method=onclick))

        if m.GetCount() > 0:
            m.Display(self.cbutton)

    def on_text_lose_focus(self, new_msg):
        if self.searching:
            return self.on_search_timer()

        # Cancel the status button timer if it's running.
        self.cancel_timer()

        if getattr(self, 'skipenter', False):
            wx.CallAfter(lambda: setattr(self, 'skipenter', False))
        else:
            # don't set status if we lost focus because the user is clicking
            # on the state button
            if wx.GetMouseState().LeftDown() and wx.FindWindowAtPoint(
                    wx.GetMousePosition()) is self.cbutton:
                return

            profile_status = self.get_profile_status()
            if new_msg == '':
                self.display.empty_text = profile_status.hint
            if new_msg != profile_status.message or self.status_state != profile_status.status:
                # entering a new text value clears all exceptions
                newmsg = StatusMessage(new_msg, self.status_state, new_msg)
                self.set_profile_status(newmsg)

    def on_profile_status_changed(self, *a):
        "Invoked when the profile's status changes."

        self.show_status(profile.status)

    @calllimit(1)
    def on_offline_allowed(self, *a):
        if not self: return
        show_offline = profile.allow_status_changes

        if not show_offline and self.offline_item:
            log.info('removing the offline item')
            self.RemoveItem(self.offline_item)
            self.offline_item = None
        elif show_offline and not self.offline_item:
            log.info('adding the offline item')
            self.offline_item = self.additem(
                [skin.get('statusicons.offline'),
                 _('Offline')], self.on_offline)

    def additem(self, *a, **k):
        i = SimpleMenuItem(*a, **k)
        self.AppendItem(i)
        return i

    @calllimit(1)
    def on_status_messages_changed(self, *a):
        '''
        Invoked when a status message changes, or the user status list changes.

        Rebuilds the status menu items.
        '''
        log.info('on_status_messages_changed, updating menu')

        self.RemoveAllItems()
        additem = self.additem

        def add_status_item(pname, name):
            additem([skin.get('statusicons.%s' % pname), name],
                    method=getattr(self, 'on_' + pname))

        # Available
        add_status_item('available', _('Available'))

        # user statuses
        self.sortedstatuses = msgs = sorted([c for c in profile.statuses],
                                            key=lambda msg: msg.away)

        # Find where to insert the special "Away" status item
        j = -1
        found = False
        for j, msg in enumerate(msgs):
            if msg.away:
                found = True
                break

        for i, msg in enumerate(msgs):
            if found and i == j:
                add_status_item('away', _('Away'))
            online_image = skin.get(
                'statusicons.away' if msg.away else 'statusicons.available')
            additem([online_image, msg.title],
                    method=lambda mi, msg=msg: self.setandshow(msg),
                    id=i)

        if not found or j == -1:
            add_status_item('away', _('Away'))

        # Custom...
        additem(_('Custom...'), method=self.on_custom)
        self.AppendSeparator()
        if global_status_enabled():
            additem([skin.get('icons.globalstatus'),
                     _('Global Status')],
                    method=self.on_global)

        log.info('updating status menu with %d extra statuses',
                 len(Hook('digsby.im.statusmessages')))
        for msg in Hook('digsby.im.statusmessages'):
            message = msg()
            if message is None:
                continue
            additem([message.icon, message.title],
                    method=lambda mi, msg=msg: self.setandshow(msg()))

        if global_status_enabled():
            additem([skin.get('statusicons.promote'),
                     _('Promote Digsby!')],
                    method=self.on_promote)

        # Invisible
        additem([skin.get('statusicons.invisible'),
                 _('Invisible')], self.on_invisible)

        # Offline
        self.offline_item = None
        self.on_offline_allowed()

    #
    # special entries in the status menu.
    #

    def on_offline(self, combo_item):
        self.setandshow(StatusMessage.Offline)

    def on_available(self, comboitem):
        self.show_status(StatusMessage.Available)
        self.display.TypeField()

    def on_away(self, comboitem):
        self.show_status(StatusMessage.Away)
        self.display.TypeField()

    def on_custom(self, combo_item):
        edit_custom_status(self)

    def on_global(self, combo_item):
        wx.CallAfter(wx.GetApp().SetStatusPrompt)

    def on_promote(self, combo_item):
        wx.CallAfter(wx.GetApp().SetStatusPrompt,
                     'ALL',
                     PROMOTE_STATUS_STRING(),
                     editable=False,
                     edit_toggle=False)

    def on_nowplaying(self, combo_item):
        self.setandshow(StatusMessage.NowPlaying)

    def on_invisible(self, combo_item):
        sta = self.get_profile_status()
        cpy = StatusMessage.Invisible.copy(message=sta.message)
        self.setandshow(cpy)

    def cancel_timer(self):
        if self.timer.IsRunning():
            self.timer.Stop()

    #
    # search functionality
    #

    def _on_left_button(self, e):
        if not self.searching:
            return self.buttoncallback(self.cbutton)

    def _on_enter(self, e):
        if self.searching:
            self.buddylist.activate_selected_item()
            self.stop_searching()
        else:
            e.Skip()

    def _on_key_down(self, e):
        if self.searching:
            if e.KeyCode == wx.WXK_ESCAPE:
                self.buddylist.SetFocus()
                self.stop_searching()
            elif e.KeyCode in txtcontrol_keys:
                e.Skip()
            else:
                self.buddylist.on_key_down(e)
        else:
            e.Skip()

    def _interpret_char_event(self, e):
        key = None
        backspace = False

        if e is not None:
            mod = e.Modifiers & ~wx.MOD_SHIFT
            if e.KeyCode == wx.WXK_BACK:
                backspace = True
            elif mod or e.KeyCode <= ord(' ') or e.KeyCode in non_alphanumeric:
                return key, backspace
            else:
                key = unichr(e.UnicodeKey)

        return key, backspace

    def ShowSearchHint(self):
        self.searchHintShown = True

        def size_like(img, i):
            img = img.ResizedSmaller(max(i.Width, i.Height)).PIL
            return img.ResizeCanvas(i.Width, i.Height).WXB

        self.cbutton.SetIcon(
            size_like(skin.get('StatusPanel.SearchIcon'), self._button_icon))
        self.DropDownButton.SetIcon(skin.get('StatusPanel.CancelSearchIcon'))
        self.display.DisplayLabel = _("Press 'Ctrl+F' to Search List")

    def HideSearchHint(self):
        self.SetButtonIcon(self._button_icon)
        self.DropDownButton.SetIcon(self.dropdownicon)
        self.searchHintShown = False
        self.display.DisplayLabel = None

    def search(self, e=None):
        if not pref('search.buddylist.enabled', True):
            if e is not None: e.Skip()
            return

        key, backspace = self._interpret_char_event(e)

        def size_like(img, i):
            img = img.ResizedSmaller(max(i.Width, i.Height)).PIL
            return img.ResizeCanvas(i.Width, i.Height).WXB

        icon = skin.get('StatusPanel.SearchIcon')
        self.ForceTextFieldBackground = True
        self.cbutton.SetIcon(size_like(icon, self._button_icon))
        self.DropDownButton.SetIcon(skin.get('StatusPanel.CancelSearchIcon'))
        self.searching = True
        if not hasattr(self, 'search_timer'):
            self.search_timer = wx.PyTimer(self.on_search_timer)
        self.search_timer.Start(500)

        self.display.TypeField()

        # emulate a keypress if one started the search
        self.TextField.ChangeValue(profile.blist.search_string)

        if key is not None:
            self.TextField.AppendText(key)
        if backspace:
            # emulate a backspace
            size = self.TextField.LastPosition
            self.TextField.Remove(size - 1, size)

        self.OnActivateSearch()

    def on_search_timer(self):
        active = wx.GetActiveWindow()
        focused = wx.Window.FindFocus()

        if active is None or not self.searching:
            self.stop_searching()

        if not hasattr(self, '_allowed_windows'):
            # active windows search will stick around for
            from gui.infobox.infobox import InfoBox
            from gui.buddylist.buddylistframe import BuddyListFrame
            from gui.searchgui import SearchEditDialog

            self._allowed_windows = frozenset(
                [InfoBox, BuddyListFrame, SearchEditDialog])
            self._empty_textfield_cancels = frozenset([BuddyListFrame])

        clz = active.__class__

        if clz not in self._allowed_windows:
            self.stop_searching()

        # if search loses focus to the buddylist and there is no text in the
        # search field, just cancel the search
        elif clz in self._empty_textfield_cancels and \
                focused is not self.TextField and \
                not self.TextField.Value:
            self.stop_searching()

    def stop_searching(self):
        if not self.searching:
            return

        log.info('stopping search')
        self.ForceTextFieldBackground = False
        self.SetButtonIcon(self._button_icon)
        self.DropDownButton.SetIcon(self.dropdownicon)
        self.search_timer.Stop()
        self.searching = False
        focused_window = wx.Window.FindFocus()
        if focused_window is self.TextField:
            self.buddylist.SetFocus()
        self.show_status(get_profile_status(), force=True)
        self.buddylist.clear_search()
        self.OnDeactivateSearch()

        hooks.notify('digsby.statistics.buddylist.search')

    def _dbutton_left(self, e):
        if self.searching:
            return self.stop_searching()
        else:
            e.Skip()

    def on_buddylist_key(self, e):
        if self.searching and e.KeyCode == wx.WXK_ESCAPE:
            self.stop_searching()
        else:
            e.Skip()
Ejemplo n.º 4
0
class PrefPanel(SimplePanel):
    def __init__(self,
                 parent,
                 content=None,
                 title='',
                 buttonlabel='',
                 buttoncb=None,
                 titlemaker=None,
                 prefix=''):
        SimplePanel.__init__(self, parent, wx.FULL_REPAINT_ON_RESIZE)

        sizer = self.Sizer = BoxSizer(VERTICAL)
        self.headersizer = BoxSizer(HORIZONTAL)
        self.bodysizer = BoxSizer(VERTICAL)
        sizer.Add(self.headersizer, 0, EXPAND | TOP, space_over_header)
        sizer.Add(self.bodysizer, 1, EXPAND | TOP, space_under_header)

        self.title = None
        self.combo = None
        self.button = None
        self.content = None
        self.contents = {}
        self.titlemaker = titlemaker
        if wxMac:
            self.menuitems = {}

        if title and isinstance(title, basestring):
            self.title = wx.StaticText(self,
                                       -1,
                                       ' ' + title + ' ',
                                       style=wx.ALIGN_CENTER_VERTICAL)

            #need grey backgound behind label on mac to hide the line
            if wxMac:
                self.title.BackgroundColour = wx.Color(232, 232, 232)
            self.title.Font = self.HeaderFont
            self.headersizer.Add(self.title, 0, *header_sizer_flags)

        if callable(content):
            content = self.content = content(self, prefix)
            self.bodysizer.Add(self.content, 1, pref_sizer_style, 7)
        elif isinstance(content, wx.WindowClass):
            content.Reparent(self)
            self.content = content
            self.bodysizer.Add(self.content, 1, pref_sizer_style, 7)
        elif isinstance(content, list):
            self.SetContents(content)

        if buttoncb:
            self.SetButton(buttonlabel, buttoncb)

        Bind = self.Bind
        Bind(wx.EVT_PAINT, self.OnPaint)

        #darker border if mac so it is visible for now
        if not wxMac:
            self.pen = wx.Pen(wx.Colour(213, 213, 213))
        else:
            self.pen = wx.Pen(wx.Colour(155, 155, 155))

    def SetTitle(self, title):
        self.title.SetLabel(title)

    @property
    def HeaderFont(self):
        try:
            return self._headerfont
        except AttributeError:
            if not wxMac:
                PrefPanel._headerfont = makeFont('arial 8 bold')
            else:
                PrefPanel._headerfont = makeFont('9 bold')
            return self._headerfont

    _fg_brush = \
    _bg_brush = \
    _fg_pen = \
    _bg_pen = lambda self: None

    def get_bg_brush(self):
        return self._bg_brush() or wx.WHITE_BRUSH

    def get_fg_brush(self):
        return self._fg_brush() or wx.TRANSPARENT_BRUSH

    def get_bg_pen(self):
        return self._bg_pen() or wx.TRANSPARENT_PEN

    def get_fg_pen(self):
        return self._fg_pen() or self.pen

    bg_brush = property(get_bg_brush)
    fg_brush = property(get_fg_brush)
    bg_pen = property(get_bg_pen)
    fg_pen = property(get_fg_pen)

    def OnPaint(self, event):
        size = self.Size
        dc = AutoDC(self)

        if not wxMac:
            # Non mac: white background, rounded rectangle around controls
            rect = wx.RectS(size)
            dc.Brush = self.bg_brush  #background
            dc.Pen = self.bg_pen  #background border
            dc.DrawRectangleRect(rect)
            ypos = self.headersizer.Size.height // 2 + space_over_header
            gc = wx.GraphicsContext.Create(dc)
            gc.SetBrush(self.fg_brush)  #foreground
            gc.SetPen(self.fg_pen)  #foreground
            gc.DrawRoundedRectangle(0, ypos, size.width - 1,
                                    size.height - ypos - 1, 5)
        else:
            # Mac: normal grey background, horizontal line above controls
            ypos = self.headersizer.Size.height // 2 + space_over_header + 2
            dc.Pen = self.fg_pen
            button_width = 0 if self.button is None else (
                self.button.Size.width)
            dc.DrawLine(10, ypos,
                        self.headersizer.Size.width - 10 - button_width, ypos)

        content = self.content
        if isinstance(content, AnyList):  # TODO: don't special case
            crect = wx.Rect(*content.Rect)
            crect = crect.Inflate(1, 1)
            dc.SetBrush(wx.TRANSPARENT_BRUSH)
            dc.SetPen(self.pen)
            dc.DrawRectangleRect(crect)

    def ChangeShownContent(self, *a):
        if self.content:
            self.content.Show(False)

        if wxMac:
            menu_item = self.menuitems[self.combo.GetStringSelection()]
        else:
            menu_item = self.combo.Value

        self.content = self.contents[menu_item]
        self.content.Show(True)
        self.Layout()

    def SetButton(self, label, callback):
        if self.button:
            self.headersizer.Detach(self.button)
            self.button.Destroy()

        # native button on mac instead of the vista clone button
        if not wxMac:
            self.button = UberButton(self,
                                     -1,
                                     label,
                                     skin='AppDefaults.PrefButton')
        else:
            self.button = wx.Button(self, -1, label)
            self.button.SetWindowVariant(wx.WINDOW_VARIANT_SMALL)

        self.button.Bind(wx.EVT_BUTTON, lambda e: callback(self.button))

        self.headersizer.AddStretchSpacer(1)
        self.headersizer.Add(self.button, 0, wx.ALIGN_CENTER_VERTICAL | RIGHT,
                             7)

    @property
    def MenuItems(self):
        combo = self.combo
        if wxMac:
            return [combo.GetClientData(i) for i in xrange(combo.Count)]
        else:
            return combo.menu.spine.items

    def SetContents(self, content, destroyold=False):
        if destroyold:
            if not self.contents:
                self.content.Destroy()
            for content in self.contents.values():
                content.Destroy()

        # the currently showing pane in a multiple pane pref panel
        if self.content:
            self.content.Show(False)
            self.content = None

        self.bodysizer.Clear()
        contents = self.contents = {}

        titlemaker = self.titlemaker

        if self.combo is None:
            if not wxMac:
                self.combo = UberCombo(self,
                                       value='',
                                       skinkey='AppDefaults.PrefCombo',
                                       valuecallback=self.ChangeShownContent)
            else:
                # use a native ComboBox on mac
                self.combo = wx.ComboBox(self,
                                         style=wx.CB_DROPDOWN | wx.CB_READONLY)
                self.combo.Bind(wx.EVT_COMBOBOX, self.ChangeShownContent)
            newcombo = True
        else:
            self.combo.RemoveAllItems()
            newcombo = False

        for object in content:
            if isinstance(object, tuple):
                window, label = object
            elif isinstance(object, wx.WindowClass):
                window = object
                label = titlemaker(window) if titlemaker else object.Label

            window.Show(False)
            window.Reparent(self)
            assert window.Parent is self
            self.bodysizer.Add(window, 1, pref_sizer_style, 7)

            menuitem = SimpleMenuItem(label)
            contents[menuitem] = window

            if wxMac:
                itemname = menuitem.GetContentAsString()
                self.combo.Append(itemname)
                self.menuitems[itemname] = menuitem
            else:
                self.combo.AppendItem(menuitem)

        if self.combo:
            if wxMac:
                self.combo.SetSelection(0)
                self.ChangeShownContent()
            else:
                self.combo.Value = self.combo[0]

        if self.combo is not None and newcombo:
            self.headersizer.Add(self.combo, 1, *combo_sizer_flags)