示例#1
0
class F4(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, wx.NewId(), "UberBar Overflow sampler",
                          (0, 0), (600, 150))

        content = wx.BoxSizer(wx.VERTICAL)

        self.skin = 'buttonbar'  #None#

        self.ubar = UberBar(self,
                            skinkey=self.skin,
                            overflowmode=True,
                            alignment=wx.ALIGN_LEFT)
        self.b1 = UberButton(self.ubar, -1, 'Button 1')
        self.b2 = UberButton(self.ubar, -1, 'Button 2')
        self.bi = UberButton(self.ubar, -1, 'Button i')
        self.b3 = UberButton(self.ubar, -1, 'Button 3')
        self.b4 = UberButton(self.ubar, -1, 'Button 4')
        self.ubar.Add(self.b1)
        self.ubar.Add(self.b2)
        self.ubar.Add(self.bi)
        self.ubar.Add(self.b3)
        self.ubar.Add(self.b4)

        #        self.b1.Show(self.b1,False)
        self.bi.Show(self.bi, False)

        self.ubar.AddMenuItem(SimpleMenuItem('Menu Item 1'))
        self.ubar.AddMenuItem(SimpleMenuItem('Menu Item 2'))
        self.ubar.AddMenuItem(SimpleMenuItem('Menu Item 3'))

        self.b4 = UberButton(self.ubar,
                             -1,
                             icon=wx.Bitmap(
                                 resdir / 'skins/default/digsbybig.png',
                                 wx.BITMAP_TYPE_PNG))
        self.ubar.AddStatic(self.b4)

        content.Add(self.ubar, 0, wx.EXPAND, 0)
        self.SetSizer(content)
示例#2
0
class Navi(wx.Panel):
    """
    Container for in bar close button and navigation arrows
    """
    def __init__(self, parent):
        """
            standard fair
        """
        wx.Panel.__init__(self, parent, style=0)

        events=[
            (wx.EVT_PAINT,self.OnPaint),
            (wx.EVT_ERASE_BACKGROUND, lambda e:None),
            (wx.EVT_BUTTON, self.OnButton)
        ]
        do(self.Bind(event, method) for (event, method) in events)

        #make sizers
        self.Sizer=wx.BoxSizer(wx.HORIZONTAL)
        self.hsizer=wx.BoxSizer(wx.HORIZONTAL)
        self.vsizer=wx.BoxSizer(wx.VERTICAL)

        #make Buttons
        self.closebutton=UberButton(self, CLOSEID, skin=self.Parent.closebuttonskin, icon=self.Parent.closeicon)
        self.prevb=UberButton(self, PREVID, skin=self.Parent.scrollbuttonskin, icon=self.Parent.lefticon)
        self.nextb=UberButton(self, NEXTID, skin=self.Parent.scrollbuttonskin, icon=self.Parent.righticon)
        self.upb=UberButton(self, UPID, skin=self.Parent.scrollbuttonskin, icon=self.Parent.upicon)
        self.downb=UberButton(self, DOWNID, skin=self.Parent.scrollbuttonskin, icon=self.Parent.downicon)

        #add butons to sizers
        self.hsizer.Add(self.prevb, 0, wx.EXPAND)
        self.hsizer.Add(self.nextb, 0, wx.EXPAND)
        self.vsizer.Add(self.upb, 1, wx.EXPAND)
        self.vsizer.Add(self.downb, 1, wx.EXPAND)
        self.Sizer.Add(self.hsizer, 0, wx.EXPAND)
        self.Sizer.Add(self.closebutton, 0, wx.CENTER|wx.ALL, 5)

        #Hide all buttons
        self.prevb.Show(False)
        self.nextb.Show(False)
        self.upb.Show(False)
        self.downb.Show(False)
        self.closebutton.Show(pref('tabs.tabbar_x', False))

        self.type=None

#    def UpdateSkin(self):
#        p = self.Parent
#
#        self.closebutton.SetSkinKey(p.closeskin)
#
#        self.closebutton.SetIcon(p.closeicon)
#
#        scrollskin = p.scrollbuttonskin
#
#        self.prevb.SetSkinKey(scrollskin)
#        self.nextb.SetSkinKey(scrollskin)
#        self.upb.SetSkinKey(scrollskin)
#        self.downb.SetSkinKey(scrollskin)
#
#        self.prevb.SetIcon(p.lefticon)
#        self.nextb.SetIcon(p.righticon)
#        self.upb.SetIcon(p.upicon)
#        self.downb.SetIcon(p.downicon)

    def Enabler(self):
        """
            Enable/Disable each button based off of acceptable scrolling
        """
        self.prevb.Enable(self.Parent.tabindex>0)
        self.nextb.Enable(self.Parent.tabendex<self.Parent.GetTabCount()-1)
        self.upb.Enable(self.Parent.rowindex>0)
        self.downb.Enable(self.Parent.rowindex<len(self.Parent.rows)-pref('tabs.rows',2))#self.Parent.visible)

    def ShowNav(self, type=None):
        """
        tells the navi which set of nav aroows to show
        type 0 - No arrows
        type 1 - left and right next to close button
        type 3 - up and down below close button
        """

        #do nothing if no change
        if self.type==type:
            #self.Layout()
            return

        #hide everything in prep for change
        self.prevb.Show(False)
        self.nextb.Show(False)
        self.upb.Show(False)
        self.downb.Show(False)
        self.Sizer.Detach(self.vsizer)

        if not type:#No arrows
            self.Sizer.Detach(self.closebutton)
            self.Sizer.Add(self.closebutton, 0, wx.CENTER|wx.ALL, 5)
            self.prevb.Show(False)
            self.nextb.Show(False)
        elif type==1:#Horizantel Arrows
            self.Sizer.Detach(self.closebutton)
            self.Sizer.Add(self.closebutton, 0, wx.CENTER|wx.ALL, 5)
            self.Sizer.SetOrientation(wx.HORIZONTAL)
            self.prevb.Show(True)
            self.nextb.Show(True)
        elif type==3:#Vertical arrows
            self.Sizer.Detach(self.closebutton)
            self.Sizer.Add(self.closebutton, 0, wx.CENTER|wx.ALL, 5)
            self.Sizer.SetOrientation(wx.VERTICAL)
            self.Sizer.Add(self.vsizer, 1, wx.EXPAND)
            self.upb.Show(True)
            self.downb.Show(True)
        elif type==4:#sidebar mode, no arrows as they are part of TabBar
            self.prevb.Show(False)
            self.nextb.Show(False)
            self.Sizer.Detach(self.closebutton)
            self.Sizer.Add(self.closebutton, 0, wx.CENTER)

        self.type=type

        #self.Layout()



    def OnButton(self, event):
        """
            Handels all events for any button clciked in the navi
        """
        if event.GetId()==CLOSEID:
            self.Parent.Parent.pagecontainer.active.tab.Close()
        elif event.GetId()==PREVID:
            if self.Parent.tabindex>0:
                self.Parent.tabindex-=1
                self.Parent.Regenerate(True)
        elif event.GetId()==NEXTID:
            endex=self.Parent.tabendex
            if endex<self.Parent.GetTabCount()-1:
                while self.Parent.tabendex==endex:
                    self.Parent.tabindex+=1
                    self.Parent.Regenerate(True)
        elif event.GetId()==UPID:
            if self.Parent.rowindex>0:
                self.Parent.rowindex-=1
                self.Parent.Regenerate(True)
        elif event.GetId()==DOWNID:
            if self.Parent.rowindex<len(self.Parent.rows)-pref('tabs.rows',2):#self.Parent.visible:
                self.Parent.rowindex+=1
                self.Parent.Regenerate(True)
        self.Enabler()

