NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

itemShip.py (25618B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\itemShip.py
      2 from __future__ import absolute_import
      3 import blue
      4 import eveicon
      5 import evetypes
      6 import localization
      7 import uthread2
      8 from collections import OrderedDict, defaultdict
      9 from carbonui.control.buttonIcon import ButtonIcon
     10 from carbonui.primitives.container import Container
     11 from carbonui.primitives.frame import Frame
     12 from carbonui.primitives.sprite import Sprite
     13 from carbonui.primitives.transform import Transform
     14 from carbonui.primitives.stretchspritehorizontal import StretchSpriteHorizontal
     15 from carbonui.uianimations import animations
     16 from carbonui import TextColor, uiconst, Align, PickState
     17 from carbonui.text.styles import TextBody, TextDetail
     18 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     19 from characterdata.factions import get_faction_logo_flat
     20 from dogma import data as dogma_data
     21 import dogma.const as dogmaConst
     22 from eve.client.script.ui.shared.neocom.neocom.highlightState import HighlightState, GetIndicatorColor
     23 from eve.common.script.sys import idCheckers
     24 from menucheckers.itemCheckers import ItemChecker
     25 from inventoryutil.client.inventory import get_fitting_flags_for_ship_type
     26 from inventorycommon.util import IsModularShip
     27 from fsdBuiltData.common.iconIDs import GetIconFile
     28 from eve.common.lib import appConst
     29 from eve.common.script.sys import eveCfg
     30 from eve.client.script.ui import eveThemeColor, eveColor
     31 from omega.client.clone_grade_svc import get_clone_grade_svc
     32 from shipgroup import get_ship_group_name
     33 from eve.client.script.ui.shared.traits import TraitAttributeIcon
     34 from eve.client.script.ui.util import uix
     35 from eve.client.script.ui.shared.cloneGrade import ORIGIN_INVENTORY
     36 from eve.client.script.ui.shared.cloneGrade.omegaCloneOverlayIcon import OmegaCloneOverlayIcon
     37 from eve.client.script.ui.control.itemIcon import ItemIcon
     38 from eve.client.script.ui.control import tooltipConst
     39 from eve.client.script.ui.shared.item import InvItem, ItemSelectionFrame
     40 from featuremanagement.client.flag import flag_bool
     41 CARD_STATE_NORMAL = 1
     42 CARD_STATE_ACTIVE = 2
     43 CARD_STATE_PACKAGED = 3
     44 SHIP_CARD_FITTING_ICON_ENABLED = 'ship-card-fitting-icon-enabled'
     45 SHIP_CARD_FITTING_ICON_ENABLED_DEFAULT = True
     46 _should_display_fitting_icon = SHIP_CARD_FITTING_ICON_ENABLED_DEFAULT
     47 
     48 def _refresh_fitting_flag(evaluate_flag):
     49     global _should_display_fitting_icon
     50     _should_display_fitting_icon = evaluate_flag()
     51 
     52 
     53 flag_bool(key=SHIP_CARD_FITTING_ICON_ENABLED, default_value=SHIP_CARD_FITTING_ICON_ENABLED_DEFAULT, callback=_refresh_fitting_flag)
     54 
     55 class ShipItemIcon(InvItem):
     56 
     57     def AddIconContainer(self):
     58         self.groupIcon = Sprite(parent=self.mainContainer, pickState=PickState.OFF, align=Align.TOPRIGHT, width=16, height=16, left=1, top=1, opacity=1, shadowOffset=(1, 1), shadowColor=(0, 0, 0))
     59         super(ShipItemIcon, self).AddIconContainer()
     60 
     61     def Load(self, node):
     62         super(ShipItemIcon, self).Load(node)
     63         self.groupIcon.texturePath = sm.GetService('bracket').GetBracketIcon(self.typeID)
     64 
     65 
     66 class ShipItemCard(InvItem):
     67     FACTION_LOGO_ANIMATION_DURATION = 0.25
     68     FACTION_LOGO_MIN_SIZE = 96
     69     FACTION_LOGO_MAX_SIZE = 108
     70     OPACITY_IDLE = 0.2
     71     OPACITY_HOVER = 0.3
     72     OPACITY_MOUSEDOWN = 0.5
     73     MAIN_PADDING = 3
     74     fittedItems = []
     75 
     76     @classmethod
     77     def GetEntryHeight(cls):
     78         return 106
     79 
     80     @classmethod
     81     def GetEntryWidth(cls):
     82         return 235
     83 
     84     @classmethod
     85     def GetEntryColMargin(cls):
     86         return 6
     87 
     88     @classmethod
     89     def IsFixedWidth(cls):
     90         return False
     91 
     92     def ConstructLayout(self):
     93         self.card_state = CARD_STATE_NORMAL
     94         self.mainContainer = Container(name='MainContainer', parent=self, clipChildren=True, padding=self.MAIN_PADDING)
     95         self.ConstructBottom()
     96         self.ConstructContent()
     97         self.ConstructBackground()
     98         self.ConstructIndicator()
     99 
    100     def ConstructContent(self):
    101         iconWrapper = Container(name='iconWrapper', parent=self.mainContainer, align=Align.TOLEFT, width=self.ICON_SIZE, padding=2)
    102         self.iconCont = Container(name='iconCont', parent=iconWrapper, align=Align.CENTER, width=self.ICON_SIZE, height=self.ICON_SIZE)
    103         self._processing_click = False
    104         self.hoverIcon = ButtonIcon(name='hoverIcon', parent=self.iconCont, width=self.ICON_SIZE, height=self.ICON_SIZE, iconSize=32, texturePath=eveicon.soldier_of_fortune, opacity=0, func=self.OnDblClick)
    105         self.hoverBg = Sprite(name='hoverBg', parent=self.iconCont, width=self.ICON_SIZE, height=self.ICON_SIZE, texturePath='res:/UI/Texture/Classes/ShipTree/groups/bgWhite.png', color=(0, 0, 0, 0), opacity=0)
    106         content = Container(name='content', parent=self.mainContainer, align=Align.TOALL, padding=(6, 6, 6, 2), clipChildren=True)
    107         groupCont = Container(name='groupCont', parent=content, align=Align.TOTOP, height=16)
    108         self.groupIcon = Sprite(parent=groupCont, pickState=PickState.OFF, align=Align.CENTERLEFT, width=16, height=16, color=TextColor.NORMAL)
    109         self.groupLabel = TextDetail(parent=groupCont, align=Align.CENTERLEFT, text='', color=TextColor.SECONDARY, left=20)
    110         self.fittingIcon = FittedItemsIcon(parent=groupCont, align=Align.CENTERRIGHT, width=16, height=16, display=False)
    111         nameCont = ContainerAutoSize(name='nameCont', parent=content, align=Align.VERTICALLY_CENTERED)
    112         self.sr.label = TextBody(parent=nameCont, align=Align.TOTOP, color=TextColor.HIGHLIGHT, bold=True, maxLines=1, autoFadeSides=16)
    113         self.typeNameLabel = TextDetail(parent=nameCont, align=Align.TOTOP, text='', color=TextColor.SECONDARY, maxLines=1, autoFadeSides=16)
    114 
    115     def ConstructOmegaIcon(self):
    116         if self.omegaIcon:
    117             return
    118         self.omegaIcon = OmegaCloneOverlayIcon(parent=self.iconCont, align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED, width=self.ICON_SIZE, height=self.ICON_SIZE, opacity=self.OMEGAICON_OPACITY, origin=ORIGIN_INVENTORY, reason=self.typeID, idx=0)
    119 
    120     def ConstructBottom(self):
    121         bottomCont = Container(name='bottom', parent=self.mainContainer, align=Align.TOBOTTOM, height=30, padding=(2, 0, 2, 2))
    122         self.ConstructActiveState(bottomCont)
    123         self.ConstructPackagedState(bottomCont)
    124         self.traitCont = Container(name='traits', parent=bottomCont, align=uiconst.TOALL, padLeft=8, padRight=8, clipChildren=True)
    125         self.bottomBG = StretchSpriteHorizontal(name='bottomBG', parent=bottomCont, align=Align.TOALL, texturePath='res:/UI/Texture/classes/Button/background_cut_bottom_left_right.png', leftEdgeSize=9, rightEdgeSize=9, color=eveColor.WHITE, opacity=0.05)
    126         self.AddTag(bottomCont, Align.TOTOP_NOPUSH)
    127 
    128     def ConstructActiveState(self, parent):
    129         self.activeStateContainer = ContainerAutoSize(name='activeState', parent=parent, align=Align.TORIGHT, padLeft=2)
    130         TextBody(parent=self.activeStateContainer, name='label', align=Align.CENTER, color=TextColor.SUCCESS, text=localization.GetByLabel('UI/Ship/Active'), padLeft=12, padRight=12)
    131         Frame(bgParent=self.activeStateContainer, name='bg', align=Align.TOALL, texturePath='res:/UI/Texture/classes/Button/background_cut_bottom_right.png', cornerSize=9, color=(0, 0, 0, 0.6))
    132 
    133     def ConstructPackagedState(self, parent):
    134         self.packagedStateContainer = ContainerAutoSize(name='packagedState', parent=parent, align=Align.TORIGHT, alignMode=Align.TORIGHT)
    135         labelCont = ContainerAutoSize(parent=self.packagedStateContainer, align=Align.TORIGHT, padRight=9)
    136         self.packagedLabelCont = ContainerAutoSize(parent=labelCont, align=Align.TORIGHT)
    137         self.packagedLabelCont.DisableAutoSize()
    138         self.packagedLabel = TextBody(parent=self.packagedLabelCont, align=Align.CENTERLEFT, color=TextColor.WARNING, text=localization.GetByLabel('UI/Ship/Packaged'), opacity=0, padLeft=4)
    139         qtyContainer = ContainerAutoSize(parent=labelCont, align=Align.TORIGHT)
    140         self.packagedQtyLabel = TextBody(parent=qtyContainer, align=Align.CENTERLEFT, text='')
    141         Frame(bgParent=self.packagedStateContainer, name='bg', align=Align.TOALL, texturePath='res:/UI/Texture/classes/Button/background_cut_bottom_right.png', cornerSize=9, color=(0, 0, 0, 0.6))
    142         Sprite(parent=self.packagedStateContainer, texturePath='res:/UI/Texture/classes/Hangar/card_footer_triangle.png', color=(0, 0, 0, 0.6), align=Align.CENTERLEFT, height=30, width=30, left=-30)
    143 
    144     def ConstructBackground(self):
    145         self.factionIconCont = Transform(parent=self.mainContainer, align=Align.CENTERRIGHT, height=75, width=90, padRight=45)
    146         self.factionIcon = Sprite(name='factionIcon', parent=self.factionIconCont, align=Align.CENTER, width=self.FACTION_LOGO_MIN_SIZE, height=self.FACTION_LOGO_MIN_SIZE, pickState=PickState.OFF, color=(1, 1, 1, 0.05), top=0)
    147         Frame(name='card_mask_frame', bgParent=self, align=Align.TOALL, texturePath='res:/UI/Texture/classes/Hangar/card_fill.png', cornerSize=16, color=(0, 0, 0, 0.9), padding=self.MAIN_PADDING)
    148         self.backgroundFrame = Frame(parent=self, name='background_frame', align=Align.TOALL, texturePath='res:/UI/Texture/classes/Hangar/card_fill.png', cornerSize=9, outputMode=uiconst.OUTPUT_COLOR_AND_GLOW, glowBrightness=0.1, color=eveThemeColor.THEME_FOCUS, padding=self.MAIN_PADDING, opacity=self.OPACITY_IDLE)
    149 
    150     def ConstructIndicator(self):
    151         self.cardIndicator = Frame(parent=self, name='indicator', align=Align.TOALL, texturePath='res:/UI/Texture/classes/Hangar/card_indicator.png', cornerSize=9, opacity=0, outputMode=uiconst.OUTPUT_COLOR_AND_GLOW, glowBrightness=0.8, padding=self.MAIN_PADDING)
    152         self.tagContainer = Frame(parent=self, name='tag', align=Align.TOALL, texturePath='res:/UI/Texture/classes/Hangar/card_indicator.png', cornerSize=9, padding=self.MAIN_PADDING, state=uiconst.UI_HIDDEN, outputMode=uiconst.OUTPUT_COLOR_AND_GLOW, glowBrightness=0.6, color=GetIndicatorColor(HighlightState.important))
    153 
    154     def ConstructSelectionFrame(self):
    155         self._selection_frame = CardSelectionFrame(parent=self, align=uiconst.TOALL, idx=0, active=self._isSelected and self._showHilite)
    156 
    157     def UpdateTag(self):
    158         super(ShipItemCard, self).UpdateTag()
    159         if self.shouldShowTag:
    160             animations.MorphScalar(self.tagContainer, 'glowBrightness', 0.6, 1, duration=1.5, loops=10, curveType=uiconst.ANIM_WAVE)
    161         else:
    162             animations.StopAllAnimations(self.tagContainer)
    163 
    164     def UpdateCardState(self):
    165         item = ItemChecker(self.rec)
    166         if item.IsActiveShip():
    167             self.SetCardState(CARD_STATE_ACTIVE)
    168         elif item.IsAssembledShip():
    169             self.SetCardState(CARD_STATE_NORMAL)
    170         else:
    171             self.SetCardState(CARD_STATE_PACKAGED)
    172 
    173     def SetCardState(self, state):
    174         self.card_state = state
    175         if state == CARD_STATE_NORMAL:
    176             self.bottomBG.texturePath = 'res:/UI/Texture/classes/Button/background_cut_bottom_left_right.png'
    177             self.bottomBG.rightEdgeSize = 9
    178             self.activeStateContainer.Hide()
    179             self.packagedStateContainer.Hide()
    180             self.cardIndicator.color = eveThemeColor.THEME_FOCUS
    181             self.cardIndicator.opacity = 0
    182         elif state == CARD_STATE_ACTIVE:
    183             self.bottomBG.texturePath = 'res:/UI/Texture/classes/Button/background_cut_bottom_left.png'
    184             self.bottomBG.rightEdgeSize = 9
    185             self.activeStateContainer.Show()
    186             self.packagedStateContainer.Hide()
    187             self.cardIndicator.color = TextColor.SUCCESS
    188         else:
    189             self.bottomBG.texturePath = 'res:/UI/Texture/classes/Hangar/card_footer_stripes.png'
    190             self.bottomBG.rightEdgeSize = 110
    191             self.activeStateContainer.Hide()
    192             self.packagedStateContainer.Show()
    193             self.cardIndicator.color = eveThemeColor.THEME_FOCUS
    194             self.cardIndicator.opacity = 0
    195 
    196     def Load(self, node):
    197         self._processing_click = False
    198         super(ShipItemCard, self).Load(node)
    199         self.UpdateCardState()
    200         self.UpdateFittingIcon()
    201         if self.card_state == CARD_STATE_PACKAGED:
    202             quantity = uix.GetItemQty(self.sr.node, 'ss')
    203             self.packagedQtyLabel.text = u'x{qty}'.format(qty=quantity)
    204         self.groupLabel.text = get_ship_group_name(self.typeID)
    205         self.groupIcon.texturePath = sm.GetService('bracket').GetBracketIcon(self.typeID)
    206         factionID = evetypes.GetFactionID(self.typeID)
    207         factionLogo = get_faction_logo_flat(factionID)
    208         if factionLogo:
    209             self.factionIcon.texturePath = factionLogo.resolve(64)
    210         else:
    211             self.factionIconCont.Hide()
    212         if self.typeID not in cfg.infoBubbleTypeElements:
    213             return
    214         self.traitCont.Flush()
    215         data = cfg.infoBubbleTypeElements[self.typeID]
    216         attributeIDs = [ x[1] for x in sorted(data.items(), key=lambda d: int(d[0])) ]
    217         for index, attributeID in enumerate(attributeIDs):
    218             icon = TraitAttributeIcon(parent=self.traitCont, align=uiconst.CENTERLEFT, attributeID=attributeID, width=16, height=16, left=24 * index)
    219             icon.OnClick = self.OnClick
    220             icon.GetMenu = self.GetMenu
    221             icon.isDragObject = True
    222             icon.GetDragData = self.GetDragData
    223             icon.OnDropData = self.OnDropData
    224 
    225     def UpdateFittingIcon(self):
    226         self.fittingIcon.display = False
    227         if self.card_state == CARD_STATE_PACKAGED:
    228             return
    229         self.fittingIcon.Update(self.id, self.typeID)
    230 
    231     def UpdateLabel(self, new = 0):
    232         super(ShipItemCard, self).UpdateLabel(new)
    233         item = ItemChecker(self.rec)
    234         if item.IsAssembledShip():
    235             self.typeNameLabel.text = evetypes.GetName(self.typeID)
    236 
    237     def OnMouseEnter(self, *args):
    238         super(ShipItemCard, self).OnMouseEnter(*args)
    239         animations.StopAllAnimations(self.factionIcon)
    240         animations.MorphScalar(self.factionIcon, 'width', startVal=self.factionIcon.width, endVal=self.FACTION_LOGO_MAX_SIZE, duration=self.FACTION_LOGO_ANIMATION_DURATION)
    241         animations.MorphScalar(self.factionIcon, 'height', startVal=self.factionIcon.height, endVal=self.FACTION_LOGO_MAX_SIZE, duration=self.FACTION_LOGO_ANIMATION_DURATION)
    242         animations.FadeTo(self.factionIcon, self.factionIcon.opacity, endVal=0.025, duration=self.FACTION_LOGO_ANIMATION_DURATION)
    243         animations.FadeTo(self.backgroundFrame, self.backgroundFrame.opacity, endVal=self.OPACITY_HOVER, duration=self.FACTION_LOGO_ANIMATION_DURATION)
    244         animations.StopAllAnimations(self.packagedLabel)
    245         self.packagedLabelCont.ExpandWidth()
    246         animations.FadeIn(self.packagedLabel, duration=self.FACTION_LOGO_ANIMATION_DURATION)
    247         if self.card_state != CARD_STATE_ACTIVE:
    248             animations.FadeIn(self.cardIndicator, duration=0.1)
    249             self.hoverIcon.SetTexturePath(eveicon.board)
    250         else:
    251             self.hoverIcon.SetTexturePath('res:/UI/Texture/WindowIcons/fitting.png')
    252         if self.typeID is None:
    253             return
    254         if not get_clone_grade_svc().is_restricted(self.typeID):
    255             animations.FadeIn(self.hoverIcon, duration=self.FACTION_LOGO_ANIMATION_DURATION)
    256             animations.FadeIn(self.hoverBg, endVal=0.5, duration=self.FACTION_LOGO_ANIMATION_DURATION / 2)
    257 
    258     def OnDblClick(self, *args):
    259         if self._processing_click:
    260             return
    261         self._processing_click = True
    262         super(ShipItemCard, self).OnDblClick(*args)
    263         uthread2.start_tasklet(self._reset_click)
    264 
    265     def _reset_click(self):
    266         uthread2.sleep(2)
    267         self._processing_click = False
    268 
    269     def OnMouseExit(self, *args):
    270         super(ShipItemCard, self).OnMouseExit(*args)
    271         animations.StopAllAnimations(self.factionIcon)
    272         animations.MorphScalar(self.factionIcon, 'width', startVal=self.factionIcon.width, endVal=self.FACTION_LOGO_MIN_SIZE, duration=self.FACTION_LOGO_ANIMATION_DURATION)
    273         animations.MorphScalar(self.factionIcon, 'height', startVal=self.factionIcon.height, endVal=self.FACTION_LOGO_MIN_SIZE, duration=self.FACTION_LOGO_ANIMATION_DURATION)
    274         animations.FadeTo(self.factionIcon, self.factionIcon.opacity, endVal=0.05, duration=self.FACTION_LOGO_ANIMATION_DURATION)
    275         animations.FadeTo(self.backgroundFrame, self.backgroundFrame.opacity, endVal=self.OPACITY_IDLE, duration=self.FACTION_LOGO_ANIMATION_DURATION)
    276         animations.StopAllAnimations(self.packagedLabel)
    277         self.packagedLabelCont.CollapseWidth()
    278         animations.FadeOut(self.packagedLabel, duration=self.FACTION_LOGO_ANIMATION_DURATION)
    279         if self.card_state != CARD_STATE_ACTIVE:
    280             animations.FadeOut(self.cardIndicator, duration=0.1)
    281         animations.FadeOut(self.hoverIcon, duration=self.FACTION_LOGO_ANIMATION_DURATION)
    282         animations.FadeOut(self.hoverBg, duration=self.FACTION_LOGO_ANIMATION_DURATION / 2)
    283 
    284     def OnColorThemeChanged(self):
    285         super(ShipItemCard, self).OnColorThemeChanged()
    286         self.backgroundFrame.rgb = eveThemeColor.THEME_FOCUSDARK[:3]
    287         if self.card_state != CARD_STATE_ACTIVE:
    288             self.cardIndicator.rgb = eveThemeColor.THEME_FOCUS[:3]
    289 
    290 
    291 class CardSelectionFrame(ItemSelectionFrame):
    292 
    293     def _create_frames_if_missing(self):
    294         if self._outer_frame is None:
    295             self._outer_frame = Frame(parent=self, texturePath='res:/UI/Texture/classes/Hangar/card_outline.png', cornerSize=9, color=eveThemeColor.THEME_FOCUS, opacity=0.6, outputMode=uiconst.OUTPUT_COLOR_AND_GLOW, glowBrightness=0.6)
    296 
    297     def OnColorThemeChanged(self):
    298         super(CardSelectionFrame, self).OnColorThemeChanged()
    299         if self._outer_frame:
    300             self._outer_frame.rgb = eveThemeColor.THEME_FOCUS[:3]
    301 
    302 
    303 class FittedItemsIcon(Sprite):
    304     default_width = 16
    305     default_height = 16
    306     default_texturePath = eveicon.fitting
    307     default_color = TextColor.NORMAL
    308     __notifyevents__ = ['OnDogmaItemChange', 'OnFittingStripped']
    309 
    310     def __init__(self, *args, **kwargs):
    311         self._tasklet = None
    312         self._itemID = None
    313         self._typeID = None
    314         self._fittedItems = []
    315         self._charges = []
    316         self._dronesInBay = []
    317         super(FittedItemsIcon, self).__init__(*args, **kwargs)
    318 
    319     def ApplyAttributes(self, attributes):
    320         super(FittedItemsIcon, self).ApplyAttributes(attributes)
    321         sm.RegisterNotify(self)
    322 
    323     def Close(self):
    324         super(FittedItemsIcon, self).Close()
    325         sm.UnregisterNotify(self)
    326 
    327     def Update(self, itemID, typeID):
    328         self._itemID = itemID
    329         self._typeID = typeID
    330         if self._tasklet:
    331             self._tasklet.Kill()
    332         self._tasklet = uthread2.start_tasklet(self._Update)
    333 
    334     def _Update(self, delay = None):
    335         if not self._should_display():
    336             self.display = False
    337             return
    338         if delay:
    339             uthread2.sleep(delay)
    340         self._update_items()
    341         self.opacity = TextColor.HIGHLIGHT.opacity if self._fittedItems or self._dronesInBay else TextColor.SECONDARY.opacity
    342         self.display = True
    343 
    344     def _update_items(self):
    345         self._fittedItems = []
    346         self._charges = []
    347         self._dronesInBay = []
    348         flags = get_fitting_flags_for_ship_type(self._typeID)
    349         if not flags:
    350             return
    351         if dogma_data.get_type_attribute(self._typeID, dogmaConst.attributeDroneCapacity) or IsModularShip(self._typeID):
    352             flags.append(appConst.flagDroneBay)
    353         inventory = sm.GetService('invCache').GetInventoryFromId(self._itemID)
    354         items = inventory.ListByFlags(flags)
    355         for item in items:
    356             if item.flagID in appConst.fittingFlags:
    357                 if idCheckers.IsModule(item.categoryID) or item.flagID in appConst.subsystemSlotFlags:
    358                     self._fittedItems.append(item)
    359                 elif idCheckers.IsCharge(item.categoryID):
    360                     self._charges.append(item)
    361             elif item.flagID == appConst.flagDroneBay:
    362                 self._dronesInBay.append(item)
    363 
    364     def _should_display(self):
    365         if self._itemID == session.shipid:
    366             return True
    367         if not _should_display_fitting_icon:
    368             return False
    369         return blue.os.desiredSimDilation > 0.9
    370 
    371     def LoadTooltipPanel(self, tooltipPanel, *args):
    372         if self._itemID == session.shipid:
    373             self._update_items()
    374         tooltipPanel.LoadGeneric1ColumnTemplate()
    375         groupedEntries = OrderedDict()
    376         for attributeID, flagID in ((dogmaConst.attributeHiSlots, appConst.flagHiSlot0),
    377          (dogmaConst.attributeMedSlots, appConst.flagMedSlot0),
    378          (dogmaConst.attributeLowSlots, appConst.flagLoSlot0),
    379          (dogmaConst.attributeSubSystemSlot, appConst.flagSubSystemSlot0),
    380          (dogmaConst.attributeRigSlots, appConst.flagRigSlot0)):
    381             totalNum = int(dogma_data.get_type_attribute(self._typeID, attributeID, 0))
    382             groupedEntries[eveCfg.GetShipFlagLocationName(flagID)] = {'icon': GetIconFile(dogma_data.get_attribute_icon_id(attributeID)),
    383              'totalNum': totalNum,
    384              'fitted': defaultdict(int),
    385              'fittedAmount': 0,
    386              'charges': dict()}
    387 
    388         for fittedItem in self._fittedItems:
    389             slotName = eveCfg.GetShipFlagLocationName(fittedItem.flagID)
    390             if slotName in groupedEntries:
    391                 groupedEntries[slotName]['fitted'][fittedItem.typeID] += 1
    392                 groupedEntries[slotName]['fittedAmount'] += 1
    393 
    394         if self._charges:
    395             moduleTypesBySlot = {item.flagID:item.typeID for item in self._fittedItems}
    396             for charge in self._charges:
    397                 moduleTypeID = moduleTypesBySlot.get(charge.flagID, None)
    398                 if moduleTypeID:
    399                     slotName = eveCfg.GetShipFlagLocationName(charge.flagID)
    400                     if slotName in groupedEntries:
    401                         charges = groupedEntries[slotName]['charges']
    402                         if moduleTypeID not in charges:
    403                             charges[moduleTypeID] = defaultdict(int)
    404                         charges[moduleTypeID][charge.typeID] += charge.stacksize
    405 
    406         for slotName, groupInfo in groupedEntries.iteritems():
    407             if groupInfo['totalNum'] == 0 and groupInfo['fittedAmount'] == 0:
    408                 continue
    409             if groupInfo['totalNum'] > 0:
    410                 text = u'{} [{}/{}]'.format(slotName, groupInfo['fittedAmount'], groupInfo['totalNum'])
    411             else:
    412                 text = u'{} [{}]'.format(slotName, groupInfo['fittedAmount'])
    413             tooltipPanel.AddCell(self._ConstructGroupEntry(groupInfo['icon'], text))
    414             for typeID, amount in sorted(groupInfo['fitted'].iteritems(), key=lambda entry: evetypes.GetName(entry[0])):
    415                 tooltipPanel.AddCell(self._ConstructTypeEntry(typeID, amount))
    416                 charges = groupInfo['charges'].get(typeID, {})
    417                 for chargeTypeID, amount in sorted(charges.iteritems(), key=lambda entry: evetypes.GetName(entry[0])):
    418                     tooltipPanel.AddCell(self._ConstructTypeEntry(chargeTypeID, amount, padLeft=16))
    419 
    420             tooltipPanel.AddSpacer()
    421 
    422         if self._dronesInBay:
    423             dronesDict = defaultdict(int)
    424             for item in self._dronesInBay:
    425                 dronesDict[item.typeID] += item.stacksize
    426 
    427             usedVolume = sum([ evetypes.GetVolume(typeID) * amount for typeID, amount in dronesDict.iteritems() ])
    428             totalVolume = dogma_data.get_type_attribute(self._typeID, dogmaConst.attributeDroneCapacity)
    429             tooltipPanel.AddCell(self._ConstructGroupEntry(eveicon.drones, u'{} [{}]'.format(localization.GetByLabel('UI/Drones/Drones'), localization.GetByLabel('UI/Inventory/ContainerQuantityAndCapacity', quantity=usedVolume, capacity=totalVolume))))
    430             for typeID, amount in dronesDict.iteritems():
    431                 tooltipPanel.AddCell(self._ConstructTypeEntry(typeID, amount))
    432 
    433         self.tooltipPanelMenu = tooltipPanel
    434 
    435     def _ConstructGroupEntry(self, icon, text):
    436         entryHeight = 24
    437         container = ContainerAutoSize(align=Align.CENTERLEFT, height=entryHeight, bgColor=(1, 1, 1, 0.1))
    438         Sprite(parent=container, align=Align.TOLEFT, width=entryHeight, height=entryHeight, texturePath=icon, color=TextColor.NORMAL)
    439         labelCont = ContainerAutoSize(parent=container, align=Align.TOLEFT)
    440         TextBody(parent=labelCont, text=text, align=Align.CENTERLEFT, left=8, width=tooltipConst.LABEL_WRAP_WIDTH)
    441         return container
    442 
    443     def _ConstructTypeEntry(self, typeID, amount, padLeft = 0):
    444         entryHeight = 24
    445         container = ContainerAutoSize(align=Align.CENTERLEFT, height=entryHeight, padLeft=padLeft)
    446         ItemIcon(parent=container, align=Align.TOLEFT, width=entryHeight, height=entryHeight, showOmegaOverlay=False, typeID=typeID)
    447         labelCont = ContainerAutoSize(parent=container, align=Align.TOLEFT)
    448         text = evetypes.GetName(typeID)
    449         if amount > 1:
    450             text = localization.GetByLabel('UI/InfoWindow/FittingItemLabelWithQuantity', quantity=amount, itemName=text)
    451         TextBody(parent=labelCont, text=text, align=Align.CENTERLEFT, left=8, width=tooltipConst.LABEL_WRAP_WIDTH - padLeft)
    452         return container
    453 
    454     def OnDogmaItemChange(self, item, change):
    455         if self._itemID != session.shipid:
    456             return
    457         self._Update()
    458 
    459     def OnFittingStripped(self, shipID):
    460         if self._itemID != shipID:
    461             return
    462         if self._tasklet:
    463             self._tasklet.Kill()
    464         self._tasklet = uthread2.start_tasklet(self._Update, delay=0.5)