Esempio n. 1
0
class BundlePanel(wx.BoxSizer):
    
    COLLAPSED, PARTIAL, FULL = range(3)
    
    icons = None
    @classmethod
    def load_icons(cls):
        if not cls.icons:
            icons = cls.icons = {}
            guiUtility = GUIUtility.getInstance()
            utility = guiUtility.utility
            base_path = os.path.join(utility.getPath(), LIBRARYNAME, "Main", "vwxGUI", "images")
            
            icons['info'] = wx.Bitmap(os.path.join(base_path, "info.png"), wx.BITMAP_TYPE_ANY)
    
    def __init__(self, parent, parent_list, hits, general_description = None, description = None, font_increment=0):
        wx.BoxSizer.__init__(self, wx.HORIZONTAL)
        
        # preload icons
        self.load_icons()
        
        self.parent = parent
        self.parent_listitem = parent
        self.parent_list = parent_list
        
        listbody_width = parent_list.GetSize()[0] 
        if listbody_width < BUNDLE_GRID_COLLAPSE:
            self.num_cols = 1
        else:
            self.num_cols = BUNDLE_NUM_COLS
        
        # logging
        self.guiutility = GUIUtility.getInstance()
        self.uelog = UserEventLogDBHandler.getInstance()
        
        self.state = BundlePanel.COLLAPSED
        self.nrhits = -1
        self.bundlelist = None
        
        self.font_increment = font_increment
        self.vsizer = wx.BoxSizer(wx.VERTICAL)
        
        self.SetBackgroundColour(DEFAULT_BACKGROUND)
        
        self.indent = parent.controls[0].icon.GetSize()[0] + 3 + 3 + self.parent_list.leftSpacer #width of icon + 3px left spacer + 3px right spacer
        
        self.AddHeader()
        self.AddGrid()
        
        self.SetHits(hits, noChange = True)
        self.UpdateHeader(general_description, description)
        
        self.AddSpacer((self.indent, -1))
        self.Add(self.vsizer, 1, wx.EXPAND|wx.BOTTOM, 7)
    
    def AddHeader(self):
        sizer = wx.BoxSizer(wx.HORIZONTAL)
        
        self.header = StaticText(self.parent, -1, ' ')
        self.info_icon = wx.StaticBitmap(self.parent, -1, self.icons['info'])

        sizer.Add(self.header, 0, wx.RIGHT, 7)
        sizer.Add(self.info_icon, 0, wx.ALIGN_CENTER_VERTICAL)
        self.vsizer.Add(sizer, 0, wx.BOTTOM, 3)
    
    def UpdateHeader(self, general_description, description):
        self.SetGeneralDescription(general_description)
        self.SetDescription(description)
    
    def AddGrid(self):
        self.grid = wx.FlexGridSizer(0, self.num_cols, 3, 7)
        self.grid.SetFlexibleDirection(wx.HORIZONTAL)
        self.grid.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_NONE)
        self.grid.SetMinSize((1,-1))
        
        for i in xrange(BUNDLE_NUM_ROWS):
            self.grid.AddGrowableRow(i, 1)
        
        for j in xrange(self.num_cols):
            self.grid.AddGrowableCol(j, 1)
        self.vsizer.Add(self.grid, 1, wx.EXPAND|wx.LEFT|wx.RIGHT, 14 + self.indent)
        
    def RebuildGrid(self, new_cols):
        if self.num_cols != new_cols:
            self.num_cols = new_cols
            
            children = self.grid.GetChildren()
            children_controls = []
            for child in children:
                children_controls.append(child.GetWindow() or child.GetSizer())
                
            for child in children_controls:
                self.grid.Detach(child)
            
            self.vsizer.Detach(self.grid)
            self.grid.Destroy()
            
            self.grid = wx.FlexGridSizer(0, self.num_cols, 3, 7)
            for child in children_controls:
                self.grid.Add(child, 0, wx.EXPAND)
                
            for j in xrange(self.num_cols):
                self.grid.AddGrowableCol(j, 1)
                
            self.vsizer.Add(self.grid, 1, wx.EXPAND|wx.LEFT|wx.RIGHT, 14 + self.indent)
            
            self.Layout()
            self.parent_listitem.Layout()
            
            return True
        return False
    
    def UpdateGrid(self, hits, noChange=False):
        N = BUNDLE_NUM_ROWS * BUNDLE_NUM_COLS
        items_to_add = min(N, self.nrhits)
        if self.nrhits > N:
            items_to_add -= 1

        self.parent.Freeze()
        children = self.grid.GetChildren()
        didChange = len(children) < min(N, self.nrhits)
        if not didChange:
            if DEBUG:
                print >> sys.stderr, "*** BundlePanel.UpdateGrid: total nr items did not change, updating labels only"
            
            #total nr items did not change
            for i in range(items_to_add):
                link_static_text = children[i].GetWindow() or children[i].GetSizer()
                if link_static_text and getattr(link_static_text, 'SetLabel', False):
                    link_static_text.SetLabel(hits[i].name)
                    link_static_text.action = hits[i]
                else:
                    didChange = True
                    break
            
            if self.nrhits > N:
                more_caption = '(%s more...)' % (self.nrhits - N + 1)
                link_static_text = children[i+1].GetWindow() or children[i+1].GetSizer()
                if link_static_text and getattr(link_static_text, 'SetLabel', False):
                    link_static_text.SetLabel(more_caption)
                    link_static_text.Unbind(wx.EVT_LEFT_UP)
                    link_static_text.Bind(wx.EVT_LEFT_UP, self.OnMoreClick)
                else:
                    didChange = True

        if didChange:
            if DEBUG:
                print >> sys.stderr, "*** BundlePanel.UpdateGrid: something did change rebuilding grid", len(children),min(N, self.nrhits)
            
            curRows = len(children) / BUNDLE_NUM_COLS
            newRows = min(self.nrhits / BUNDLE_NUM_COLS, BUNDLE_NUM_ROWS)
            rowsChanged = curRows != newRows            
            
            self.grid.ShowItems(False)
            self.grid.Clear(deleteWindows = True)
            for i in range(items_to_add):
                hit = hits[i] 
    
                new_text = LinkStaticText(self.parent, hit.name, icon = False, icon_type = 'tree', icon_align = wx.ALIGN_LEFT, font_increment = self.font_increment, font_colour = BUNDLE_FONT_COLOR)
                new_text.Bind(wx.EVT_LEFT_UP, self.OnBundleLinkClick)
                new_text.SetMinSize((1,-1))
                new_text.action = hit
                self.grid.Add(new_text, 0, wx.EXPAND)
                
            if self.nrhits > N:
                caption = '(%s more...)' % (self.nrhits - N + 1)
                
                more_label = LinkStaticText(self.parent, caption, icon = False, icon_align = wx.ALIGN_LEFT, font_increment = self.font_increment, font_colour = BUNDLE_FONT_COLOR)
                more_label.Bind(wx.EVT_LEFT_UP, self.OnMoreClick)
                self.grid.Add(more_label, 0, wx.EXPAND)
                
            self.parent_listitem.AddEvents(self.grid)
            
            if self.state != self.COLLAPSED:
                self.ShowGrid(False)
                
            if rowsChanged and not noChange:
                self.parent_listitem.OnChange()
                    
        self.parent.Thaw()
        
        return didChange
        
    def OnEventSize(self, width):
        if width < BUNDLE_GRID_COLLAPSE:
            return self.RebuildGrid(1)
        
        return self.RebuildGrid(BUNDLE_NUM_COLS)
    
    def ShowGrid(self, show):
        if show:
            self.grid.ShowItems(True)
        else:
            self.grid.ShowItems(False)
    
    def UpdateList(self, hits):
        self.hits = hits
        
        if self.bundlelist:
            self.bundlelist.SetData(hits)
            if self.state == BundlePanel.FULL:
                self.bundlelist.OnLoadAll()
    
    def ShowList(self, show):
        if self.bundlelist is None and show:
            max_list = BUNDLE_NUM_ROWS * BUNDLE_NUM_COLS
            if len(self.hits) != BUNDLE_NUM_ROWS * BUNDLE_NUM_COLS:
                max_list -= 1
            
            self.bundlelist = BundleListView(parent = self.parent, list_item_max = max_list)
            self.vsizer.Add(self.bundlelist, 0, wx.EXPAND|wx.BOTTOM, self.indent - 7) #a 7px spacer is already present 
            
            # SetData does wx.Yield, which could cause a collapse event to be processed within the setdata
            # method. Thus we have to do this after the add to the sizer
            self.bundlelist.SetData(self.hits)
        
        elif self.bundlelist is not None and not show:
            self.vsizer.Detach(self.bundlelist)
            self.bundlelist.Show(False)
            self.bundlelist.Destroy()
            self.bundlelist = None
    
    def CollapseExpandedItem(self):
        if self.state != BundlePanel.COLLAPSED:
            self.bundlelist.list.OnCollapse()
    
    def RefreshDataBundleList(self, key, data):
        if self.bundlelist is not None:
            self.bundlelist.RefreshData(key, data)
    
    def SetDescription(self, description):
        self.header.SetToolTipString(description)
        self.info_icon.SetToolTipString(description)
    
    def SetGeneralDescription(self, general_description):
        if general_description:
            general_description = unicode(general_description)
        else:
            general_description = u'Similar'
        self.header.SetLabel(u'%s items (%s):' % (general_description, self.nrhits))
    
    def SetHits(self, hits, noChange=False):
        self.nrhits = len(hits)
        
        gridChanged = self.UpdateGrid(hits, noChange)
        self.UpdateList(hits)
        
        self.Layout()
        return gridChanged
    
    def ChangeState(self, new_state, doLayout=True):
        if self.state != new_state:
            old_state = self.state
            self.state = new_state
            
            if new_state == BundlePanel.COLLAPSED:
                self.ShowList(False)
                self.ShowGrid(True)
            else:
                if new_state == BundlePanel.PARTIAL or new_state == BundlePanel.FULL:
                    self.ShowGrid(False)
                    if old_state == BundlePanel.COLLAPSED:
                        self.ShowList(True)

                    if new_state == BundlePanel.FULL and self.bundlelist:
                        self.bundlelist.OnLoadAll()

            if DEBUG:
                statestr = lambda st: ['COLLAPSED', 'PARTIAL', 'FULL'][st]
                print >>sys.stderr, '*** BundlePanel.ChangeState: %s --> %s' % (statestr(old_state), statestr(new_state))
    
    def ExpandHit(self, hit):
        id = hit.infohash
        
        self.bundlelist.ExpandItem(id)
        self.parent_listitem.ShowSelected()
    
    def OnBundleLinkClick(self, event):
        #do expand
        self.ExpandAndHideParent()
        
        staticText = event.GetEventObject()
        action = getattr(staticText, 'action', None)
        if action is not None:
            # Reason for non-persistence (for now) is least-surprise.
            # If the user collapses a bundled listitem, the previously 
            # clicked item is still at the same location.
            if action in self.hits:
                self.hits.remove(action)
                self.hits.insert(0, action)
        
            self.ChangeState(BundlePanel.PARTIAL)
            self.ExpandHit(action)
        
        def db_callback():
            self.uelog.addEvent(message="Bundler GUI: BundleLink click; %s; %s;" %
                                (self.nrhits, self.parent_listitem.general_description), type = 3)
        self.guiutility.frame.guiserver.add_task(db_callback)
    
    def OnMoreClick(self, event):
        #do expand
        self.ExpandAndHideParent()
        self.ChangeState(BundlePanel.FULL)
        
        def db_callback():
            self.uelog.addEvent(message="Bundler GUI: More click; %s; %s;" %
                                (self.nrhits, self.parent_listitem.general_description), type = 3)
        self.guiutility.frame.guiserver.add_task(db_callback)
    
    def ExpandAndHideParent(self):
        self.parent.Freeze()
        
        if not self.parent_listitem.expanded:
            # Make sure the listitem is marked as expanded
            self.parent_listitem.OnClick()
        
        # but hide the panel
        self.parent_listitem.ShowExpandedPanel(False)
        self.parent.Thaw()
    
    #Called from GUI to get expanded torrentdetails panel
    def GetExpandedPanel(self):
        if self.bundlelist:
            item = self.bundlelist.GetExpandedItem()
            if item:
                return item.GetExpandedPanel()
            
    def SetBackgroundColour(self, colour):
        self.parent.Freeze()
        if getattr(self, 'grid', False):
            for sizeritem in self.grid.GetChildren():
                child = sizeritem.GetWindow() or sizeritem.GetSizer()
                if child and getattr(child, 'SetBackgroundColour', False): 
                    child.SetBackgroundColour(colour)
                        
        self.parent.Thaw()