#        SmokeAndMirrorsBomb(self,[self.prevb,self.nextb,self.upb,self.downb,self.closebutton])

        self.Parent.Refresh()

        self.Parent.UpdateNotify()

    def OnPaint(self,event):
        dc=wx.PaintDC(self)
        rect=wx.RectS(self.Size)

        dc.Brush=wx.WHITE_BRUSH
        dc.Pen=wx.TRANSPARENT_PEN

        dc.DrawRectangleRect(rect)
示例#3
0
class TabBar(SimplePanel, UberWidget):
    """
        Where the tabs live, handles all display and organization functionality
    """
    def __init__(self, parent, skinkey):
        SimplePanel.__init__(self, parent)

        self.tabs     = [] # a list of all the tabs
        self.rows     = [] # a list of all the visible row, each a list of all the tabs in that row
        self.rowindex = 0  # the first visible row
        self.tabindex = 0  # the first tab of the first visible row
        self.tabendex = 0  # the last tab of the last visible row

        events = [(wx.EVT_PAINT, self.OnPaint),
                  (wx.EVT_SIZE, self.OnSize),
                  (wx.EVT_BUTTON, self.OnButton),
                  (wx.EVT_MOUSEWHEEL, self.OnWheel),
                  (wx.EVT_MOTION,self.OnMotion)]

        for event, method in events: self.Bind(event, method)


        self.flagedrows = set()
        self.lastsize=self.Size

        self.rowheight=0#height of a row in pixels

        self.SetSkinKey(skinkey,True)

        #buttons for verticle alignment
        self.cupb = UberButton(self, CUPID, skin=self.scrollbuttonskin, icon=self.upicon)
        self.cupb.Show(False)

        self.cdownb = UberButton(self, CDOWNID, skin=self.scrollbuttonskin, icon=self.downicon)
        self.cdownb.Show(False)

        #the navigation box
        self.navi=Navi(self)


        self.dragorigin = None#when draging the tab that you are dragging
        self.dragtarget = None#when dragging the mouse is over and at that point released on

        # the arrow image shown when dragging tabs
        self.dropmarker=OverlayImage(self, self.dropmarkerimage)

        # self.dropmarker = Storage(Show = lambda v=True: None)
        self.dragside=None#was the tab droped on the left or right of the target tab

        #linking prefs
        link = profile.prefs.link #@UndefinedVariable
        link('tabs.rows',      self.Generate, False)
        link('tabs.tabbar_x',  self.Generate, False)
        link('tabs.hide_at_1', self.Generate, False)
        link('tabs.side_tabs', self.SkinRedirect, False)

        self.Top.Bind(wx.EVT_MENU, self.OnMenuEvent)

    side_tabs = prefprop('tabs.side_tabs')
    tab_rows = prefprop('tabs.rows', 2)

    def UpdateSkin(self):
        key = self.tabskin = self.skinkey
        g   = lambda k, default = sentinel: skin.get(key + '.' + k, default)
        sg   = lambda k, default = sentinel: skin.get('side' + key + '.' + k, default)

        elems = (('spacing', 'spacing',          2),
                 ('bg', 'backgrounds.bar'),
                 ('dropmarkerimage', 'dropmarker.image'),
#                 ('dropmarkeroverlay', 'dropmarker.overlay', 0),
                 ('dropmarkeroffset', 'dropmarker.offset', 0),
                 ('closebuttonskin', 'closebuttonskin', ''),
                 ('closeicon', 'icons.close', None),
                 ('scrollbuttonskin', 'scrollbuttonskin', ''),
                 ('lefticon', 'icons.left', ''),
                 ('righticon', 'icons.right', ''),
                 ('upicon', 'icons.up', ''),
                 ('downicon', 'icons.down', ''))

        for elem in elems:
            setattr(self, 'top' + elem[0], g(*elem[1:]))
            setattr(self, 'side' + elem[0], sg(elem[1],getattr(self,'top' + elem[0])))
            setattr(self, elem[0], getattr(self, ('side' if self.side_tabs else 'top') + elem[0]))


        if hasattr(self,'dropmarker'):
            self.dropmarker.SetImage(self.dropmarkerimage)
            self.dropmarker.SetRotation((self.side_tabs and not self.dropmarkerimage))

        navi = getattr(self, 'navi', None)
        if navi is not None:
            self.cdownb.SetSkinKey(self.scrollbuttonskin)
            self.cupb.SetSkinKey(self.scrollbuttonskin)
            self.cdownb.SetIcon(self.downicon)
            self.cupb.SetIcon(self.upicon)

            self.navi.closebutton.SetSkinKey(self.closebuttonskin)

            self.navi.closebutton.SetIcon(self.closeicon)

            scrollskin = self.scrollbuttonskin

            navi.prevb.SetSkinKey(scrollskin)
            navi.nextb.SetSkinKey(scrollskin)
            navi.upb.SetSkinKey(scrollskin)
            navi.downb.SetSkinKey(scrollskin)

            navi.prevb.SetIcon(self.lefticon)
            navi.nextb.SetIcon(self.righticon)
            navi.upb.SetIcon(self.upicon)
            navi.downb.SetIcon(self.downicon)

        wx.CallAfter(self.Generate)


    def SkinRedirect(self,val=None):
        elems = ('spacing',
                 'bg',
                 'dropmarkerimage',
                 #'dropmarkeroverlay',
                 'closebuttonskin',
                 'closeicon',
                 'scrollbuttonskin',
                 'lefticon',
                 'righticon',
                 'upicon',
                 'downicon'
                 )

        for elem in elems:
            setattr(self, elem, getattr(self,('side' if self.side_tabs else 'top') + elem))

        self.UpdateChildSkins()

    def UpdateChildSkins(self):
        self.cdownb.SetSkinKey(self.scrollbuttonskin,True)
        self.cupb.SetSkinKey(self.scrollbuttonskin,True)

        navi, sbs = self.navi, self.scrollbuttonskin
        navi.closebutton.SetSkinKey(self.closebuttonskin,True)
        navi.prevb.SetSkinKey(sbs, True)
        navi.nextb.SetSkinKey(sbs, True)
        navi.upb.SetSkinKey(sbs, True)
        navi.downb.SetSkinKey(sbs, True)

        self.UpdateChildrenIcons()

        for tab in self.tabs:
            tab.UpdateMode()

        self.Generate()

    def __repr__(self):
        return '<TabBar %r>' % self.tabs

    def OnDragStart(self, tab):
        'Catches the tab drag event and starts the tab dragging system.'

        self.NotifyDrag(tab)

    def OnMotion(self,event):
        'Positioning updates during drag and drop'

        if event.LeftIsDown() and (self.dragorigin or self.Manager.source):
            self.DragCalc(event.Position)

    def __getitem__(self, index):
        return self.tabs[index]

    def OnPaint(self, event):
        dc   = wx.PaintDC(self)
        rect = RectS(self.Size)
        if not self.side_tabs:
            rcount = min(len(self.rows), pref('tabs.rows', 2))
            height = self.tabs[0].Size.height

            y=0
            for unused_i in xrange(rcount):
                self.bg.Draw(dc, Rect(rect.x, y, rect.width, height))
                y += height

        else:
            self.bg.Draw(dc,rect)

    def Add(self, page, focus, resort = True):
        """
            Adds a tab to the bar.  Should only be used by parent NoteBook.
            page - page in PageContainer the tab is to be associated with
            focus - whether that tab should steal focus from current tab
        """
        tab = Tab(self, page, skinkey = self.tabskin)
        tab.Bind(wx.EVT_CONTEXT_MENU, self.ShowMenu)
        tab.Show(False)
        self.tabs.append(tab)

        if focus:
            wx.CallAfter(tab.SetActive, True)

        elif resort:
            if self.side_tabs:
                self.ReVgenerate()
            else:
                self.Regenerate(True)

        return tab

    def ShowMenu(self, e):
        self._menutab = e.EventObject

        try:
            menu = self._tabmenu
        except AttributeError:
            from gui.uberwidgets.umenu import UMenu
            menu = self._tabmenu = UMenu(self)
            menu.AddItem('Close &Other Tabs', id = CLOSE_OTHER_TABS)
            menu.AddSep()
            menu.AddItem('&Close Tab', id = CLOSE_TAB)

        menu.PopupMenu()

    def OnMenuEvent(self, e):
        '''Invoked when a tab context menu item is clicked.'''

        if e.Id == CLOSE_TAB:
            self._menutab.CloseTab()
        elif e.Id == CLOSE_OTHER_TABS:
            menutab = self._menutab

            # switch to that tab first
            menutab.active = True

            with self.Frozen():
                for tab in self.tabs[:]:
                    if tab is not menutab:
                        tab.CloseTab()
        else:
            e.Skip()

    def Generate(self, val=None):
        self.navi.closebutton.Show(pref('tabs.tabbar_x', False))

        if self.side_tabs:
            self.ReVgenerate(True)
        else:
            self.Regenerate()

    def ReVgenerate(self,total=False, safe=False, dotoggle=True):
        """
        It's like Doo... err.. Regenerate, only vertical
        """

