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

activeitem.py (35909B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\inflight\activeitem.py
      2 from collections import OrderedDict
      3 import blue
      4 import telemetry
      5 import carbonui.const as uiconst
      6 import eveformat.client
      7 import evetypes
      8 import localization
      9 import structures
     10 import uthread
     11 import utillib
     12 from brennivin import itertoolsext
     13 from carbon.common.script.util.format import FmtDate, FmtDist
     14 from carbon.common.script.util.timerstuff import AutoTimer
     15 from carbonui import fontconst, TextColor
     16 from carbonui.control.contextMenu.menuConst import DISABLED_ENTRY0
     17 from carbonui.control.contextMenu.menuEntryData import MenuEntryData
     18 from carbonui.window.widget import WidgetWindow
     19 from carbonui.primitives.container import Container
     20 from carbonui.primitives.fill import Fill
     21 from eve.client.script.parklife import states as state
     22 from eve.client.script.ui.control import eveIcon, eveLabel
     23 from eve.client.script.ui.inflight import actionPanelDefaults
     24 from eve.client.script.ui.inflight.selectedItemConst import ICON_ID_AND_CMD_BY_ACTIONID, RESET_ACTIONS
     25 from eve.client.script.ui.services import menuAction
     26 from eve.client.script.ui.services.menuSvcExtras.movementFunctions import GetDefaultWarpToLabel
     27 from eve.client.script.ui.shared import killRights
     28 from eve.client.script.ui.shared.pointerTool import pointerToolConst
     29 from eve.client.script.ui.util import uix
     30 from eve.common.lib import appConst as const
     31 from eve.common.script.sys import idCheckers
     32 from eve.common.script.util import eveFormat
     33 from eveservices.menu import GetMenuService, StartMenuService
     34 from menu import MenuLabel
     35 from npcs.client.components.entity_bounty import get_entity_bounty
     36 from spacecomponents.client.components import cargobay
     37 from spacecomponents.common.helper import HasCargoBayComponent
     38 from spacecomponents.common.helper import HasItemTrader
     39 from uihider import UiHiderMixin
     40 
     41 class ActiveItem(UiHiderMixin, WidgetWindow):
     42     __guid__ = 'form.ActiveItem'
     43     uniqueUiName = pointerToolConst.UNIQUE_NAME_SELECTED_ITEM_VIEW
     44     __notifyevents__ = ['OnDronesSelected',
     45      'ProcessSessionChange',
     46      'OnStateChange',
     47      'DoBallRemove',
     48      'OnDistSettingsChange',
     49      'OnPlanetViewChanged',
     50      'DoBallsRemove',
     51      'OnActiveTrackingChange']
     52     default_minSize = (256, 100)
     53     default_windowID = 'selecteditemview'
     54     default_open = True
     55     default_isKillable = False
     56     default_captionLabelPath = 'UI/Inflight/ActiveItem/SelectedItem'
     57 
     58     @staticmethod
     59     def default_left(*args):
     60         return actionPanelDefaults.GetActiveItemLeft()
     61 
     62     @staticmethod
     63     def default_top(*args):
     64         return actionPanelDefaults.GetActiveItemTop()
     65 
     66     @staticmethod
     67     def default_width():
     68         return actionPanelDefaults.GetActiveItemWidth()
     69 
     70     @staticmethod
     71     def default_height():
     72         return actionPanelDefaults.GetActiveItemHeight()
     73 
     74     def ApplyAttributes(self, attributes):
     75         super(ActiveItem, self).ApplyAttributes(attributes)
     76         panelname = attributes.panelname
     77         self.scope = uiconst.SCOPE_INFLIGHT
     78         self.canAnchorToOthers = 0
     79         self.lastActionSerial = None
     80         self.lastSessionChange = None
     81         self.laseUpdateWidth = None
     82         self.lastIcon = None
     83         self.lastBountyCheck = None
     84         self.bounty = None
     85         self.sr.actions = None
     86         self.sr.updateTimer = None
     87         self.itemIDs = []
     88         self.panelname = panelname
     89         self.lastActionDist = None
     90         self.lastCloakingStatus = False
     91         self.sr.noSelectedHint = None
     92         self.sr.blink = None
     93         self.UpdateActions()
     94         self._ConstructGroupData()
     95         if self.destroyed:
     96             return
     97         self.sr.toparea = Container(name='toparea', align=uiconst.TOTOP, parent=self.sr.main, height=36)
     98         self.sr.utilMenuArea = Container(name='utilMenuArea', align=uiconst.TORIGHT, parent=self.sr.toparea, width=20)
     99         self.utilMenu = killRights.KillRightsUtilMenu(menuAlign=uiconst.TOPRIGHT, parent=self.sr.utilMenuArea, align=uiconst.TOPRIGHT)
    100         self.sr.utilMenuArea.display = False
    101         self.sr.actions = Container(name='actions', align=uiconst.TOTOP, parent=self.sr.main, left=0, top=0)
    102         self.sr.actions.isTabStop = 1
    103         self.sr.iconpar = Container(name='iconpar', align=uiconst.TOPLEFT, parent=self.sr.toparea, width=32, height=32, left=1, top=2, state=uiconst.UI_HIDDEN)
    104         self.sr.icon = eveIcon.Icon(parent=self.sr.iconpar, align=uiconst.TOALL)
    105         self.sr.icon.OnClick = (self.ShowInfo, self.sr.icon)
    106         self.sr.icon.GetMenu = (self.GetIconMenu, self.sr.icon)
    107         self.sr.icon.GetDragData = self.GetShipDragData
    108         self.sr.chariconpar = Container(name='chariconpar', align=uiconst.TOPLEFT, parent=self.sr.toparea, width=32, height=32, left=37, top=2, state=uiconst.UI_HIDDEN)
    109         self.sr.charicon = eveIcon.Icon(parent=self.sr.chariconpar, align=uiconst.TOALL)
    110         self.sr.charicon.GetDragData = self.GetCharDragData
    111         self.sr.charicon.OnClick = (self.ShowInfo, self.sr.charicon)
    112         self.sr.pushCont = Container(name='push', width=39, parent=self.sr.toparea, align=uiconst.TOLEFT)
    113         self.sr.text = eveLabel.EveLabelSmall(text='', parent=self.sr.toparea, align=uiconst.TOTOP, left=const.defaultPadding, top=const.defaultPadding, state=uiconst.UI_NORMAL)
    114         self.inited = 1
    115         self.bountySvc = sm.GetService('bountySvc')
    116         selected = None
    117         stateSvc = sm.GetServiceIfRunning('stateSvc')
    118         if stateSvc:
    119             selected = stateSvc.GetExclState(state.selected)
    120         if selected:
    121             self.OnMultiSelect([selected])
    122         else:
    123             self.UpdateAll()
    124 
    125     def _ConstructGroupData(self):
    126         self.postorder = ['UI/Inflight/OrbitObject',
    127          'UI/Inflight/Submenus/KeepAtRange',
    128          ('UI/Inflight/LockTarget', 'UI/Inflight/UnlockTarget'),
    129          ('UI/Inflight/LookAtObject', 'UI/Inflight/ResetCamera'),
    130          'UI/Inflight/SetAsCameraInterest',
    131          'UI/Commands/ShowInfo']
    132         self.typeActions = {const.typeUpwellSmallStargate: ['UI/Inflight/JumpUsingBridge', 'UI/Inflight/BoardStructure', 'UI/Inflight/AccessHangarTransfer']}
    133         for typeID in structures.FLEX_STRUCTURE_TYPES:
    134             if typeID not in self.typeActions:
    135                 self.typeActions[typeID] = []
    136             self.typeActions[typeID].extend(['UI/Inflight/BoardStructure', 'UI/Inflight/AccessHangarTransfer'])
    137 
    138         self.groups = {const.groupStation: ['UI/Inflight/DockInStation'],
    139          const.groupWreck: ['UI/Commands/OpenCargo'],
    140          const.groupCargoContainer: ['UI/Commands/OpenCargo'],
    141          const.groupMissionContainer: ['UI/Commands/OpenCargo'],
    142          const.groupSecureCargoContainer: ['UI/Commands/OpenCargo'],
    143          const.groupAuditLogSecureContainer: ['UI/Commands/OpenCargo'],
    144          const.groupFreightContainer: ['UI/Commands/OpenCargo'],
    145          const.groupSpawnContainer: ['UI/Commands/OpenCargo'],
    146          const.groupSpewContainer: ['UI/Commands/OpenCargo'],
    147          const.groupDeadspaceOverseersBelongings: ['UI/Commands/OpenCargo'],
    148          const.groupPlanetaryCustomsOffices: ['UI/PI/Common/AccessCustomOffice'],
    149          const.groupOrbitalConstructionPlatforms: ['UI/DustLink/OpenUpgradeHold'],
    150          const.groupMoon: ['UI/Inflight/WarpToMoonMiningPoint'],
    151          const.groupReprocessingArray: ['UI/Inflight/POS/AccessPOSRefinery'],
    152          const.groupCompressionArray: ['UI/Inflight/POS/AccessPOSCompression'],
    153          const.groupMobileReactor: ['UI/Inflight/POS/AccessPOSStorage'],
    154          const.groupControlTower: ['UI/Inflight/POS/AccessPOSFuelBay', 'UI/Inflight/POS/AccessPOSStrontiumBay'],
    155          const.groupSilo: ['UI/Inflight/POS/AccessPOSStorage'],
    156          const.groupAssemblyArray: ['UI/Inflight/POS/AccessPOSStorage'],
    157          const.groupMobileLaboratory: ['UI/Inflight/POS/AccessPOSStorage'],
    158          const.groupCorporateHangarArray: ['UI/Inflight/POS/AccessPOSStorage'],
    159          const.groupPersonalHangar: ['UI/Inflight/POS/AccessPOSStorage'],
    160          const.groupMobileMissileSentry: ['UI/Inflight/POS/AccessPOSAmmo'],
    161          const.groupMobileHybridSentry: ['UI/Inflight/POS/AccessPOSAmmo'],
    162          const.groupMobileProjectileSentry: ['UI/Inflight/POS/AccessPOSAmmo'],
    163          const.groupMobileLaserSentry: ['UI/Inflight/POS/AccessPOSCrystalStorage'],
    164          const.groupShipMaintenanceArray: ['UI/Inflight/POS/AccessPOSVessels'],
    165          const.groupStargate: ['UI/Inflight/Jump'],
    166          const.groupWormhole: ['UI/Inflight/EnterWormhole'],
    167          const.groupWarpGate: ['UI/Inflight/ActivateGate'],
    168          const.groupAbyssalTraces: ['UI/Inflight/ActivateGate'],
    169          const.groupBillboard: ['UI/Commands/ReadNews'],
    170          const.groupAgentsinSpace: ['UI/Chat/StartConversation'],
    171          const.groupDestructibleAgentsInSpace: ['UI/Chat/StartConversation'],
    172          const.groupMiningDrone: ['UI/Drones/MineWithDrone', 'UI/Drones/ReturnDroneAndOrbit', ('UI/Drones/LaunchDrones', 'UI/Drones/ReturnDroneToBay', 'UI/Drones/ScoopDroneToBay')],
    173          const.groupSalvageDrone: ['UI/Drones/Salvage', 'UI/Drones/ReturnDroneAndOrbit', ('UI/Drones/LaunchDrones', 'UI/Drones/ReturnDroneToBay', 'UI/Drones/ScoopDroneToBay')]}
    174         structureOptions = ['UI/Inflight/DockInStation', 'UI/Inflight/AccessHangarTransfer']
    175         self.categories = {const.categoryShip: ['UI/Inflight/BoardShip'],
    176          const.categoryStructure: structureOptions,
    177          const.categoryDrone: ['UI/Drones/EngageTarget', 'UI/Drones/ReturnDroneAndOrbit', ('UI/Drones/LaunchDrones', 'UI/Drones/ReturnDroneToBay', 'UI/Drones/ScoopDroneToBay')]}
    178 
    179     def UpdateActions(self):
    180         self.actions = ICON_ID_AND_CMD_BY_ACTIONID.copy()
    181         self.subMenus = ['UI/Inflight/MoonMiningPoint']
    182 
    183     def Blink(self, on_off = 1):
    184         if on_off and self.sr.blink is None:
    185             self.sr.blink = Fill(parent=self.sr.top, padding=(1, 1, 1, 1), color=(1.0, 1.0, 1.0, 0.25))
    186         if on_off:
    187             sm.GetService('ui').BlinkSpriteA(self.sr.blink, 0.25, 500, None, passColor=0)
    188         elif self.sr.blink:
    189             sm.GetService('ui').StopBlink(self.sr.blink)
    190             b = self.sr.blink
    191             self.sr.blink = None
    192             b.Close()
    193 
    194     def BlinkBtn(self, key):
    195         for btn in self.sr.actions.children:
    196             if btn.name.replace(' ', '').lower() == key.replace(' ', '').lower():
    197                 sm.GetService('ui').BlinkSpriteA(btn.children[0], 1.0, 500, None, passColor=0)
    198             else:
    199                 sm.GetService('ui').StopBlink(btn.children[0])
    200 
    201     def OnResizeUpdate(self, *args):
    202         self.CheckActions(forceSizeUpdate=1, ignoreScaling=1)
    203 
    204     def OnActiveTrackingChange(self, isActivelyTracking):
    205         self.AdjustUIToActiveTrackingStatus(isActivelyTracking)
    206 
    207     def AdjustUIToActiveTrackingStatus(self, isTracking):
    208         caption = localization.GetByLabel('UI/Inflight/ActiveItem/SelectedItem')
    209         if isTracking:
    210             caption += ' - ' + localization.GetByLabel('UI/Inflight/ActiveItem/Tracking')
    211         self.SetCaption(caption)
    212 
    213     @telemetry.ZONE_METHOD
    214     def CheckActions(self, forceSizeUpdate = 0, ignoreScaling = 0):
    215         if self.destroyed or not self.sr.actions:
    216             return
    217         self.sr.toparea.height = max(40, self.sr.text.textheight + self.sr.text.top * 2)
    218         scaling = getattr(self, 'scaling', 0) if ignoreScaling == 0 else 0
    219         if forceSizeUpdate and not scaling and self.ImVisible():
    220             l, t, w, h = self.GetAbsolute()
    221             al, at = self.sr.actions.GetAbsolutePosition()
    222             maxHeight = t + h - at - 8
    223             self.LayoutButtons(self.sr.actions, uiconst.UI_PICKCHILDREN, maxHeight)
    224             gl, gt, gr, gb = self.GetGroupRect(self.sr.actions.children)
    225             diff = gb - (t + h - 6)
    226             if diff > 0:
    227                 self.SetHeight_PushOrPullWindowsBelow(self.height + diff)
    228 
    229     @telemetry.ZONE_METHOD
    230     def LayoutButtons(self, parent, state = None, maxHeight = None):
    231         if parent is None:
    232             return
    233         l, t, w, h = parent.GetAbsolute()
    234         colwidth = 33
    235         if w <= colwidth:
    236             return
    237         perRow = w / colwidth
    238         isSmall = len(parent.children) / float(perRow) > 2.0
    239         iconSize = 24 if isSmall else 32
    240         if maxHeight and iconSize > maxHeight:
    241             iconSize = 24
    242         if len(parent.children) * (iconSize + 1) + 1 > w:
    243             iconSize = 24
    244         left = 1
    245         top = 0
    246         parent.height = iconSize + 1
    247         for icon in parent.children:
    248             if l + left + iconSize + 1 >= l + w:
    249                 left = 1
    250                 top += iconSize + 1
    251                 parent.height += iconSize + 1
    252             icon.left = left
    253             icon.top = top
    254             icon.width = iconSize
    255             icon.height = iconSize
    256             if state is not None:
    257                 icon.state = state
    258             left += iconSize + 1
    259 
    260     def _OnClose(self, *args):
    261         self.sr.updateTimer = None
    262 
    263     def ProcessSessionChange(self, isRemote, session, change):
    264         self.lastActionSerial = None
    265         self.lastActionDist = None
    266         self.lastCloakingStatus = None
    267         self.lastSessionChange = blue.os.GetWallclockTime()
    268 
    269     def OnDronesSelected(self, itemIDs):
    270         self.OnMultiSelect(itemIDs)
    271 
    272     def OnMultiSelect(self, itemIDs):
    273         self.itemIDs = itemIDs
    274         self.lastActionSerial = None
    275         self.lastActionDist = None
    276         self.lastBountyCheck = None
    277         self.bounty = None
    278         if self.ImVisible():
    279             uthread.pool('ActiveItem::UpdateAll', self.UpdateAll, 1)
    280 
    281     def OnStateChange(self, itemID, flag, true, *args):
    282         if itemID != session.shipid and not self.destroyed:
    283             uthread.new(self._OnStateChange, itemID, flag, true)
    284 
    285     @telemetry.ZONE_METHOD
    286     def _OnStateChange(self, itemID, flag, true):
    287         if flag == state.selected and true:
    288             self.OnMultiSelect([itemID])
    289         if itemID in self.itemIDs and flag not in (state.selected, state.mouseOver):
    290             self.lastActionSerial = None
    291             self.lastActionDist = None
    292             self.UpdateAll()
    293 
    294     def OnDistSettingsChange(self):
    295         uthread.new(self._OnDistSettingsChange)
    296 
    297     @telemetry.ZONE_METHOD
    298     def _OnDistSettingsChange(self):
    299         self.ResetLastActionAndCloakingAndUpdateAll()
    300 
    301     def ResetLastActionAndCloakingAndUpdateAll(self):
    302         self.lastActionSerial = None
    303         self.lastCloakingStatus = None
    304         self.UpdateAll(1)
    305 
    306     def TryGetInvItem(self, itemID):
    307         if session.shipid is None:
    308             return
    309         ship = sm.GetService('invCache').GetInventoryFromId(session.shipid)
    310         if ship:
    311             for invItem in ship.List():
    312                 if invItem.itemID == itemID:
    313                     return invItem
    314 
    315     def GetItem(self, itemID):
    316         item = uix.GetBallparkRecord(itemID)
    317         if not item:
    318             item = self.TryGetInvItem(itemID)
    319         return item
    320 
    321     def Load(self, itemID):
    322         pass
    323 
    324     def OnTabSelect(self, *args):
    325         if not getattr(self, 'inited', 0):
    326             return
    327         self.ResetLastActionAndCloakingAndUpdateAll()
    328 
    329     def OnExpanded(self, *args):
    330         if not getattr(self, 'inited', 0):
    331             return
    332         super(ActiveItem, self).OnExpanded(*args)
    333         self.ResetLastActionAndCloakingAndUpdateAll()
    334 
    335     def OnEndMaximize_(self, *args):
    336         if not getattr(self, 'inited', 0):
    337             return
    338         self.ResetLastActionAndCloakingAndUpdateAll()
    339 
    340     def FadeInSelf(self):
    341         self.opacity = 0.001
    342         super(ActiveItem, self).FadeInSelf()
    343         if not getattr(self, 'inited', 0):
    344             return
    345         self.ResetLastActionAndCloakingAndUpdateAll()
    346 
    347     def ShowNoSelectedHint(self, hint = None):
    348         if self.sr.noSelectedHint is None and hint:
    349             self.sr.noSelectedHint = eveLabel.EveCaptionMedium(text=hint, parent=self.sr.main, align=uiconst.RELATIVE, left=16, top=18, width=256, color=TextColor.SECONDARY)
    350         elif self.sr.noSelectedHint:
    351             if hint:
    352                 self.sr.noSelectedHint.text = hint
    353                 self.sr.noSelectedHint.state = uiconst.UI_DISABLED
    354             else:
    355                 self.sr.noSelectedHint.state = uiconst.UI_HIDDEN
    356 
    357     def FlushContent(self):
    358         self.SetText('')
    359         self.ShowNoSelectedHint(localization.GetByLabel('UI/Inflight/ActiveItem/NoObjectSelected'))
    360         self.sr.actions.Flush()
    361         self.sr.iconpar.state = uiconst.UI_HIDDEN
    362         self.sr.chariconpar.state = uiconst.UI_HIDDEN
    363 
    364     @telemetry.ZONE_METHOD
    365     def UpdateAll(self, updateActions = 0):
    366         if not self or self.destroyed:
    367             return
    368         if session.shipid in self.itemIDs:
    369             self.itemIDs.remove(session.shipid)
    370         bp = sm.GetService('michelle').GetBallpark()
    371         if not self.ImVisible() or not bp or not self.itemIDs:
    372             self.sr.updateTimer = None
    373             self.FlushContent()
    374             return
    375         goForSlim = 1
    376         slimItems = []
    377         invItems = []
    378         fleetMember = None
    379         for itemID in self.itemIDs:
    380             blue.pyos.BeNice()
    381             if sm.GetService('fleet').IsMember(itemID):
    382                 fleetMember = cfg.eveowners.Get(itemID)
    383                 break
    384             slimItem = None
    385             if goForSlim:
    386                 slimItem = uix.GetBallparkRecord(itemID)
    387                 if slimItem:
    388                     slimItems.append(slimItem)
    389             if not slimItem:
    390                 invItem = self.TryGetInvItem(itemID)
    391                 if invItem:
    392                     invItems.append(invItem)
    393                     goForSlim = 0
    394 
    395         if not slimItems and not invItems and not fleetMember:
    396             self.itemIDs = []
    397             self.lastActionSerial = None
    398             self.lastActionDist = None
    399             self.FlushContent()
    400             return
    401         if not self or self.destroyed:
    402             return
    403         text = ''
    404         blue.pyos.BeNice()
    405         updateActions = updateActions or 0
    406         typeID = None
    407         fleetSlim = None
    408         if fleetMember:
    409             multi = 1
    410             text = fleetMember.name
    411             typeID = fleetMember.typeID
    412             fleetSlim = self.GetSlimItemForCharID(fleetMember.id)
    413             blue.pyos.BeNice()
    414         elif invItems:
    415             text = uix.GetItemName(invItems[0])
    416             typeID = invItems[0].typeID
    417             multi = len(invItems)
    418             blue.pyos.BeNice()
    419         elif slimItems:
    420             text = uix.GetSlimItemName(slimItems[0])
    421             typeID = slimItems[0].typeID
    422             multi = len(slimItems)
    423             if multi == 1:
    424                 slimItem = slimItems[0]
    425                 itemID = slimItem.itemID
    426                 ball = bp.GetBall(itemID)
    427                 if not ball:
    428                     self.itemIDs = []
    429                     self.sr.updateTimer = None
    430                     self.FlushContent()
    431                     return
    432                 if slimItem.groupID == const.groupStargate:
    433                     text = u'{text} ({security_status})'.format(text=text, security_status=eveformat.solar_system_security_status(slimItem.jumps[0].locationID))
    434                 dist = ball.surfaceDist
    435                 if dist is not None:
    436                     md = None
    437                     myball = bp.GetBall(session.shipid)
    438                     if myball:
    439                         md = myball.mode
    440                         if self.lastCloakingStatus is None or self.lastCloakingStatus != myball.isCloaked:
    441                             updateActions = 1
    442                         self.lastCloakingStatus = myball.isCloaked
    443                     text += '<br>' + localization.GetByLabel('UI/Inflight/ActiveItem/SelectedItemDistance', distToItem=FmtDist(dist, maxdemicals=1))
    444                     if not self.lastActionDist or md != self.lastActionDist[1] or self.CheckDistanceUpdate(self.lastActionDist[0], dist):
    445                         self.lastActionDist = (dist, md)
    446                         updateActions = 1
    447                 sec = slimItem.securityStatus
    448                 if sec:
    449                     text += '<br>' + localization.GetByLabel('UI/Inflight/ActiveItem/SelectedItemSecurity', secStatus=sec)
    450                 if slimItem.categoryID == const.categoryStructure:
    451                     timerText = self.GetStructureTimerText(slimItem)
    452                     if timerText:
    453                         text += timerText
    454                         updateActions = 1
    455             blue.pyos.BeNice()
    456         corpID = None
    457         charID = None
    458         categoryID = None
    459         bountyItemID = None
    460         bountyTypeID = None
    461         bountySlim = None
    462         displayUtilMenu = False
    463         if multi > 1:
    464             text += '<br>' + localization.GetByLabel('UI/Inflight/ActiveItem/MultipleItems', itemCount=multi)
    465             blue.pyos.BeNice()
    466         elif multi == 1:
    467             if slimItems:
    468                 slim = slimItems[0]
    469                 if slim.categoryID == const.categoryShip:
    470                     if idCheckers.IsCharacter(slim.charID):
    471                         charID = slim.charID
    472                         categoryID = slim.categoryID
    473                 if slim.categoryID == const.categoryEntity:
    474                     bountyTypeID = slim.typeID
    475                     bountySlim = slim
    476                 elif slim.charID:
    477                     bountyItemID = slim.charID
    478                     bountySlim = slim
    479                 killRightID, price = self.bountySvc.GetBestKillRight(slim.charID)
    480                 self.utilMenu.UpdateKillRightInfo(killRightID, price, slim.charID, slim.itemID)
    481                 stateSvc = sm.GetService('stateSvc')
    482                 if killRightID is not None and not (stateSvc.CheckSuspect(slim) or stateSvc.CheckCriminal(slim)) and slim.categoryID != const.categoryStructure:
    483                     displayUtilMenu = True
    484             blue.pyos.BeNice()
    485         self.sr.utilMenuArea.display = displayUtilMenu
    486         self.utilMenu.display = displayUtilMenu
    487         if self.lastIcon != (typeID, itemID, charID):
    488             uthread.pool('ActiveItem::GetIcon', self.GetIcon, typeID, itemID, charID, corpID, categoryID)
    489             self.lastIcon = (typeID, itemID, charID)
    490         else:
    491             self.sr.iconpar.state = uiconst.UI_PICKCHILDREN
    492             if categoryID == const.categoryShip and charID:
    493                 self.sr.chariconpar.state = uiconst.UI_PICKCHILDREN
    494         if (bountyItemID, bountyTypeID) != self.lastBountyCheck:
    495             bounty, bountyHint, reducedBountyIndication = self.CheckBounty(bountyTypeID, bountySlim)
    496             blue.pyos.BeNice()
    497             if bounty:
    498                 self.bounty = localization.GetByLabel('UI/Common/BountyAmount', bountyAmount=eveFormat.FmtISK(bounty, 0))
    499             else:
    500                 self.bounty = None
    501             self.lastBountyCheck = (bountyItemID, bountyTypeID)
    502             self.lastBountyInfo = (bountyHint, reducedBountyIndication)
    503         else:
    504             bountyHint, reducedBountyIndication = self.lastBountyInfo
    505         if self.bounty:
    506             text += '<br>'
    507             text += self.bounty
    508         if reducedBountyIndication:
    509             text += reducedBountyIndication
    510         if updateActions:
    511             self.ReloadActions(slimItems, invItems, fleetMember, fleetSlim)
    512         else:
    513             self.CheckActions(1)
    514         self.SetText(text, bountyHint)
    515         self.ShowNoSelectedHint()
    516         blue.pyos.BeNice()
    517         self.laseUpdateWidth = self.absoluteRight - self.absoluteLeft
    518         if not self.sr.updateTimer and not invItems:
    519             self.sr.updateTimer = AutoTimer(500, self.UpdateAll)
    520 
    521     def GetStructureTimerText(self, slimItem):
    522         timerText = ''
    523         isVulnerable = structures.STATE_VULNERABILITY.get(slimItem.state, None)
    524         if isVulnerable is not False:
    525             return timerText
    526         if slimItem.timer:
    527             start, end, paused = slimItem.timer
    528             if paused:
    529                 end = end + blue.os.GetWallclockTime() - paused
    530             stateText = localization.GetByLabel(structures.STATE_LABELS[slimItem.state])
    531             label = localization.GetByLabel('UI/Inflight/ActiveItem/InStateUntil', structureState=stateText, timestamp=FmtDate(end))
    532             timerText = '<br>%s' % label
    533         return timerText
    534 
    535     @telemetry.ZONE_METHOD
    536     def DoBallsRemove(self, pythonBalls, isRelease):
    537         for ball, slimItem, terminal in pythonBalls:
    538             self.DoBallRemove(ball, slimItem, terminal)
    539 
    540     def DoBallRemove(self, ball, slimItem, terminal):
    541         if self.ImVisible() and ball and ball.id in self.itemIDs:
    542             uthread.pool('ActiveItem::UpdateAll', self.UpdateAll, 1)
    543 
    544     def SetText(self, text, hint = None):
    545         if text != self.sr.text.text:
    546             self.sr.text.text = text
    547             self.sr.text.hint = hint
    548             self.CheckActions(1)
    549 
    550     def GetIcon(self, typeID, itemID, charID, corpID, categoryID):
    551         self.sr.icon.LoadIconByTypeID(typeID, itemID=itemID, ignoreSize=True)
    552         self.sr.icon.typeID = typeID
    553         self.sr.icon.itemID = itemID
    554         self.sr.icon.categoryID = categoryID
    555         self.sr.iconpar.state = uiconst.UI_PICKCHILDREN
    556         self.sr.chariconpar.state = uiconst.UI_HIDDEN
    557         self.sr.pushCont.width = 39
    558         if categoryID == const.categoryShip and charID:
    559             typeID = cfg.eveowners.Get(charID).typeID
    560             self.sr.charicon.LoadIconByTypeID(typeID, itemID=charID, ignoreSize=True)
    561             self.sr.charicon.typeID = typeID
    562             self.sr.charicon.itemID = charID
    563             self.sr.pushCont.width = 75
    564             self.sr.chariconpar.state = uiconst.UI_PICKCHILDREN
    565 
    566     def GetShipDragData(self):
    567         categoryID = self.sr.icon.categoryID
    568         if categoryID != const.categoryShip:
    569             return []
    570         typeID = self.sr.icon.typeID
    571         fakeNode = utillib.KeyVal()
    572         fakeNode.__guid__ = 'listentry.Item'
    573         fakeNode.typeID = typeID
    574         fakeNode.itemID = self.sr.icon.itemID
    575         label = evetypes.GetName(typeID)
    576         charID = self.sr.charicon.itemID
    577         if charID:
    578             label = '%s (%s)' % (cfg.eveowners.Get(charID).name, label)
    579         fakeNode.genericDisplayLabel = label
    580         return [fakeNode]
    581 
    582     def GetCharDragData(self, *args):
    583         charID = self.sr.charicon.itemID
    584         if not charID:
    585             return []
    586         fakeNode = utillib.KeyVal()
    587         fakeNode.charID = charID
    588         fakeNode.typeID = self.sr.charicon.typeID
    589         fakeNode.itemID = charID
    590         fakeNode.info = cfg.eveowners.Get(charID)
    591         fakeNode.__guid__ = 'listentry.User'
    592         return [fakeNode]
    593 
    594     def CheckBounty(self, bountyTypeID, slimItem):
    595         bounty = None
    596         bountyHint = None
    597         reducedBountyIndication = None
    598         if bountyTypeID:
    599             bountyAmount = get_entity_bounty(bountyTypeID, slimItem.itemID)
    600             if bountyAmount:
    601                 bounty, bountyHint, reducedBountyIndication = self.GetNpcBounty(bountyAmount)
    602         elif slimItem:
    603             if self.bountySvc.CanHaveBounty(slimItem):
    604                 bounty = self.bountySvc.GetBounty(slimItem.charID, slimItem.corpID, slimItem.allianceID)
    605         return (bounty, bountyHint, reducedBountyIndication)
    606 
    607     def GetNpcBounty(self, bountyAmount):
    608         return (bountyAmount, None, None)
    609 
    610     def GetSlimItemForCharID(self, charID):
    611         ballpark = sm.GetService('michelle').GetBallpark()
    612         if ballpark:
    613             for itemID, rec in ballpark.slimItems.iteritems():
    614                 if rec.charID == charID:
    615                     return rec
    616 
    617     def ShowInfo(self, btn, *args):
    618         if btn and btn.typeID:
    619             sm.GetService('info').ShowInfo(btn.typeID, btn.itemID)
    620 
    621     def GetIconMenu(self, btn, *args):
    622         if btn and btn.typeID:
    623             return GetMenuService().GetMenuFromItemIDTypeID(getattr(btn, 'itemID', None), btn.typeID)
    624 
    625     def CheckDistanceUpdate(self, lastdist, dist):
    626         diff = abs(lastdist - dist)
    627         if not diff:
    628             return False
    629         elif dist:
    630             return bool(diff / dist > 0.01)
    631         else:
    632             return bool(lastdist != dist)
    633 
    634     def ImVisible(self):
    635         return bool(not self.IsCollapsed() and not self.IsMinimized() and self.opacity > 0 and self.display)
    636 
    637     @telemetry.ZONE_METHOD
    638     def ReloadActions(self, slimItems, invItems, fleetMember, fleetSlim):
    639         if not self or self.destroyed:
    640             return
    641         if not self.ImVisible():
    642             self.sr.updateTimer = None
    643             return
    644         itemIDsAtStart = self.itemIDs[:]
    645         actions = []
    646         if invItems:
    647             data = [ (invItem, 0, None) for invItem in invItems ]
    648             actions = StartMenuService().InvItemMenu(data)
    649         elif slimItems:
    650             if len(slimItems) == 1:
    651                 actions = GetMenuService().UnmergedCelestialMenu(slimItems[0])
    652             else:
    653                 actions = GetMenuService().CelestialMenu([ (slimItem.itemID,
    654                  None,
    655                  slimItem,
    656                  None,
    657                  None) for slimItem in slimItems ])
    658         elif fleetSlim:
    659             actions = GetMenuService().CelestialMenu(fleetSlim.itemID, slimItem=fleetSlim)
    660         elif fleetMember:
    661             actions = GetMenuService().CharacterMenu(fleetMember.id)
    662         if itemIDsAtStart != self.itemIDs:
    663             return
    664         reasonsWhyNotAvailable = getattr(actions, 'reasonsWhyNotAvailable', {})
    665         warptoLabel = GetDefaultWarpToLabel()[0]
    666         if not self or self.destroyed:
    667             return
    668         allActions = self.GetAllActionsFromMenus(actions)
    669         serial = ''
    670         valid = {}
    671         for each in allActions:
    672             name = self._FindNameForAction(each)
    673             if name in self.actions:
    674                 valid[name] = each
    675                 if type(each[1]) not in (str, unicode):
    676                     serial += '%s_' % name
    677 
    678         blue.pyos.BeNice()
    679         if serial != self.lastActionSerial:
    680             if self.absoluteLeft == 0:
    681                 blue.pyos.synchro.Yield()
    682             if self.destroyed:
    683                 return
    684             self.sr.actions.Flush()
    685             self.sr.actions.height = 0
    686             typeID = None
    687             groupID = None
    688             categoryID = None
    689             if slimItems:
    690                 typeID = slimItems[0].typeID
    691                 groupID = slimItems[0].groupID
    692                 categoryID = slimItems[0].categoryID
    693             elif invItems:
    694                 typeID = invItems[0].typeID
    695                 groupID = invItems[0].groupID
    696                 categoryID = invItems[0].categoryID
    697             isAlignDisabled = type(valid.get('UI/Inflight/AlignTo', ('', ''))[1]) in (str, unicode)
    698             if isAlignDisabled:
    699                 approachLabelPath = 'UI/Inflight/ApproachObject'
    700             else:
    701                 approachLabelPath = 'UI/Inflight/AlignTo'
    702             order = [approachLabelPath, warptoLabel]
    703             if typeID and typeID in self.typeActions:
    704                 order += self.typeActions[typeID]
    705             elif groupID and groupID in self.groups:
    706                 order += self.groups[groupID]
    707             elif categoryID and categoryID in self.categories:
    708                 order += self.categories[categoryID]
    709             item = self.ChooseItem(slimItems, invItems)
    710             if item:
    711                 if HasCargoBayComponent(typeID) and not HasItemTrader(typeID):
    712                     if cargobay.IsAccessibleByCharacter(item, session.charid):
    713                         order.append('UI/Commands/OpenCargo')
    714                 if HasItemTrader(typeID):
    715                     order.append('UI/Inflight/SpaceComponents/ItemTrader/Access')
    716             order = itertoolsext.remove_duplicates_and_preserve_order(order)
    717             order += self.postorder
    718             if groupID == const.groupPlanet:
    719                 order += [('UI/PI/Common/ViewInPlanetMode', 'UI/PI/Common/ExitPlanetMode')]
    720             for actionName in order:
    721                 if actionName is None:
    722                     continue
    723                 if isinstance(actionName, tuple):
    724                     action = None
    725                     for each in actionName:
    726                         tryaction = valid.get(each, None)
    727                         if tryaction and type(tryaction[1]) not in (str, unicode):
    728                             actionName = each
    729                             action = tryaction
    730                             break
    731 
    732                     if action is None:
    733                         actionName = actionName[0]
    734                         if actionName in valid:
    735                             action = valid.get(actionName)
    736                         elif actionName in reasonsWhyNotAvailable:
    737                             action = (actionName, reasonsWhyNotAvailable.get(actionName))
    738                         else:
    739                             action = (actionName, localization.GetByLabel('UI/Menusvc/MenuHints/NoReasonGiven'))
    740                 elif actionName in valid:
    741                     action = valid.get(actionName)
    742                 elif actionName in reasonsWhyNotAvailable:
    743                     action = (actionName, reasonsWhyNotAvailable.get(actionName))
    744                 else:
    745                     action = (actionName, localization.GetByLabel('UI/Menusvc/MenuHints/NoReasonGiven'))
    746                 disabled = type(action[1]) in (str, unicode)
    747                 if isinstance(action[0], MenuLabel):
    748                     actionID = action[0][0]
    749                 else:
    750                     actionID = action[0]
    751                 par = Container(parent=self.sr.actions, align=uiconst.TOPLEFT, width=32, height=32, state=uiconst.UI_HIDDEN)
    752                 actionObject = self.actions[actionID]
    753                 par.name = actionObject.uniqueUiName or actionID
    754                 cmdName = actionObject.cmdName or ''
    755                 icon = menuAction.Action(icon=actionObject.iconPath, parent=par, align=uiconst.TOALL, disabled=disabled)
    756                 icon.uniqueUiName = actionObject.uniqueUiName
    757                 icon.actionID = actionID
    758                 icon.action = action
    759                 icon.itemIDs = self.itemIDs[:]
    760                 icon.cmdName = cmdName
    761                 if disabled:
    762                     icon.opacity = 0.5
    763                 if actionID in RESET_ACTIONS:
    764                     eveIcon.Icon(icon='ui_44_32_8', parent=par, pos=(1, 1, 0, 0), align=uiconst.TOALL, state=uiconst.UI_DISABLED, idx=0)
    765 
    766             self.lastActionSerial = serial
    767         self.CheckActions(1)
    768 
    769     def GetAllActionsFromMenus(self, actions):
    770         allActionsDict = OrderedDict()
    771         subMenuActions = []
    772         for each in actions:
    773             if not each or self._IsDisabledEntry(each):
    774                 continue
    775             name = self._FindNameForAction(each)
    776             allActionsDict[name] = each
    777             if name in self.subMenus:
    778                 for eachSubOption in each[1]:
    779                     subMenuActions.append(eachSubOption)
    780 
    781         for eachSubOption in subMenuActions:
    782             name = self._FindNameForAction(eachSubOption)
    783             if name not in allActionsDict:
    784                 allActionsDict[name] = eachSubOption
    785 
    786         allActions = allActionsDict.values()
    787         return allActions
    788 
    789     def _IsDisabledEntry(self, each):
    790         if isinstance(each, MenuEntryData):
    791             return each.IsEnabled()
    792         else:
    793             return each[1] == DISABLED_ENTRY0
    794 
    795     def _FindNameForAction(self, each):
    796         if isinstance(each[0], tuple):
    797             name = each[0][0]
    798         else:
    799             name = each[0]
    800         return name
    801 
    802     def ChooseItem(self, slimItems, invItems):
    803         if slimItems:
    804             return slimItems[0]
    805         if invItems:
    806             return invItems[0]
    807 
    808     def OnPlanetViewChanged(self, newPlanetID, oldPlanetID):
    809         for planetID in (newPlanetID, oldPlanetID):
    810             if planetID in self.itemIDs:
    811                 self.OnMultiSelect(self.itemIDs)