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

compare.py (33341B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\neocom\compare.py
      2 from collections import OrderedDict
      3 import eveformat
      4 import telemetry
      5 import carbonui.const as uiconst
      6 import dogma.data as dogma_data
      7 import evetypes
      8 import inventorycommon
      9 import localization
     10 import metaGroups
     11 from carbonui.control.checkbox import Checkbox
     12 from carbonui.control.dragResizeCont import DragResizeCont
     13 from carbonui.primitives.container import Container
     14 from clientsettings.setting import UserSettingBool
     15 from dogma.attributes import uiAttributeGroups
     16 from dogma.attributes.format import FormatUnit, GetFormatAndValue
     17 import dogma.const as dogma_const
     18 import dogma.attributes.groups as dogma_groups
     19 from eve.client.script.ui.control import eveScroll
     20 from carbonui.button.group import ButtonGroup, ButtonSizeMode
     21 from eve.client.script.ui.control.entries.checkbox import CheckboxEntry
     22 from eve.client.script.ui.control.entries.header import Header
     23 from eve.client.script.ui.control.entries.item import Item
     24 from eve.client.script.ui.control.entries.label_text import LabelTextTop
     25 from eve.client.script.ui.control.entries.util import GetFromClass
     26 from carbonui.control.window import Window
     27 from player_messaging.client.ui_message import message_player, prompt_player_fsd_dialog
     28 from eve.common.script.util import eveFormat
     29 from eveservices.menu import GetMenuService
     30 from inventorycommon.const import compareCategories
     31 from inventorycommon.typeHelpers import GetAveragePrice
     32 from localization import GetByLabel
     33 from utillib import KeyVal
     34 LEFT_PANEL_MIN_WIDTH = 160
     35 LEFT_PANEL_MAX_WIDTH = 280
     36 allowedGuids = ['xtriui.InvItem',
     37  'xtriui.FittingSlot',
     38  'listentry.InvItem',
     39  'listentry.GenericMarketItem',
     40  'listentry.QuickbarItem',
     41  'listentry.InvAssetItem',
     42  'xtriui.ShipUIModule',
     43  'uicls.GenericDraggableForTypeID',
     44  'listentry.Item',
     45  'listentry.KillItems',
     46  'listentry.FittingModuleEntry']
     47 EST_PRICE_CONFIG = 'compare_estPriceChecked'
     48 only_show_diffrentiators = UserSettingBool('compare_onlyShowDifferent', False)
     49 
     50 class TypeData(object):
     51 
     52     def __init__(self, typeID, itemID = None, godmaAttributes = None):
     53         if godmaAttributes is not None:
     54             self.godmaAttributes = godmaAttributes
     55         else:
     56             self.godmaAttributes = {}
     57             dynamicItemSvc = sm.GetService('dynamicItemSvc')
     58             if dynamicItemSvc.IsDynamicItem(typeID):
     59                 for attributeID, value in dynamicItemSvc.GetDynamicItemAttributes(itemID).iteritems():
     60                     self.godmaAttributes[attributeID] = value
     61 
     62             else:
     63                 for attribute in sm.GetService('godma').GetType(typeID).displayAttributes:
     64                     self.godmaAttributes[attribute.attributeID] = attribute.value
     65 
     66         self.typeID = typeID
     67         self.itemID = itemID
     68 
     69     def GetAttributeValue(self, attributeID):
     70         return self.godmaAttributes.get(attributeID, None)
     71 
     72 
     73 class TypeCompare(Window):
     74     default_windowID = 'typecompare'
     75     default_iconNum = 'res:/ui/Texture/WindowIcons/comparetool.png'
     76     default_captionLabelPath = 'Tooltips/Neocom/CompareTool'
     77     default_descriptionLabelPath = 'Tooltips/Neocom/CompareTool_description'
     78     default_minSize = (350, 400)
     79     default_width = 600
     80     default_height = 500
     81 
     82     def ApplyAttributes(self, attributes):
     83         super(TypeCompare, self).ApplyAttributes(attributes)
     84         typeID = attributes.typeID
     85         self.dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation()
     86         self.types = []
     87         self.attributeIDsUserSelectable = []
     88         self.attributesCombined = []
     89         self.attributeIDsCurrentlyChecked = []
     90         self.activeAttributesChecked = set()
     91         self.banAttrs = sm.GetService('info').GetSkillAttrs()
     92         self.attributeLimit = 10
     93         self.typeLimit = 40
     94         self.settingsinited = 0
     95         self.graphinited = 0
     96         self.topLevelMarketGroup = None
     97         self.ConstructLayout()
     98         if typeID:
     99             typeIDs = evetypes.GetVariations(typeID)
    100             for _typeID in typeIDs:
    101                 if not evetypes.IsDynamicType(_typeID):
    102                     self.AddTypeID(_typeID)
    103 
    104         only_show_diffrentiators.on_change.connect(self.OnOnlyShowDiffrentiatorsSetting)
    105 
    106     def ConstructLayout(self):
    107         self.mainCont = Container(name='mainCont', parent=self.sr.main)
    108         self.ConstructAttributeScroll()
    109         self.ConstructTypeScroll()
    110         self.ConstructButtonGroup()
    111 
    112     def ConstructButtonGroup(self):
    113         btns = ((localization.GetByLabel('UI/Commands/UncheckAll'),
    114           self.SelectAll,
    115           (0,),
    116           None), (localization.GetByLabel('UI/Commands/ResetAll'),
    117           self.RemoveAllEntries,
    118           (),
    119           None))
    120         ButtonGroup(btns=btns, parent=self.sr.main, padTop=8, idx=0, button_size_mode=ButtonSizeMode.DYNAMIC)
    121 
    122     def ConstructAttributeScroll(self):
    123         self.leftCont = DragResizeCont(parent=self.mainCont, align=uiconst.TOLEFT, state=uiconst.UI_HIDDEN, settingsID='compare_DragResizeCont', minSize=LEFT_PANEL_MIN_WIDTH, defaultSize=200, maxSize=LEFT_PANEL_MAX_WIDTH)
    124         self.attributeScroll = eveScroll.Scroll(name='attributescroll', parent=self.leftCont, padding=(0,
    125          20,
    126          0,
    127          const.defaultPadding))
    128         self.attributeScroll.sr.id = 'typecompare_attributescroll'
    129         self.attributeScroll.hiliteSorted = 0
    130         self.attributeScroll.ShowHint(localization.GetByLabel('UI/Compare/NothingToCompare'))
    131 
    132     def ConstructTypeScroll(self):
    133         self.typeScroll = eveScroll.Scroll(name='typescroll', parent=self.mainCont, padTop=const.defaultPadding)
    134         self.typeScroll.ShowHint(localization.GetByLabel('UI/Compare/CompareToolHint'))
    135         self.typeScroll.sr.id = 'typecompare_typescroll'
    136         self.typeScroll.sr.content.OnDropData = self.OnDropData
    137 
    138     def SettingMenu(self, menuParent):
    139         onlyShowDifferent = only_show_diffrentiators.is_enabled()
    140         menuParent.AddCheckBox(text=localization.GetByLabel('UI/Compare/OnlyShowDifferent'), checked=onlyShowDifferent, callback=only_show_diffrentiators.toggle)
    141 
    142     def GetMenuMoreOptions(self):
    143         menuData = super(TypeCompare, self).GetMenuMoreOptions()
    144         menuData.AddCheckbox(text=GetByLabel('UI/Compare/OnlyShowDifferent'), setting=only_show_diffrentiators)
    145         return menuData
    146 
    147     def OnOnlyShowDiffrentiatorsSetting(self, value):
    148         self.ReloadAttributeScroll()
    149         self.ReloadTypeCompareScroll()
    150 
    151     def RemoveAllEntries(self, *args):
    152         self.types = []
    153         self.topLevelMarketGroup = None
    154         self.SelectAll()
    155 
    156     def SelectAll(self, onOff = 0):
    157         if onOff:
    158             return
    159         self.attributeIDsCurrentlyChecked = []
    160         self.activeAttributesChecked.clear()
    161         self.OnColumnChanged()
    162 
    163     def AddTypeID(self, typeID, itemID = None):
    164         currTypeIDs = [ node.typeID for node in self.types ]
    165         if typeID in currTypeIDs:
    166             return
    167         if not self._IsValidTopLevelMarketGroupID(typeID, itemID):
    168             prompt_player_fsd_dialog('CannotCompareNoneItem')
    169             return
    170         categoryID = evetypes.GetCategoryID(typeID)
    171         if categoryID not in compareCategories:
    172             message_player(localization.GetByLabel('UI/Compare/CannotCompareThisItem', typeID=typeID))
    173             return
    174         typeData = TypeData(typeID, itemID)
    175         if typeData not in self.types:
    176             self.types.append(typeData)
    177             self.OnColumnChanged()
    178             if only_show_diffrentiators.is_enabled():
    179                 self.ReloadAttributeScroll()
    180 
    181     def AddVariantsOf(self, typeID):
    182         typeIDs = evetypes.GetVariations(typeID)
    183         for _typeID in typeIDs:
    184             self.AddTypeID(_typeID)
    185 
    186     def _IsValidTopLevelMarketGroupID(self, typeID, itemID):
    187         dynamicItemSvc = sm.GetService('dynamicItemSvc')
    188         if dynamicItemSvc.IsDynamicItem(typeID):
    189             mutatedItem = sm.GetService('dynamicItemSvc').GetDynamicItem(itemID)
    190             topLevelMarketGroupID = self.GetTopLevelMarketGroupID(mutatedItem.sourceTypeID)
    191         else:
    192             topLevelMarketGroupID = self.GetTopLevelMarketGroupID(typeID)
    193         return topLevelMarketGroupID != -1
    194 
    195     def RemoveEntry(self, node):
    196         nodes = self.typeScroll.GetSelected() or [node]
    197         toRemove = [ node.typeID for node in nodes ]
    198         self.types = [ typeData for typeData in self.types if typeData.typeID not in toRemove ]
    199         if not self.types:
    200             self.attributeIDsCurrentlyChecked = []
    201             self.activeAttributesChecked.clear()
    202             self.topLevelMarketGroup = None
    203         self.OnColumnChanged()
    204 
    205     def OnColumnChanged(self, force = 1, *args):
    206         self.attributesCombined = self.GetCombinedDogmaAttributes()
    207         if force:
    208             self.ReloadAttributeScroll()
    209         self.ReloadTypeCompareScroll()
    210 
    211     def GetTopLevelMarketGroupID(self, typeID):
    212         marketGroupIDFromType = None
    213         marketGroupID = evetypes.GetMarketGroupID(typeID)
    214         if marketGroupID is None:
    215             parentID = evetypes.GetParentIDOfVariations(typeID)
    216             if parentID is not None:
    217                 marketGroupIDFromType = evetypes.GetMarketGroupID(parentID)
    218                 topLevelMarketGroupID = self.GetTopLevelMarketGroupIDEx(marketGroupIDFromType)
    219             else:
    220                 return -1
    221         else:
    222             topLevelMarketGroupID = self.GetTopLevelMarketGroupIDEx(marketGroupID)
    223         if not self.topLevelMarketGroup:
    224             if marketGroupIDFromType:
    225                 self.topLevelMarketGroup = self.GetTopLevelMarketGroupIDEx(marketGroupIDFromType)
    226             else:
    227                 self.topLevelMarketGroup = self.GetTopLevelMarketGroupIDEx(marketGroupID)
    228         return topLevelMarketGroupID
    229 
    230     def GetTopLevelMarketGroupIDEx(self, marketGroupID):
    231         mg = sm.GetService('marketutils').GetMarketGroup(marketGroupID)
    232         if mg and hasattr(mg, 'parentGroupID'):
    233             parentGroupID = mg.parentGroupID
    234             while parentGroupID:
    235                 mg = sm.GetService('marketutils').GetMarketGroup(parentGroupID)
    236                 parentGroupID = mg.parentGroupID
    237 
    238             return mg.marketGroupID
    239         else:
    240             return None
    241 
    242     def GetCombinedDogmaAttributes(self):
    243         attributeIDs = []
    244         attributes = []
    245         for typeData in self.types:
    246             dynamicItemSvc = sm.GetService('dynamicItemSvc')
    247             if dynamicItemSvc.IsDynamicItem(typeData.typeID):
    248                 item = dynamicItemSvc.GetDynamicItem(typeData.itemID)
    249                 attributeDict = sm.GetService('info').GetAttributeDictForType(item.sourceTypeID)
    250             else:
    251                 attributeDict = sm.GetService('info').GetAttributeDictForType(typeData.typeID)
    252             for attributeID, value in attributeDict.iteritems():
    253                 if attributeID not in self.banAttrs and attributeID not in attributeIDs:
    254                     attributeIDs.append(attributeID)
    255                     dgmAttribs = dogma_data.get_attribute(attributeID)
    256                     if dgmAttribs.published or dgmAttribs.attributeID in (dogma_const.attributeHp, dogma_const.attributeBaseWarpSpeed):
    257                         attributes.append(dgmAttribs)
    258 
    259         attributesToRemove = []
    260         for attribute in attributes:
    261             removeIt = True
    262             for typeData in self.types:
    263                 value = typeData.GetAttributeValue(attribute.attributeID)
    264                 if value:
    265                     removeIt = False
    266                     break
    267 
    268             if removeIt:
    269                 attributesToRemove.append(attribute)
    270 
    271         return attributes
    272 
    273     def ReloadAttributeScroll(self):
    274         scrolllist = self.GetAttributeScrollList()
    275         self.attributeScroll.Load(contentList=scrolllist, noContentHint=localization.GetByLabel('UI/Compare/NothingToCompare'))
    276 
    277     def GetAttributeScrollList(self):
    278         attributes = self.GetAttributesToOffer()
    279         attributeIDs = {x.attributeID for x in attributes}
    280         self.attributeIDsUserSelectable = []
    281         categoryID = evetypes.GetCategoryID(self.types[0].typeID) if self.types else None
    282         if categoryID in (const.categoryShip, const.categoryDrone):
    283             scrolllist = self._GetScrollListShipOrDrone(attributes, attributeIDs, categoryID)
    284         else:
    285             scrolllist = []
    286             for attribute in attributes:
    287                 self._AppendScrollEntry(attribute, scrolllist)
    288 
    289         if scrolllist and self.ShouldShowEstPriceCheckbox():
    290             estPriceEntry = self.GetEstPriceEntry()
    291             scrolllist.insert(0, estPriceEntry)
    292         return scrolllist
    293 
    294     def _GetScrollListShipOrDrone(self, attributes, attributeIDs, categoryID):
    295         scrolllist = []
    296         attrAndFittings = OrderedDict()
    297         attrAndFittings.update(self.GetShipAndDroneAttributes())
    298         attrAndFittings.update(self.GetFittings())
    299         for caption, attrs in attrAndFittings.iteritems():
    300             normalAttributes = attrs['normalAttributes']
    301             groupedAttributes = [ x[1] for x in attrs.get('groupedAttributes', []) ]
    302             allAttributes = list(normalAttributes) + list(groupedAttributes)
    303             shipAttr = [ attribute for attribute in attributes if attribute.attributeID in allAttributes ]
    304             if shipAttr:
    305                 scrolllist.append(GetFromClass(Header, {'label': caption}))
    306                 for attribute in shipAttr:
    307                     self._AppendScrollEntry(attribute, scrolllist)
    308 
    309                 if categoryID == const.categoryShip and caption == localization.GetByLabel('UI/Compare/Propulsion'):
    310                     if dogma_const.attributeBaseWarpSpeed in attributeIDs:
    311                         attribute = dogma_data.get_attribute(dogma_const.attributeBaseWarpSpeed)
    312                         self._AppendScrollEntry(attribute, scrolllist)
    313 
    314         if categoryID == const.categoryDrone:
    315             otherAttributes = [ x for x in attributes if x.attributeID not in self.attributeIDsUserSelectable ]
    316             if otherAttributes:
    317                 scrolllist.append(GetFromClass(Header, {'label': localization.GetByLabel('UI/InfoWindow/Miscellaneous')}))
    318                 for attribute in otherAttributes:
    319                     self._AppendScrollEntry(attribute, scrolllist)
    320 
    321         return scrolllist
    322 
    323     def GetShipAndDroneAttributes(self):
    324         shipAttributes = OrderedDict()
    325         shipAttributes[GetByLabel('UI/Fitting/Structure')] = {'normalAttributes': [dogma_const.attributeHp,
    326                               dogma_const.attributeCapacity,
    327                               dogma_const.attributeStructureDamageLimit,
    328                               dogma_const.attributeDroneCapacity,
    329                               dogma_const.attributeFrigateEscapeBayCapacity,
    330                               dogma_const.attributeDroneBandwidth,
    331                               dogma_const.attributeMass,
    332                               dogma_const.attributeVolume,
    333                               dogma_const.attributeAgility,
    334                               dogma_const.attributeSpecialAmmoHoldCapacity,
    335                               dogma_const.attributeSpecialGasHoldCapacity,
    336                               dogma_const.attributeSpecialIceHoldCapacity,
    337                               dogma_const.attributeSpecialIndustrialShipHoldCapacity,
    338                               dogma_const.attributeSpecialLargeShipHoldCapacity,
    339                               dogma_const.attributeSpecialMediumShipHoldCapacity,
    340                               dogma_const.attributeSpecialMineralHoldCapacity,
    341                               dogma_const.attributeGeneralMiningHoldCapacity,
    342                               dogma_const.attributeSpecialAsteroidHoldCapacity,
    343                               dogma_const.attributeSpecialSalvageHoldCapacity,
    344                               dogma_const.attributeSpecialShipHoldCapacity,
    345                               dogma_const.attributeSpecialSmallShipHoldCapacity,
    346                               dogma_const.attributeSpecialCommandCenterHoldCapacity,
    347                               dogma_const.attributeSpecialPlanetaryCommoditiesHoldCapacity,
    348                               dogma_const.attributeSpecialSubsystemHoldCapacity,
    349                               dogma_const.attributeSpecialCorpseHoldCapacity,
    350                               dogma_const.attributeSpecialBoosterHoldCapacity,
    351                               dogma_const.attributeSpecialMobileDepotHoldCapacity,
    352                               dogma_const.attributeSpecialColonyResourcesHoldCapacity],
    353          'groupedAttributes': [('em', dogma_const.attributeEmDamageResonance),
    354                                ('thermal', dogma_const.attributeThermalDamageResonance),
    355                                ('kinetic', dogma_const.attributeKineticDamageResonance),
    356                                ('explosive', dogma_const.attributeExplosiveDamageResonance)]}
    357         shipAttributes[GetByLabel('UI/Common/Armor')] = {'normalAttributes': [dogma_const.attributeArmorHP, dogma_const.attributeArmorDamageLimit],
    358          'groupedAttributes': [('em', dogma_const.attributeArmorEmDamageResonance),
    359                                ('thermal', dogma_const.attributeArmorThermalDamageResonance),
    360                                ('kinetic', dogma_const.attributeArmorKineticDamageResonance),
    361                                ('explosive', dogma_const.attributeArmorExplosiveDamageResonance)]}
    362         shipAttributes[GetByLabel('UI/Common/Shield')] = {'normalAttributes': [dogma_const.attributeShieldCapacity, dogma_const.attributeShieldRechargeRate, dogma_const.attributeShieldDamageLimit],
    363          'groupedAttributes': [('em', dogma_const.attributeShieldEmDamageResonance),
    364                                ('thermal', dogma_const.attributeShieldThermalDamageResonance),
    365                                ('kinetic', dogma_const.attributeShieldKineticDamageResonance),
    366                                ('explosive', dogma_const.attributeShieldExplosiveDamageResonance)]}
    367         shipAttributes[GetByLabel('UI/Common/EWarResistances')] = {'normalAttributes': [dogma_const.attributeECMResistance,
    368                               dogma_const.attributeRemoteAssistanceImpedance,
    369                               dogma_const.attributeRemoteRepairImpedance,
    370                               dogma_const.attributeEnergyWarfareResistance,
    371                               dogma_const.attributeSensorDampenerResistance,
    372                               dogma_const.attributeStasisWebifierResistance,
    373                               dogma_const.attributeTargetPainterResistance,
    374                               dogma_const.attributeWeaponDisruptionResistance]}
    375         shipAttributes[GetByLabel('UI/Fitting/FittingWindow/Capacitor')] = {'normalAttributes': [dogma_const.attributeCapacitorCapacity, dogma_const.attributeRechargeRate]}
    376         shipAttributes[GetByLabel('UI/Fitting/FittingWindow/Targeting')] = {'normalAttributes': [dogma_const.attributeMaxTargetRange,
    377                               dogma_const.attributeMaxRange,
    378                               dogma_const.attributeMaxLockedTargets,
    379                               dogma_const.attributeSignatureRadius,
    380                               dogma_const.attributeOptimalSigRadius,
    381                               dogma_const.attributeScanResolution,
    382                               dogma_const.attributeScanLadarStrength,
    383                               dogma_const.attributeScanMagnetometricStrength,
    384                               dogma_const.attributeScanRadarStrength,
    385                               dogma_const.attributeScanGravimetricStrength,
    386                               dogma_const.attributeProximityRange,
    387                               dogma_const.attributeFalloff,
    388                               dogma_const.attributeTrackingSpeed]}
    389         shipAttributes[GetByLabel('UI/InfoWindow/SharedFacilities')] = {'normalAttributes': [dogma_const.attributeFleetHangarCapacity,
    390                               dogma_const.attributeShipMaintenanceBayCapacity,
    391                               dogma_const.attributeMaxJumpClones,
    392                               dogma_const.attributeReclonerFuelType]}
    393         shipAttributes[GetByLabel('UI/InfoWindow/FighterFacilities')] = {'normalAttributes': [dogma_const.attributeFighterCapacity,
    394                               dogma_const.attributeFighterTubes,
    395                               dogma_const.attributeFighterLightSlots,
    396                               dogma_const.attributeFighterSupportSlots,
    397                               dogma_const.attributeFighterHeavySlots,
    398                               dogma_const.attributeFighterStandupLightSlots,
    399                               dogma_const.attributeFighterStandupSupportSlots,
    400                               dogma_const.attributeFighterStandupHeavySlots]}
    401         shipAttributes[GetByLabel('UI/InfoWindow/OnDeath')] = {'normalAttributes': [dogma_const.attributeOnDeathAOERadius,
    402                               dogma_const.attributeOnDeathSignatureRadius,
    403                               dogma_const.attributeOnDeathDamageEM,
    404                               dogma_const.attributeOnDeathDamageTherm,
    405                               dogma_const.attributeOnDeathDamageKin,
    406                               dogma_const.attributeOnDeathDamageExp]}
    407         shipAttributes[GetByLabel('UI/InfoWindow/JumpDriveSystems')] = {'normalAttributes': [dogma_const.attributeJumpDriveCapacitorNeed,
    408                               dogma_const.attributeJumpDriveRange,
    409                               dogma_const.attributeJumpDriveConsumptionType,
    410                               dogma_const.attributeJumpDriveConsumptionAmount,
    411                               dogma_const.attributeJumpDriveDuration,
    412                               dogma_const.attributeJumpPortalCapacitorNeed,
    413                               dogma_const.attributeJumpPortalConsumptionMassFactor,
    414                               dogma_const.attributeJumpPortalDuration,
    415                               dogma_const.attributeSpecialFuelBayCapacity,
    416                               dogma_const.attributeGroupJumpDriveConsumptionAmount,
    417                               dogma_const.attributeConduitJumpPassengerCount]}
    418         shipAttributes[GetByLabel('UI/Compare/Propulsion')] = {'normalAttributes': [dogma_const.attributeMaxVelocity]}
    419         return shipAttributes
    420 
    421     def _AppendScrollEntry(self, attribute, scrolllist):
    422         displayName = dogma_data.get_attribute_display_name(attribute.attributeID)
    423         if displayName:
    424             scrolllist.append(self.GetScrollEntry(attribute))
    425             self.attributeIDsUserSelectable.append(attribute.attributeID)
    426 
    427     @telemetry.ZONE_METHOD
    428     def GetAttributesToOffer(self):
    429         if not only_show_diffrentiators.is_enabled() or len(self.types) < 2:
    430             return self.attributesCombined
    431         else:
    432             return self._GetAttributesThatDiffrentiate()
    433 
    434     def _GetAttributesThatDiffrentiate(self):
    435         allAvailableAttributesByID = OrderedDict()
    436         for attribute in self.attributesCombined:
    437             allAvailableAttributesByID[attribute.attributeID] = attribute
    438 
    439         differentAttributesDict = OrderedDict()
    440         for eachAttributeID, attribute in allAvailableAttributesByID.iteritems():
    441             attributeValues = []
    442             for typeData in self.types:
    443                 if eachAttributeID == dogma_const.attributeBaseWarpSpeed:
    444                     value = self.GetWarpValue(typeData)
    445                 else:
    446                     value = typeData.GetAttributeValue(attribute.attributeID)
    447                 if attributeValues and value not in attributeValues:
    448                     differentAttributesDict[eachAttributeID] = attribute
    449                     break
    450                 else:
    451                     attributeValues.append(value)
    452 
    453         attributes = differentAttributesDict.values()
    454         return attributes
    455 
    456     def ShouldShowEstPriceCheckbox(self):
    457         if not only_show_diffrentiators.is_enabled():
    458             return True
    459         if len(self.types) < 2:
    460             return True
    461         estPriceSet = set()
    462         for typeData in self.types:
    463             estPriceSet.add(GetAveragePrice(typeData.typeID))
    464 
    465         return len(estPriceSet) > 1
    466 
    467     def GetFittings(self):
    468         fittingDict = {localization.GetByLabel('UI/Fitting/FittingWindow/Fitting'): {'normalAttributes': dogma_groups.fitting_attribute_ids}}
    469         return fittingDict
    470 
    471     def GetScrollEntry(self, entry):
    472         displayName = dogma_data.get_attribute_display_name(entry.attributeID)
    473         return GetFromClass(AttributeCheckbox, {'line': 1,
    474          'info': entry,
    475          'label': displayName,
    476          'iconID': getattr(entry, 'iconID', None),
    477          'item': entry,
    478          'text': FormatUnit(getattr(entry, 'unitID', None)) or ' ',
    479          'hint': displayName,
    480          'checked': entry.attributeID in self.attributeIDsCurrentlyChecked,
    481          'cfgname': entry.attributeID,
    482          'OnChange': self.OnAttributeSelectedChanged})
    483 
    484     def OnAttributeSelectedChanged(self, checkbox, *args):
    485         attributeID = checkbox.GetSettingsKey()
    486         if checkbox.GetValue():
    487             if len(self.activeAttributesChecked) < self.attributeLimit:
    488                 if attributeID not in self.attributeIDsCurrentlyChecked:
    489                     self.attributeIDsCurrentlyChecked.append(attributeID)
    490                 self.OnColumnChanged(force=0)
    491             else:
    492                 checkbox.SetValue(False)
    493                 message = localization.GetByLabel('UI/Compare/CanOnlyCompareAmountAttributes', amount=self.attributeLimit)
    494                 prompt_player_fsd_dialog('CustomInfo', {'info': message})
    495         else:
    496             if attributeID in self.attributeIDsCurrentlyChecked:
    497                 self.attributeIDsCurrentlyChecked.remove(attributeID)
    498             self.OnColumnChanged(force=0)
    499 
    500     def GetEstPriceEntry(self):
    501         return GetFromClass(SingleLineCheckbox, {'line': 1,
    502          'label': localization.GetByLabel('UI/Contracts/ContractsWindow/EstPrice'),
    503          'checked': settings.user.ui.Get(EST_PRICE_CONFIG, True),
    504          'OnChange': self.OnEstPriceSelectedChanged})
    505 
    506     def OnEstPriceSelectedChanged(self, checkbox, *args):
    507         settings.user.ui.Set(EST_PRICE_CONFIG, checkbox.GetValue())
    508         self.OnColumnChanged(force=0)
    509 
    510     def ReloadTypeCompareScroll(self):
    511         scrolllist, headers = self.GetCompareTypeInfoContentList()
    512         self.leftCont.display = bool(scrolllist)
    513         self.typeScroll.Load(contentList=scrolllist, headers=headers, noContentHint=localization.GetByLabel('UI/Compare/CompareToolHint'))
    514 
    515     def GetCompareTypeInfoContentList(self):
    516         scrolllist = []
    517         headers, uniqueHeaders, treatedHeaders, initialHeaders = ([],
    518          [],
    519          [],
    520          [])
    521         estPriceChecked = settings.user.ui.Get(EST_PRICE_CONFIG, True)
    522         estPriceHeaderText = localization.GetByLabel('UI/Contracts/ContractsWindow/EstPrice') if estPriceChecked else ''
    523         if self.types:
    524             headers = [localization.GetByLabel('/Carbon/UI/Common/TypeName'), localization.GetByLabel('UI/Compare/MetaGroup')]
    525             attrDictToUse = self.GetAttributesToOffer()
    526             self.activeAttributesChecked.clear()
    527             for typeData in self.types:
    528                 data = KeyVal(typeID=typeData.typeID, itemID=typeData.itemID, godmaAttributes=typeData.godmaAttributes)
    529                 typeName = evetypes.GetName(typeData.typeID)
    530                 metaGroupID = evetypes.GetMetaGroupIDOrNone(typeData.typeID)
    531                 text = '%s<t>%s' % (typeName, metaGroups.get_name(metaGroupID))
    532                 data.Set('sort_%s' % headers[0], typeName)
    533                 data.Set('sort_%s' % headers[1], metaGroupID)
    534                 if estPriceChecked:
    535                     estPriceHeader = (estPriceHeaderText, -1)
    536                     if estPriceHeader not in uniqueHeaders:
    537                         uniqueHeaders.append(estPriceHeader)
    538                     estPrice = inventorycommon.typeHelpers.GetAveragePrice(typeData.typeID)
    539                     if estPrice is not None:
    540                         formattedIsk = eveFormat.FmtISK(estPrice, showFractionsAlways=1)
    541                         text += '<t><right>%s' % formattedIsk
    542                         data.Set('sort_%s' % estPriceHeaderText, estPrice)
    543                     else:
    544                         text += '<t>'
    545                         data.Set('sort_%s' % estPriceHeaderText, None)
    546                 attributeLoop = {}
    547                 for each in attrDictToUse:
    548                     if each.attributeID in self.attributeIDsCurrentlyChecked:
    549                         self.activeAttributesChecked.add(each.attributeID)
    550                         attributeLoop[each.attributeID] = each
    551 
    552                 for each in self.attributeIDsUserSelectable:
    553                     attribute = attributeLoop.get(each, None)
    554                     if not attribute:
    555                         continue
    556                     displayNameRaw = dogma_data.get_attribute_display_name(attribute.attributeID)
    557                     displayName = eveformat.replace_text_ignoring_tags(displayNameRaw, old=' ', new='<br>')
    558                     if (displayName, attribute.attributeID) not in uniqueHeaders:
    559                         uniqueHeaders.append((displayName, attribute.attributeID))
    560                     if attribute.attributeID == dogma_const.attributeBaseWarpSpeed:
    561                         value = self.GetWarpValue(typeData)
    562                     else:
    563                         value = typeData.GetAttributeValue(attribute.attributeID)
    564                     data.Set('sort_%s' % displayName, value)
    565                     if value is None:
    566                         taa = localization.GetByLabel('UI/Generic/NotAvailableShort')
    567                     else:
    568                         taa = GetFormatAndValue(attribute, value)
    569                     text += '<t>%s' % taa
    570 
    571                 data.label = text
    572                 data.getIcon = 1
    573                 data.GetMenu = self.GetEntryMenu
    574                 data.viewMode = 'details'
    575                 data.ignoreRightClick = 1
    576                 data.OnDropData = self.OnDropData
    577                 scrolllist.append(GetFromClass(Item, data))
    578 
    579             for header, attributeID in uniqueHeaders:
    580                 if header in headers:
    581                     header = header + ' '
    582                 headers.append(header)
    583 
    584             initialHeaders = headers
    585             treatedHeaders = []
    586             for each in initialHeaders:
    587                 treatedHeaders.append(each.replace(' ', '<br>'))
    588 
    589         return (scrolllist, initialHeaders)
    590 
    591     def GetWarpValue(self, typeData):
    592         godma = sm.GetService('godma')
    593         cmp = max(godma.GetTypeAttribute(typeData.typeID, dogma_const.attributeBaseWarpSpeed, defaultValue=1), 1.0)
    594         cmp *= godma.GetTypeAttribute(typeData.typeID, dogma_const.attributeWarpSpeedMultiplier, defaultValue=1)
    595         cmp *= const.AU
    596         return cmp
    597 
    598     def GetEntryMenu(self, item):
    599         m = GetMenuService().GetMenuFromItemIDTypeID(None, item.typeID, includeMarketDetails=True).filter(['UI/Compare/CompareButton'])
    600         item.DoSelectNode()
    601         sel = self.typeScroll.GetSelected()
    602         text = localization.GetByLabel('UI/Commands/Remove')
    603         if len(sel) > 1:
    604             text = localization.GetByLabel('UI/Commands/RemoveMultiple', itemcount=len(sel))
    605         m += [(text, self.RemoveEntry, (item,))]
    606         return m
    607 
    608     def OnDropData(self, dragObj, nodes):
    609         for node in nodes:
    610             if getattr(node, '__guid__', None) in allowedGuids:
    611                 typeID = self._GetNodeTypeID(node)
    612                 if typeID:
    613                     if not sm.GetService('info').GetAttributeDictForType(typeID):
    614                         message_player(localization.GetByLabel('UI/Compare/CannotCompareThisItem', typeID=typeID))
    615                         return
    616                     if not hasattr(node, 'itemID'):
    617                         node.itemID = None
    618                     self.AddTypeID(typeID, node.itemID)
    619 
    620     def _GetNodeTypeID(self, node):
    621         invType = node.get('invtype')
    622         if invType:
    623             return invType
    624         typeID = node.get('typeID')
    625         if typeID:
    626             return typeID
    627 
    628 
    629 class SingleLineCheckbox(CheckboxEntry):
    630 
    631     def Load(self, args):
    632         CheckboxEntry.Load(self, args)
    633         self.sr.checkbox.left = 4
    634 
    635 
    636 class AttributeCheckbox(LabelTextTop):
    637     __guid__ = 'listentry.AttributeCheckbox'
    638 
    639     def Startup(self, *args):
    640         LabelTextTop.Startup(self, args)
    641         self.sr.checkbox = Checkbox(parent=self, align=uiconst.TOPLEFT, state=uiconst.UI_DISABLED, callback=self.CheckBoxChange, pos=(4, 4, 0, 0))
    642 
    643     def Load(self, args):
    644         LabelTextTop.Load(self, args)
    645         data = self.sr.node
    646         self.sr.checkbox.SetChecked(data.checked, 0)
    647         self.sr.checkbox.SetSettingsKey(data.cfgname)
    648         self.sr.icon.left = 20
    649         if self.sr.icon.display:
    650             self.sr.label.left = self.sr.icon.left + self.sr.icon.width + 2
    651             self.sr.text.left = self.sr.icon.left + self.sr.icon.width + 2
    652         else:
    653             self.sr.label.left = 24
    654             self.sr.text.left = 24
    655 
    656     def CheckBoxChange(self, *args):
    657         self.sr.node.checked = self.sr.checkbox.checked
    658         self.sr.node.OnChange(*args)
    659 
    660     def OnClick(self, *args):
    661         self.sr.checkbox.OnClick(*args)