#        print "Starting: Regenerate",self.Top.Title,'\n'#,'='*80,'\n','\n'.join(format_stack())
#        print '='*80

        #TODO: Should we be careful about the tab leaving the bar?
        tabs = self.tabs

        if not tabs: return

        do(tab.Show(False) for tab in self.tabs)
        for tab in self.tabs: tab.row = None
        del self.rows[:]

        # Safty precautions prevent list access errors
        if self.tabindex < 0 or self.tabindex >= len(tabs):
            self.tabindex = 0

        # Preset variables
        n = self.tabindex   # the first tab shown
        self.rowheight = tabs[0].GetMinHeight()
        area = self.Notebook.Size.height - 32 # Height in pixels of the tabbar

        # number of fully visible rows in the given area at the given height
        i = area//self.rowheight

        count = len(tabs)

        #one tab per row
        for r in xrange(count): tabs[r].row=r

        rows = self.rows
        size = self.Size

        #Sets navimode and position
        navi = self.navi
        navi.ShowNav(4)
        navi.Hide()
        navi.Position = wx.Point(size.width - navi.Size.width,0)

        # Totally reconstructs the list if it's told to or there are not tabs in the rows or
        # if there isn't one more tab than there is room for and there is enough room to fit
        # them all and number of tabs in the row equals the number of tabs
        if total or not rows or (i + 1 != len(rows[0])) and not (i > len(rows[0])) and len(rows[0]) == len(tabs):
            rows.append([])
            col = rows[0]

            #if all tabs fit
            if i >= count:
                n=0
                self.tabindex=0
                do(col.append(tab) for tab in tabs)
                av=col[0].MinSize.height
            #calculate and show range
            else:
                for t in xrange(n,n+i+1):
                    if t < len(tabs):col.append(tabs[t])

                # populate with earlier stuff
                while len(col) < i and n > 0:
                    n-=1
                    col.insert(0,tabs[n])

                if col: av = col[0].MinSize.height
        else:
            #just leave the new values the same as the old
            col = rows[0]
            av  = col[0].MinSize.height

        # Show all tabs in the bar
        count = 16
        for t in col:
            t.Size = (self.Size.width,av)
            t.Position = (0,count)
            count += av
            t.Show()

        self.tabindex=n
        endex = self.tabendex=n+len(col)

        if dotoggle:
            self.Toggle()

        cupb, cdownb = self.cupb, self.cdownb


        cupb.Enable(self.tabindex != 0)
        cdownb.Enable(endex < len(tabs) or tabs[endex - 1].Position.y +
                           tabs[endex-1].Size.height > size.height - 16)


        self.UpdateNotify()

    def Regenerate(self, safe = False, dotoggle=True):
        '''
        Regenerates layout information.

        safe is a flag to indicate if we should try to keep the currently active
        tab in view at all times. (This doesn't occur when scrolling, for
        instance.)
        '''

