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

dronesWindow.py (26509B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\inflight\drones\dronesWindow.py
      2 import appConst
      3 import log
      4 import eve.client.script.ui.shared.pointerTool.pointerToolConst as pConst
      5 import eveicon
      6 import evetypes
      7 import localization
      8 import uthread
      9 import uthread2
     10 from carbon.client.script.util.misc import Uthreaded
     11 from carbonui import TextColor, uiconst
     12 from carbonui.control.contextMenu.menuEntryData import MenuEntryData
     13 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     14 from carbonui.uicore import uicore
     15 from carbonui.util.various_unsorted import SortListOfTuples
     16 from carbonui.window.header.small import SmallWindowHeader
     17 from carbonui.window.widget import WidgetWindow
     18 from eve.client.script.ui.control.entries.util import GetFromClass
     19 from eve.client.script.ui.control.eveScroll import Scroll
     20 from eve.client.script.ui.inflight import actionPanelDefaults
     21 from eve.client.script.ui.inflight.drones import droneSignals, dronesDragData
     22 from eve.client.script.ui.inflight.drones.droneEntry import DroneInSpaceEntry, DroneInBayEntry, NoDroneInBayEntry, NoDroneInSpaceEntry
     23 from eve.client.script.ui.inflight.drones.droneGroup import DroneSubGroup, DroneSubGroupInSpace, DroneSubGroupInBay
     24 from eve.client.script.ui.inflight.drones.droneGroupHeader import DroneGroupHeaderInBay, DroneGroupHeaderInSpace
     25 from eve.client.script.ui.inflight.drones.droneGroupsController import GetDroneGroupsController
     26 from eve.client.script.ui.inflight.drones.droneSettings import drones_aggressive_setting, drones_focus_fire_setting, VIEW_MODE_ICONS, drones_view_mode_setting, VIEW_MODE_LIST, drones_view_mode_compact_setting
     27 from eve.client.script.ui.inflight.drones.dronesConst import DRONESTATE_INBAY, DRONESTATE_INSPACE
     28 from eve.client.script.ui.inflight.drones.dronesDragData import HasInSpaceDroneIDs
     29 from eve.client.script.ui.inflight.drones.dronesUtil import GetDronesInLocalSpace, GetDronesInBay, GetDroneIDsInBay, GetDroneIDsInSpace, ExpandRadialMenuForInBayGroup, ExpandRadialMenuForInSpaceGroup
     30 from eve.client.script.ui.inflight.drones.dronesWarpWarning import warp_warning_enabled_setting, WarpWarning
     31 from eve.client.script.ui.services.menuSvcExtras import droneFunctions
     32 from eve.common.lib import appConst as const
     33 from eveDrones.droneConst import DAMAGESTATE_NOT_READY
     34 from eveservices.menu import GetMenuService
     35 from localization import GetByLabel
     36 from menu import MenuLabel, MenuList
     37 from utillib import KeyVal
     38 NODRONES_CLOSE_TIME = 5
     39 
     40 class DronesWindow(WidgetWindow):
     41     __notifyevents__ = ['OnAttribute',
     42      'OnAttributes',
     43      'OnDroneControlLost',
     44      'OnItemChange',
     45      'OnClientEvent_WarpStarted',
     46      'OnClientEvent_WarpFinished',
     47      'OnWarpStarted2',
     48      'ProcessSessionChange']
     49     default_windowID = 'droneview'
     50     uniqueUiName = pConst.UNIQUE_NAME_DRONE_WND
     51     default_minSize = (240, 120)
     52     default_scope = uiconst.SCOPE_INFLIGHT
     53     default_isKillable = False
     54     default_iconNum = eveicon.drones_window
     55 
     56     def get_wnd_menu_unique_name(self):
     57         return pConst.UNIQUE_NAME_DRONE_SETTINGS
     58 
     59     @staticmethod
     60     def default_left(*args):
     61         return actionPanelDefaults.GetDroneViewLeft()
     62 
     63     @staticmethod
     64     def default_top(*args):
     65         return actionPanelDefaults.GetDroneViewTop()
     66 
     67     @staticmethod
     68     def default_width():
     69         return actionPanelDefaults.GetDroneViewWidth()
     70 
     71     @staticmethod
     72     def default_height():
     73         return actionPanelDefaults.GetDroneViewHeight()
     74 
     75     def ApplyAttributes(self, attributes):
     76         self.inBayHeader = None
     77         super(DronesWindow, self).ApplyAttributes(attributes)
     78         self.panelname = attributes.panelName
     79         self.lastActionSerial = None
     80         self.actionsTimer = None
     81         self.reloadThread = None
     82         self.lastUpdateIDs = None
     83         self.reloadPending = False
     84         self.inSpace = None
     85         self.droneGroupsController = GetDroneGroupsController()
     86         self.droneGroupsController.on_groups_changed.connect(self.OnDroneGroupsChanged)
     87         if self.panelname:
     88             self.SetCaption(self.panelname)
     89         self.content.clipChildren = True
     90         self.warpWarning = WarpWarning(parent=self.content, align=uiconst.TOTOP, state=uiconst.UI_HIDDEN, padBottom=2)
     91         self.ConstructBottomScroll()
     92         self.ConstructTopScroll()
     93         openState = uicore.registry.GetListGroupOpenState(('dronegroups', DRONESTATE_INBAY), default=False)
     94         uicore.registry.GetLockedGroup('dronegroups', DRONESTATE_INBAY, localization.GetByLabel('UI/Inflight/Drone/DronesInBay'), openState=openState)
     95         openState = uicore.registry.GetListGroupOpenState(('dronegroups', DRONESTATE_INSPACE), default=False)
     96         uicore.registry.GetLockedGroup('dronegroups', DRONESTATE_INSPACE, localization.GetByLabel('UI/Inflight/Drone/DronesInLocalSpace'), openState=openState)
     97         droneSettingChanges = {appConst.attributeDroneIsAggressive: drones_aggressive_setting.is_enabled(),
     98          appConst.attributeDroneFocusFire: drones_focus_fire_setting.is_enabled()}
     99         sm.GetService('godma').GetStateManager().ChangeDroneSettings(droneSettingChanges)
    100         if self and not self.destroyed:
    101             self.CheckReloadDronesScroll()
    102         self.UpdateAll()
    103         self.on_compact_mode_changed.connect(self.OnWindowStateChanged)
    104         self.on_stacked_changed.connect(self.OnWindowStateChanged)
    105         self.on_collapsed_changed.connect(self.OnWindowStateChanged)
    106         drones_view_mode_setting.on_change.connect(self.OnDronesViewModeChanged)
    107         drones_view_mode_compact_setting.on_change.connect(self.OnDronesViewModeChanged)
    108         self.UpdateHeaderPadding()
    109         uthread2.StartTasklet(self.CheckCloseThread)
    110 
    111     def OnDroneGroupsChanged(self):
    112         self.CheckReloadDronesScroll(force=True)
    113 
    114     def ConstructTopScroll(self):
    115         self.inBayHeader = DroneGroupHeaderInBay(parent=self.content, align=uiconst.TOTOP, idx=0)
    116         self.inBayHeader.on_open_changed.connect(self.OnInBayHeaderExpandedChanged)
    117         self.inBayHeader.OnMouseMoveDrag = self.OnInBayHeaderMouseMoveDrag
    118         self.inBayHeader.OnMouseDownDrag = self.OnInBayHeaderMouseDownDrag
    119         self.inBayHeader.OnMouseUp = self.OnInBayHeaderMousUp
    120         self.topScroll = Scroll(name='dronesInBayScroll', parent=self.content, align=uiconst.TOALL, state=uiconst.UI_PICKCHILDREN)
    121         self.topScroll.sr.content.OnDropData = self.OnDropData
    122         self.topScroll.sr.content.OnDragEnter = self.OnDragEnter
    123         self.topScroll.multiSelect = 1
    124         self.topScroll.OnChar = self.OnDronesScrollChar
    125 
    126     def OnInBayHeaderMousUp(self, *args):
    127         self.OnMouseUp(*args)
    128 
    129     def OnInBayHeaderMouseDownDrag(self, *args):
    130         self.dragMousePosition = (uicore.uilib.x, uicore.uilib.y)
    131         self.OnMouseDownDrag(*args)
    132 
    133     def OnInBayHeaderMouseMoveDrag(self, *args):
    134         self.OnMouseMoveDrag(*args)
    135 
    136     def OnDropData(self, dragSource, dragData):
    137         droneIDs = dronesDragData.GetInSpaceDroneIDs(dragData)
    138         if droneIDs:
    139             droneFunctions.ReturnToDroneBay(droneIDs)
    140             droneSignals.on_drones_dropped_to_bay()
    141 
    142     def OnDragEnter(self, dragSource, dragData):
    143         if HasInSpaceDroneIDs(dragData):
    144             droneSignals.on_in_bay_entry_drag_enter()
    145 
    146     def OnInBayHeaderExpandedChanged(self, isExpanded):
    147         self.topScroll.display = isExpanded
    148         self.bottomCont.align = uiconst.TOBOTTOM if isExpanded else uiconst.TOTOP
    149         if not isExpanded and not self.inSpaceHeader.isExpanded:
    150             self.inSpaceHeader.SetAsExpanded()
    151 
    152     def ConstructBottomScroll(self):
    153         self.bottomCont = ContainerAutoSize(name='bottomCont', parent=self.content, align=uiconst.TOBOTTOM)
    154         self.inSpaceHeader = DroneGroupHeaderInSpace(parent=self.bottomCont, align=uiconst.TOTOP, padTop=4)
    155         self.inSpaceHeader.on_open_changed.connect(self.OnInSpaceHeaderExpanedChanged)
    156         self.bottomScroll = Scroll(name='dronesInSpaceScroll', align=uiconst.TOTOP, parent=self.bottomCont, state=uiconst.UI_PICKCHILDREN, padTop=2)
    157         self.bottomScroll.multiSelect = 1
    158         self.bottomScroll.OnChar = self.OnDronesScrollChar
    159         self.bottomScroll.sr.content.state = uiconst.UI_PICKCHILDREN
    160         self.bottomScroll.on_content_resize.connect(self.OnBottomScrollContentResize)
    161 
    162     def OnInSpaceHeaderExpanedChanged(self, isExpanded):
    163         self.bottomScroll.display = isExpanded
    164         if not isExpanded and not self.inBayHeader.isExpanded:
    165             self.inBayHeader.SetAsExpanded()
    166 
    167     def OnBottomScrollContentResize(self, height):
    168         self.UpdateBottomScrollHeight()
    169 
    170     def UpdateBottomScrollHeight(self):
    171         window_height = self.height if not self.stacked else self.GetCurrentAbsoluteSize()[1]
    172         maxHeight = max(window_height - (86 if self.compact else 128), 0)
    173         self.bottomScroll.height = min(self.bottomScroll.GetEntriesTotalHeight(), maxHeight)
    174 
    175     def _OnResize(self, *args, **kw):
    176         super(DronesWindow, self)._OnResize(*args, **kw)
    177         self.UpdateBottomScrollHeight()
    178 
    179     def OnDronesViewModeChanged(self, *args):
    180         self.CheckReloadDronesScroll(force=True)
    181 
    182     def UpdateHeaderPadding(self):
    183         self.extend_content_into_header = self.compact
    184         if self.compact:
    185             _, _, content_pad_right, _ = self.content_padding
    186             _, inset_right = self.header_inset
    187             reserved_spaced_right = max(inset_right - content_pad_right, 0)
    188         else:
    189             reserved_spaced_right = 0
    190         self.inBayHeader.reserved_space_right = reserved_spaced_right
    191 
    192     def OnWindowStateChanged(self, *args):
    193         self.UpdateHeaderPadding()
    194         self._update_header()
    195         self.CheckReloadDronesScroll(force=True)
    196 
    197     def Prepare_Header_(self):
    198         self._SetHeader(SmallWindowHeader(show_caption=not self.compact, fixed_height=self._get_header_fixed_height()))
    199 
    200     def _update_header(self):
    201         self.header.show_caption = not self.compact
    202         self.header.fixed_height = self._get_header_fixed_height()
    203 
    204     def _get_header_fixed_height(self):
    205         if self.compact:
    206             return 40
    207 
    208     def ProcessSessionChange(self, *etc):
    209         self.CheckReloadDronesScroll(True)
    210 
    211     ProcessSessionChange = Uthreaded(ProcessSessionChange)
    212 
    213     def OnDroneControlLost(self, droneID):
    214         self.CheckReloadDronesScroll(True)
    215 
    216     def OnAttributes(self, l):
    217         for attributeName, item, newValue in l:
    218             self.OnAttribute(attributeName, item, newValue)
    219 
    220     def OnAttribute(self, attributeName, item, newValue):
    221         if not self or self.destroyed:
    222             return
    223         if item.itemID == session.charid and attributeName == 'maxActiveDrones':
    224             t = self.lastUpdateIDs
    225             if t is None:
    226                 self.CheckReloadDronesScroll()
    227 
    228     def OnItemChange(self, item, change, location):
    229         if item.locationID == session.shipid:
    230             if item.flagID == const.flagDroneBay or change.get(const.ixFlag, None) == const.flagDroneBay:
    231                 self.CheckReloadDronesScroll()
    232         elif change.get(const.ixLocationID, None) == session.shipid and change.get(const.ixFlag, None) == const.flagDroneBay:
    233             self.CheckReloadDronesScroll()
    234 
    235     def OnDronesScrollChar(self, *args):
    236         return False
    237 
    238     def GetSelected(self, fromNode):
    239         nodes = []
    240         sel = fromNode.scroll.GetSelectedNodes(fromNode)
    241         for node in sel:
    242             if node.Get('typeID', None) is None:
    243                 continue
    244             if evetypes.GetGroupID(node.typeID) == evetypes.GetGroupID(fromNode.typeID):
    245                 if node.droneState == fromNode.droneState:
    246                     nodes.append(node)
    247 
    248         return nodes
    249 
    250     def UpdateAll(self):
    251         if self.content.state != uiconst.UI_PICKCHILDREN:
    252             self.actionsTimer = None
    253             return
    254         self.CheckReloadDronesScroll()
    255 
    256     def GetSubGroups(self, what):
    257         return []
    258 
    259     def CheckReloadDronesScroll(self, force = False, *args):
    260         if force:
    261             self.lastUpdateIDs = None
    262         if self.reloadThread:
    263             self.reloadPending = True
    264         else:
    265             self.reloadThread = uthread.new(self._CheckReloadDronesScroll)
    266 
    267     def _CheckReloadDronesScroll(self):
    268         if session.stationid:
    269             return
    270         inBayIDs = GetDroneIDsInBay()
    271         inLocalSpaceIDs = GetDroneIDsInSpace()
    272         uthread.new(sm.GetService('tactical').GetInBayDroneDamageTracker().FetchInBayDroneDamageToServer, inBayIDs)
    273         allDroneIDs = (inBayIDs, inLocalSpaceIDs)
    274         if self.lastUpdateIDs != allDroneIDs:
    275             self.lastUpdateIDs = allDroneIDs
    276             self._ReloadScroll(inBayIDs, inLocalSpaceIDs)
    277         self.CheckHint()
    278         self.UpdateWarpWarning()
    279         self.inBayHeader.UpdateLabelText()
    280         self.inSpaceHeader.UpdateLabelText()
    281         uthread2.Sleep(0.1)
    282         if self.reloadPending:
    283             self.reloadPending = False
    284             self.CheckReloadDronesScroll()
    285         self.reloadThread = None
    286 
    287     def _ReloadScroll(self, inBayIDs, inLocalSpaceIDs):
    288         self._ReloadTopScroll(inBayIDs)
    289         self._ReloadBottomScroll(inLocalSpaceIDs)
    290 
    291     def _ReloadTopScroll(self, inBayIDs):
    292         scrolllist = self.GetMainGroupEntriesInBay(DRONESTATE_INBAY, inBayIDs)
    293         self.topScroll.Load(contentList=scrolllist)
    294 
    295     def _ReloadBottomScroll(self, inLocalSpaceIDs):
    296         scrolllist = self.GetMainGroupEntriesInSpace(DRONESTATE_INSPACE, inLocalSpaceIDs)
    297         self.bottomScroll.Load(contentList=scrolllist)
    298 
    299     def CheckCloseThread(self):
    300         noDronesCounter = 0
    301         while not self.destroyed:
    302             uthread2.Sleep(1.0)
    303             if not self.HasDronesInBayOrSpace():
    304                 noDronesCounter += 1
    305             else:
    306                 noDronesCounter = 0
    307             if noDronesCounter >= NODRONES_CLOSE_TIME:
    308                 self.Close()
    309 
    310     def HasDronesInBayOrSpace(self):
    311         return GetDronesInBay() or sm.GetService('michelle').GetDrones()
    312 
    313     def GetSubFolderMenu(self, node):
    314         m = MenuList([None])
    315         data = self.droneGroupsController.GetDroneDataForSubGroup(node.droneState, node.groupName)
    316         if not data:
    317             return m
    318         if node.droneState == DRONESTATE_INSPACE:
    319             m += GetMenuService().DroneMenu(data)
    320         elif node.droneState == DRONESTATE_INBAY:
    321             m += GetMenuService().InvItemMenu(data).filter([MenuLabel('UI/Inventory/ItemActions/BuyThisType'),
    322              MenuLabel('UI/Inventory/ItemActions/AddTypeToMarketQuickbar'),
    323              MenuLabel('UI/Inventory/ItemActions/ViewTypesMarketDetails'),
    324              MenuLabel('UI/Inventory/ItemActions/FindInContracts')])
    325         favoriteGroup = self.droneGroupsController.GetFavoriteGroupID()
    326         groupIdentifier = node.id[-1]
    327         if favoriteGroup == groupIdentifier:
    328             m.append(MenuEntryData(text=MenuLabel('UI/Drones/UnmarkAsFavorite'), func=lambda : self.RemoveAsFavorite(groupIdentifier), texturePath=eveicon.star_slash))
    329         else:
    330             m.append(MenuEntryData(text=MenuLabel('UI/Drones/MarkAsFavorite'), func=lambda : self.AddAsFavorite(groupIdentifier), texturePath=eveicon.star))
    331         return m
    332 
    333     def AddAsFavorite(self, groupIdentifier):
    334         self.droneGroupsController.SetFavoriteGroupID(groupIdentifier)
    335         self.CheckReloadDronesScroll(force=True)
    336 
    337     def RemoveAsFavorite(self, groupIdentifier):
    338         favoriteGroupID = self.droneGroupsController.GetFavoriteGroupID()
    339         if groupIdentifier == favoriteGroupID:
    340             self.droneGroupsController.SetFavoriteGroupID(None)
    341             self.CheckReloadDronesScroll(force=True)
    342 
    343     def GroupMenu(self, droneNode):
    344         selected = self.GetSelected(droneNode)
    345         itemIDs = [ n.itemID for n in selected ]
    346         m = MenuList()
    347         move = [(MenuLabel('UI/Commands/NewGroup'), self.droneGroupsController.CreateSubGroup, (evetypes.GetGroupID(droneNode.typeID), itemIDs))]
    348         inGroup = []
    349         for node in selected:
    350             group = self.droneGroupsController.GetDroneGroup(node.itemID)
    351             if group:
    352                 inGroup.append(node)
    353 
    354         if inGroup:
    355             itemIDs = [ entry.itemID for entry in inGroup ]
    356             move += [(MenuLabel('UI/Commands/OutOfThisGroup'), self.droneGroupsController.RemoveFromGroup, (itemIDs,))]
    357         groupNames = self.droneGroupsController.GetGroupNames()
    358         groupNames.sort(key=lambda x: x.lower())
    359         move += [ (groupName, self.droneGroupsController.MoveToGroup, (evetypes.GetGroupID(droneNode.typeID), groupName, [ d.itemID for d in selected ])) for groupName in groupNames ]
    360         m += [(MenuLabel('UI/Commands/MoveDrone'), move)]
    361         return m
    362 
    363     def OnMouseDownOnDroneMainGroup(self, group, *args):
    364         self.OnMouseDownOnDroneMainGroup_thread(group)
    365 
    366     def OnMouseDownOnDroneMainGroup_thread(self, group):
    367         groupNode = group.sr.node
    368         droneData = self.droneGroupsController.GetDroneDataForMainGroup(groupNode.droneState)
    369         if droneData:
    370             return self.GetRadialMenuOnGroup(group, droneData)
    371 
    372     def OnMouseDownOnDroneSubGroup(self, group, *args):
    373         uthread.new(self.OnMouseDownOnDroneSubGroup_thread, group)
    374 
    375     def OnMouseDownOnDroneSubGroup_thread(self, group):
    376         groupNode = group.sr.node
    377         droneData = self.droneGroupsController.GetDroneDataForSubGroup(groupNode.droneState, groupNode.groupName)
    378         if droneData:
    379             return self.GetRadialMenuOnGroup(group, droneData)
    380 
    381     def GetRadialMenuOnGroup(self, group, droneData):
    382         groupNode = group.sr.node
    383         if groupNode.droneState == DRONESTATE_INBAY:
    384             ExpandRadialMenuForInBayGroup(group, groupNode.cleanLabel, droneData)
    385         elif groupNode.droneState == DRONESTATE_INSPACE:
    386             ExpandRadialMenuForInSpaceGroup(group, groupNode.cleanLabel, droneData)
    387 
    388     def GetSpaceDrone(self, droneID):
    389         return sm.GetService('michelle').GetDroneState(droneID)
    390 
    391     def _GetSubGroupEntryClass(self, droneState):
    392         if droneState == DRONESTATE_INSPACE:
    393             groupCls = DroneSubGroupInSpace
    394         else:
    395             if droneState == DRONESTATE_INBAY:
    396                 return DroneSubGroupInBay
    397             groupCls = DroneSubGroup
    398         return groupCls
    399 
    400     def GetSubGroupListEntry(self, group, state, items):
    401         numDrones = self.GetNumberOfDronesInGroup(state, items)
    402         icon = None
    403         groupIdentifier = group['id']
    404         favoriteGroup = self.droneGroupsController.GetFavoriteGroupID()
    405         if favoriteGroup and favoriteGroup == groupIdentifier:
    406             icon = eveicon.star
    407         cls = self._GetSubGroupEntryClass(state)
    408         return GetFromClass(cls, {'GetSubContent': self.GetSubGroupContent,
    409          'MenuFunction': self.GetSubFolderMenu,
    410          'label': localization.GetByLabel('UI/Inflight/Drone/DroneGroupWithCount', groupLabel=group['label'], count=numDrones),
    411          'id': (state, group['id']),
    412          'droneGroupID': group['droneGroupID'],
    413          'groupItems': None,
    414          'iconMargin': 18,
    415          'state': 'locked',
    416          'sublevel': 0,
    417          'droneState': state,
    418          'BlockOpenWindow': 1,
    419          'showlen': 0,
    420          'groupName': group['label'],
    421          'OnMouseDown': self.OnMouseDownOnDroneSubGroup,
    422          'showicon': icon,
    423          'isFavorite': bool(icon)})
    424 
    425     def GetNumberOfDronesInGroup(self, droneState, items):
    426         t = 0
    427         if droneState == DRONESTATE_INBAY:
    428             dronebay = {}
    429             for drone in GetDronesInBay():
    430                 dronebay[drone.itemID] = drone
    431 
    432             for droneID in items:
    433                 if droneID not in dronebay:
    434                     log.LogWarn("Drone we thought was in the dronebay wasn't actually there, droneID = ", droneID)
    435                     continue
    436                 t += dronebay[droneID].stacksize
    437 
    438         else:
    439             t = len(items)
    440         return t
    441 
    442     def GetMainGroupEntriesInBay(self, droneState, droneIDs, subLevel = 0):
    443         scrollList = []
    444         dronebay = {}
    445         for drone in GetDronesInBay():
    446             dronebay[drone.itemID] = drone
    447 
    448         subGroups = {}
    449         for droneID in droneIDs:
    450             group = self.droneGroupsController.GetDroneGroup(droneID)
    451             if group:
    452                 subGroups.setdefault(group['label'], []).append(droneID)
    453                 continue
    454             if droneID in dronebay:
    455                 entry = self.GetBayDroneEntry(dronebay[droneID], subLevel, droneState)
    456                 scrollList.append(((0, entry.label.lower()), entry))
    457 
    458         self.AppendSubGroups(scrollList, droneState, subGroups)
    459         return SortListOfTuples(scrollList)
    460 
    461     def GetMainGroupEntriesInSpace(self, droneState, droneIDs, subLevel = 0):
    462         scrollList = []
    463         subGroups = {}
    464         for droneID in droneIDs:
    465             group = self.droneGroupsController.GetDroneGroup(droneID)
    466             if group:
    467                 subGroups.setdefault(group['label'], []).append(droneID)
    468                 continue
    469             entry = self.GetSpaceDroneEntry(self.GetSpaceDrone(droneID), subLevel, droneState)
    470             scrollList.append(((0, entry.label.lower()), entry))
    471 
    472         self.AppendSubGroups(scrollList, droneState, subGroups)
    473         return SortListOfTuples(scrollList)
    474 
    475     def AppendSubGroups(self, scrollList, droneState, subGroups):
    476         for groupName, droneIDs in subGroups.iteritems():
    477             group = self.droneGroupsController.GetSubGroup(groupName)
    478             if group:
    479                 entry = self.GetSubGroupListEntry(group, droneState, droneIDs)
    480                 scrollList.append(((1, entry.label.lower()), entry))
    481 
    482         if not scrollList:
    483             noItemEntry = self.GetNoDroneInBayEntry() if droneState == DRONESTATE_INBAY else self.GetNoDroneInSpaceEntry()
    484             scrollList.append((0, noItemEntry))
    485 
    486     def GetSubGroupContent(self, nodedata, newitems = 0):
    487         scrollList = []
    488         subGroupInfo = self.droneGroupsController.GetSubGroup(nodedata.groupName)
    489         if nodedata.droneState == DRONESTATE_INBAY:
    490             drones = GetDronesInBay()
    491             for drone in drones:
    492                 if drone.itemID in subGroupInfo['droneIDs']:
    493                     entry = self.GetBayDroneEntry(drone, 1, nodedata.droneState)
    494                     scrollList.append(((entry.subLevel, entry.label), entry))
    495 
    496         elif nodedata.droneState == DRONESTATE_INSPACE:
    497             drones = GetDronesInLocalSpace()
    498             for drone in drones:
    499                 if drone.droneID in subGroupInfo['droneIDs']:
    500                     entry = self.GetSpaceDroneEntry(drone, 1, nodedata.droneState)
    501                     scrollList.append(((entry.subLevel, entry.label), entry))
    502 
    503         return SortListOfTuples(scrollList)
    504 
    505     def GetNoDroneInBayEntry(self):
    506         return GetFromClass(NoDroneInBayEntry, self._GetNoItemEntryData())
    507 
    508     def GetNoDroneInSpaceEntry(self):
    509         return GetFromClass(NoDroneInSpaceEntry, self._GetNoItemEntryData())
    510 
    511     def _GetNoItemEntryData(self):
    512         data = KeyVal()
    513         data.label = localization.GetByLabel('/Carbon/UI/Controls/Common/NoItem')
    514         data.sublevel = 0
    515         data.itemID = None
    516         data.height = 24
    517         data.fontColor = TextColor.SECONDARY
    518         data.selectable = False
    519         return data
    520 
    521     def GetDroneDamageState(self, droneID):
    522         damageTracker = sm.GetService('tactical').GetInBayDroneDamageTracker()
    523         if damageTracker.IsDroneDamageReady(droneID):
    524             damageState = damageTracker.GetDamageStateForDrone(droneID)
    525         else:
    526             damageState = DAMAGESTATE_NOT_READY
    527         return damageState
    528 
    529     def GetBayDroneEntry(self, drone, level, droneState):
    530         data = KeyVal()
    531         data.itemID = drone.itemID
    532         data.itemIDs = [drone.itemID]
    533         data.typeID = drone.typeID
    534         data.invItem = drone
    535         data.damageState = self.GetDroneDamageState(drone.itemID)
    536         data.displayName = evetypes.GetName(drone.typeID)
    537         if drone.stacksize > 1:
    538             data.label = localization.GetByLabel('UI/Inflight/Drone/DroneBayEntryWithStacksize', drone=drone.typeID, stacksize=drone.stacksize)
    539         else:
    540             data.label = evetypes.GetName(drone.typeID)
    541         data.sublevel = level
    542         data.customMenu = self.GroupMenu
    543         data.droneState = droneState
    544         return GetFromClass(DroneInBayEntry, data)
    545 
    546     def GetSpaceDroneEntry(self, drone, level, droneState):
    547         data = KeyVal()
    548         data.itemID = drone.droneID
    549         data.itemIDs = [drone.droneID]
    550         data.typeID = drone.typeID
    551         data.ownerID = drone.ownerID
    552         data.controllerID = drone.controllerID
    553         data.controllerOwnerID = drone.controllerOwnerID
    554         data.displayName = data.label = evetypes.GetName(drone.typeID)
    555         data.sublevel = level
    556         data.customMenu = self.GroupMenu
    557         data.droneState = droneState
    558         data.isWindowCompact = self.compact
    559         return GetFromClass(DroneInSpaceEntry, data)
    560 
    561     def CheckHint(self):
    562         if not self.topScroll.GetNodes():
    563             self.topScroll.ShowHint(localization.GetByLabel('UI/Inflight/Drone/NoDrones'))
    564         else:
    565             self.topScroll.ShowHint()
    566 
    567     def GetMenuMoreOptions(self):
    568         menuData = super(DronesWindow, self).GetMenuMoreOptions()
    569         menuData.AddCheckbox(text=localization.GetByLabel('UI/Drones/WarpWarningOption'), setting=warp_warning_enabled_setting)
    570         menuData.AddSeparator()
    571         menuData.AddCaption(text=GetByLabel('UI/Inventory/ViewMode'))
    572         setting = drones_view_mode_compact_setting if self.compact else drones_view_mode_setting
    573         menuData.AddRadioButton(text=GetByLabel('UI/Inventory/Icons'), value=VIEW_MODE_ICONS, setting=setting)
    574         menuData.AddRadioButton(text=GetByLabel('UI/Inventory/List'), value=VIEW_MODE_LIST, setting=setting)
    575         return menuData
    576 
    577     def OnClientEvent_WarpStarted(self, *args):
    578         self.UpdateWarpWarning()
    579 
    580     def OnWarpStarted2(self, *args):
    581         self.UpdateWarpWarning()
    582 
    583     def OnClientEvent_WarpFinished(self, *args):
    584         self.UpdateWarpWarning()
    585 
    586     def UpdateWarpWarning(self):
    587         if not warp_warning_enabled_setting.is_enabled():
    588             self.warpWarning.Hide()
    589             return
    590         michelle = sm.GetService('michelle')
    591         if michelle.IsPreparingWarp() and michelle.AreDronesOut():
    592             self.warpWarning.FadeIn()
    593         else:
    594             self.warpWarning.FadeOut()