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

fittingBrowser.py (35395B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\fittingScreen\browsers\fittingBrowser.py
      2 import itertools
      3 from collections import defaultdict
      4 import blue
      5 import log
      6 import evetypes
      7 from carbon.common.script.sys.serviceConst import ROLE_WORLDMOD, ROLEMASK_ELEVATEDPLAYER
      8 from carbonui import uiconst
      9 from carbonui.button.const import HEIGHT_NORMAL
     10 from carbonui.control.contextMenu.menuData import MenuData
     11 from carbonui.control.dragdrop.dragdata import TypeDragData
     12 from carbonui.primitives.container import Container
     13 from carbonui.primitives.fill import Fill
     14 from carbonui.primitives.sprite import Sprite
     15 from carbonui.uicore import uicore
     16 from carbonui.util.various_unsorted import SortListOfTuples
     17 from characterdata.races import get_race
     18 from characterdata.races import get_race_name
     19 from eve.client.script.ui.control.entries.fitting import FittingEntry
     20 from eve.client.script.ui.control.entries.generic import Generic
     21 from eve.client.script.ui.control.entries.util import GetFromClass
     22 from eve.client.script.ui.control.eveLabel import EveLabelMedium
     23 from eve.client.script.ui.control.listgroup import ListGroup as Group, ListGroup
     24 from eve.client.script.ui.shared.fitting.fittingUtil import CanDeleteFit, GetDeletableNodes, DeleteFittings
     25 from eve.client.script.ui.shared.fittingMgmtWindow import OpenOrLoadMultiFitWnd
     26 from eve.client.script.ui.shared.fittingScreen import FITTING_OWNER_CORP, FITTING_OWNER_COMMUNITY, FITTING_OWNER_PERSONAL, FITTING_OWNER_ALLIANCE, BROWSE_FITTINGS
     27 from eve.client.script.ui.shared.fittingScreen.shipFittingSimulationButton import ShipFittingSimulationButton
     28 from eve.client.script.ui.shared.traits import HasTraits, TraitsContainer
     29 from eve.client.script.ui.util import uix
     30 from eve.common.lib import appConst
     31 from eve.common.script.sys.eveCfg import IsControllingStructure
     32 from eve.common.script.sys.eveCfg import IsDocked
     33 from eveservices.menu import GetMenuService
     34 from globalConfig.getFunctions import AreCommunityFittingsEnabled, GetMaxShipsToFit
     35 from localization import GetByLabel
     36 from menu import MenuLabel
     37 from utillib import KeyVal
     38 NON_EMPIRE = -1
     39 MARKET_GROUP_CITADEL = 2199
     40 MARKET_GROUP_ENGINEERING_COMPLEX = 2324
     41 MARKET_GROUP_REFINERY = 2327
     42 MARKET_GROUP_NAVIGATION_STRUCTURES = 2511
     43 MARKET_GROUP_SPECIAL_SHIPS = 1612
     44 
     45 class HullBrowserListProvider(object):
     46     show_fitted_ships = True
     47 
     48     def __init__(self, onDropDataFunc, browserID = None):
     49         self.onDropDataFunc = onDropDataFunc
     50         self.fittingSvc = sm.GetService('fittingSvc')
     51         self.ghostFittingSvc = sm.GetService('ghostFittingSvc')
     52         self.specialTypeIDs = None
     53         self.currentShipTypeID = None
     54         self.browserID = browserID
     55         if session.role & ROLEMASK_ELEVATEDPLAYER:
     56             try:
     57                 self.fittingSpawner = sm.GetService('fittingspawner')
     58             except:
     59                 self.fittingSpawner = None
     60 
     61     def GetFittingScrolllist(self, currentShipTypeID, hull_filter_settings, *args):
     62         communityFittingEnabled = AreCommunityFittingsEnabled(sm.GetService('machoNet'))
     63         self.currentShipTypeID = currentShipTypeID
     64         showPersonalFittings = hull_filter_settings.fittings_show_personal.is_enabled()
     65         showCorpFittings = hull_filter_settings.fittings_show_corp.is_enabled()
     66         showAllianceFittings = bool(session.allianceid) and hull_filter_settings.fittings_show_alliance.is_enabled()
     67         onlyShowCurrentShip = hull_filter_settings.fittings_show_only_for_current_ship.is_enabled()
     68         onlyWithSkillsFor = hull_filter_settings.fittings_show_only_if_have_skills.is_enabled()
     69         showCommunityFittings = communityFittingEnabled and hull_filter_settings.fittings_show_community.is_enabled()
     70         ownerFilterIsSet = showPersonalFittings or showCorpFittings or showAllianceFittings or showCommunityFittings
     71         if not ownerFilterIsSet:
     72             showCorpFittings = True
     73             showPersonalFittings = True
     74             showAllianceFittings = True
     75             if communityFittingEnabled:
     76                 showCommunityFittings = True
     77         personalFittings, corpFittings, communityFittings, allianceFittings = self.fittingSvc.GetAllFittingsForCharacter()
     78         if hull_filter_settings.tab_selected_setting.get() != BROWSE_FITTINGS:
     79             return
     80         fittingNumByTypeID = self.GetNumShipsByTypeID(personalFittings, corpFittings, communityFittings, allianceFittings)
     81         filterText = hull_filter_settings.text_filter.get().lower()
     82         filteringSettings = KeyVal(filterTextLower=filterText, personalFittings=personalFittings, corpFittings=corpFittings, allianceFittings=allianceFittings, communityFittings=communityFittings, showPersonalFittings=showPersonalFittings, showCorpFittings=showCorpFittings, showCommunityFittings=showCommunityFittings, showAllianceFittings=showAllianceFittings, onlyShowCurrentShip=onlyShowCurrentShip, onlyWithSkillsFor=onlyWithSkillsFor, ownerFilterIsSet=ownerFilterIsSet)
     83         fittings = self.FilterOutFittings(filteringSettings)
     84         self.PopulatedSpecialEditionShips()
     85         fittingsByGroupID, shipsByGroupID, shipGroups, shipsByGroupAndRaceIDs, fittingsByGroupAndRaceIDs, unsupportedFittings = self.GetShipsAndGroups(filteringSettings, fittings)
     86         scrolllist = []
     87         noFittingsFound = len(fittings) == 0 and ownerFilterIsSet
     88         if noFittingsFound:
     89             scrolllist.append(GetFromClass(Generic, {'label': GetByLabel('UI/Common/NothingFound')}))
     90         else:
     91 
     92             def GetScrollListForShipGroups(groupsToGet, sublevel = 0, groupPrefix = ''):
     93                 listOfGroups = []
     94                 for groupName, groupID in groupsToGet:
     95                     shipsForGroup = shipsByGroupAndRaceIDs[groupID]
     96                     isGroupForCurrentShip = any((self.currentShipTypeID in x for x in shipsForGroup.itervalues()))
     97                     fittingsByRaceID = fittingsByGroupAndRaceIDs[groupID]
     98                     groupNameForID = '%s%s' % (groupPrefix, groupName)
     99                     type_ids = list(itertools.chain(*[ type_ids for type_ids in itertools.chain(shipsForGroup.values()) ]))
    100                     data = {'GetSubContent': self.GetRacesForGroup,
    101                      'label': groupName,
    102                      'groupItems': shipsForGroup,
    103                      'fittingsByRaceID': fittingsByRaceID,
    104                      'shipsByRaceID': shipsForGroup,
    105                      'id': (self.browserID, groupNameForID),
    106                      'showlen': 0,
    107                      'sublevel': sublevel,
    108                      'fittingNumByTypeID': fittingNumByTypeID,
    109                      'showCommunityFits': communityFittingEnabled,
    110                      'isGroupForCurrentShip': isGroupForCurrentShip,
    111                      'typeIDs': type_ids}
    112                     data.update(self.GetCommonGroupData())
    113                     groupEntry = (groupName, GetFromClass(ShipParentGroup, data))
    114                     listOfGroups.append(groupEntry)
    115 
    116                 listOfGroups = SortListOfTuples(listOfGroups)
    117                 return listOfGroups
    118 
    119             normalShips = {(groupName, groupID) for groupName, groupID in shipGroups if groupID > 0}
    120             shipScrollList = GetScrollListForShipGroups(normalShips)
    121             specialShips = {(groupName, groupID) for groupName, groupID in shipGroups if groupID < 0}
    122             if specialShips:
    123 
    124                 def GetSpecialShipGroup(nodedata):
    125                     return GetScrollListForShipGroups(nodedata.groupItems, sublevel=1, groupPrefix='special')
    126 
    127                 data = {'GetSubContent': GetSpecialShipGroup,
    128                  'groupItems': specialShips,
    129                  'label': GetByLabel('UI/Fitting/FittingWindow/SpecialEditionShips'),
    130                  'id': (self.browserID, 'specialShips'),
    131                  'showlen': 0,
    132                  'isGroupForCurrentShip': self.currentShipTypeID in self.specialTypeIDs,
    133                  'typeIds': ()}
    134                 data.update(self.GetCommonGroupData())
    135                 groupEntry = GetFromClass(ShipParentGroup, data)
    136                 shipScrollList.append(groupEntry)
    137             if ownerFilterIsSet and unsupportedFittings:
    138                 shipScrollList += self.AddUnsupportedGroup(unsupportedFittings)
    139             if shipScrollList:
    140                 scrolllist.extend(shipScrollList)
    141             else:
    142                 scrolllist.append(GetFromClass(Generic, {'label': GetByLabel('UI/Common/NothingFound')}))
    143         return scrolllist
    144 
    145     def AddUnsupportedGroup(self, unsupportedFittings):
    146         data = {'GetSubContent': self.GetScrollListForUnsupportedFittings,
    147          'groupItems': unsupportedFittings,
    148          'label': GetByLabel('UI/Fitting/FittingWindow/UnsupportedShip'),
    149          'id': (self.browserID, 'unsupportedFittings'),
    150          'showlen': 0}
    151         data.update(self.GetCommonGroupData())
    152         groupEntry = GetFromClass(ListGroup, data)
    153         return [groupEntry]
    154 
    155     def GetScrollListForUnsupportedFittings(self, nodedata):
    156         fittings = nodedata.groupItems
    157         scrollList = []
    158         for eachFitting in fittings:
    159             shipTypeExists = evetypes.Exists(eachFitting.shipTypeID)
    160             if shipTypeExists and session.role & ROLEMASK_ELEVATEDPLAYER:
    161                 scrollList += self.GetFittingEntriesForType([eachFitting], sublevel=0)
    162                 continue
    163             fittingName = eachFitting.name
    164             if shipTypeExists:
    165                 shipTypeName = evetypes.GetName(eachFitting.shipTypeID)
    166             else:
    167                 shipTypeName = GetByLabel('UI/Fitting/FittingWindow/UnknownShipType')
    168             fittingName = '%s (%s) - %s' % (fittingName, shipTypeName, 'unsupported')
    169             scrollList.append(GetFromClass(Generic, {'label': fittingName,
    170              'GetMenu': self.GetFittingMenuForUnsupportedFittings,
    171              'sublevel': 1,
    172              'ownerID': eachFitting.ownerID,
    173              'fittingID': eachFitting.fittingID}))
    174 
    175         return scrollList
    176 
    177     def GetFittingMenuForUnsupportedFittings(self, entry):
    178         node = entry.sr.node
    179         node.scroll.SelectNode(node)
    180         selectedNodes = node.scroll.GetSelectedNodes(node)
    181         if CanDeleteFit(node.ownerID):
    182             m = [(MenuLabel('UI/Fitting/FittingWindow/FittingManagement/DeleteFitting'), self.DeleteFitting, [entry, selectedNodes])]
    183         return m
    184 
    185     def GetCommonGroupData(self, showIcon = 'hide'):
    186         return {'state': 'locked',
    187          'BlockOpenWindow': 1,
    188          'showicon': showIcon,
    189          'DropData': self.onDropDataFunc}
    190 
    191     def GetNumShipsByTypeID(self, personalFittings, corpFittings, communityFittings, allianceFittings):
    192         fittingNumByTypeID = defaultdict(lambda : defaultdict(int))
    193         for owner, fittingDict in [(FITTING_OWNER_PERSONAL, personalFittings),
    194          (FITTING_OWNER_CORP, corpFittings),
    195          (FITTING_OWNER_COMMUNITY, communityFittings),
    196          (FITTING_OWNER_ALLIANCE, allianceFittings)]:
    197             for fitting in fittingDict.itervalues():
    198                 fittingNumByTypeID[fitting.shipTypeID][owner] += 1
    199 
    200         return fittingNumByTypeID
    201 
    202     def GetRacesForGroup(self, nodedata):
    203         shipScrollist = []
    204         fittingsByRaceID = nodedata.fittingsByRaceID
    205         shipsByRaceID = nodedata.shipsByRaceID
    206         sublevel = nodedata.sublevel + 1
    207         parentGroupNameForID = nodedata.id[1]
    208         for raceID, fittingsForRace in nodedata.groupItems.iteritems():
    209             raceIconID, raceName = self.GetNameAndIconIDForRace(raceID)
    210             groupNameForID = '%s_%s' % (parentGroupNameForID, raceID)
    211             isGroupForCurrentShip = self.currentShipTypeID in fittingsForRace
    212             data = {'GetSubContent': self.GetShipGroupSubContent,
    213              'label': raceName,
    214              'fittings': fittingsByRaceID[raceID],
    215              'groupItems': fittingsForRace,
    216              'allShips': shipsByRaceID[raceID],
    217              'id': (self.browserID, groupNameForID),
    218              'iconID': raceIconID,
    219              'sublevel': sublevel,
    220              'fittingNumByTypeID': nodedata.fittingNumByTypeID,
    221              'showCommunityFits': nodedata.showCommunityFits,
    222              'isGroupForCurrentShip': isGroupForCurrentShip,
    223              'typeIDs': shipsByRaceID[raceID]}
    224             data.update(self.GetCommonGroupData())
    225             groupEntry = ((raceIconID == NON_EMPIRE, raceName), GetFromClass(ShipParentGroup, data))
    226             shipScrollist.append(groupEntry)
    227 
    228         shipScrollist = SortListOfTuples(shipScrollist)
    229         return shipScrollist
    230 
    231     def GetNameAndIconIDForRace(self, raceID):
    232         if raceID == NON_EMPIRE:
    233             raceName = GetByLabel('UI/Fitting/FittingWindow/NonEmpireFaction')
    234             raceIconID = 1443
    235         else:
    236             race = get_race(raceID)
    237             raceName = get_race_name(raceID, race)
    238             raceIconID = race.iconID
    239         return (raceIconID, raceName)
    240 
    241     def GetMaxFittingNumber(self, ownerID):
    242         maxFittings = None
    243         if ownerID == session.charid:
    244             maxFittings = appConst.maxCharFittings
    245         elif ownerID == session.corpid:
    246             maxFittings = appConst.maxCorpFittings
    247         return maxFittings
    248 
    249     def FilterOutFittings(self, filteringSettings):
    250         fittings = {}
    251         if filteringSettings.showPersonalFittings:
    252             fittings.update(filteringSettings.personalFittings)
    253         if filteringSettings.showCorpFittings:
    254             fittings.update(filteringSettings.corpFittings)
    255         if filteringSettings.showCommunityFittings:
    256             fittings.update(filteringSettings.communityFittings)
    257         if filteringSettings.showAllianceFittings:
    258             fittings.update(filteringSettings.allianceFittings)
    259         if filteringSettings.onlyWithSkillsFor:
    260             withSkillsFor = {}
    261             for eachFittingID, eachFitting in fittings.iteritems():
    262                 if self.fittingSvc.HasSkillForFit(eachFitting):
    263                     withSkillsFor[eachFittingID] = eachFitting
    264 
    265             fittings = withSkillsFor
    266         if not filteringSettings.filterTextLower and not filteringSettings.onlyShowCurrentShip:
    267             return fittings
    268         validFittings = {}
    269         for fittingID, fitting in fittings.iteritems():
    270             if filteringSettings.onlyShowCurrentShip and self.currentShipTypeID and fitting.shipTypeID != self.currentShipTypeID:
    271                 continue
    272             if self.TextMatched(filteringSettings.filterTextLower, fitting):
    273                 validFittings[fittingID] = fitting
    274 
    275         return validFittings
    276 
    277     def TextMatched(self, filterTextLower, fitting):
    278         if not filterTextLower:
    279             return True
    280         if self.MatchedWithFittingName(filterTextLower, fitting):
    281             return True
    282         if self.MatchedWithShipOrGroupName(filterTextLower, fitting.shipTypeID):
    283             return True
    284         return False
    285 
    286     def MatchedWithShipOrGroupName(self, filterTextLower, shipTypeID):
    287         try:
    288             shipTypeName = evetypes.GetName(shipTypeID)
    289         except evetypes.TypeNotFoundException:
    290             return False
    291 
    292         matchedWithShipName = shipTypeName.lower().find(filterTextLower) >= 0
    293         if matchedWithShipName:
    294             return True
    295         try:
    296             shipGroupName = evetypes.GetGroupName(shipTypeID)
    297         except evetypes.TypeNotFoundException:
    298             return False
    299 
    300         return shipGroupName.lower().find(filterTextLower) >= 0
    301 
    302     def MatchedWithFittingName(self, filterTextLower, fitting):
    303         foundTextInFittingName = fitting.name.lower().find(filterTextLower) >= 0
    304         return foundTextInFittingName
    305 
    306     def GetShipsAndGroups(self, filteringSettings, fittings):
    307         fittingsByGroupID = defaultdict(list)
    308         fittingsByGroupAndRaceIDs = defaultdict(lambda : defaultdict(set))
    309         if not filteringSettings.ownerFilterIsSet:
    310             shipGroups, shipsByGroupID, shipsByGroupAndRaceIDs = self.GetAllShipGroupsAndShipsByGroupID(filteringSettings)
    311         else:
    312             shipGroups = set()
    313             shipsByGroupID = defaultdict(set)
    314             shipsByGroupAndRaceIDs = defaultdict(lambda : defaultdict(set))
    315         unsupportedFittings = []
    316         for fittingID, fitting in fittings.iteritems():
    317             shipTypeID = fitting.shipTypeID
    318             if not evetypes.Exists(shipTypeID):
    319                 log.LogError('Ship in stored fittings does not exist, shipID=%s, fittingID=%s' % (shipTypeID, fittingID))
    320                 unsupportedFittings.append(fitting)
    321                 continue
    322             if not evetypes.IsPublished(shipTypeID):
    323                 unsupportedFittings.append(fitting)
    324                 continue
    325             groupID = evetypes.GetGroupID(shipTypeID)
    326             if shipTypeID in self.specialTypeIDs:
    327                 groupID = -groupID
    328             fittingsByGroupID[groupID].append(fitting)
    329             groupName = evetypes.GetGroupName(shipTypeID)
    330             shipGroups.add((groupName, groupID))
    331             raceID = self._GetShipRaceID(shipTypeID)
    332             shipsByGroupAndRaceIDs[groupID][raceID].add(shipTypeID)
    333             fittingsByGroupAndRaceIDs[groupID][raceID].add(fitting)
    334 
    335         return (fittingsByGroupID,
    336          shipsByGroupID,
    337          shipGroups,
    338          shipsByGroupAndRaceIDs,
    339          fittingsByGroupAndRaceIDs,
    340          unsupportedFittings)
    341 
    342     def _GetShipRaceID(self, shipTypeID):
    343         factionID = evetypes.GetFactionID(shipTypeID)
    344         raceID = appConst.raceByFaction.get(factionID, NON_EMPIRE)
    345         return raceID
    346 
    347     def GetAllShipGroupsAndShipsByGroupID(self, filteringSettings):
    348         shipGroups = set()
    349         shipsByGroupID = defaultdict(set)
    350         shipsByGroupAndRaceIDs = defaultdict(lambda : defaultdict(set))
    351         grouplist = self._GetGroupList()
    352         shipTypesIDsFromMarket = {i for i in itertools.chain.from_iterable([ x.types for x in grouplist ])}
    353         godma = sm.GetService('godma')
    354         for shipTypeID in shipTypesIDsFromMarket:
    355             if filteringSettings.filterTextLower and not self.MatchedWithShipOrGroupName(filteringSettings.filterTextLower, shipTypeID):
    356                 continue
    357             if filteringSettings.onlyShowCurrentShip and self.currentShipTypeID != shipTypeID:
    358                 continue
    359             if filteringSettings.onlyWithSkillsFor and not godma.CheckSkillRequirementsForType(shipTypeID):
    360                 continue
    361             groupID = evetypes.GetGroupID(shipTypeID)
    362             if shipTypeID in self.specialTypeIDs:
    363                 groupID = -groupID
    364             groupName = evetypes.GetGroupName(shipTypeID)
    365             shipGroups.add((groupName, groupID))
    366             shipsByGroupID[groupID].add(shipTypeID)
    367             raceID = self._GetShipRaceID(shipTypeID)
    368             shipsByGroupAndRaceIDs[groupID][raceID].add(shipTypeID)
    369 
    370         return (shipGroups, shipsByGroupID, shipsByGroupAndRaceIDs)
    371 
    372     def _GetGroupList(self):
    373         marketGroups = sm.GetService('marketutils').GetMarketGroups()
    374         grouplist = marketGroups[appConst.marketCategoryShips][:]
    375         return grouplist
    376 
    377     def _GetStructureGroups(self, marketGroups):
    378         structureGroups = []
    379         groupsToFind = [MARKET_GROUP_ENGINEERING_COMPLEX,
    380          MARKET_GROUP_CITADEL,
    381          MARKET_GROUP_REFINERY,
    382          MARKET_GROUP_NAVIGATION_STRUCTURES]
    383         for m in marketGroups[appConst.marketCategoryStarBaseStructures]:
    384             if m.marketGroupID in groupsToFind:
    385                 structureGroups.append(m)
    386 
    387         return structureGroups
    388 
    389     def GetShipGroupSubContent(self, nodedata, *args):
    390         scrolllist = []
    391         fittingsByType = defaultdict(list)
    392         fittingNumByTypeID = nodedata.fittingNumByTypeID
    393         for fitting in nodedata.fittings:
    394             shipTypeID = fitting.shipTypeID
    395             if not evetypes.Exists(shipTypeID):
    396                 log.LogError('Ship in stored fittings does not exist, shipID=%s, fittingID=%s' % (shipTypeID, fitting.fittingID))
    397                 continue
    398             fittingsByType[shipTypeID].append(fitting)
    399 
    400         showCommunityFits = nodedata.showCommunityFits
    401         allShips = nodedata.allShips
    402         sublevel = nodedata.sublevel + 1
    403         for typeID in allShips:
    404             typeName = evetypes.GetName(typeID)
    405             numFittingForType = fittingNumByTypeID[typeID]
    406             fittingsForType = fittingsByType.get(typeID, [])
    407             entry = self.GetShipTypeGroup(typeID, typeName, fittingsForType, numFittingForType, sublevel, showCommunityFits)
    408             metaGroupID = evetypes.GetMetaGroupID(typeID)
    409             scrolllist.append(((metaGroupID, typeName), entry))
    410 
    411         scrolllist = SortListOfTuples(scrolllist)
    412         return scrolllist
    413 
    414     def GetShipTypeGroup(self, typeID, typeName, fittingsForType, numFittingForType, sublevel, showCommunityFits = False):
    415         numPersonal = numFittingForType[FITTING_OWNER_PERSONAL]
    416         numCorp = numFittingForType[FITTING_OWNER_CORP]
    417         numCommunity = numFittingForType[FITTING_OWNER_COMMUNITY]
    418         numAlliance = numFittingForType[FITTING_OWNER_ALLIANCE]
    419         label = '<b>%s</b>' % typeName
    420         subContentFunc = self.GetFittingSubContent if self.show_fitted_ships else None
    421         data = {'label': label,
    422          'typeID': typeID,
    423          'getIcon': True,
    424          'sublevel': sublevel,
    425          'maxLines': 2,
    426          'GetSubContent': subContentFunc,
    427          'hideExpander': not self.show_fitted_ships,
    428          'id': ('FittingShipTypeListGroup', typeID),
    429          'showlen': 0,
    430          'typeName': typeName,
    431          'fittings': fittingsForType,
    432          'numPersonal': numPersonal,
    433          'numCorp': numCorp,
    434          'numCommunity': numCommunity,
    435          'numAlliance': numAlliance,
    436          'showCommunityFits': showCommunityFits,
    437          'isCurrentShipType': typeID == self.currentShipTypeID}
    438         data.update(self.GetCommonGroupData(''))
    439         return GetFromClass(self.GetHullListEntryClass(), data)
    440 
    441     def GetHullListEntryClass(self):
    442         return HullTypeListGroup
    443 
    444     def GetFittingSubContent(self, nodedata, *args):
    445         return self.GetFittingEntriesForType(nodedata.fittings)
    446 
    447     def GetFittingEntriesForType(self, fittings, sublevel = 3, *args):
    448         scrolllist = []
    449         for eachFitting in fittings:
    450             fittingName = eachFitting.name
    451             if eachFitting.ownerID == session.corpid:
    452                 fittingOwnerType = FITTING_OWNER_CORP
    453             elif eachFitting.ownerID == appConst.COMMUNITY_FITTING_CORP:
    454                 fittingOwnerType = FITTING_OWNER_COMMUNITY
    455             elif eachFitting.ownerID == session.allianceid:
    456                 fittingOwnerType = FITTING_OWNER_ALLIANCE
    457             else:
    458                 fittingOwnerType = FITTING_OWNER_PERSONAL
    459             sortBy = (fittingOwnerType, fittingName.lower())
    460             entry = GetFromClass(FittingEntry, {'label': fittingName,
    461              'fittingID': eachFitting.fittingID,
    462              'fitting': eachFitting,
    463              'ownerID': eachFitting.ownerID,
    464              'showinfo': 1,
    465              'showicon': 'hide',
    466              'sublevel': sublevel,
    467              'ignoreRightClick': 1,
    468              'OnClick': self.OnFittingClicked,
    469              'OnDropData': self.onDropDataFunc,
    470              'GetMenu': self.GetFittingMenu,
    471              'ownerType': fittingOwnerType})
    472             scrolllist.append((sortBy, entry))
    473 
    474         scrolllist = SortListOfTuples(scrolllist)
    475         return scrolllist
    476 
    477     def OnFittingClicked(self, entry, *args):
    478         if uicore.uilib.Key(uiconst.VK_SHIFT) or uicore.uilib.Key(uiconst.VK_CONTROL):
    479             return
    480         fitting = entry.sr.node.fitting
    481         typesInUse = {x[0] for x in fitting.fitData}
    482         obsoleteSubSystems = {x for x in typesInUse if evetypes.GetGroupID(x) == appConst.groupElectronicSubSystems}
    483         if obsoleteSubSystems:
    484             self.fittingSvc.DisplayFitting(fitting)
    485         else:
    486             self.ghostFittingSvc.SimulateFitting(fitting)
    487 
    488     def GetFittingMenu(self, entry):
    489         node = entry.sr.node
    490         selectedNodes = node.scroll.GetSelectedNodes(node)
    491         multiSelected = len(selectedNodes) > 1
    492         fittingID = entry.sr.node.fittingID
    493         ownerID = entry.sr.node.ownerID
    494         maxShipsAllowed = GetMaxShipsToFit(sm.GetService('machoNet'))
    495         m = []
    496         if not multiSelected:
    497             m += [(GetByLabel('UI/Fitting/FittingWindow/ViewFitting'), self.ViewFitting, [node])]
    498             m += [(GetByLabel('UI/Fitting/FittingWindow/SimulateFitting'), self.OnFittingClicked, [entry])]
    499             if not IsControllingStructure() and evetypes.GetCategoryID(node.fitting.shipTypeID) != appConst.categoryStructure:
    500                 m += [(MenuLabel('UI/Fitting/FittingWindow/FitFittingToActiveShip'), self.FitToActiveShip, [ownerID, fittingID])]
    501             m += [(MenuLabel('UI/Market/MarketQuote/BuyAll'), self.BuyItemsForFit, [node.fitting])]
    502             if maxShipsAllowed and IsDocked():
    503                 m += [(MenuLabel('UI/Fitting/FittingWindow/FittingManagement/OpenMultifit'), self.DoBulkFit, [entry])]
    504             if session.role & ROLE_WORLDMOD:
    505                 fittingSpawner = sm.GetService('fittingspawner')
    506                 m.append(None)
    507                 m.append(('DEV Hax This Together!', fittingSpawner.SpawnFitting, [ownerID, node.fitting]))
    508                 m.append(('DEV: fittingID=%s' % fittingID, blue.pyos.SetClipboardData, (str(fittingID),)))
    509         m += [None]
    510         deletable = GetDeletableNodes(selectedNodes)
    511         if deletable:
    512             m += [(MenuLabel('UI/Fitting/FittingWindow/FittingManagement/DeleteFitting'), self.DeleteFitting, [entry, deletable])]
    513         return m
    514 
    515     def DeleteFitting(self, entry, selectedNodes):
    516         DeleteFittings(selectedNodes)
    517 
    518     def DoBulkFit(self, entry):
    519         fitting = entry.sr.node.fitting
    520         OpenOrLoadMultiFitWnd(fitting)
    521 
    522     def ViewFitting(self, node):
    523         fitting = node.fitting
    524         self.fittingSvc.DisplayFitting(fitting)
    525 
    526     def PopulatedSpecialEditionShips(self):
    527         if self.specialTypeIDs is None:
    528             specialCategory = sm.GetService('marketutils').GetMarketGroup(MARKET_GROUP_SPECIAL_SHIPS)
    529             typeIDs = set()
    530             for eachGroup in specialCategory:
    531                 typeIDs.update(eachGroup.types)
    532 
    533             self.specialTypeIDs = typeIDs
    534 
    535     def FitToActiveShip(self, ownerID, fittingID):
    536         self.fittingSvc.LoadFittingFromFittingIDAndGetBuyOptionOnFailure(ownerID, fittingID)
    537 
    538     def BuyItemsForFit(self, fitting):
    539         shipTypeID = fitting.shipTypeID
    540         fitData = fitting.fitData
    541         from shipfitting.multiBuyUtil import BuyFit
    542         BuyFit(shipTypeID, fitData)
    543 
    544 
    545 class ShipParentGroup(Group):
    546     isDragObject = True
    547 
    548     def Load(self, node):
    549         Group.Load(self, node)
    550         containesMyShip = node.isGroupForCurrentShip
    551         if containesMyShip:
    552             self.sr.expander.icon.ignoreColorBlindMode = False
    553             self.sr.expander.SetRGBA(0.0, 0.5, 0.0, 0.8)
    554 
    555     def GetDragData(self):
    556         return [ TypeDragData(type_id) for type_id in self.sr.node.typeIDs ]
    557 
    558     def GetMenu(self):
    559         menuData = MenuData()
    560         menuData.AddEntry(MenuLabel('UI/Compare/CompareButton'), lambda : GetMenuService().CompareTypes(self.sr.node.typeIDs))
    561         menuData.AddSeparator()
    562         return menuData + super(ShipParentGroup, self).GetMenu()
    563 
    564 
    565 class FittingHullBrowserListProvider(HullBrowserListProvider):
    566 
    567     def GetHullListEntryClass(self):
    568         return FittingHullTypeListGroup
    569 
    570     def _GetGroupList(self):
    571         groupList = super(FittingHullBrowserListProvider, self)._GetGroupList()
    572         marketGroups = sm.GetService('marketutils').GetMarketGroups()
    573         groupList.extend(self._GetStructureGroups(marketGroups))
    574         return groupList
    575 
    576 
    577 class ComparisonHullBrowserListProvider(HullBrowserListProvider):
    578     show_fitted_ships = False
    579 
    580 
    581 class HullTypeListGroup(Group):
    582     iconSize = 32
    583     default_iconSize = iconSize
    584     isDragObject = True
    585 
    586     def Startup(self, *etc):
    587         Group.Startup(self, etc)
    588         self.sr.myShipSelection = Fill(name='myShipSelection', bgParent=self, padding=(0, 1, 0, 1), color=(0.0, 0.5, 0.0, 0.1))
    589         self.sr.myShipSelection.display = False
    590         self.sr.label.maxLines = 2
    591         self.sr.label.align = uiconst.TOPLEFT
    592         self.sr.label.top = 2
    593         self.techIcon = Sprite(name='techIcon', parent=self, left=1, width=16, height=16, idx=0)
    594         self.ConstructFittingIcons()
    595         self.ConstructRightIcons()
    596 
    597     def ConstructRightIcons(self):
    598         pass
    599 
    600     def ConstructFittingIcons(self):
    601         self.fittingIconCont = Container(name='fittingIconCont', parent=self, align=uiconst.TOBOTTOM_NOPUSH, height=12, state=uiconst.UI_PICKCHILDREN, padBottom=2)
    602         self.personalCont = Container(name='personalCont', parent=self.fittingIconCont, align=uiconst.TOLEFT, width=40)
    603         personalSprite = Sprite(parent=self.personalCont, align=uiconst.CENTERLEFT, pos=(0, 0, 12, 12), texturePath='res:/UI/Texture/WindowIcons/member.png', state=uiconst.UI_NORMAL)
    604         self.personalLabel = EveLabelMedium(parent=self.personalCont, text='0', align=uiconst.CENTERLEFT, left=personalSprite.width + 2, state=uiconst.UI_NORMAL)
    605         personalSprite.hint = self.personalLabel.hint = GetByLabel('UI/Fitting/FittingWindow/SavedPersonalFittings')
    606         self.corpCont = Container(name='corpCont', parent=self.fittingIconCont, align=uiconst.TOLEFT, width=40)
    607         corpSprite = Sprite(parent=self.corpCont, align=uiconst.CENTERLEFT, pos=(0, 0, 9, 9), texturePath='res:/UI/Texture/classes/FlagIcon/2.png', state=uiconst.UI_NORMAL)
    608         self.corpLabel = EveLabelMedium(parent=self.corpCont, text='0', align=uiconst.CENTERLEFT, left=corpSprite.width + 2, state=uiconst.UI_NORMAL)
    609         corpSprite.hint = self.corpLabel.hint = GetByLabel('UI/Fitting/FittingWindow/SavedCorpFittings')
    610         self.communityCont = Container(name='communityCont', parent=self.fittingIconCont, align=uiconst.TOLEFT, width=40)
    611         communitySprite = Sprite(parent=self.communityCont, align=uiconst.CENTERLEFT, pos=(0, 0, 12, 12), texturePath='res:/UI/Texture/classes/Fitting/iconCommunityFitsSmall.png', state=uiconst.UI_NORMAL)
    612         self.communityLabel = EveLabelMedium(parent=self.communityCont, text='0', align=uiconst.CENTERLEFT, left=communitySprite.width + 2, state=uiconst.UI_NORMAL)
    613         communitySprite.hint = self.communityLabel.hint = GetByLabel('UI/Fitting/FittingWindow/SavedCommunityFittings')
    614         self.allianceCont = Container(name='allianceCont', parent=self.fittingIconCont, align=uiconst.TOLEFT, width=40)
    615         allianceSprite = Sprite(parent=self.allianceCont, align=uiconst.CENTERLEFT, pos=(0, 0, 12, 12), texturePath='res:/UI/Texture/classes/Fitting/iconAllianceSmall.png', state=uiconst.UI_NORMAL)
    616         self.allianceLabel = EveLabelMedium(parent=self.allianceCont, text='0', align=uiconst.CENTERLEFT, left=allianceSprite.width + 2, state=uiconst.UI_NORMAL)
    617         allianceSprite.hint = self.allianceLabel.hint = GetByLabel('UI/Fitting/FittingWindow/SavedAllianceFittings')
    618 
    619     def Load(self, node):
    620         Group.Load(self, node)
    621         typeID = node.typeID
    622         self.sr.icon.LoadIconByTypeID(typeID=typeID, size=self.iconSize, ignoreSize=True, isCopy=False)
    623         self.sr.icon.SetSize(self.iconSize, self.iconSize)
    624         techSprite = uix.GetTechLevelIcon(self.techIcon, 1, typeID)
    625         self.techIcon.left = self.sr.icon.left
    626         labelLeft = self.sr.icon.left + self.sr.icon.width + 4
    627         self.sr.label.left = labelLeft
    628         self.fittingIconCont.padLeft = labelLeft
    629         numPersonal = node.numPersonal
    630         if numPersonal:
    631             self.personalLabel.text = numPersonal
    632         self.personalCont.display = bool(numPersonal)
    633         numCorp = node.numCorp
    634         if numCorp:
    635             self.corpLabel.text = numCorp
    636         self.corpCont.display = bool(numCorp)
    637         numCommunity = node.numCommunity
    638         if numCommunity:
    639             self.communityLabel.text = numCommunity
    640         self.communityCont.display = bool(numCommunity) and bool(node.showCommunityFits)
    641         numAlliance = node.numAlliance
    642         if numAlliance:
    643             self.allianceLabel.text = numAlliance
    644         self.allianceCont.display = bool(numCorp)
    645         self.sr.myShipSelection.display = node.isCurrentShipType
    646 
    647     def GetHeight(self, *args):
    648         node, _ = args
    649         textwidth, textheight = EveLabelMedium.MeasureTextSize(node.label, maxLines=2)
    650         node.height = max(textheight + 6, HEIGHT_NORMAL)
    651         return node.height
    652 
    653     def GetMenu(self):
    654         typeID = self.sr.node.typeID
    655         return GetMenuService().GetMenuFromItemIDTypeID(None, typeID, includeMarketDetails=True)
    656 
    657     def GetDragData(self):
    658         typeID = self.sr.node.typeID
    659         keyVal = KeyVal(__guid__='listentry.GenericMarketItem', typeID=typeID, label=evetypes.GetName(typeID))
    660         return [keyVal]
    661 
    662     def LoadTooltipPanel(self, tooltipPanel, *args):
    663         node = self.sr.node
    664         typeID = node.typeID
    665         tooltipPanel.LoadGeneric1ColumnTemplate()
    666         tooltipPanel.AddLabelLarge(text='<b>%s</b>' % evetypes.GetName(typeID))
    667         numFittingOwners = bool(node.numPersonal) + bool(node.numCorp) + bool(node.numCommunity) + bool(node.numAlliance)
    668         useShortText = numFittingOwners >= 2
    669         if useShortText:
    670             tooltipPanel.AddLabelMedium(text='<b>%s</b>' % GetByLabel('UI/Fitting/FittingWindow/SavedFittings'))
    671         numAndPaths = [(node.numPersonal, 'UI/Fitting/FittingWindow/PersonalFittingsNum', 'UI/Fitting/FittingWindow/PersonalFittingsNumShort'), (node.numCorp, 'UI/Fitting/FittingWindow/CorpFittingsNum', 'UI/Fitting/FittingWindow/CorpFittingsNumShort')]
    672         if node.showCommunityFits:
    673             numAndPaths += [(node.numCommunity, 'UI/Fitting/FittingWindow/CommunityFittingsNum', 'UI/Fitting/FittingWindow/CommunityFittingsNumShort')]
    674         if session.allianceid:
    675             numAndPaths += [(node.numAlliance, 'UI/Fitting/FittingWindow/AllianceFittingsNum', 'UI/Fitting/FittingWindow/AllianceFittingsNumShort')]
    676         for numFittings, longPath, shortPath in numAndPaths:
    677             if not numFittings:
    678                 continue
    679             labelPath = shortPath if useShortText else longPath
    680             text = GetByLabel(labelPath, numFittings=numFittings)
    681             tooltipPanel.AddLabelMedium(text=text)
    682 
    683         if not numFittingOwners:
    684             tooltipPanel.AddLabelMedium(text=GetByLabel('UI/Fitting/FittingWindow/NoFittingsSaved'))
    685         if HasTraits(typeID):
    686             tooltipPanel.AddSpacer(width=300, height=1)
    687             TraitsContainer(parent=tooltipPanel, typeID=typeID, padding=7)
    688 
    689 
    690 class FittingHullTypeListGroup(HullTypeListGroup):
    691 
    692     def ConstructRightIcons(self):
    693         iconSize = 24
    694         switchCont = Container(name='switchCont', parent=self, align=uiconst.TORIGHT, width=iconSize, padRight=4)
    695         simulateBtn = ShipFittingSimulationButton(parent=switchCont, align=uiconst.CENTERLEFT, pos=(0,
    696          0,
    697          iconSize,
    698          iconSize), func=self.SimulateShip, iconSize=iconSize)
    699         simulateBtn.hint = GetByLabel('UI/Fitting/FittingWindow/SimulateShip')
    700 
    701     def SimulateShip(self):
    702         ghostFittingSvc = sm.GetService('ghostFittingSvc')
    703         shipTypeID = self.sr.node.typeID
    704         ghostFittingSvc.SimulateFitting(KeyVal(shipTypeID=shipTypeID, fitData=[]))