#        print "Starting: Regenerate",self.Top.Title,'\n','='*80,'\n','\n'.join(format_stack())
#        print '='*80
        # early exit for when the tabbar isn't visible.
        if not self.IsShown() and len(self.tabs) == 1:
            return

        with self.Frozen():
            self._Regenerate(safe = safe, dotoggle = dotoggle)

        self.Refresh(False)

    def _Regenerate(self, safe = False, dotoggle = True):
        self.cupb.Show(False)
        self.cdownb.Show(False)
        parentpage = self.Parent.pagecontainer

        # style is the number of rows (or 0 for single)
        style = self.tab_rows

        # Should we be careful about the tab leaving the bar?
        careful = not safe and parentpage.active

        # Hide all tabs preparation for refilling
        for tab in self.tabs:
            tab.Show(False)
            tab.row = None
        del self.rows[:]

        # navi set up, see if arrows are needed and placement
        tally = sum(tab.MinSize.width for tab in self.tabs) #total size of tabs

        navi = self.navi
        tabs = self.tabs
        rows = self.rows

        if not tabs: return

        # Tab alignment calculations

        # Saftey precautions prevent list access errors
        if self.tabindex < 0 or self.tabindex >= len(tabs):
            self.tabindex = 0

        # Preset variables
        n = self.tabindex   # the first tab shown
        i = n
        row = 0
        self.rowheight = tabs[0].MinHeight


        my_w, nav_w = self.Size.width, navi.Size.width

        # Decide what kind of navigation panel, if any, to use...
        if tally >= my_w - nav_w and not style:
            navi.ShowNav(1) # arrows left and right
        elif tally >= (my_w - nav_w):
            navi.ShowNav(3) # arrows up and down next to the X
        else:
            navi.ShowNav(0)

        #Where to put navigation panel.
        navi.Freeze()
        navi.Show(True)
        navi.Fit()
        navi.Position = wx.Point(self.Size.width-navi.Size.width,0)
        navi.Size = wx.Size(-1,self.Size.height)
        navi.Thaw()

        #More preparing vars
        area = self.Notebook.Size.width - navi.Size.width

        #While more tabs are not in a row
        while len(tabs) > i:
            tally = tabs[i].MinSize.width
            rows.append([])

            # Loop through each visible tab, fitting tabs on the right.
            while i < len(tabs) and tally < area:
                i += 1
                if i < len(tabs):
                    tally += tabs[i].MinSize.width

            #Be carefull that the active tab doesn't scroll off the bar
            if careful and not style:
                activeindex = tabs.index(parentpage.active.tab)
                change=False

                #add tabs until the active tab is visible
                while activeindex>=i and n!=i:
                    i += 1
                    tally += tabs[i].MinSize.width
                    change = True

                #Remove tab if more tabs than room
                if tally >= area and change:
                    tally -= tabs[n].MinSize.width
                    n += 1
                    self.tabindex=n




            # If extra space, fit tabs to the right of the row
            if not style: # if single row,
                while n > 0 and area - tally > tabs[n-1].MinSize.width:
                    n -= 1
                    self.tabindex = n
                    tally += tabs[n].MinSize.width


            # Injects tabs calculated to fit in that row into that row
            if range(n, i):
                rows[row] = [tabs[t] for t in xrange(n, i)]
                for tab in rows[row]:
                    tab.row=row
            else:
                rows[row].append(tabs[i])
                i += 1

            if not style: break # If we're in single row, break now.
            row += 1
            n = i
        # Row calculation

        if self.rowindex >= len(rows):
            self.rowindex = len(rows) - 1

        #cycle through visible rows
        row = self.rowindex
        visible = self.tab_rows or 1

        if careful and style:
            #print "Being Careful"
            active = parentpage.active.tab
            #print active
            for ir,r in enumerate(rows):
                #print ir,r
                if active in r:
                    if ir<row:
                        #print "moving index down"
                        row = self.rowindex = ir
                    elif ir >= row + visible:
                        #print "moving index up"
                        row = ir - (visible - 1)


        # If we're closing tabs above where is visible, keep the visible
        # index "where it is"
        if len(rows) - (row + 1) < visible and len(rows) >= visible:
            row = len(rows) - visible
            self.rowindex = row

        # Place tabs!
        while row < len(rows) and row < self.rowindex + visible and len( rows[row] ) != 0:

            # if this is a row that needs to be scrunched...
            if rows.index(rows[row]) == len(rows)-1 and \
                (style or len(rows[row]) == len(tabs)):

                for t in xrange(0,len(rows[row])):
                    thistab = rows[row][t]
                    thistab.SetSize(thistab.MinSize)
                    if not t:
                        # The first tab is set to it's minimum width at x: 0
                        thistab.SetPosition((0, self.rowheight*(row-self.rowindex)))
                    else:
                        # Every other tab is placed right next to the tab
                        # before it.
                        thistab.SetPosition((rows[row][t-1].Position.x \
                                             + rows[row][t-1].Size.width,
                                             self.rowheight*(row-self.rowindex)))
                    thistab.Show(True)

            # If there are more rows than the current row...
            elif len(rows) > row:
                # Get a list of tab indices, widest to smallest.
                ordered = [rows[row].index(t)
                           for t in sorted(rows[row],
                                           key=lambda o: o.MinSize.width,
                                                         reverse=True) ]
                length = len(ordered)
                reserved=0
                o=0 # o_O ?

                # Average width of tab if all tabs are the same size, and
                # fill up all the area.
                av = (area - reserved) / (length - o)
                mark = 0
                while o < length:
                    # Loop from "current" tab to the end
                    for t in xrange(o, length):
                        tab = rows[row][ordered[t]]

                        # If this tab is larger than average...
                        if tab.GetMinSize()[0] > av:
                            # Make it it's minimum, and keep track of it
                            tab.SetSize(tab.MinSize)
                            reserved += tab.MinSize.width
                            o += 1
                            mark = o

                            # If we're not on the last tab, recalc average
                            if (length - o):
                                av=(area-reserved)/(length-o)
                        else:
                            o += 1
                            break

                # For tabs less than the average, set them to average
                for t in xrange(mark, length):
                    tab = rows[row][ordered[t]]
                    tab.SetSize((av, tab.MinSize.height))

                # For every tab in the row
                for t, tab in enumerate(rows[row]):
                    if not t: # If it's the first tab:
                        if length==1:
                            # If the row is so small it can only fit one tab,
                            # make due.
                            tab.Size = wx.Size(area, tab.MinSize.height)
                        tab.Position = wx.Point(0, self.rowheight * (row - self.rowindex))
                    else:
                        tab.Position = wx.Point(rows[row][t-1].Position.x + rows[row][t-1].Size.width,
                                                self.rowheight * (row - self.rowindex))

                    tab.Show(True)
            row += 1
        if dotoggle:
            self.Toggle()

        # If total rows is less than total rows being shown, shrink the
        # tab area so that it's only just big enough.
        if len(rows) < style or not style:
            rows_shown = len(rows)
        else:
            rows_shown = style
        if  self.Parent.SashPosition != rows_shown * self.rowheight:#self.MinSize.height
            self.MinSize = wx.Size(-1, rows_shown * self.rowheight)
            self.Parent.SetSashPosition(self.MinSize.height)
