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

uix.py (33310B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\util\uix.py
      2 import math
      3 import sys
      4 import appConst
      5 import numbers
      6 import types
      7 import blue
      8 import caching
      9 import dogma.data
     10 import dogma.units
     11 import eveicon
     12 import evetypes
     13 import localization
     14 import log
     15 import metaGroups
     16 import telemetry
     17 import trinity
     18 import utillib
     19 from carbon.common.script.util.format import FmtAmt
     20 from carbonui import fontconst, uiconst
     21 from carbonui.control.scroll_const import GetHiddenColumnsKey
     22 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     23 from carbonui.primitives.sprite import Sprite
     24 from carbonui.uicore import uicore
     25 from carbonui.util.bunch import Bunch
     26 from carbonui.util.various_unsorted import MapIcon
     27 from dogma.attributes.format import GetFormatAndValue
     28 from dogma.const import attributeNonDestructible
     29 from eve.client.script.ui.control import eveLabel
     30 from eve.client.script.ui.control.eveIcon import GetLogoIcon, Icon
     31 from carbonui.control.window import Window
     32 from eve.client.script.ui.control.themeColored import FillThemeColored
     33 from eve.common.lib import appConst as const
     34 from eve.common.script.sys import idCheckers
     35 from eve.common.script.sys.idCheckers import IsSolarSystem, IsKnownSpaceSystem
     36 from eve.common.script.util.eveFormat import FmtISK, GetAveragePrice
     37 from fsdBuiltData.common.iconIDs import GetIconFile
     38 from inventorycommon.const import VIEWMODE_ICONS, VIEWMODE_DETAILS, VIEWMODE_LIST, VIEWMODE_CARDS
     39 from inventorycommon.util import GetItemVolume
     40 from player_messaging.client.ui_message import prompt_player_fsd_dialog
     41 from spacecomponents.common.helper import HasDeployComponent, HasNpcPilotComponent, HasMineableComponent, GetMineableComponentTypeAttributes
     42 from inventorycommon.typeHelpers import GetAveragePrice as GetAverageTypePrice
     43 isTakeTimeEnabled = True
     44 slotTextByEffect = {const.effectRigSlot: 'UI/Inventory/SlotRigs',
     45  const.effectHiPower: 'UI/Inventory/SlotHigh',
     46  const.effectMedPower: 'UI/Inventory/SlotMedium',
     47  const.effectLoPower: 'UI/Inventory/SlotLow'}
     48 categoryGroupTypeStringForTypeID = {}
     49 
     50 def TakeTime(label, func, *args, **kw):
     51     if isTakeTimeEnabled:
     52         t = blue.pyos.taskletTimer.EnterTasklet(label)
     53         try:
     54             return func(*args, **kw)
     55         finally:
     56             blue.pyos.taskletTimer.ReturnFromTasklet(t)
     57 
     58     else:
     59         return func(*args, **kw)
     60 
     61 
     62 @telemetry.ZONE_FUNCTION
     63 def GetItemData(rec, viewMode, viewOnly = 0, container = None, scrollID = None, *args, **kw):
     64     attribs = {}
     65     for attribute in sm.GetService('godma').GetType(rec.typeID).displayAttributes:
     66         attribs[attribute.attributeID] = attribute.value
     67 
     68     sort_slotsize = 0
     69     for effect in dogma.data.get_type_effects(rec.typeID):
     70         if effect.effectID in (const.effectHiPower, const.effectMedPower, const.effectLoPower):
     71             sort_slotsize = 1
     72             break
     73 
     74     data = Bunch()
     75     data.__guid__ = 'listentry.InvItem'
     76     data.item = rec
     77     data.rec = rec
     78     data.itemID = rec.itemID
     79     data.godmaattribs = attribs
     80     data.invtype = rec.typeID
     81     data.container = container
     82     data.viewMode = viewMode
     83     data.viewOnly = viewOnly
     84     data.locked = rec.flagID == const.flagLocked
     85     data.sublevel = kw.get('sublevel', 0)
     86     if not data.locked and rec.flagID != const.flagUnlocked and container and container.invController.isLockable:
     87         if hasattr(container, 'invController'):
     88             containerItem = container.invController.GetInventoryItem()
     89             if containerItem:
     90                 maybeLocked = containerItem.groupID in (const.groupAuditLogSecureContainer, const.groupStation)
     91                 maybeLocked = maybeLocked or evetypes.GetCategoryID(data.invtype) == const.categoryBlueprint and containerItem.typeID in (const.typeOffice, const.typeAssetSafetyWrap)
     92                 if maybeLocked:
     93                     data.locked = sm.GetService('lockedItems').IsItemLocked(rec)
     94     data.factionID = sm.GetService('faction').GetCurrentFactionID()
     95     if rec.singleton or rec.typeID in (const.typeBookmark,):
     96         data.Set('sort_%s' % localization.GetByLabel('UI/Common/Quantity'), 0)
     97     else:
     98         data.Set('sort_%s' % localization.GetByLabel('UI/Common/Quantity'), rec.stacksize)
     99     ml = evetypes.GetMetaLevel(data.invtype)
    100     if not ml:
    101         metaLevel = ''
    102     else:
    103         metaLevel = FmtAmt(ml)
    104     data.metaLevel = metaLevel
    105     data.groupName = evetypes.GetGroupName(rec.typeID)
    106     data.categoryName = evetypes.GetCategoryName(rec.typeID)
    107     estPrice = GetAveragePrice(rec)
    108     data.estPrice = rec.stacksize * estPrice if estPrice and rec.stacksize else None
    109     data.sort_ammosizeconstraints = const.attributeChargeSize in attribs
    110     data.sort_slotsize = sort_slotsize
    111     data.Set('sort_%s' % localization.GetByLabel('UI/Common/Name'), GetItemName(rec, data).lower())
    112     data.Set('sort_%s' % localization.GetByLabel('UI/Common/Type'), GetCategoryGroupTypeStringForItem(rec).lower())
    113     data.Set('sort_%s' % localization.GetByLabel('UI/Inventory/ItemMetaLevel'), ml or 0.0)
    114     data.Set('sort_%s' % localization.GetByLabel('UI/Inventory/ItemGroup'), data.groupName.lower())
    115     data.Set('sort_%s' % localization.GetByLabel('UI/Inventory/ItemCategory'), data.categoryName.lower())
    116     data.Set('sort_%s' % localization.GetByLabel('UI/Contracts/ContractsWindow/EstPrice'), data.estPrice or 0)
    117     if data.rec.typeID == const.typePlasticWrap:
    118         data.volume = ''
    119         data.Set('sort_%s' % localization.GetByLabel('UI/Inventory/ItemVolume'), 0)
    120     else:
    121         volume = GetItemVolume(rec)
    122         unit = dogma.units.get_display_name(const.unitVolume)
    123         decimalPlaces = 2 if abs(volume - int(volume)) > const.FLOAT_TOLERANCE else 0
    124         data.volume = localization.GetByLabel('UI/InfoWindow/ValueAndUnit', value=FmtAmt(volume, showFraction=decimalPlaces), unit=unit)
    125         data.Set('sort_%s' % localization.GetByLabel('UI/Inventory/ItemVolume'), volume)
    126     data.scrollID = scrollID
    127     return data
    128 
    129 
    130 @telemetry.ZONE_FUNCTION
    131 def GetItemLabel(rec, data, new = 0):
    132     if getattr(data, 'label', None) and data.viewMode == VIEWMODE_ICONS and not new:
    133         return data.label
    134     name = GetItemName(rec, data)
    135     if data.viewMode in (VIEWMODE_LIST, VIEWMODE_DETAILS):
    136         label = GetItemLabelForListOrDetails(rec, data, name)
    137         data.label = label
    138     elif data.viewMode == VIEWMODE_CARDS:
    139         data.label = name
    140     else:
    141         data.label = '<center>%s' % name
    142     return data.label
    143 
    144 
    145 class Column(object):
    146     NAME = 0
    147     QUANTITY = 1
    148     GROUP = 2
    149     CATEGORY = 3
    150     SIZE = 4
    151     SLOT = 5
    152     VOLUME = 6
    153     META_LEVEL = 7
    154     TECH_LEVEL = 8
    155     ESTIMATED_PRICE = 9
    156     _COLUMN_BY_LABEL = {'UI/Common/Name': NAME,
    157      'UI/Common/Quantity': QUANTITY,
    158      'UI/Inventory/ItemGroup': GROUP,
    159      'UI/Inventory/ItemCategory': CATEGORY,
    160      'UI/Inventory/ItemSize': SIZE,
    161      'UI/Inventory/ItemSlot': SLOT,
    162      'UI/Inventory/ItemVolume': VOLUME,
    163      'UI/Inventory/ItemMetaLevel': META_LEVEL,
    164      'UI/Inventory/ItemTechLevel': TECH_LEVEL,
    165      'UI/Contracts/ContractsWindow/EstPrice': ESTIMATED_PRICE}
    166     _cached_column_localized_labels_by_language_id = {}
    167 
    168     @classmethod
    169     def from_localized_label(cls, text):
    170         language_id = session.languageID
    171         if language_id not in cls._cached_column_localized_labels_by_language_id:
    172             Column._cache_localized_labels_for_language(session.languageID)
    173         return cls._cached_column_localized_labels_by_language_id[language_id][text]
    174 
    175     @classmethod
    176     def _cache_localized_labels_for_language(cls, language_id):
    177         cls._cached_column_localized_labels_by_language_id[language_id] = {localization.GetByLabel(label, languageID=language_id):header for label, header in cls._COLUMN_BY_LABEL.items()}
    178 
    179 
    180 def _get_item_size(rec, data, name):
    181     attribs = getattr(data, 'godmaattribs', {})
    182     if const.attributeChargeSize in attribs:
    183         return GetFormatAndValue(attributeType=utillib.KeyVal(unitID=const.unitSizeclass, attributeID=const.attributeChargeSize, dataType=const.attributeDataTypeTypeInteger), value=attribs[const.attributeChargeSize])
    184     else:
    185         return ''
    186 
    187 
    188 def _get_item_slot(rec, data, name):
    189     attribs = getattr(data, 'godmaattribs', {})
    190     if const.attributeImplantness in attribs:
    191         return GetFormatAndValue(attributeType=utillib.KeyVal(unitID=int, attributeID=const.attributeImplantness, dataType=const.attributeDataTypeTypeInteger), value=attribs[const.attributeImplantness])
    192     elif const.attributeBoosterness in attribs:
    193         return GetFormatAndValue(attributeType=utillib.KeyVal(unitID=int, attributeID=const.attributeBoosterness, dataType=const.attributeDataTypeTypeInteger), value=attribs[const.attributeBoosterness])
    194     else:
    195         return GetPowerSlotSizeSymbol(rec.typeID) or ''
    196 
    197 
    198 def _get_item_tech_level(rec, data, name):
    199     techLevel = evetypes.GetTechLevel(rec.typeID)
    200     if techLevel:
    201         techLevel = FmtAmt(techLevel)
    202     return '<right>%s' % techLevel
    203 
    204 
    205 _item_column_value_getter_by_column = {Column.NAME: lambda rec, data, name: name,
    206  Column.QUANTITY: lambda rec, data, name: '<right>%s' % GetItemQty(data, 'ln'),
    207  Column.GROUP: lambda rec, data, name: data.groupName,
    208  Column.CATEGORY: lambda rec, data, name: data.categoryName,
    209  Column.SIZE: _get_item_size,
    210  Column.SLOT: _get_item_slot,
    211  Column.VOLUME: lambda rec, data, name: '<right>%s' % data.volume,
    212  Column.META_LEVEL: lambda rec, data, name: '<right>%s' % data.metaLevel,
    213  Column.TECH_LEVEL: _get_item_tech_level,
    214  Column.ESTIMATED_PRICE: lambda rec, data, name: ('<right>%s' % FmtISK(data.estPrice) if data.estPrice else '')}
    215 
    216 def GetImplantsCostForCharacter():
    217     try:
    218         godma = sm.GetService('godma')
    219         godmaItem = godma.GetItem(session.charid)
    220         if godmaItem:
    221             implantCost = 0
    222             for implant in godmaItem.implants:
    223                 if godma.GetTypeAttribute2(implant.typeID, attributeNonDestructible):
    224                     continue
    225                 avgPrice = GetAverageTypePrice(implant.typeID)
    226                 if avgPrice:
    227                     implantCost += avgPrice
    228 
    229             return implantCost
    230         return None
    231     except Exception as e:
    232         log.LogException('Failing to get implant cost')
    233 
    234 
    235 def GetItemLabelForListOrDetails(rec, data, name):
    236     column_values = []
    237     for header in GetVisibleItemHeaders(data.scrollID):
    238         getter = _item_column_value_getter_by_column[Column.from_localized_label(header)]
    239         column_values.append(getter(rec, data, name))
    240 
    241     return u'<t>'.join(column_values)
    242 
    243 
    244 @telemetry.ZONE_FUNCTION
    245 def GetPowerSlotSizeSymbol(recTypeID):
    246     fType = None
    247     for effect in dogma.data.get_type_effects(recTypeID):
    248         labelPath = slotTextByEffect.get(effect.effectID)
    249         if labelPath:
    250             fType = localization.GetByLabel(labelPath)
    251             break
    252 
    253     return fType
    254 
    255 
    256 @telemetry.ZONE_FUNCTION
    257 def GetItemQty(data, fmt = 'ln'):
    258     ret = getattr(data, 'qty_%s' % fmt, None)
    259     if ret is not None and ret[0] == data.item.stacksize:
    260         return ret[1]
    261     ret = ''
    262     if not (data.item.singleton or data.item.typeID in (const.typeBookmark,)):
    263         ret = FmtAmt(data.item.stacksize, fmt)
    264     setattr(data, 'qty_%s' % fmt, (data.item.stacksize, ret))
    265     return ret
    266 
    267 
    268 NAMEABLE_GROUPS = (const.groupWreck,
    269  const.groupCargoContainer,
    270  const.groupSecureCargoContainer,
    271  const.groupAuditLogSecureContainer,
    272  const.groupFreightContainer,
    273  const.groupBiomass)
    274 
    275 def IsValidNamedItem(invItem):
    276     if not invItem.singleton:
    277         return False
    278     elif invItem.categoryID == const.categoryShip:
    279         return True
    280     elif invItem.groupID in NAMEABLE_GROUPS:
    281         return True
    282     elif HasDeployComponent(invItem.typeID):
    283         return True
    284     else:
    285         return False
    286 
    287 
    288 def PrimeEveLocationsBeforeGetItemName(invItems):
    289     itemIDsToPrime = {x.itemID for x in invItems if IsValidNamedItem(x)}
    290     if itemIDsToPrime:
    291         cfg.evelocations.Prime(itemIDsToPrime)
    292 
    293 
    294 @telemetry.ZONE_FUNCTION
    295 def GetItemName(invItem, data = None):
    296     if data and getattr(data, 'name', None):
    297         return data.name
    298     name = evetypes.GetName(invItem.typeID)
    299     if invItem.categoryID == const.categoryStation and invItem.groupID == const.groupStation:
    300         try:
    301             name = localization.GetByLabel('UI/Station/StationInSolarSystem', station=invItem.itemID, solarsystem=invItem.locationID)
    302         except KeyError:
    303             sys.exc_clear()
    304 
    305     elif IsValidNamedItem(invItem):
    306         locationName = cfg.evelocations.Get(invItem.itemID).name
    307         if locationName:
    308             name = locationName
    309     elif invItem.typeID == const.typeAssetSafetyWrap:
    310         name = sm.GetService('assetSafety').GetWrapName(invItem.itemID)
    311     elif invItem.categoryID == const.categoryStructure and invItem.singleton and IsSolarSystem(invItem.locationID):
    312         try:
    313             locationName = cfg.evelocations.Get(invItem.itemID).name
    314             if locationName:
    315                 name = locationName
    316         except KeyError:
    317             pass
    318 
    319     if data:
    320         data.name = name
    321     if name is None:
    322         name = '%s %s' % (evetypes.GetName(invItem.typeID), invItem.itemID)
    323     return name
    324 
    325 
    326 @telemetry.ZONE_FUNCTION
    327 def GetCategoryGroupTypeStringForItem(invItem):
    328     try:
    329         return categoryGroupTypeStringForTypeID[invItem.typeID]
    330     except KeyError:
    331         try:
    332             retString = '%s %s %s' % (evetypes.GetCategoryNameByCategory(invItem.categoryID), evetypes.GetGroupNameByGroup(invItem.groupID), evetypes.GetName(invItem.typeID))
    333             categoryGroupTypeStringForTypeID[invItem.typeID] = retString
    334             return retString
    335         except (IndexError,
    336          TypeError,
    337          evetypes.CategoryNotFoundException,
    338          evetypes.GroupNotFoundException,
    339          evetypes.TypeNotFoundException):
    340             return GetFallbackCategoryGroupTypeStringForItem(invItem)
    341 
    342 
    343 def GetFallbackCategoryGroupTypeStringForItem(invItem):
    344     if invItem is None:
    345         log.LogTraceback('None is not an invItem')
    346         sys.exc_clear()
    347         return 'Unknown Unknown Unknown'
    348     log.LogTraceback('InvalidItem Report')
    349     sys.exc_clear()
    350     log.LogError('--------------------------------------------------')
    351     log.LogError('Invalid Item Report')
    352     log.LogError('Item: ', invItem)
    353     reason = ''
    354     typeName = 'Unknown'
    355     groupName = 'Unknown'
    356     categoryName = 'Unknown'
    357     try:
    358         typeID = getattr(invItem, 'typeID', None)
    359     except IndexError:
    360         typeID = None
    361         sys.exc_clear()
    362 
    363     try:
    364         groupID = getattr(invItem, 'groupID', None)
    365     except IndexError:
    366         groupID = None
    367         sys.exc_clear()
    368 
    369     try:
    370         categoryID = getattr(invItem, 'categoryID', None)
    371     except IndexError:
    372         categoryID = None
    373         sys.exc_clear()
    374 
    375     if typeID is None:
    376         log.LogError('typeID is missing. Probably caused by a coding mistake.')
    377         reason += 'item attribute typeID is missing (Probably a coding mistake). '
    378     else:
    379         typeExists = evetypes.Exists(typeID)
    380         if not typeExists:
    381             log.LogError('THERE IS NO type info FOR typeID:', typeID)
    382             log.LogError('THE DATABASE REQUIRES CLEANUP FOR THIS TYPE')
    383             reason += 'The type %s no longer exists. Database requires cleanup. ' % typeID
    384     if groupID is None:
    385         log.LogError('groupID is missing. Probably caused by a coding mistake.')
    386         reason += 'item attribute groupID is missing (Probably a coding mistake?). '
    387         typeExists = evetypes.Exists(typeID)
    388         if typeExists:
    389             log.LogWarn('Extracting groupID from type')
    390             groupID = evetypes.GetGroupID(typeID)
    391     groupExists = evetypes.GroupExists(groupID)
    392     if groupID is not None:
    393         if not groupExists:
    394             log.LogError('THERE IS NO group info FOR groupID:', groupID)
    395             log.LogError('THE DATABASE REQUIRES CLEANUP FOR THIS GROUP')
    396             reason += 'The group %s no longer exists. Database requires cleanup. ' % groupID
    397     if categoryID is None:
    398         log.LogError('categoryID is missing. Probably caused by a coding mistake.')
    399         reason += 'item attribute categoryID is missing (Probably a coding mistake?). '
    400         if groupExists:
    401             log.LogWarn('Extracting categoryID from group')
    402             categoryID = evetypes.GetCategoryIDByGroup(groupID)
    403     if categoryID is not None:
    404         if not evetypes.CategoryExists(categoryID):
    405             log.LogError('THERE IS NO category info FOR categoryID:', categoryID)
    406             log.LogError('THE DATABASE REQUIRES CLEANUP FOR THIS CATEGORY')
    407             reason += 'The category %s no longer exists. Database requires cleanup. ' % categoryID
    408     if typeExists:
    409         typeName = evetypes.GetName(typeID)
    410     if groupExists:
    411         groupName = evetypes.GetGroupNameByGroup(groupID)
    412     if evetypes.CategoryExists(categoryID):
    413         categoryName = evetypes.GetCategoryNameByCategory(categoryID)
    414     log.LogError('--------------------------------------------------')
    415     prompt_player_fsd_dialog('CustomInfo', {'info': 'Invalid item detected:<br>Item:%s<br>CGT:%s %s %s<br>Reason: %s' % (invItem,
    416               categoryName,
    417               groupName,
    418               typeName,
    419               reason)})
    420     return '%s %s %s' % (categoryName, groupName, typeName)
    421 
    422 
    423 def GetSlimItemName(slimItem):
    424     if slimItem.categoryID == const.categoryShip or HasNpcPilotComponent(slimItem.typeID):
    425         if not slimItem.charID or slimItem.charID == session.charid and slimItem.itemID != session.shipid:
    426             return evetypes.GetName(slimItem.typeID)
    427         if idCheckers.IsCharacter(slimItem.charID):
    428             return cfg.eveowners.Get(slimItem.charID).name
    429     else:
    430         if slimItem.groupID == appConst.groupHarvestableCloud or slimItem.categoryID == appConst.categoryAsteroid:
    431             return evetypes.GetName(slimItem.typeID)
    432         if HasMineableComponent(slimItem.typeID):
    433             attributes = GetMineableComponentTypeAttributes(slimItem.typeID)
    434             output_type = attributes.outputType
    435             return evetypes.GetName(output_type)
    436         if slimItem.categoryID == const.categoryOrbital:
    437             return localization.GetByLabel('UI/Inventory/SlimItemNames/SlimOrbital', typeID=slimItem.typeID, planetID=slimItem.planetID, corpName=cfg.eveowners.Get(slimItem.ownerID).name)
    438         if slimItem.groupID == const.groupDisruptedGate:
    439             return localization.GetByLabel('UI/Inventory/SlimItemNames/SlimDisruptedGate', solarSystemID=slimItem.targetSolarSystemID)
    440     locationname = cfg.evelocations.Get(slimItem.itemID).name
    441     if locationname and locationname[0] != '@':
    442         if slimItem.groupID == const.groupBeacon:
    443             dungeonNameID = getattr(slimItem, 'dungeonNameID', None)
    444             if dungeonNameID:
    445                 translatedName = localization.GetByMessageID(dungeonNameID)
    446                 return translatedName
    447         return locationname
    448     else:
    449         return evetypes.GetName(slimItem.typeID)
    450 
    451 
    452 def EditStationName(stationname, compact = 0, usename = 0):
    453     if compact:
    454         longForm = localization.GetByLabel('UI/Locations/LocationMoonLong') + ' '
    455         shortForm = localization.GetByLabel('UI/Locations/LocationMoonShort')
    456         stationname = stationname.replace(longForm, shortForm).replace(longForm.lower(), shortForm.lower())
    457     _stationname = stationname.split(' - ')
    458     if len(_stationname) >= 2 and usename:
    459         stationname = _stationname[-1]
    460     return stationname
    461 
    462 
    463 def GetTextHeight(strng, width = 0, fontsize = fontconst.EVE_MEDIUM_FONTSIZE, linespace = None, hspace = 0, uppercase = 0, specialIndent = 0, getTextObj = 0, tabs = [], maxLines = None, fontStyle = None, bold = False, **kwds):
    464     return uicore.font.GetTextHeight(strng, width=width, font=None, fontStyle=fontStyle, fontsize=fontsize, linespace=linespace, letterspace=hspace, uppercase=uppercase, specialIndent=specialIndent, getTextObj=getTextObj, tabs=tabs, maxLines=maxLines, bold=bold)
    465 
    466 
    467 def GetTextWidth(strng, fontsize = fontconst.EVE_MEDIUM_FONTSIZE, hspace = 0, uppercase = 0, fontStyle = None, bold = False):
    468     return uicore.font.GetTextWidth(strng, fontsize=fontsize, letterspace=hspace, uppercase=uppercase, fontStyle=fontStyle, bold=bold)
    469 
    470 
    471 def GetStanding(standing, type = 0):
    472     if standing > 5:
    473         return localization.GetByLabel('UI/Standings/Excellent')
    474     elif standing > 0:
    475         return localization.GetByLabel('UI/Standings/Good')
    476     elif standing == 0:
    477         return localization.GetByLabel('UI/Standings/Neutral')
    478     elif standing < -5:
    479         return localization.GetByLabel('UI/Standings/Terrible')
    480     elif standing < 0:
    481         return localization.GetByLabel('UI/Standings/Bad')
    482     else:
    483         return localization.GetByLabel('UI/Standings/Unknown')
    484 
    485 
    486 def GetMappedRankBase(rank, warFactionID, align):
    487     logo = Sprite(align=align)
    488     if rank < 0:
    489         logo.texture = None
    490     else:
    491         iconNum = '%s_%s' % (rank / 4 + 1, rank % 4 + 1)
    492         MapLogo(iconNum, logo, root='res:/UI/Texture/Medals/Ranks/%s_' % warFactionID)
    493     if align != uiconst.TOALL:
    494         logo.width = logo.height = 128
    495     else:
    496         logo.width = logo.height = 0
    497     return logo
    498 
    499 
    500 def GetRankSprite(rank, warFactionID, align):
    501     texturePath = _GetRankTexturePath(rank, warFactionID)
    502     if align != uiconst.TOALL:
    503         size = 128
    504     else:
    505         size = 0
    506     logo = Sprite(name='rankSprite', align=align, pos=(0,
    507      0,
    508      size,
    509      size), texturePath=texturePath)
    510     return logo
    511 
    512 
    513 def _GetRankTexturePath(rank, warFactionID):
    514     if rank < 0:
    515         return None
    516     texturePath = 'res:/UI/Texture/Medals/Ranks/%s_%s.png' % (warFactionID, rank)
    517     return texturePath
    518 
    519 
    520 def MapLogo(iconNum, sprite, root = 'res:/UI/Texture/Corps/corps'):
    521     texpix, num = iconNum.split('_')
    522     while texpix.find('0') == 0:
    523         texpix = texpix[1:]
    524 
    525     sprite.texturePath = '%s%s%s.dds' % (root, ['', '0'][int(texpix) < 10], texpix)
    526     num = int(num)
    527     sprite.rectWidth = sprite.rectHeight = 128
    528     sprite.rectLeft = [0, 128][num in (2, 4)]
    529     sprite.rectTop = [0, 128][num > 2]
    530 
    531 
    532 def GetTechLevelIconPathAndHint(typeID = None):
    533     icon = None
    534     hint = None
    535     if typeID is None:
    536         return (icon, hint)
    537     try:
    538         if evetypes.GetCategoryID(typeID) in (const.categoryBlueprint, const.categoryAncientRelic):
    539             ptID = cfg.blueprints.Get(typeID).productTypeID
    540             if ptID is not None:
    541                 typeID = ptID
    542     except Exception:
    543         pass
    544 
    545     metaGroupID = evetypes.GetMetaGroupID(typeID)
    546     if metaGroupID:
    547         icon = GetIconFile(metaGroups.get_icon_id(metaGroupID))
    548         hint = metaGroups.get_name(metaGroupID)
    549     return (icon, hint)
    550 
    551 
    552 def GetTechLevelColor(typeID = None):
    553     if typeID is None:
    554         return
    555     metaGroupID = evetypes.GetMetaGroupID(typeID)
    556     return metaGroups.get_color(metaGroupID)
    557 
    558 
    559 def GetTechLevelIcon(tlicon = None, offset = 0, typeID = None):
    560     icon, hint = GetTechLevelIconPathAndHint(typeID)
    561     if icon:
    562         if tlicon is None:
    563             tlicon = Icon(icon=icon, parent=None, width=16, height=16, align=uiconst.TOPLEFT, idx=0, hint=hint)
    564         else:
    565             MapIcon(tlicon, icon)
    566             tlicon.hint = hint
    567         if offset:
    568             tlicon.top = offset
    569         tlicon.state = uiconst.UI_NORMAL
    570     elif tlicon:
    571         tlicon.state = uiconst.UI_HIDDEN
    572     return tlicon
    573 
    574 
    575 def GetBallparkRecord(itemID):
    576     bp = sm.GetService('michelle').GetBallpark()
    577     if bp and hasattr(bp, 'GetInvItem'):
    578         return bp.GetInvItem(itemID)
    579     else:
    580         return None
    581 
    582 
    583 def Close(owner, wndNames):
    584     for each in wndNames:
    585         if getattr(owner, each, None):
    586             wnd = getattr(owner, each, None)
    587             setattr(owner, each, None)
    588             if not wnd.destroyed:
    589                 wnd.Close()
    590 
    591 
    592 def GetContainerHeader(caption, where, bothlines = 1, padHorizontal = 0, padTop = 0):
    593     container = ContainerAutoSize(name='headercontainer', parent=where, align=uiconst.TOTOP, alignMode=uiconst.TOTOP, padding=(padHorizontal,
    594      padTop,
    595      padHorizontal,
    596      2))
    597     FillThemeColored(bgParent=container, colorType=uiconst.COLORTYPE_UIHILIGHT, align=uiconst.TOALL, opacity=0.15)
    598     t = eveLabel.EveLabelLarge(text=caption, name='header1', parent=container, padding=(8, 4, 8, 4), align=uiconst.TOTOP)
    599     return container
    600 
    601 
    602 def ListWnd(lst, listtype = None, caption = None, hint = None, ordered = 0, minw = 200, minh = 256, minChoices = 1, maxChoices = 1, initChoices = [], validator = None, isModal = 1, scrollHeaders = [], iconMargin = 0, windowName = 'listwindow', lstDataIsGrouped = 0, unstackable = 0, noContentHint = None):
    603     from eve.client.script.ui.control.listwindow import ListWindow
    604     return ListWindow.ShowList(lst, listtype, caption, hint, ordered, minw, minh, minChoices, maxChoices, initChoices, validator, isModal, scrollHeaders, iconMargin, windowName, lstDataIsGrouped, unstackable, noContentHint)
    605 
    606 
    607 def HybridWnd(format, caption, windowID, modal = 1, buttons = None, location = None, minW = 256, minH = 256, blockconfirm = 0, icon = None, unresizeAble = 0, ignoreCurrent = 1):
    608     if windowID is None:
    609         raise ValueError('Created Hybrid window without a windowID', caption)
    610     else:
    611         wnd = Window.GetIfOpen(windowID=windowID)
    612         if wnd:
    613             return
    614     if buttons is None:
    615         buttons = uiconst.OK
    616     from eve.client.script.ui.control.hybridWindow import HybridWindow
    617     wnd = HybridWindow.Open(ignoreCurrent=ignoreCurrent, format=format, caption=caption, modal=modal, windowID=windowID, buttons=buttons, location=location, minW=minW, minH=minH, icon=icon, blockconfirm=blockconfirm)
    618     wnd.MakeUnstackable()
    619     if unresizeAble:
    620         wnd.MakeUnResizeable()
    621     import uthread
    622     uthread.new(wnd.OnScale_)
    623     if modal == 1:
    624         if wnd.ShowModal() == uiconst.ID_OK:
    625             return wnd.result
    626         else:
    627             return
    628     return wnd
    629 
    630 
    631 def GetDialogIconTexturePath(icon):
    632     if isinstance(icon, (types.StringType, eveicon.IconData)):
    633         return icon
    634     else:
    635         mapping = {uiconst.INFO: 'res:/ui/Texture/WindowIcons/info.png',
    636          uiconst.WARNING: 'res:/ui/Texture/WindowIcons/warning.png',
    637          uiconst.QUESTION: 'res:/ui/Texture/WindowIcons/question.png',
    638          uiconst.ERROR: 'res:/ui/Texture/WindowIcons/stop.png',
    639          uiconst.FATAL: 'res:/UI/Texture/WindowICons/criminal.png'}
    640         return mapping.get(icon, 'res:/ui/Texture/WindowIcons/warning.png')
    641 
    642 
    643 def TextBox(header, txt, modal = 0, windowID = 'generictextbox2', tabs = [], preformatted = 0, scrolltotop = 1):
    644     wnd = Window.GetIfOpen(windowID=windowID)
    645     if wnd is None or wnd.destroyed or uicore.uilib.Key(uiconst.VK_SHIFT):
    646         format = [{'type': 'textedit',
    647           'readonly': 1,
    648           'label': '_hide',
    649           'key': 'text'}]
    650         wnd = HybridWnd(format, header, windowID, modal, buttons=uiconst.CLOSE, location=None, minW=256, minH=128)
    651         if wnd:
    652             wnd.form.align = uiconst.TOALL
    653             wnd.form.left = wnd.form.width = 3
    654             wnd.form.top = -2
    655             wnd.form.height = 6
    656             wnd.form.sr.text.parent.align = uiconst.TOALL
    657             wnd.form.sr.text.parent.left = wnd.form.sr.text.parent.top = wnd.form.sr.text.parent.width = wnd.form.sr.text.parent.height = 0
    658             wnd.form.sr.text.parent.children[0].height = 0
    659             wnd.form.sr.text.autoScrollToBottom = 0
    660     if wnd is not None:
    661         i = 1
    662         for t in tabs:
    663             setattr(wnd.form.sr.text.content.control, 'tabstop%s' % i, t)
    664             i = i + 1
    665 
    666         wnd.form.sr.text.SetValue(txt, scrolltotop=scrolltotop, preformatted=preformatted)
    667         if wnd.state == uiconst.UI_NORMAL:
    668             wnd.SetOrder(0)
    669         else:
    670             wnd.Maximize()
    671 
    672 
    673 def QtyPopup(maxvalue = None, minvalue = 0, setvalue = '', hint = None, caption = None, label = '', digits = 0):
    674     if caption is None:
    675         caption = localization.GetByLabel('UI/Common/SetQuantity')
    676     if maxvalue is not None and hint is None:
    677         hint = localization.GetByLabel('UI/Common/SetQtyBetween', min=FmtAmt(minvalue), max=FmtAmt(maxvalue))
    678         if setvalue == 0:
    679             setvalue = maxvalue
    680     maxvalue = maxvalue or min(maxvalue, sys.maxint)
    681     format = []
    682     if hint is not None:
    683         format += [{'type': 'text',
    684           'text': hint,
    685           'frame': 0}]
    686     if label is not None:
    687         format += [{'type': 'labeltext',
    688           'label': label,
    689           'text': '',
    690           'frame': 0,
    691           'labelwidth': 240}]
    692     if digits:
    693         format += [{'type': 'edit',
    694           'setvalue': setvalue,
    695           'floatonly': [minvalue, maxvalue, digits],
    696           'key': 'qty',
    697           'label': '_hide',
    698           'required': 1,
    699           'frame': 0,
    700           'setfocus': 1,
    701           'selectall': 1}]
    702     else:
    703         format += [{'type': 'edit',
    704           'setvalue': setvalue,
    705           'intonly': [minvalue, maxvalue],
    706           'key': 'qty',
    707           'label': '_hide',
    708           'required': 1,
    709           'frame': 0,
    710           'setfocus': 1,
    711           'selectall': 1}]
    712     return HybridWnd(format, caption, windowID='setQuantityPopup', modal=1, buttons=uiconst.OKCANCEL, location=None, minW=300, minH=120)
    713 
    714 
    715 @caching.Memoize
    716 def GetInvItemDefaultHiddenHeaders():
    717     return [localization.GetByLabel('UI/Inventory/ItemMetaLevel'), localization.GetByLabel('UI/Inventory/ItemTechLevel'), localization.GetByLabel('UI/Inventory/ItemCategory')]
    718 
    719 
    720 @telemetry.ZONE_FUNCTION
    721 @caching.Memoize
    722 def GetInvItemDefaultHeaders():
    723     return [localization.GetByLabel('UI/Common/Name'),
    724      localization.GetByLabel('UI/Common/Quantity'),
    725      localization.GetByLabel('UI/Inventory/ItemGroup'),
    726      localization.GetByLabel('UI/Inventory/ItemCategory'),
    727      localization.GetByLabel('UI/Inventory/ItemSize'),
    728      localization.GetByLabel('UI/Inventory/ItemSlot'),
    729      localization.GetByLabel('UI/Inventory/ItemVolume'),
    730      localization.GetByLabel('UI/Inventory/ItemMetaLevel'),
    731      localization.GetByLabel('UI/Inventory/ItemTechLevel'),
    732      localization.GetByLabel('UI/Contracts/ContractsWindow/EstPrice')]
    733 
    734 
    735 @telemetry.ZONE_FUNCTION
    736 def GetVisibleItemHeaders(scrollID):
    737     key = GetHiddenColumnsKey(scrollID)
    738     defaultHeaders = GetInvItemDefaultHeaders()
    739     hiddenColumns = settings.user.ui.Get('filteredColumns_%s' % uiconst.SCROLLVERSION, {}).get(key, [])
    740     allHiddenColumns = hiddenColumns + settings.user.ui.Get('filteredColumnsByDefault_%s' % uiconst.SCROLLVERSION, {}).get((key, session.languageID), [])
    741     filterColumns = filter(lambda x: x not in allHiddenColumns, defaultHeaders)
    742     return filterColumns
    743 
    744 
    745 def GetLightYearDistance(fromSystem, toSystem, fraction = True):
    746     for system in (fromSystem, toSystem):
    747         if not isinstance(system, numbers.Integral):
    748             return None
    749         if not IsKnownSpaceSystem(system):
    750             return None
    751 
    752     def GetLoc(system):
    753         if isinstance(system, numbers.Integral):
    754             return cfg.evelocations.Get(system)
    755         if type(system) == types.InstanceType:
    756             return system
    757 
    758     fromSystem = GetLoc(fromSystem)
    759     toSystem = GetLoc(toSystem)
    760     dist = math.sqrt((toSystem.x - fromSystem.x) ** 2 + (toSystem.y - fromSystem.y) ** 2 + (toSystem.z - fromSystem.z) ** 2) / const.LIGHTYEAR
    761     if fraction:
    762         dist = float(int(dist * 10)) / 10
    763     return dist
    764 
    765 
    766 def HideButtonFromGroup(btns, label, button = None):
    767     if label:
    768         btn = btns.FindChild('%s_Btn' % label)
    769         if btn:
    770             btn.state = uiconst.UI_HIDDEN
    771     if button:
    772         btn.state = uiconst.UI_HIDDEN
    773 
    774 
    775 def ShowButtonFromGroup(btns, label, button = None):
    776     if label:
    777         btn = btns.FindChild('%s_Btn' % label)
    778         if btn:
    779             btn.state = uiconst.UI_NORMAL
    780     if button:
    781         btn.state = uiconst.UI_NORMAL
    782 
    783 
    784 def FormatMedalData(data):
    785     from eve.client.script.ui.shared.medalribbonranks import Ribbon
    786     from eve.client.script.ui.shared.medalribbonranks import Medal
    787     fdata = []
    788     for part in (1, 2):
    789         dpart = {1: Ribbon,
    790          2: Medal}.get(part, None)
    791         pdata = []
    792         for row in data.Filter('part').get(part):
    793             label, icon = row.graphic.split('.')
    794             color = row.color
    795             pdata.append((label, icon, color))
    796 
    797         fdata.append([dpart, pdata])
    798 
    799     return fdata
    800 
    801 
    802 def GetFullscreenProjectionViewAndViewport():
    803     viewport = trinity.device.viewport
    804     camera = sm.GetService('sceneManager').GetActiveCamera()
    805     return (camera.projectionMatrix, camera.viewMatrix, viewport)
    806 
    807 
    808 def GetOwnerLogo(parent, ownerID, size = 64, noServerCall = False):
    809     if idCheckers.IsCharacter(ownerID):
    810         logo = Icon(icon=None, parent=parent, pos=(0,
    811          0,
    812          size,
    813          size), ignoreSize=True)
    814         if size < 64:
    815             fetchSize = 64
    816         else:
    817             fetchSize = size
    818         sm.GetService('photo').GetPortrait(ownerID, fetchSize, logo)
    819     elif idCheckers.IsCorporation(ownerID) or idCheckers.IsAlliance(ownerID) or idCheckers.IsFaction(ownerID):
    820         GetLogoIcon(itemID=ownerID, parent=parent, pos=(0,
    821          0,
    822          size,
    823          size), ignoreSize=True)
    824     else:
    825         raise RuntimeError('ownerID %d is not of an owner type!!' % ownerID)