Exemple #1
0
    def SetSizeAutomatically(self):
        width = self.minWidth
        for each in self.children:
            if hasattr(each, 'GetEntryWidth'):
                width = max(width, each.GetEntryWidth())

        self.width = width
        ContainerAutoSize.SetSizeAutomatically(self)
Exemple #2
0
class MaterialGroup(Container):
    default_name = 'MaterialGroup'
    default_height = 60
    default_alignMode = uiconst.TOPLEFT
    default_align = uiconst.TOPLEFT

    def ApplyAttributes(self, attributes):
        Container.ApplyAttributes(self, attributes)
        self.jobData = attributes.jobData
        self.materialsData = attributes.materialsData
        self.numRows = attributes.numRows
        self.industryGroupID = attributes.industryGroupID
        self.perRow = self.GetNumPerRow()
        self.materials = []
        self.lines = []
        self.connectorCircle = None
        self.pCircleIntersect = None
        self.materialsCont = ContainerAutoSize(name='materialsCont',
                                               parent=self,
                                               align=uiconst.TOPLEFT,
                                               alignMode=uiconst.TOPLEFT)
        self.icon = GroupIconSprite(name='icon',
                                    parent=self,
                                    align=uiconst.CENTERLEFT,
                                    pos=(7, 0, 22, 22),
                                    industryGroupID=self.industryGroupID)
        self.ConstructMaterials()
        self.ConstructLines()

    def AnimEntry(self, timeOffset, animate=True):
        for i, material in enumerate(self.materials):
            if animate:
                uicore.animations.FadeTo(material,
                                         0.0,
                                         1.0,
                                         duration=0.3,
                                         timeOffset=timeOffset + i * 0.02)
            else:
                material.opacity = 1.0

        for i, line in enumerate(self.lines):
            line.AnimEntry(timeOffset, i, animate)

        if animate:
            uicore.animations.FadeTo(self.connectorCircle,
                                     0.0,
                                     1.0,
                                     duration=0.3,
                                     timeOffset=timeOffset)
        else:
            self.connectorCircle.opacity = 1.0

    def ConstructLines(self):
        for line in self.lines:
            line.Close()

        self.lines = []
        usedRows = int(ceil(len(self.materialsData) / float(self.perRow)))
        pointsStart = self.GetLineStartingPoints()
        pConnect = self.GetLineConnectionPoint(pointsStart)
        y = self.top + self.materialsCont.top + self.materialsCont.height / 2
        x = self.GetLineCirceOffset(y)
        width, height = self.parent.GetCurrentAbsoluteSize()
        self.pCircleIntersect = (width / 2 - x, pConnect[1])
        pEnd = geo2.Vec2Subtract(self.pCircleIntersect,
                                 (1 * RADIUS_CONNECTOR_SMALL, 0))
        if len(pointsStart) == 1:
            self.DrawLine((pointsStart[0], pEnd))
        else:
            for point in pointsStart:
                if point[1] == pEnd[1]:
                    self.DrawLine((point, geo2.Vec2Subtract(pConnect, (3, 0))))
                else:
                    self.DrawLine((point, (pConnect[0], point[1]), pConnect))

            self.DrawLine((geo2.Vec2Add(pConnect, (3, 0)), pEnd))
        if not self.connectorCircle:
            self.connectorCircle = MaterialGroupDashedCircle(
                parent=self.materialsCont,
                numSegments=len(self.materialsData),
                radius=RADIUS_CONNECTOR_SMALL,
                materialsByGroupID=((self.industryGroupID,
                                     self.materialsData), ),
                jobData=self.jobData)
        self.connectorCircle.left = pEnd[0]
        self.connectorCircle.top = pEnd[1] - RADIUS_CONNECTOR_SMALL
        self.UpdateState(animate=False)

    def DrawLine(self, points):
        line = IndustryLineTrace(parent=self.materialsCont)
        line.AddPoints(points)
        self.lines.append(line)

    def GetEndPoint(self):
        return self.pCircleIntersect

    def ConstructMaterials(self):
        for i, materialData in enumerate(self.materialsData):
            top = 66 * (i / self.perRow)
            cls = self.GetMaterialClass()
            material = cls(parent=self.materialsCont,
                           jobData=self.jobData,
                           materialData=materialData,
                           align=uiconst.TOPLEFT,
                           left=ICONCOLUMN_WIDTH + i % self.perRow * 48,
                           top=top)
            self.materials.append(material)

        self.materialsCont.SetSizeAutomatically()
        self.materialsCont.top = (self.height - self.materialsCont.height) / 2

    def GetMaterialClass(self):
        if self.IsOptional():
            return OptionalMaterial
        else:
            return Material

    def IsOptional(self):
        for materialData in self.materialsData:
            if not materialData.IsOptional():
                return False

        return True

    def IsOptionSelected(self):
        """
        Returns True unless all items are optional and none of them have an item selected
        """
        for materialData in self.materialsData:
            if materialData.IsOptionSelected():
                return True

        return False

    def GetLineStartingPoints(self):
        ret = defaultdict(int)
        for material in self.materials:
            x = material.left + Material.default_width
            y = material.top + Material.default_height / 2
            ret[y] = max(ret[y], x)

        ret = zip(ret.values(), ret.keys())
        return ret

    def OnRunsChanged(self):
        self.UpdateState()

    def UpdateState(self, animate=True):
        for line in self.lines:
            line.UpdateColor(self.IsReady(), self.IsOptional(),
                             self.IsOptionSelected(), animate)

        numReadySegments = sum(
            [materialData.valid for materialData in self.materialsData])
        self.connectorCircle.UpdateState(numReadySegments,
                                         self.IsReady(),
                                         self.IsOptionSelected(),
                                         animate=animate)

    def GetNumPerRow(self):
        numMaterials = len(self.materialsData)
        if self.numRows >= 2:
            if numMaterials > 3:
                return min(5, int(ceil(numMaterials / 2.0)))
        return numMaterials

    def GetLineConnectionPoint(self, points):
        """
        Returs coordinates of point where all lines coming from the groups connect
        """
        x = max((x for x, _ in points))
        x += OFFSET_CONNECTIONPOINT
        y = sum((y for _, y in points)) / len(points)
        return (x, y)

    def GetLineCirceOffset(self, y):
        """
        Returns x-part of intersection line with outer circle
        """
        r = RADIUS_CENTERCIRCLE_OUTER
        y = float(y)
        return r * cos(asin((r - y) / r))

    def IsReady(self):
        """
        Are all materials in group owned in full quantity
        """
        for materialData in self.materialsData:
            if not materialData.valid:
                return False

        return True