#            self.Size=self.MinSize

        # Determine if the Navi needs to enable or show arrows
        navi.Enabler()
        #self.Parent.Layout()    # Relayout self
        self.tabendex = i-1     # final tab being shown

        self.UpdateNotify()

        navi.Size = wx.Size(-1,rows_shown * self.rowheight)

    def Remove(self, target):
        'Removes the tab specified from the bar.'

        index=self.tabs.index(target)
        self.tabs.remove(target)
        #if no more tabs close window
        if len(self.tabs)==0:
            self.Notebook.window.Close()
        else:
            #if index is between index and endex and bring one tab from the left
            if index>self.tabindex and index<self.tabendex and self.tabindex>0:
                self.tabindex-=1

            if self.side_tabs:
                self.ReVgenerate(total=True)
            else:
                self.Regenerate(safe = True)


    def OnSize(self, event):
        'ReLayout the tabs if the bar on event of a resize'
        event.Skip()

        if self.side_tabs and self.tabs:
            cupb   = self.cupb
            cdownb = self.cdownb
            size   = self.Size
            tabs = self.tabs
            endex = self.tabendex

            # position and size buttons
            cupb.Position = (0,0)
            cupb.Size = (size.width, 16)
            cupb.Show()
            cupb.Enable(self.tabindex != 0)

            cdownb.Position = (0, size.height - 16)
            cdownb.Size     = (size.width, 16)
            cdownb.Show()
            cdownb.Enable(endex < len(tabs) or tabs[endex - 1].Position.y +
                           tabs[endex-1].Size.height > size.height - 16)


        sz = self.Size
        if ((sz.width != self.lastsize.width and not self.side_tabs) or (sz != self.lastsize and self.side_tabs)) and self.IsShownOnScreen():
            self.lastsize = sz
            if self.side_tabs:
                self.ReVgenerate(dotoggle = False)
            else:
                self.Regenerate(False,dotoggle = False)

        try:
            wx.CallAfter(wx.CallAfter,self.Parent.pagecontainer.active.panel.input_area.expandEvent)
        except AttributeError:
            pass

        self.Refresh(False)


    def GetTabCount(self):
        """
            Returns the number of tabs in the bar
        """
        return len([t for t in self if t])

    def NextTab(self):
        self.SetNextActive(self.ActiveTab, wrap = True)

    def PrevTab(self):
        self.SetLastActive(self.ActiveTab, wrap = True)


    def SetNextActive(self, origin,wrap=False):
        """
            Sets the tab after the curent active
            -if it does not exist does the previbus
            -or the first if wrap is true
        """
        if origin in self.tabs:
            index=self.tabs.index(origin)
            if not index < len(self.tabs)-1 and wrap:
                self.tabs[0].SetActive(True)
            elif index < len(self.tabs)-1:
                self.tabs[index+1].SetActive(True)
            elif index>0:
                self.tabs[index-1].SetActive(True)
            self.Refresh(False)

    def SetLastActive(self, origin, wrap = False):
        """
            Sets the tab before the curent active
            -if it does not exist does the next
            -or the last if wrap is true
        """
        if origin in self.tabs:
            index=self.tabs.index(origin)
            if not index > 0 and wrap:
                self.tabs[len(self.tabs)-1].SetActive(True)
            elif index >0:
                self.tabs[index-1].SetActive(True)
            elif index<0:
                self.tabs[index+1].SetActive(True)
            self.Refresh(False)

    def SyncActive(self,atab):
        """
            Moves the index and endex so that the active tab is in the bar
        """
        if not atab: return

        if self.side_tabs:
            if atab < self.tabindex:
                self.tabindex=atab
                self.ReVgenerate(True)
            else:
                thetab=self.tabs[atab]
                while atab >= self.tabendex or thetab.Position.y+thetab.Size.height > self.Size.height-16:
                    self.tabindex+=1
                    self.ReVgenerate(True)
        else:
            style = self.tab_rows
            if atab < self.rowindex:
                self.rowindex=atab
                self.Regenerate()
            elif atab > self.rowindex+style-1:
                self.rowindex=atab-style+1
                self.Regenerate()

    def OnWheel(self,event):
        """
            Event that handles mouse wheeling,
            maps the events to SetNextActive and SetLastActive
        """
        if RectS(self.Size).Contains(event.Position):
            direction = event.GetWheelRotation()
            if direction<0:
                self.SetNextActive(self.ActiveTab, True)
            elif direction>0:
                self.SetLastActive(self.ActiveTab, True)

    @property
    def Notebook(self):
        return self.Parent

    @property
    def Manager(self):
        return self.Notebook.manager

    @property
    def ActiveTab(self):
        active = self.Notebook.pagecontainer.active
        if active is not None:
            return active.tab

    def OnButton(self,event):
        """
            The button events for vertical alignment for up and down
        """
        if event.GetId()==CUPID:
            if self.tabindex > 0:
                self.tabindex -= 1
                self.ReVgenerate(total = True)
        elif event.GetId()==CDOWNID:
            if self.tabendex<len(self.tabs) or self.tabs[self.tabendex-1].Position.y+self.tabs[self.tabendex-1].Size.height>self.Size.height-16:
                self.tabindex+=1
                self.ReVgenerate(total=True)

        self.UpdateNotify()

    def NotifyDrag(self, origin):
        """
            When a tab is dragged this is called to start the tabbar handling dragging
            origin - tab being dragged
        """
        # Lets the TabMan know a tab as been dragged
        self.dragorigin = origin
        origin.SetCursor(wx.StockCursor(wx.CURSOR_NO_ENTRY))
        self.Manager.Notify(self.Notebook)


    def DragCalc(self,point):
        """
            This does the dragging calculations for the tabs
        """

        sidetabs = self.side_tabs

        # if here is no local origin tab but there is a remote source identified in TabMan
        if not self.dragorigin and self.Manager.source:
            #setting a local drag origin
            master = self.Manager.source
            dragorigin=master.tabbar.dragorigin
            # announcing to TabMan it is expecting a tab
            self.Manager.Request(self.Notebook)
        # if there is a local origin use that
        else:
            dragorigin=self.dragorigin

        # if dragtarget is out of date find what you're dragging to
        if not self.dragtarget or not self.dragtarget.Rect.Contains(point):

            wap = wx.FindWindowAtPointer()
            self.dragtarget = wap if isinstance(wap,Tab) else None
            self.dragside   = None

        # if there is a tab as target
        if self.dragtarget and self.dragtarget != dragorigin:
            dtrect=self.dragtarget.Rect
            # data to decide what side the tab would be dropped on
            if not sidetabs:
                x  = point[0] - dtrect.x
                x2 = dtrect.width / 2
            else:
                x  = point[1] - dtrect.y
                x2 = dtrect.height / 2
            # make the left/top or right/bottom decision
            if x <= x2:#left/top
                if self.dragside!=False:
                    self.dragside=False
                    if not sidetabs:
                        self.DrawDropMarker(dtrect.x, dtrect.y)# + (dtrect.height // 2)
                    else:
                        self.DrawDropMarker(dtrect.x, dtrect.y)# + dtrect.width // 2

            elif not self.dragside:#right/bottom
                self.dragside=True
                if not sidetabs:
                    self.DrawDropMarker(dtrect.x+dtrect.width,dtrect.y)#+(dtrect.height//2)
                else: self.DrawDropMarker(dtrect.x,dtrect.y+dtrect.height)#+dtrect.width//2
            self.SetFocus()
        # if being dropped in the whitespace of the TabBar
        elif (dragorigin and self.dragtarget!=dragorigin) or (dragorigin==None and self.dragtarget==None) and self.Rect.Contains(point):
            # find what row the tab is being dropped in
            if not sidetabs:
                row=self.rows[(point[1]//self.rowheight)+self.rowindex]
                tab=row[len(row)-1]
                self.dragside=True
            # or in vertical if at the beginning or end
            else:
                if point.y>self.rowheight:
                    tab=self.rows[0][len(self.rows[0])-1]
                    self.dragside=True
                else:
                    tab=self.rows[0][0]
                    self.dragside=False
            dtrect=tab.Rect
            #Place marker
            if not sidetabs: self.DrawDropMarker(dtrect.x+dtrect.width,dtrect.y)#+(dtrect.height//2)
            elif self.dragside==True: self.DrawDropMarker(dtrect.x,dtrect.y+dtrect.height)#+dtrect.width//2
            else: self.DrawDropMarker(dtrect.x+dtrect.width/2,dtrect.y)
            self.SetFocus()
            #cleanup
            self.dragtarget=tab

        else:#if not in tabbar anymore don't show arrow
            self.dropmarker.Show(False)

    def DragFinish(self,new=False):
        """
        Ends dragging and does any rearranging if required
        """

        if not wx.IsDestroyed(self.dragorigin):
            self.dragorigin.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))

        if self.dragorigin and self.dragorigin.previewtabs:
            # Destroy the preview tab
            self.dragorigin.previewtabs.Stop()
            self.dragorigin.previewtabs = None

        rect       = RectPS(self.Notebook.ClientToScreen(self.Position), self.Size)
        parentrect = self.Notebook.window.Rect
        mousepos   = wx.GetMousePosition()
        manager    = self.Manager

        #if released out of the window...
        if not new and ((manager.destination and not parentrect.Contains(mousepos)) or not rect.Contains(mousepos)):
            if self.ActiveTab==self.dragorigin:
                self.SetNextActive(self.dragorigin)
            self.dragorigin.Show(False)

            #If no or invalid destination in manager create a new window and sets it destination
            dest = manager.destination
            if not dest or not dest.tabbar.Rect.Contains(dest.ScreenToClient(wx.GetMousePosition())):
                # FIXME: SWIG doesn't like subtracting a wx.Size from a wx.Point, so do it the hard way
                # until the SIP migration is finished.
                originsize = self.dragorigin.GetSize()
                newpoint = wx.Point(mousepos[0] - originsize[0], mousepos[1] - originsize[1])
                destination = self.Notebook.winman.NewWindow(newpoint, self.Notebook.window.GetSize()).notebook
            #else set the destination to the manager's destination
            else:
                destination = dest

            #clear tabman's destination
            manager.Request()

            # Grab a reference to the tab's page
            page = self.dragorigin.page

            # Make the tab "forget" about the page
            self.Notebook.did_remove(page.panel)
            del self.dragorigin.page
            del page.tab

            # Remove the tab from the tabs list, and destroy the wxWindow
            self.tabs.remove(self.dragorigin)

            self.dragorigin.Close()

            # remove page from this notebook and insert it into the target notebook
            #page.Parent.RemoveChild(page)
            destination.Insert(page, False)

            # cleanup
            manager.Notify()
            self.dragorigin=None

            # re-sort tabs
            if self.side_tabs:
                self.ReVgenerate(True)
            else:
                self.Regenerate(safe = True)

        # if released inside of the window
        # used both for moving within a window and as the last step of a
        # interwindow move in case of interwindow tab has already been moved to
        # this window at the end of the list and all that is left is to move it
        # to the correct position.
        elif self.dragtarget and self.dragorigin and self.dragorigin!=self.dragtarget and self.Rect.Contains(self.Notebook.ScreenToClient(mousepos)):
            #remove the tab from the list
            self.tabs.remove(self.dragorigin)

            #decide which side of the target the tab should be dropped
            pos = self.tabs.index(self.dragtarget) + (1 if self.dragside else 0)

            # Reinsert the tab in it's new position
            self.tabs.insert(pos, self.dragorigin)

            after = self.tabs[pos+1] if pos+1 < len(self.tabs) else None
            if after is not None:
                after = after.page.panel

            # call after so that windows are all in their correct places
            wx.CallAfter(self.Notebook.did_rearrange, self.dragorigin.page.panel, after)