Esempio n. 2
0
class TitleHeader(ListHeader):
    def __init__(self,
                 parent,
                 parent_list,
                 columns,
                 font_increment=2,
                 fontweight=wx.FONTWEIGHT_BOLD,
                 radius=LIST_RADIUS,
                 spacers=[0, 0]):
        self.font_increment = font_increment
        self.fontweight = fontweight

        ListHeader.__init__(self,
                            parent,
                            parent_list,
                            columns,
                            radius=radius,
                            spacers=spacers)

    @warnWxThread
    def AddComponents(self, columns, spacers):
        vSizer = wx.BoxSizer(wx.VERTICAL)
        vSizer.AddSpacer((-1, 3))

        self.title = StaticText(self)
        _set_font(self.title, self.font_increment, self.fontweight)

        titlePanel = self.GetTitlePanel(self)
        subtitlePanel = self.GetSubTitlePanel(self)
        righttitlePanel = self.GetRightTitlePanel(self)
        belowPanel = self.GetBelowPanel(self)

        if titlePanel:
            subSizer = wx.BoxSizer(wx.HORIZONTAL)
            subSizer.Add(self.title)
            subSizer.Add(titlePanel, 0, wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 3)
            titlePanel = subSizer
        else:
            titlePanel = self.title

        if subtitlePanel:
            subSizer = wx.BoxSizer(wx.VERTICAL)
            subSizer.Add(titlePanel, 0, wx.BOTTOM, 3)
            subSizer.Add(subtitlePanel)
            subtitlePanel = subSizer
        else:
            subtitlePanel = titlePanel

        subSizer = wx.BoxSizer(wx.HORIZONTAL)
        subSizer.Add(subtitlePanel)
        if righttitlePanel:
            subSizer.Add(righttitlePanel, 1, wx.LEFT, 3)
        righttitlePanel = subSizer

        vSizer.Add(righttitlePanel, 0, wx.EXPAND | wx.LEFT | wx.RIGHT,
                   self.radius + spacers[0])
        if belowPanel:
            vSizer.Add(belowPanel, 1, wx.EXPAND | wx.TOP, 3)

        vSizer.AddSpacer((-1, 3))

        if len(columns) > 0:
            hSizer = wx.BoxSizer(wx.HORIZONTAL)
            self.AddColumns(hSizer, self, columns)
            vSizer.Add(hSizer, 0, wx.EXPAND | wx.LEFT | wx.RIGHT,
                       self.radius + spacers[0])
        self.SetSizer(vSizer)

    def GetTitlePanel(self, parent):
        pass

    def GetSubTitlePanel(self, parent):
        pass

    def GetRightTitlePanel(self, parent):
        pass

    def GetBelowPanel(self, parent):
        pass

    @warnWxThread
    def SetTitle(self, title):
        if title != self.title.GetLabel():
            self.Freeze()

            self.title.SetLabel(title)
            self.title.Refresh()
            self.Layout()
            self.Thaw()

    @warnWxThread
    def SetToolTip(self, tooltip):
        self.title.SetToolTipString(tooltip)