Exemple #3
0
class InfoBubble(Container):
    default_name = 'InfoBubble'
    default_align = uiconst.TOPLEFT
    default_state = uiconst.UI_NORMAL
    default_width = 350
    default_height = 200
    default_idx = 0
    default_topOffset = 0
    default_opacity = 0.0

    def __init__(self, **kw):
        Container.__init__(self, **kw)
        self.StartupInfoBubble()

    def ApplyAttributes(self, attributes):
        Container.ApplyAttributes(self, attributes)
        self.parentObj = attributes.parentObj
        self.topOffset = attributes.Get('topOffset', self.default_topOffset)
        self.__isClosing = False
        self.mainCont = Container(name='mainCont', parent=self, padding=10)
        self.bgFrame = Container(name='bgFrame',
                                 parent=self,
                                 state=uiconst.UI_DISABLED)
        self.frameTop = StretchSpriteHorizontal(
            parent=self.bgFrame,
            align=uiconst.TOTOP,
            texturePath=
            'res:/UI/Texture/classes/ShipTree/InfoBubble/frameUpper.png',
            height=36,
            opacity=0.0)
        self.frameBottom = StretchSpriteHorizontal(
            parent=self.bgFrame,
            align=uiconst.TOBOTTOM,
            texturePath=
            'res:/UI/Texture/classes/ShipTree/InfoBubble/frameLower.png',
            height=36,
            opacity=0.0)
        self.bgFill = Container(name='bgFill',
                                parent=self,
                                state=uiconst.UI_DISABLED,
                                opacity=0.0)
        StretchSpriteHorizontal(
            parent=self.bgFill,
            align=uiconst.TOTOP,
            texturePath=
            'res:/UI/Texture/classes/ShipTree/InfoBubble/backTopExtender.png',
            height=1)
        StretchSpriteHorizontal(
            parent=self.bgFill,
            align=uiconst.TOBOTTOM,
            texturePath=
            'res:/UI/Texture/classes/ShipTree/InfoBubble/backBottom.png',
            height=6)
        Sprite(parent=self.bgFill,
               align=uiconst.TOALL,
               texturePath=
               'res:/UI/Texture/classes/ShipTree/InfoBubble/backMiddle.png')
        self.topContainer = ContainerAutoSize(
            name='topContainer',
            parent=self.mainCont,
            align=uiconst.TOTOP,
            callback=self.OnMainContentSizeChanged)
        self.iconCont = Container(name='iconCont',
                                  parent=self.topContainer,
                                  align=uiconst.TOPLEFT,
                                  width=80,
                                  height=80,
                                  left=0,
                                  top=0)
        self.topRightCont = ContainerAutoSize(name='topRightCont',
                                              align=uiconst.TOPLEFT,
                                              left=90,
                                              width=self.width - 115,
                                              parent=self.topContainer)
        self.caption = EveCaptionMedium(parent=self.topRightCont,
                                        align=uiconst.TOTOP)
        self.attributeCont = ContainerAutoSize(name='attributeCont',
                                               parent=self.topRightCont,
                                               align=uiconst.TOTOP,
                                               padTop=5)
        self.mainContent = ContainerAutoSize(
            name='mainContent',
            parent=self.mainCont,
            align=uiconst.TOTOP,
            callback=self.OnMainContentSizeChanged,
            padTop=5)

    def StartupInfoBubble(self):
        self.mainContent.SetSizeAutomatically()
        self.OnMainContentSizeChanged()
        self.SetLeftAndTop()

    def SetLeftAndTop(self):
        left, top, width, height = self.parentObj.GetAbsolute()
        self.left = left - (self.width - width) / 2
        if top > self.height:
            self.top = top - self.height + self.topOffset
        else:
            self.top = top + height + 2

    def OnMainContentSizeChanged(self, *args):
        self.height = self.topContainer.height + self.mainContent.height + 25
        self.SetLeftAndTop()

    def AnimShow(self):
        uicore.animations.FadeIn(self.bgFill, duration=0.3)
        uicore.animations.FadeIn(self.frameTop, duration=0.3)
        uicore.animations.FadeIn(self.frameBottom,
                                 duration=0.3,
                                 timeOffset=0.15)
        uicore.animations.FadeIn(self, duration=0.15)
        for i, attrIcon in enumerate(self.attributeCont.children):
            timeOffset = i * 0.05
            uicore.animations.FadeTo(attrIcon,
                                     0.0,
                                     1.0,
                                     duration=0.3,
                                     timeOffset=timeOffset)
            uicore.animations.MorphScalar(attrIcon,
                                          'left',
                                          attrIcon.left + 5,
                                          attrIcon.left,
                                          duration=0.1,
                                          timeOffset=timeOffset)

    def Close(self):
        if self.__isClosing:
            return
        self.__isClosing = True
        uicore.animations.FadeOut(self, duration=0.15, callback=self._Close)

    def _Close(self):
        Container.Close(self)

    def ConstructElements(self, attributeIDs):
        numInRow = 7
        size = InfoBubbleAttributeIcon.default_width
        for i, (_, attributeID) in enumerate(attributeIDs.iteritems()):
            left = i % numInRow * (size + 1)
            top = i / numInRow * (size + 1)
            InfoBubbleAttributeIcon(parent=self.attributeCont,
                                    align=uiconst.TOPLEFT,
                                    attributeID=attributeID,
                                    left=left,
                                    top=top)