#            # Resort
            if self.side_tabs:
                self.ReVgenerate(True)
            else:
                self.Regenerate()
        elif new:
            if self.side_tabs:
                self.ReVgenerate(True)
            else:
                self.Regenerate()

        #if there is a dragorigin run onMouseLeave on it to reset it's look
        if self.dragorigin:
            self.dragorigin.OnMouseLeave()

        #local cleanup
        self.dropmarker.Show(False)
        self.dragorigin=None
        self.dragtarget=None

        #destination and manager cleanup
        dest = manager.destination
        if dest:
            dest.tabbar.dragorigin=None
            dest.tabbar.dragtarget=None

        manager.Request()
        manager.Notify()

        if len(self.tabs)==0: self.Notebook.window.Close()

    def DrawDropMarker(self,x,y):
        """
        Places the marker for where to drop the tab
        x and y are center position, saves calculation that way
        """

#        if self.side_tabs:
#            self.dropmarker.SetSize((self.Size.width - self.dropmarkeroverlay, -1))
#        else:
#            self.dropmarker.SetSize((-1, self.rowheight - self.dropmarkeroverlay))
        self.dropmarker.Teleport(self.ClientToScreen((x, y+self.dropmarkeroffset)))

        if not self.dropmarker.IsShown():
            self.Manager.ShowDropMarker(self.dropmarker)

    def Toggle(self, switch = None):
        'Toggle whether the tabbar is hidden or shown.'

        if pref('tabs.hide_at_1', True) and len(self.tabs) <= 1 and not switch:
            self.Notebook.Split(False)

            #make sure the content of the IM win resizes if tabbaar hides
            self.ProcessEvent(wx.CommandEvent(wx.wxEVT_SIZE))
        else:
            self.Notebook.Split(True)


    def UpdateNotify(self):

        frows = self.flagedrows
        frows.clear()

        # Resort
        if self.side_tabs:
            tabs=self.tabs
            for i, tab in enumerate(tabs):
                if tab.notified:
                    frows.add(i)
                elif i in self.flagedrows:
                    frows.remove(i)

            self.cupb.SetNotify(len(frows) and min(frows) < self.tabindex)
            self.cdownb.SetNotify(len(frows) and (max(frows) >= self.tabendex or
                                                  tabs[max(frows)].Position.y + tabs[max(frows)].Size.height > self.Size.height - 16))
        else:
            for i, row in enumerate(self.rows):
                flaged = False
                for tab in row:
                    if tab and tab.notified:
                        flaged = True
                        frows.add(i)
            self.navi.upb.SetNotify(len(frows) and min(frows)<self.rowindex)
            self.navi.downb.SetNotify(len(frows) and max(frows)>self.rowindex + self.tab_rows - 1)
示例#4
0
class CapabilitiesBar(SimplePanel):
    '''
    A specialized UberBar used used in the infobox and the IM window
    has a subbar with to/from combos.
    '''
    def __init__(self,
                 parent,
                 buddy_callback,
                 showCapabilities=True,
                 infoboxmode=False):
        SimplePanel.__init__(self, parent)

        self.buddy_callback = buddy_callback

        self.Bind(wx.EVT_PAINT, lambda e: wx.PaintDC(self))

        self.infoboxmode = infoboxmode
        self._lastcaps = None

        self.UpdateSkin()
        self.Sizer = wx.BoxSizer(wx.VERTICAL)

        # create delegates for callbacks
        for action in ('OnSendFiles', 'OnSendFolder', 'OnViewPastChats',
                       'OnAlert', 'OnBlock', 'OnAddContact'):
            setattr(self, action, Delegate())

        # Create the uberbar for the capabilities.
        self.cbar = bar = UberBar(self,
                                  skinkey=self.capabilitiesskin,
                                  overflowmode=True)
        # FIXME: we should simply not allow the capabilities bar to be created for native mode
        if not showCapabilities or nativeIMWindow:
            self.cbar.Hide()

        if not infoboxmode:
            self.cbar.Bind(wx.EVT_CONTEXT_MENU,
                           lambda e: self.ActionsBarMenu.PopupMenu(event=e))

        # Create all the buttons for the capabilities bar.

        iconsize = skin.get('ActionsBar.IconSize')
        icons = skin.get('ActionsBar.Icons')

        for attr, title, tooltip in buttons:
            icon = getattr(icons, attr).Resized(iconsize)
            if attr == 'files':
                # "files" has a dropdown menu
                button = UberButton(bar,
                                    -1,
                                    title,
                                    icon=icon,
                                    type='menu',
                                    menu=self.FileMenu)

                # Change the label and action of the files button when it's overflowed into
                # the menu on the right.
                button.overflow_label = _('Send File')
                button.overflow_callback = self.OnSendFiles
            else:
                # hack until I fix this :[ -kevin
                if attr == 'video' and infoboxmode: continue

                button = UberButton(bar, -1, title, icon=icon)
                button.overflow_label = title

            button.SetToolTipString(tooltip)

            setattr(self, 'b' + attr, button)
            bar.Add(button, calcSize=False)

        bar.OnUBSize()

        #TODO Add button logics

        #        if not self.infoboxmode:
        #            self.badd = UberButton(bar,-1,'',icon = getattr(icons, 'add').Resized(iconsize))
        #            bar.AddStatic(self.badd)
        #            self.badd.Bind(wx.EVT_BUTTON,lambda e: self.OnAddContact())

        # Create multichat icon for the roomlist
        if pref('messaging.groupchat.enabled', False) and not self.infoboxmode:
            self.bmultichat = UberButton(
                bar,
                -1,
                icon=skin.get('actionsbar.icons.roomlist').Resized(16),
                type='toggle')
            self.bmultichat.SetToolTipString(_('Group Chat'))
            bar.AddStatic(self.bmultichat)

        self.ihistory = SimpleMenuItem(_('View Past Chats'),
                                       method=self.OnViewPastChats)

        def show_prefs_notifications(a):
            import gui.pref.prefsdialog as prefsdialog
            prefsdialog.show('notifications')

        self.ialert = SimpleMenuItem(_("Alert Me When..."),
                                     method=show_prefs_notifications)
        self.iblock = SimpleMenuItem(_("Block"), method=self.OnBlock)

        if not self.infoboxmode:
            self.iadd = SimpleMenuItem(_("Add Contact"),
                                       method=self.OnAddContact)
            bar.AddMenuItem(self.iadd)
        bar.AddMenuItem(self.ihistory)
        bar.AddMenuItem(self.ialert)

        if not self.infoboxmode:
            bar.AddMenuItem(SimpleMenuItem(id=-1))
            bar.AddMenuItem(self.iblock)

        self.Sizer.Add(bar, 0, wx.EXPAND)

        # create the To/From bar
        self.tfbar = tfbar = UberBar(self, skinkey=self.tofromskin)
        self.tfbar.Hide()

        tofrom_font = skin.get('tofrombar.font', default_font)
        tofrom_color = skin.get('tofrombar.fontcolor', wx.BLACK)

        talign = wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT

        self.to_label = ClearText(tfbar, _('To:'), alignment=talign)
        self.to_label.Font = tofrom_font
        self.to_label.FontColor = tofrom_color
        self.from_label = ClearText(tfbar, _('From:'), alignment=talign)
        self.from_label.Font = tofrom_font
        self.from_label.FontColor = tofrom_color

        self.cto = UberCombo(tfbar,
                             skinkey=self.tofromcomboskin,
                             typeable=False,
                             size=(100, 20),
                             minmenuwidth=200)
        self.cfrom = UberCombo(tfbar,
                               skinkey=self.tofromcomboskin,
                               typeable=False,
                               size=(100, 20),
                               minmenuwidth=200)

        tfbar.Add(self.to_label, calcSize=False)
        tfbar.Add(self.cto, True, calcSize=False)
        tfbar.Add(self.from_label, calcSize=False)
        tfbar.Add(self.cfrom, True)

        self.Sizer.Add(tfbar, 0, wx.EXPAND)

        profile.prefs.link(action_icons_key, self.UpdateIcons)

        self.cbar.overflowmenu.BeforeDisplay += self.ApplyCaps

    ToCombo = property(lambda self: self.cto)
    FromCombo = property(lambda self: self.cfrom)

    def UpdateSkin(self):
        'Tells the subbars what skins they should load.'

        sget = skin.get
        self.capabilitiesskin = sget('actionsbar.toolbarskin', None)
        self.tofromskin = sget('tofrombar.toolbarskin', None)

        self.menuskin = sget('%s.menuskin' % self.capabilitiesskin, None)

        self.tofromcomboskin = sget("tofrombar.comboboxskin", None)

        self.iconsize = sget('ActionsBar.IconSize')
        self.icons = sget('ActionsBar.Icons')

        if hasattr(self, 'to_label'):
            tofrom_font = sget('tofrombar.font', lambda: default_font())
            tofrom_color = sget('tofrombar.fontcolor', lambda: wx.BLACK)

            self.to_label.Font = self.from_label.Font = tofrom_font
            self.to_label.FontColor = self.from_label.FontColor = tofrom_color

        if hasattr(self, 'cbar'): self.cbar.SetSkinKey(self.capabilitiesskin)
        if hasattr(self, 'tfbar'): self.tfbar.SetSkinKey(self.tofromskin)
        if hasattr(self, 'cto'): self.cto.SetSkinKey(self.tofromcomboskin)
        if hasattr(self, 'cfrom'): self.cfrom.SetSkinKey(self.tofromcomboskin)

        self.UpdateIcons()

    def UpdateIcons(self, *a):
        'Updates icon sizes for buttons.'

        icons_pref = pref(action_icons_key)
        textonly = icons_pref == 'text'

        icons = self.icons
        size = self.iconsize
        #TODO: Add Button logics
        #        allbuttons = list(buttons)
        #        allbuttons.append(('add',''))

        with self.Frozen():
            for attr, title, tooltip in buttons:
                icon = None if textonly else getattr(icons, attr).Resized(size)
                button = getattr(self, 'b' + attr, None)
                if button is not None:
                    button.SetIcon(icon)
                    button.SetAlignment(wx.VERTICAL if icons_pref ==
                                        'above' else wx.HORIZONTAL)
                    button.SetLabel('' if icons_pref == 'icons' else title)

        self.Parent.Layout()
        self.Refresh()

    def ShowToFrom(self, show=True):
        'Show or hide the to/from bar.'

        return self.tfbar.Show(show)

    @property
    def RoomListButtonShown(self):
        bmultichat = getattr(self, 'bmultichat', None)
        return bmultichat is not None and bmultichat.IsShown()

    @property
    def ToFromShown(self):
        'Returns True if the To/From bar is shown.'

        return self.tfbar.IsShown()

    def ShowCapabilities(self, show=True):
        'Show/Hide the capabilities bar.'

        return self.cbar.Show(show)
        #self.Layout()

    def CapabilitiesIsShown(self):
        'Returns True if the capabilities bar is shown.'

        return self.cbar.IsShown()

    def GetButton(self, button):
        'Returns one of the butons by name.'

        return getattr(self, 'b' + button, None)

    def ApplyCaps(self, contact=None, convo=None):
        'Those shows and hides options depending on the capabilities the Contact reports.'

        if contact is None and convo is None:
            convo = self.buddy_callback()
            from common.Conversation import Conversation
            if not isinstance(convo, Conversation):
                contact = convo
                convo = None

        c = None
        if convo is not None:
            if convo.ischat:
                c = set([caps.IM])
            elif contact is None:
                contact = convo.buddy

        if c is None:
            c = set(contact.caps)

        if contact is not None:
            c.add(('blocked', contact.blocked))

        # early exit if capabilities are the same.
        if c == self._lastcaps: return

        buttons_caps = [('binfo', contact is not None and not any(
            (contact.sms and contact.mobile, self.infoboxmode))),
                        ('bim', caps.IM in c), ('bfiles', caps.FILES in c),
                        ('bemail', caps.EMAIL in c), ('bsms', caps.SMS in c),
                        ('bvideo', caps.VIDEO in c)]

        for name, val in buttons_caps:
            ctrl = getattr(self, name, None)
            if ctrl is not None:
                ctrl.Show(ctrl, val, False)

        cbar = self.cbar
        menu = cbar.overflowmenu
        count = menu.spine.items.count
        iblock = self.iblock

        if caps.BLOCKABLE in c and not count(iblock):
            cbar.AddMenuItem(SimpleMenuItem(id=-1))
            cbar.AddMenuItem(iblock)
        elif not caps.BLOCKABLE in c and count(iblock):
            cbar.overflowmenu.RemoveItem(iblock)

        if contact is not None:
            if contact.blocked:
                content = _('Unblock {name}')
            else:
                content = _('Block {name}')

            iblock.content = [content.format(name=contact.name)]

        self.set_groupchat_visibility(contact, convo)

        i = len(menu.spine.items) - 1
        if menu.GetItem(i).id == -1:
            menu.RemoveItem(i)

        # don't show the dropdown on the right for widgets.
        self.cbar.overflowbutton.Show(not getattr(contact, 'iswidget', False))

        self._lastcaps = c

        cbar.GenWidthRestriction(True)

        self.update_add_contact_shown(convo)

        self.Parent.Layout()

    def update_add_contact_shown(self, convo):
        if not hasattr(self, 'iadd'):
            return

        ischat = convo is not None and convo.ischat
        overflow = self.cbar.overflowmenu

        if ischat:
            if overflow.GetIndex(self.iadd) != -1:
                overflow.RemoveItem(self.iadd)
        else:
            if overflow.GetIndex(self.iadd) == -1:
                overflow.InsertItem(overflow.GetIndex(self.ihistory),
                                    self.iadd)

    def set_groupchat_visibility(self, contact, convo):
        if not hasattr(self, 'bmultichat'):
            return

        proto = getattr(contact, 'protocol', getattr(convo, 'protocol', None))
        groupchat = False
        if proto is not None:
            groupchat = getattr(proto,
                                'supports_group_chat', False) and getattr(
                                    contact, 'supports_group_chat', False)
            self.bmultichat.Show(groupchat)

    @property
    def FileMenu(self):

        self._filemenu = SimpleMenu(self, self.menuskin)
        self.send_file_item = SimpleMenuItem(_('Send File'),
                                             lambda *a: self.OnSendFiles())
        #            if b and b.online:
        self._filemenu.AppendItem(self.send_file_item)
        self._filemenu.AppendItem(
            SimpleMenuItem(_('Transfer History'),
                           lambda *a: FileTransferDialog.Display()))

        return self._filemenu

#        try:
#            return self._filemenu
#        except AttributeError:
#            self._filemenu = self.build_file_menu()
#            return self._filemenu

    @property
    def ActionsBarMenu(self):
        try:
            return self._actionsbarmenu
        except AttributeError:
            self._actionsbarmenu = self.build_actionsbar_menu()
            return self._actionsbarmenu

    def build_actionsbar_menu(self):
        m = UMenu(self, onshow=self.update_actionsbar_menu)

        c = self._actionsbar_checks = {}
        for name, label in [('icons', _('Icons Only')),
                            ('text', _('Text Only')),
                            ('next', _('Icons Next to Text')),
                            ('above', _('Icons Above Text'))]:

            def cb(name=name):
                with self.Frozen():
                    setpref(action_icons_key, name)

            c[name] = m.AddCheckItem(label, callback=cb)

        m.AddSep()
        m.AddItem(
            _('Hide Actions Bar'),
            callback=lambda: setpref('messaging.show_actions_bar', False))

        return m

    def update_actionsbar_menu(self, menu):
        p = pref(action_icons_key)
        for name, item in self._actionsbar_checks.iteritems():
            item.Check(p == name)