class BasePinContainer(Window):
    __guid__ = 'planet.ui.BasePinContainer'
    __notifyevents__ = ['OnRefreshPins', 'ProcessColonyDataSet']
    default_height = 185
    default_width = 300
    default_minSize = (300, 185)
    default_maxSize = (450, None)
    default_state = uiconst.UI_NORMAL
    default_align = uiconst.TOPLEFT
    default_name = 'BasePinContainer'
    default_opacity = 0.0
    default_topParentHeight = 0
    default_isCollapseable = False
    default_isPinable = False
    default_isStackable = False
    default_isMinimizable = False
    default_windowID = 'PlanetPinWindow'
    INFO_CONT_HEIGHT = 70

    def GetBaseHeight(self):
        return self.main.height + 26

    def ApplyAttributes(self, attributes):
        Window.ApplyAttributes(self, attributes)
        self.main = ContainerAutoSize(parent=self.sr.main,
                                      name='main',
                                      padding=3,
                                      state=uiconst.UI_PICKCHILDREN,
                                      align=uiconst.TOTOP,
                                      alignMode=uiconst.TOTOP)
        self.planetUISvc = sm.GetService('planetUI')
        self.planetSvc = sm.GetService('planetSvc')
        self.pin = attributes.Get('pin', None)
        self.uiEffects = uicls.UIEffects()
        self.showingActionContainer = False
        self.currentRoute = None
        self.showNext = None
        self.lastCalled = None
        self.commodityToRoute = None
        self.buttonTextValue = ''
        infoCont = Container(parent=self.main,
                             name='infoCont',
                             padding=5,
                             align=uiconst.TOTOP,
                             height=self.INFO_CONT_HEIGHT)
        self.infoContLeft = Container(name='leftCol',
                                      parent=infoCont,
                                      align=uiconst.TOLEFT_PROP,
                                      width=0.5)
        self.infoContRight = Container(name='rightCol',
                                       parent=infoCont,
                                       align=uiconst.TOLEFT_PROP,
                                       width=0.5)
        self._GetInfoCont()
        self._UpdateInfoCont()
        self.buttonCont = GridContainer(parent=self.main,
                                        name='buttonCont',
                                        height=40,
                                        align=uiconst.TOTOP,
                                        padding=(-1, 0, -1, 0))
        BumpedUnderlay(bgParent=self.buttonCont)
        self.buttonCont.lines = 1
        self.buttonCont.columns = 6
        self.buttonTextCont = self._DrawAlignTopCont(22, 'buttonTextCont')
        self.buttonText = EveLabelSmall(parent=self.buttonTextCont,
                                        align=uiconst.CENTER,
                                        color=(1.0, 1.0, 1.0, 1.0),
                                        state=uiconst.UI_NORMAL)
        self.buttonTextCont.height = max(22, self.buttonText.textheight)
        self.actionCont = Container(parent=self.sr.main,
                                    name='actionCont',
                                    padding=(6, 0, 6, 6),
                                    clipChildren=True)
        self.SetCaption(self._GetPinName())
        self.main.SetSizeAutomatically()
        self.height = self.GetBaseHeight()
        self.LoadActionButtons(self._GetActionButtons())
        uicore.animations.FadeTo(self, 0.0, 1.0, duration=0.3)
        self.updateInfoContTimer = base.AutoTimer(100, self._UpdateInfoCont)
        sm.GetService('audio').SendUIEvent(
            'wise:/msg_pi_pininteraction_open_play')
        self.ResizeActionCont(None)

    def ShowDefaultPanel(self):
        if hasattr(self, 'defaultPanel'):
            self.ShowPanel(self.defaultPanel, self.defaultPanelID)

    def _GetPinName(self):
        return planetCommon.GetGenericPinName(self.pin.typeID, self.pin.id)

    def _GetInfoCont(self):
        pass

    def _GetActionButtons(self):
        btns = [
            util.KeyVal(id=planetCommonUI.PANEL_STATS,
                        panelCallback=self.PanelShowStats),
            util.KeyVal(id=planetCommonUI.PANEL_LINKS,
                        panelCallback=self.PanelShowLinks),
            util.KeyVal(id=planetCommonUI.PANEL_ROUTES,
                        panelCallback=self.PanelShowRoutes),
            util.KeyVal(id=planetCommonUI.PANEL_DECOMMISSION,
                        panelCallback=self.PanelDecommissionPin)
        ]
        return btns

    def ShowPanel(self, panelCallback, panelID, *args):
        _, label = planetCommonUI.PANELDATA[panelID]
        name = localization.GetByLabel(label)
        self.buttonText.text = name
        self.buttonTextValue = name
        if self.showingActionContainer:
            self.showNext = panelCallback
            return
        self.showNext = None
        self.showingActionContainer = True
        self.actionCont.Flush()
        if self.lastCalled != name:
            if args:
                cont = panelCallback(*args)
            else:
                cont = panelCallback()
            if cont:
                cont.state = uiconst.UI_HIDDEN
                self.lastCalled = name
                cont.opacity = 0.0
                self.ResizeActionCont(panelID)
                cont.state = uiconst.UI_PICKCHILDREN
                self.uiEffects.MorphUI(cont,
                                       'opacity',
                                       1.0,
                                       time=250.0,
                                       float=1,
                                       maxSteps=1000)
                uicore.registry.SetFocus(cont)
        else:
            self.HideCurrentPanel()
        self.showingActionContainer = False
        if self.showNext:
            self.ShowPanel(self.showNext, panelID)

    def HideCurrentPanel(self):
        self.actionCont.Flush()
        self.ResizeActionCont()
        self.lastCalled = None
        self.buttonTextValue = ''

    def _DrawAlignTopCont(self,
                          height,
                          name,
                          padding=(0, 0, 0, 0),
                          state=uiconst.UI_PICKCHILDREN):
        return Container(parent=self.main,
                         name=name,
                         pos=(0, 0, 0, height),
                         padding=padding,
                         state=state,
                         align=uiconst.TOTOP)

    def OnIconButtonMouseEnter(self, iconButton, *args):
        ButtonIcon.OnMouseEnter(iconButton, *args)
        self.buttonText.text = iconButton.name

    def OnIconButtonMouseExit(self, iconButton, *args):
        ButtonIcon.OnMouseExit(iconButton, *args)
        self.buttonText.text = self.buttonTextValue

    def LoadActionButtons(self, buttons):
        iconSize = 32
        w = self.width - 6
        maxIcons = 7.0
        n = float(len(buttons))
        pad = 5 + 1 * iconSize * (1.0 - n / maxIcons)
        w -= 2 * pad
        space = (w - n * iconSize) / n
        self.buttonCont.columns = len(buttons)
        for i, b in enumerate(buttons):
            iconPath, cerberusPath = planetCommonUI.PANELDATA[b.id]
            panelName = localization.GetByLabel(cerberusPath)
            if i == 0:
                self.defaultPanel = b.panelCallback
                self.defaultPanelID = b.id
            cont = Container(name=panelName, parent=self.buttonCont)
            ib = ButtonIcon(texturePath=iconPath,
                            parent=cont,
                            align=uiconst.CENTER,
                            name=panelName,
                            hint=b.Get('hint', ''),
                            width=iconSize,
                            height=iconSize,
                            iconSize=iconSize,
                            func=self._OnIconButtonClicked,
                            args=(b.panelCallback, b.id))
            ib.OnMouseEnter = (self.OnIconButtonMouseEnter, ib)
            ib.OnMouseExit = (self.OnIconButtonMouseExit, ib)

    def _OnIconButtonClicked(self, panelCallback, panelID, *args):
        self.ShowPanel(panelCallback, panelID)

    def CloseByUser(self, *args):
        self.planetUISvc.CloseCurrentlyOpenContainer()

    def PanelShowLinks(self):
        cont = Container(parent=self.actionCont, state=uiconst.UI_HIDDEN)
        self.linkScroll = scroll = Scroll(parent=cont,
                                          name='linksScroll',
                                          align=uiconst.TOALL)
        self.linkScroll.sr.id = 'planetBasePinLinkScroll'
        self.LoadLinkScroll()
        btns = [[
            localization.GetByLabel('UI/PI/Common/CreateNew'),
            self._CreateNewLink, None
        ],
                [
                    localization.GetByLabel('UI/PI/Common/DeleteLink'),
                    self._DeleteLink, None
                ]]
        ButtonGroup(btns=btns, idx=0, parent=cont)
        return cont

    def LoadLinkScroll(self):
        scrolllist = []
        planet = sm.GetService('planetUI').GetCurrentPlanet()
        colony = planet.GetColony(session.charid)
        links = colony.colonyData.GetLinksForPin(self.pin.id)
        for linkedPinID in links:
            link = colony.GetLink(self.pin.id, linkedPinID)
            linkedPin = colony.GetPin(linkedPinID)
            distance = link.GetDistance()
            bandwidthUsed = link.GetBandwidthUsage()
            percentageUsed = 100 * (bandwidthUsed / link.GetTotalBandwidth())
            data = util.KeyVal()
            data.label = '%s<t>%s<t>%s' % (planetCommon.GetGenericPinName(
                linkedPin.typeID, linkedPin.id), util.FmtDist(distance),
                                           localization.GetByLabel(
                                               'UI/Common/Percentage',
                                               percentage=percentageUsed))
            data.hint = ''
            data.OnMouseEnter = self.OnLinkEntryHover
            data.OnMouseExit = self.OnLinkEntryExit
            data.OnDblClick = self.OnLinkListentryDblClicked
            data.id = (link.endpoint1.id, link.endpoint2.id)
            sortBy = linkedPinID
            scrolllist.append((sortBy, listentry.Get('Generic', data=data)))

        scrolllist = uiutil.SortListOfTuples(scrolllist)
        self.linkScroll.Load(
            contentList=scrolllist,
            noContentHint=localization.GetByLabel(
                'UI/PI/Common/NoLinksPresent'),
            headers=[
                localization.GetByLabel('UI/PI/Common/Destination'),
                localization.GetByLabel('UI/Common/Distance'),
                localization.GetByLabel('UI/PI/Common/CapacityUsed')
            ])

    def OnLinkListentryDblClicked(self, entry):
        myPinManager = sm.GetService('planetUI').myPinManager
        link = myPinManager.linksByPinIDs[entry.sr.node.id]
        for node in self.linkScroll.GetNodes():
            myPinManager.RemoveHighlightLink(node.id)

        sm.GetService('planetUI').OpenContainer(link)

    def OnLinkEntryHover(self, entry):
        node = entry.sr.node
        self.planetUISvc.myPinManager.HighlightLink(self.pin.id, node.id)

    def OnLinkEntryExit(self, entry):
        node = entry.sr.node
        self.planetUISvc.myPinManager.RemoveHighlightLink(node.id)

    def _CreateNewLink(self, *args):
        self.planetUISvc.myPinManager.SetLinkParent(self.pin.id)
        self.CloseByUser()

    def _DeleteLink(self, *args):
        selected = self.linkScroll.GetSelected()
        if len(selected) > 0:
            self.planetUISvc.myPinManager.RemoveLink(selected[0].id)
            self.LoadLinkScroll()

    def _DrawEditBox(self, parent, text):
        textHeight = uix.GetTextHeight(text,
                                       width=self.width - 30,
                                       fontsize=fontConst.EVE_MEDIUM_FONTSIZE)
        edit = Edit(setvalue=text,
                    parent=parent,
                    align=uiconst.TOTOP,
                    height=textHeight + 18,
                    top=-6,
                    hideBackground=1,
                    readonly=True)
        edit.scrollEnabled = False
        return edit

    def ResizeActionCont(self, panelID=None):
        if panelID:
            minHeight, maxHeight = planetCommonUI.PANEL_MIN_MAX_HEIGHT[panelID]
            if maxHeight:
                height = (minHeight + maxHeight) / 2
            else:
                height = minHeight
        else:
            height = minHeight = maxHeight = 0
        baseHeight = self.GetBaseHeight()
        uicore.animations.MorphScalar(self,
                                      'height',
                                      self.height,
                                      height + baseHeight,
                                      duration=0.3)
        self.SetMinSize((self.default_minSize[0], baseHeight + minHeight))
        if minHeight == maxHeight:
            self.SetFixedHeight(baseHeight + minHeight)
        elif maxHeight:
            self.SetFixedHeight(None)
            self.SetMaxSize((self.default_maxSize[0], baseHeight + maxHeight))
        else:
            self.SetFixedHeight(None)
            self.SetMaxSize((self.default_maxSize[0], None))

    def _UpdateInfoCont(self):
        pass

    def PanelShowStorage(self):
        cont = Container(parent=self.actionCont, state=uiconst.UI_HIDDEN)
        self.storageContentScroll = Scroll(parent=cont,
                                           name='storageContentsScroll',
                                           id='planetStorageContentsScroll')
        self.storageContentScroll.sr.fixedColumns = {'': 28}
        self.LoadStorageContentScroll()
        btns = [[
            localization.GetByLabel('UI/PI/Common/CreateRoute'),
            self._CreateRoute, 'storageContentScroll'
        ],
                [
                    localization.GetByLabel('UI/PI/Common/ExpeditedTransfer'),
                    self._CreateTransfer, None
                ]]
        self.createRouteButton = ButtonGroup(btns=btns, parent=cont, idx=0)
        return cont

    def LoadStorageContentScroll(self):
        scrolllist = []
        for typeID, amount in self.pin.contents.iteritems():
            data = util.KeyVal()
            volume = evetypes.GetVolume(typeID) * amount
            data.label = '<t>%s<t>%s<t>%s' % (evetypes.GetName(typeID), amount,
                                              volume)
            data.amount = amount
            data.typeID = typeID
            data.itemID = None
            data.getIcon = (True, )
            data.OnDblClick = self.OnStorageEntryDblClicked
            sortBy = amount
            scrolllist.append((sortBy, listentry.Get('Item', data=data)))

        scrolllist = uiutil.SortListOfTuples(scrolllist)
        self.storageContentScroll.Load(
            contentList=scrolllist,
            noContentHint=localization.GetByLabel(
                'UI/PI/Common/NoContentsPresent'),
            headers=[
                '',
                localization.GetByLabel('UI/PI/Common/Type'),
                localization.GetByLabel('UI/Common/Amount'),
                localization.GetByLabel('UI/Common/Volume')
            ])

    def OnStorageEntryDblClicked(self, entry):
        self._CreateRoute('storageContentScroll')

    def PanelShowProducts(self):
        cont = Container(parent=self.actionCont, state=uiconst.UI_HIDDEN)
        self.productScroll = Scroll(parent=cont, name='productsScroll')
        self.productScroll.sr.id = 'planetBasePinProductScroll'
        self.LoadProductScroll()
        btns = [[
            localization.GetByLabel('UI/PI/Common/CreateRoute'),
            self._CreateRoute, 'productScroll'
        ]]
        self.createRouteButton = ButtonGroup(btns=btns, parent=cont, idx=0)
        btns = [[
            localization.GetByLabel('UI/PI/Common/DeleteRoute'),
            self._DeleteRoute, ()
        ]]
        self.deleteRouteButton = ButtonGroup(btns=btns, parent=cont, idx=0)
        self.createRouteButton.state = uiconst.UI_HIDDEN
        self.deleteRouteButton.state = uiconst.UI_HIDDEN
        return cont

    def LoadProductScroll(self):
        scrolllist = []
        colony = self.planetUISvc.planet.GetColony(session.charid)
        if colony is None or colony.colonyData is None:
            raise RuntimeError(
                'Cannot load product scroll for pin on a planet that has no colony'
            )
        sourcedRoutes = colony.colonyData.GetSourceRoutesForPin(self.pin.id)
        routesByTypeID = {}
        for route in sourcedRoutes:
            typeID = route.GetType()
            if typeID not in routesByTypeID:
                routesByTypeID[typeID] = []
            routesByTypeID[typeID].append(route)

        for typeID, amount in self.pin.GetProductMaxOutput().iteritems():
            typeName = evetypes.GetName(typeID)
            for route in routesByTypeID.get(typeID, []):
                qty = route.GetQuantity()
                amount -= qty
                data = util.KeyVal(
                    label='%s<t>%s<t>%s' %
                    (qty, typeName,
                     localization.GetByLabel('UI/PI/Common/Routed')),
                    typeID=typeID,
                    itemID=None,
                    getIcon=True,
                    routeID=route.routeID,
                    OnMouseEnter=self.OnRouteEntryHover,
                    OnMouseExit=self.OnRouteEntryExit,
                    OnClick=self.OnProductEntryClicked,
                    OnDblClick=self.OnProductEntryDblClicked)
                scrolllist.append(listentry.Get('Item', data=data))

            if amount > 0:
                data = util.KeyVal()
                data.label = '%s<t>%s<t>%s' % (amount, evetypes.GetName(
                    typeID), localization.GetByLabel('UI/PI/Common/NotRouted'))
                data.typeID = typeID
                data.amount = amount
                data.itemID = None
                data.getIcon = True
                data.OnClick = self.OnProductEntryClicked
                data.OnDblClick = self.OnProductEntryDblClicked
                scrolllist.append(listentry.Get('Item', data=data))

        self.productScroll.Load(
            contentList=scrolllist,
            noContentHint=localization.GetByLabel(
                'UI/PI/Common/NoProductsPresent'),
            headers=[
                localization.GetByLabel('UI/Common/Amount'),
                localization.GetByLabel('UI/PI/Common/Type'), ''
            ])

    def OnProductEntryClicked(self, entry):
        node = entry.sr.node
        if node.Get('routeID', None) is None:
            self.createRouteButton.state = uiconst.UI_NORMAL
            self.deleteRouteButton.state = uiconst.UI_HIDDEN
        else:
            self.createRouteButton.state = uiconst.UI_HIDDEN
            self.deleteRouteButton.state = uiconst.UI_NORMAL

    def OnProductEntryDblClicked(self, entry):
        node = entry.sr.node
        if node.Get('routeID', None) is None:
            self._CreateRoute('productScroll')

    def _CreateRoute(self, scroll):
        selected = getattr(self, scroll).GetSelected()
        if len(selected) > 0:
            entry = selected[0]
            self.planetUISvc.myPinManager.EnterRouteMode(
                self.pin.id, entry.typeID)
            self.ShowPanel(self.PanelCreateRoute,
                           planetCommonUI.PANEL_CREATEROUTE, entry.typeID,
                           entry.amount)

    def SubmitRoute(self):
        if not getattr(self, 'routeAmountEdit', None):
            return
        sm.GetService('planetUI').myPinManager.CreateRoute(
            self.routeAmountEdit.GetValue())
        self.HideCurrentPanel()
        self.commodityToRoute = None

    def _DeleteRoute(self):
        selected = self.productScroll.GetSelected()
        if len(selected) > 0:
            entry = selected[0]
            if entry.routeID:
                self.planetUISvc.myPinManager.RemoveRoute(entry.routeID)
                self.LoadProductScroll()
                self.createRouteButton.state = uiconst.UI_HIDDEN
                self.deleteRouteButton.state = uiconst.UI_HIDDEN

    def _DeleteRouteFromEntry(self):
        if not hasattr(self, 'routeScroll'):
            return
        selected = self.routeScroll.GetSelected()
        if len(selected) > 0:
            entry = selected[0]
            if entry.routeID:
                self.planetUISvc.myPinManager.RemoveRoute(entry.routeID)
                self.LoadRouteScroll()
                self.uiEffects.MorphUI(self.routeInfo,
                                       'opacity',
                                       0.0,
                                       time=125.0,
                                       float=1,
                                       newthread=0,
                                       maxSteps=1000)

    def _CreateTransfer(self, *args):
        if sm.GetService('planetUI').GetCurrentPlanet().IsInEditMode():
            raise UserError('CannotTransferInEditMode')
        self.planetUISvc.myPinManager.EnterRouteMode(self.pin.id,
                                                     None,
                                                     oneoff=True)
        self.ShowPanel(self.PanelSelectTransferDest,
                       planetCommonUI.PANEL_TRANSFER)

    def PanelDecommissionPin(self):
        typeName = evetypes.GetName(self.pin.typeID)
        if evetypes.GetGroupID(self.pin.typeID) == const.groupCommandPins:
            text = localization.GetByLabel(
                'UI/PI/Common/DecommissionCommandPin', typeName=typeName)
        else:
            text = localization.GetByLabel('UI/PI/Common/DecommissionLink',
                                           typeName=typeName)
        cont = Container(parent=self.actionCont, state=uiconst.UI_HIDDEN)
        Label(parent=cont, text=text, align=uiconst.TOTOP)
        btns = [[
            localization.GetByLabel('UI/PI/Common/Proceed'),
            self._DecommissionSelf, None
        ]]
        ButtonGroup(btns=btns, idx=0, parent=cont)
        return cont

    def _DecommissionSelf(self, *args):
        sm.GetService('audio').SendUIEvent(
            'wise:/msg_pi_build_decommission_play')
        self.planetUISvc.myPinManager.RemovePin(self.pin.id)
        self.CloseByUser()

    def OnRouteEntryHover(self, entry):
        self.planetUISvc.myPinManager.ShowRoute(entry.sr.node.routeID)

    def OnRouteEntryExit(self, entry):
        self.planetUISvc.myPinManager.StopShowingRoute(entry.sr.node.routeID)

    def OnRefreshPins(self, pinIDs):
        if hasattr(
                self,
                'lastCalled') and self.lastCalled == localization.GetByLabel(
                    'UI/PI/Common/Storage'):
            self.LoadStorageContentScroll()

    def ProcessColonyDataSet(self, planetID):
        if self.planetUISvc.planetID != planetID:
            return
        self.pin = sm.GetService('planetSvc').GetPlanet(planetID).GetPin(
            self.pin.id)

    def PanelShowStats(self, *args):
        cont = Container(parent=self.actionCont,
                         align=uiconst.TOALL,
                         state=uiconst.UI_HIDDEN)
        self.statsScroll = scroll = Scroll(parent=cont, name='StatsScroll')
        scrolllist = self.GetStatsEntries()
        scroll.Load(contentList=scrolllist,
                    headers=[
                        localization.GetByLabel('UI/PI/Common/Attribute'),
                        localization.GetByLabel('UI/Common/Value')
                    ])
        return cont

    def GetStatsEntries(self):
        scrolllist = []
        if self.pin.GetCpuUsage() > 0:
            data = util.KeyVal(
                label='%s<t>%s' %
                (localization.GetByLabel('UI/PI/Common/CpuUsage'),
                 localization.GetByLabel('UI/PI/Common/TeraFlopsAmount',
                                         amount=self.pin.GetCpuUsage())))
            scrolllist.append(listentry.Get('Generic', data=data))
        if self.pin.GetCpuOutput() > 0:
            data = util.KeyVal(
                label='%s<t>%s' %
                (localization.GetByLabel('UI/PI/Common/CpuOutput'),
                 localization.GetByLabel('UI/PI/Common/TeraFlopsAmount',
                                         amount=self.pin.GetCpuOutput())))
            scrolllist.append(listentry.Get('Generic', data=data))
        if self.pin.GetPowerUsage() > 0:
            data = util.KeyVal(
                label='%s<t>%s' %
                (localization.GetByLabel('UI/PI/Common/PowerUsage'),
                 localization.GetByLabel('UI/PI/Common/MegaWattsAmount',
                                         amount=self.pin.GetPowerUsage())))
            scrolllist.append(listentry.Get('Generic', data=data))
        if self.pin.GetPowerOutput() > 0:
            data = util.KeyVal(
                label='%s<t>%s' %
                (localization.GetByLabel('UI/PI/Common/PowerOutput'),
                 localization.GetByLabel('UI/PI/Common/MegaWattsAmount',
                                         amount=self.pin.GetPowerOutput())))
            scrolllist.append(listentry.Get('Generic', data=data))
        return scrolllist

    def OnPlanetRouteWaypointAdded(self, currentRoute):
        self.currentRoute = currentRoute
        self.UpdatePanelCreateRoute()

    def PanelCreateRoute(self, typeID, amount):
        cont = Container(parent=self.actionCont,
                         pos=(0, 0, 0, 130),
                         align=uiconst.TOTOP,
                         state=uiconst.UI_HIDDEN)
        cont._OnClose = self.OnPanelCreateRouteClosed
        w = self.width - 5
        self.sourceMaxAmount = amount
        self.routeMaxAmount = amount
        self.commodityToRoute = typeID
        self.commoditySourceMaxAmount = amount
        self.currRouteCycleTime = self.pin.GetCycleTime()
        resourceTxt = localization.GetByLabel(
            'UI/PI/Common/ItemAmount',
            itemName=evetypes.GetName(typeID),
            amount=int(self.routeMaxAmount))
        CaptionAndSubtext(
            parent=cont,
            caption=localization.GetByLabel('UI/PI/Common/CommodityToRoute'),
            subtext=resourceTxt,
            iconTypeID=typeID,
            top=0,
            width=w)
        CaptionAndSubtext(
            parent=cont,
            caption=localization.GetByLabel('UI/PI/Common/QtyAmount'),
            width=w,
            top=30,
            state=uiconst.UI_DISABLED)
        self.routeAmountEdit = SinglelineEdit(
            name='routeAmountEdit',
            parent=cont,
            setvalue=self.routeMaxAmount,
            height=14,
            width=56,
            align=uiconst.TOPLEFT,
            top=44,
            ints=(0, self.routeMaxAmount),
            OnChange=self.OnRouteAmountEditChanged)
        self.routeAmountText = EveLabelSmall(parent=cont,
                                             left=60,
                                             top=46,
                                             state=uiconst.UI_NORMAL)
        self.routeDestText = CaptionAndSubtext(
            parent=cont,
            caption=localization.GetByLabel('UI/Common/Destination'),
            top=70,
            width=w)
        btns = [[
            localization.GetByLabel('UI/PI/Common/CreateRoute'),
            self.SubmitRoute, ()
        ]]
        self.createRouteButton = ButtonGroup(btns=btns,
                                             parent=cont,
                                             line=False,
                                             alwaysLite=True)
        self.UpdatePanelCreateRoute()
        return cont

    def OnPanelCreateRouteClosed(self, *args):
        sm.GetService('planetUI').myPinManager.LeaveRouteMode()

    def OnRouteAmountEditChanged(self, newVal):
        try:
            routeAmount = int(newVal)
        except ValueError:
            return

        if not self.currRouteCycleTime:
            return
        routeAmount = min(routeAmount, self.routeMaxAmount)
        routeAmount = max(routeAmount, 0.0)
        volume = planetCommon.GetCommodityTotalVolume(
            {self.commodityToRoute: routeAmount})
        volumePerHour = planetCommon.GetBandwidth(volume,
                                                  self.currRouteCycleTime)
        sm.GetService('planetUI').myPinManager.OnRouteVolumeChanged(
            volumePerHour)

    def UpdatePanelCreateRoute(self):
        if not self.currentRoute or len(self.currentRoute) < 2:
            destName = localization.GetByLabel(
                'UI/PI/Common/NoDestinationSelected')
            self.routeMaxAmount = self.sourceMaxAmount
            self.currRouteCycleTime = self.pin.GetCycleTime()
        else:
            self.routeDestPin = sm.GetService(
                'planetUI').GetCurrentPlanet().GetColony(
                    session.charid).GetPin(self.currentRoute[-1])
            isValid, invalidTxt, self.currRouteCycleTime = planetCommon.GetRouteValidationInfo(
                self.pin, self.routeDestPin, self.commodityToRoute)
            destName = planetCommon.GetGenericPinName(self.routeDestPin.typeID,
                                                      self.routeDestPin.id)
            if not isValid:
                destName = localization.GetByLabel(
                    'UI/PI/Common/InvalidDestination',
                    destName=destName,
                    reason=invalidTxt)
            if not isValid:
                self.routeMaxAmount = 0
            elif self.routeDestPin.IsProcessor(
            ) and self.commodityToRoute in self.routeDestPin.GetConsumables():
                destMaxAmount = self.routeDestPin.GetConsumables().get(
                    self.commodityToRoute)
                if self.pin.IsStorage():
                    self.routeMaxAmount = destMaxAmount
                else:
                    self.routeMaxAmount = min(destMaxAmount,
                                              self.commoditySourceMaxAmount)
            else:
                self.routeMaxAmount = self.commoditySourceMaxAmount
        self.routeAmountEdit.SetText(self.routeMaxAmount)
        self.routeAmountEdit.IntMode(0, self.routeMaxAmount)
        self.routeAmountText.text = localization.GetByLabel(
            'UI/PI/Common/RoutedPortion', maxAmount=self.routeMaxAmount)
        self.OnRouteAmountEditChanged(self.routeMaxAmount)
        self.routeDestText.SetSubtext(destName)

    def PanelSelectTransferDest(self, *args):
        cont = Container(parent=self.actionCont,
                         pos=(0, 0, 0, 15),
                         align=uiconst.TOTOP,
                         state=uiconst.UI_HIDDEN)
        cont._OnClose = self.OnPanelSelectTransferDestClosed
        Label(parent=cont,
              text=localization.GetByLabel(
                  'UI/PI/Common/SelectTransferDestination'),
              align=uiconst.TOTOP)
        return cont

    def OnPanelSelectTransferDestClosed(self, *args):
        sm.GetService('planetUI').myPinManager.LeaveRouteMode()

    def SetPin(self, pin):
        self.pin = pin

    def PanelShowRoutes(self, *args):
        self.showRoutesCont = cont = Container(parent=self.actionCont,
                                               state=uiconst.UI_HIDDEN)
        self.routeScroll = Scroll(parent=cont, name='routeScroll')
        self.routeScroll.multiSelect = False
        self.routeScroll.sr.id = 'planetBaseShowRoutesScroll'
        self.routeInfo = Container(parent=cont,
                                   pos=(0, 0, 0, 100),
                                   align=uiconst.TOBOTTOM,
                                   state=uiconst.UI_HIDDEN,
                                   idx=0,
                                   padTop=4)
        w = self.width / 2 - 10
        self.routeInfoSource = CaptionAndSubtext(
            parent=self.routeInfo,
            caption=localization.GetByLabel('UI/PI/Common/Origin'),
            width=w)
        self.routeInfoDest = CaptionAndSubtext(
            parent=self.routeInfo,
            caption=localization.GetByLabel('UI/PI/Common/Destination'),
            width=w,
            top=38)
        self.routeInfoType = CaptionAndSubtext(
            parent=self.routeInfo,
            caption=localization.GetByLabel('UI/Common/Commodity'),
            width=w,
            left=w)
        self.routeInfoBandwidth = CaptionAndSubtext(
            parent=self.routeInfo,
            caption=localization.GetByLabel('UI/PI/Common/CapacityUsed'),
            width=w,
            left=w,
            top=38)
        btns = []
        if self.pin.IsStorage() and hasattr(self, '_CreateRoute'):
            btns.append([
                localization.GetByLabel('UI/PI/Common/CreateRoute'),
                self._CreateRoute, 'routeScroll'
            ])
        btns.append([
            localization.GetByLabel('UI/PI/Common/DeleteRoute'),
            self._DeleteRouteFromEntry, ()
        ])
        self.routeInfoBtns = ButtonGroup(btns=btns,
                                         parent=self.routeInfo,
                                         idx=0)
        self.LoadRouteScroll()
        return cont

    def GetRouteTypeLabel(self, route, pin):
        if not route or not pin:
            return localization.GetByLabel('UI/Common/Unknown')
        elif route.GetSourcePinID() == pin.id:
            return localization.GetByLabel('UI/PI/Common/Outgoing')
        elif route.GetDestinationPinID() == pin.id:
            return localization.GetByLabel('UI/PI/Common/Incoming')
        else:
            return localization.GetByLabel('UI/PI/Common/Transiting')

    def LoadRouteScroll(self):
        scrolllist = []
        routesShown = []
        colony = self.planetUISvc.GetCurrentPlanet().GetColony(
            self.pin.ownerID)
        if colony is None or colony.colonyData is None:
            raise RuntimeError(
                'Unable to load route scroll without active colony on planet')
        links = colony.colonyData.GetLinksForPin(self.pin.id)
        for linkedPinID in links:
            link = colony.GetLink(self.pin.id, linkedPinID)
            for routeID in link.routesTransiting:
                if routeID in routesShown:
                    continue
                route = colony.GetRoute(routeID)
                typeID = route.GetType()
                qty = route.GetQuantity()
                typeName = evetypes.GetName(typeID)
                data = util.KeyVal(
                    label='<t>%s<t>%s<t>%s' %
                    (typeName, qty, self.GetRouteTypeLabel(route, self.pin)),
                    typeID=typeID,
                    itemID=None,
                    getIcon=True,
                    OnMouseEnter=self.OnRouteEntryHover,
                    OnMouseExit=self.OnRouteEntryExit,
                    routeID=route.routeID,
                    OnClick=self.OnRouteEntryClick,
                    amount=qty)
                scrolllist.append(listentry.Get('Item', data=data))
                routesShown.append(route.routeID)

        self.routeScroll.Load(
            contentList=scrolllist,
            noContentHint=localization.GetByLabel(
                'UI/PI/Common/NoIncomingOrOutgoingRoutes'),
            headers=[
                '',
                localization.GetByLabel('UI/Common/Commodity'),
                localization.GetByLabel('UI/Common/Quantity'),
                localization.GetByLabel('UI/PI/Common/Type')
            ])

    def OnRouteEntryClick(self, *args):
        selectedRoutes = self.routeScroll.GetSelected()
        if len(selectedRoutes) < 1:
            self.routeInfo.state = uiconst.UI_HIDDEN
            return
        selectedRouteData = selectedRoutes[0]
        selectedRoute = None
        colony = self.planetUISvc.GetCurrentPlanet().GetColony(session.charid)
        links = colony.colonyData.GetLinksForPin(self.pin.id)
        for linkedPinID in links:
            link = colony.GetLink(self.pin.id, linkedPinID)
            for routeID in link.routesTransiting:
                if routeID == selectedRouteData.routeID:
                    selectedRoute = route = colony.GetRoute(routeID)
                    break

        if selectedRoute is None or not evetypes.Exists(
                selectedRoute.GetType()):
            return
        if selectedRoute.GetSourcePinID() == self.pin.id:
            self.routeInfoSource.SetSubtext(
                localization.GetByLabel('UI/PI/Common/ThisStructure'))
        else:
            sourcePin = sm.GetService('planetUI').planet.GetPin(
                selectedRoute.GetSourcePinID())
            self.routeInfoSource.SetSubtext(
                planetCommon.GetGenericPinName(sourcePin.typeID, sourcePin.id))
        if selectedRoute.GetDestinationPinID() == self.pin.id:
            self.routeInfoDest.SetSubtext(
                localization.GetByLabel('UI/PI/Common/ThisStructure'))
        else:
            destPin = sm.GetService('planetUI').planet.GetPin(
                selectedRoute.GetDestinationPinID())
            self.routeInfoDest.SetSubtext(
                planetCommon.GetGenericPinName(destPin.typeID, destPin.id))
        routeTypeID = route.GetType()
        routeQty = route.GetQuantity()
        self.routeInfoType.SetSubtext(
            localization.GetByLabel('UI/PI/Common/ItemAmount',
                                    itemName=evetypes.GetName(routeTypeID),
                                    amount=int(routeQty)))
        bandwidthAttr = cfg.dgmattribs.Get(const.attributeLogisticalCapacity)
        self.routeInfoBandwidth.SetSubtext(
            GetFormatAndValue(bandwidthAttr,
                              selectedRoute.GetBandwidthUsage()))
        createRouteBtn = self.routeInfoBtns.GetBtnByLabel(
            localization.GetByLabel('UI/PI/Common/CreateRoute'))
        if createRouteBtn:
            if selectedRoute.GetDestinationPinID() == self.pin.id:
                createRouteBtn.state = uiconst.UI_NORMAL
            else:
                createRouteBtn.state = uiconst.UI_HIDDEN
        self.routeInfo.opacity = 0.0
        self.routeInfo.state = uiconst.UI_PICKCHILDREN
        self.uiEffects.MorphUI(self.routeInfo,
                               'opacity',
                               1.0,
                               time=125.0,
                               float=1,
                               newthread=0,
                               maxSteps=1000)