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

industryTooltips.py (36939B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\industry\views\industryTooltips.py
      2 from collections import defaultdict
      3 import math
      4 from carbonui import const as uiconst, fontconst, TextColor
      5 from carbonui.primitives.fill import Fill
      6 from eve.client.script.ui.control.entries.item import Item
      7 from eve.client.script.ui.control.entries.util import GetFromClass
      8 from menu import MenuLabel
      9 from carbonui.control.scrollContainer import ScrollContainer
     10 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     11 from carbonui.primitives.frame import Frame
     12 from carbonui.primitives.sprite import Sprite
     13 from carbonui.primitives.transform import Transform
     14 from carbonui.util.color import Color
     15 from dogma.const import attributeManufactureSlotLimit, attributeMaxLaborotorySlots, attributeReactionSlotLimit
     16 from eve.client.script.ui.control.itemIcon import ItemIcon
     17 from eve.client.script.ui.control.eveLabel import EveLabelMedium, Label, EveHeaderMedium, EveHeaderLarge
     18 from eve.client.script.ui.control.eveScroll import Scroll
     19 from eve.client.script.ui.control.themeColored import FillThemeColored
     20 from eve.client.script.ui.shared.cloneGrade import ORIGIN_INDUSTRY
     21 from eve.client.script.ui.shared.cloneGrade.omegaTooltipPanelCell import OmegaTooltipPanelCell
     22 from eve.client.script.ui.shared.industry import industryUIConst
     23 from eve.client.script.ui.shared.industry.systemCostIndexGauge import SystemCostIndexGauge
     24 from eve.client.script.ui.shared.industry.views.errorFrame import ErrorFrame
     25 from eve.client.script.ui.shared.industry.views.industryCaptionLabel import IndustryCaptionLabel
     26 from eve.client.script.ui.shared.shipTree.infoBubble import SkillEntry
     27 from eve.common.script.util import industryCommon
     28 from eve.common.script.util.eveFormat import FmtISK
     29 import evetypes
     30 import industry
     31 from inventorycommon import const
     32 import localization
     33 from localization import GetByLabel
     34 from localization.formatters import FormatNumeric
     35 from carbonui.primitives.container import Container
     36 from eveservices.menu import GetMenuService
     37 import structures
     38 from eve.client.script.ui import eveColor
     39 from omega.client.clone_grade_svc import get_clone_grade_svc
     40 from security.common.const import INDUSTRY_SECURITY_TAX_RATE
     41 MARGIN = (8, 8, 8, 0)
     42 PADBOTTOM = (0, 0, 0, 8)
     43 
     44 def AddMaterialGroupRow(materialGroupID, panel):
     45     cont = ContainerAutoSize(align=uiconst.TOPLEFT)
     46     icon = Sprite(parent=cont, align=uiconst.TOPLEFT, texturePath=industryUIConst.ICON_BY_INDUSTRYGROUP[materialGroupID], pos=(4, 0, 12, 12), color=industryUIConst.COLOR_FRAME)
     47     label = IndustryCaptionLabel(parent=cont, align=uiconst.TOPLEFT, text=localization.GetByLabel(industryUIConst.LABEL_BY_INDUSTRYGROUP[materialGroupID]), left=24)
     48     panel.AddCell(cont, colSpan=3, cellPadding=(0, 0, 0, 5))
     49 
     50 
     51 def AddItemRow(panel, materialData):
     52     panel.AddCell(MaterialIconAndLabel(materialData=materialData), cellPadding=(0, 0, 0, 3))
     53     available = FormatNumeric(materialData.available, useGrouping=True)
     54     quantity = FormatNumeric(materialData.quantity, useGrouping=True)
     55     color = industryUIConst.COLOR_NOTREADY if materialData.errors else industryUIConst.COLOR_READY
     56     colorHex = Color.RGBtoHex(*color)
     57     label = EveLabelMedium(align=uiconst.CENTERRIGHT, text='<color=%s>%s / %s' % (colorHex, available, quantity))
     58     panel.AddCell(label, cellPadding=(8, 0, 0, 3))
     59 
     60 
     61 def AddOutcomeRow(panel, product):
     62     panel.AddCell(MaterialIconAndLabel(materialData=product), cellPadding=(0, 0, 0, 0))
     63     label = EveLabelMedium(align=uiconst.CENTERRIGHT, text='x %s' % product.quantity)
     64     panel.AddCell(label, cellPadding=(8, 0, 0, 0))
     65     panel.AddSpacer(width=0, height=8, colSpan=2)
     66 
     67 
     68 def AddPriceRow(panel, price):
     69     if not price:
     70         iskPrice = localization.GetByLabel('UI/Inventory/PriceUnavailable')
     71     else:
     72         iskPrice = FmtISK(price)
     73     label = EveLabelMedium(text=localization.GetByLabel('UI/Industry/TotalEstimatedPrice'), color=Color.GRAY)
     74     panel.AddCell(label)
     75     label = EveLabelMedium(align=uiconst.TOPRIGHT, text=iskPrice)
     76     panel.AddCell(label, cellPadding=(8, 0, 0, 0))
     77     panel.AddCell(cellPadding=PADBOTTOM, colSpan=2)
     78 
     79 
     80 def AddVolumeRow(panel, value):
     81     label = EveLabelMedium(text=localization.GetByLabel('UI/Common/Volume'), color=Color.GRAY)
     82     panel.AddCell(label)
     83     text = localization.GetByLabel('UI/Inventory/ContainerCapacity', capacity=value)
     84     label = EveLabelMedium(align=uiconst.TOPRIGHT, text=text)
     85     panel.AddCell(label, cellPadding=(8, 0, 0, 0))
     86     panel.AddCell(cellPadding=PADBOTTOM, colSpan=2)
     87 
     88 
     89 def AddTypeBonusRow(panel, text, bonusME = None, bonusTE = None):
     90     label = EveLabelMedium(text=text, color=Color.GRAY)
     91     panel.AddCell(label)
     92     if bonusME:
     93         label = EveLabelMedium(align=uiconst.TOPRIGHT, text=bonusME)
     94         panel.AddCell(label, cellPadding=(8, 0, 0, 0))
     95     if bonusTE:
     96         label = EveLabelMedium(align=uiconst.TOPRIGHT, text=bonusTE)
     97         panel.AddCell(label, cellPadding=(8, 0, 0, 0))
     98     panel.FillRow()
     99     panel.AddCell(cellPadding=PADBOTTOM, colSpan=2)
    100 
    101 
    102 def AddDescriptionRow(panel, text, cellPadding = PADBOTTOM):
    103     if text:
    104         description = EveLabelMedium(text=text, color=Color.GRAY, align=uiconst.TOTOP)
    105         panel.AddCell(description, colSpan=2, cellPadding=cellPadding)
    106 
    107 
    108 def AddSkillRow(panel, typeID, level = None, cellPadding = PADBOTTOM):
    109     if level is None:
    110         showLevel = False
    111         level = 1
    112     else:
    113         showLevel = True
    114     panel.AddRow(rowClass=SkillEntry, typeID=typeID, level=level, showLevel=showLevel)
    115 
    116 
    117 def AddErrorRow(panel, error, errorArgs):
    118     text = industryCommon.GetErrorLabel(error, *errorArgs)
    119     if not text:
    120         text = error.name
    121     description = EveLabelMedium(text=text, align=uiconst.TOPLEFT)
    122     cell = panel.AddCell(description, colSpan=2, cellPadding=(8, 4, 8, 4))
    123     frame = ErrorFrame(bgParent=cell)
    124     frame.Show()
    125 
    126 
    127 def AddModifierRow(name, value, panel, hint = None):
    128     state = uiconst.UI_NORMAL if hint else uiconst.UI_DISABLED
    129     label = panel.AddLabelMedium(text=name, align=uiconst.TOPLEFT, cellPadding=(0, 0, 0, 2), state=state)
    130     label.hint = hint
    131     panel.AddLabelMedium(text=value, align=uiconst.TOPRIGHT, cellPadding=(12, 0, 0, 2))
    132 
    133 
    134 def AddModifierRows(caption, modifiers, panel):
    135     label = IndustryCaptionLabel(text=caption, width=220)
    136     panel.AddCell(label, colSpan=panel.columns, cellPadding=(0, 0, 0, 2))
    137     for modifier in modifiers:
    138         AddModifierRow(modifier.GetName(), modifier.GetPercentageLabel(), panel)
    139 
    140 
    141 def AddJobModifierRows(panel, modifierCls, jobData):
    142     modifiers = jobData.GetModifiers(modifierCls)
    143     if not modifiers:
    144         return
    145     caption = jobData.GetModifierCaption(modifierCls)
    146     AddModifierRows(caption, modifiers, panel)
    147     panel.AddSpacer(0, 6, colSpan=2)
    148 
    149 
    150 def AddSystemCostIndexRow(activityID, facilityData, tooltipPanel, cellPadding = (8, 0, 0, 8)):
    151     text = '<color=gray>' + localization.GetByLabel('UI/Industry/SystemCostIndex')
    152     tooltipPanel.AddLabelMedium(text=text, align=uiconst.TOPLEFT, cellPadding=(0, 0, 0, 0))
    153     height = 13
    154     gauge = SystemCostIndexGauge(gaugeHeight=height, align=uiconst.TOPRIGHT, pos=(0,
    155      2,
    156      50,
    157      height), facilityData=facilityData, activityID=activityID)
    158     tooltipPanel.AddCell(gauge, cellPadding=cellPadding)
    159 
    160 
    161 def AddTaxRateRows(panel, taxRates):
    162     for serviceID, taxRate in taxRates.iteritems():
    163         if taxRate is None:
    164             continue
    165         text = '<color=gray>%s %s:' % (localization.GetByLabel(structures.GetServiceLabel(serviceID)), localization.GetByLabel('UI/Industry/Tax'))
    166         panel.AddLabelMedium(text=text, align=uiconst.TOPLEFT, cellPadding=(0, 0, 0, 0))
    167         color = '<color=red>' if taxRate > 0 else '<color=green>'
    168         panel.AddLabelSmall(text='%s%.2f%%' % (color, taxRate), align=uiconst.TOPRIGHT, cellPadding=(0, 0, 0, 0))
    169 
    170 
    171 class MaterialTooltipPanel:
    172 
    173     def __init__(self, jobData, materialData, tooltipPanel):
    174         self.jobData = jobData
    175         self.materialData = materialData
    176         self.tooltipPanel = tooltipPanel
    177         tooltipPanel.margin = MARGIN
    178         tooltipPanel.columns = 2
    179         self.Reconstruct()
    180         self.jobData.on_updated.connect(self.Reconstruct)
    181 
    182     def Reconstruct(self, *args):
    183         if self.tooltipPanel.destroyed:
    184             return
    185         self.tooltipPanel.Flush()
    186         AddItemRow(self.tooltipPanel, self.materialData)
    187         self.tooltipPanel.AddCell(cellPadding=(0, 0, 0, 4), colSpan=2)
    188         price = self.materialData.GetEstimatedUnitPrice() * self.materialData.quantity
    189         AddPriceRow(self.tooltipPanel, price)
    190 
    191 
    192 class OutcomeTooltipPanel:
    193 
    194     def __init__(self, jobData, tooltipPanel):
    195         self.jobData = jobData
    196         self.tooltipPanel = tooltipPanel
    197         tooltipPanel.state = uiconst.UI_NORMAL
    198         tooltipPanel.margin = MARGIN
    199         tooltipPanel.columns = 2
    200         if self.jobData.IsProductSelectable() and not self.jobData.IsInstalled():
    201             AddDescriptionRow(self.tooltipPanel, GetByLabel('UI/Industry/SelectOutcome'))
    202             scroll = Scroll(align=uiconst.TOPLEFT, width=250)
    203             scroll.OnSelectionChange = self.OnScrollSelectionChange
    204             scrollList = self.GetScrollContent()
    205             scroll.Load(contentList=scrollList)
    206             scroll.ScrollToSelectedNode()
    207             scroll.height = min(len(scrollList) * 29 + 2, 200)
    208             scroll.Confirm = self.Confirm
    209             self.tooltipPanel.AddCell(scroll, colSpan=3, cellPadding=PADBOTTOM)
    210         else:
    211             AddOutcomeRow(self.tooltipPanel, self.jobData.product)
    212             price = self.jobData.product.GetEstimatedUnitPrice() * self.jobData.product.quantity
    213             AddPriceRow(self.tooltipPanel, price)
    214             if hasattr(self.jobData.product, 'GetPackagedVolume'):
    215                 volume = self.jobData.product.GetPackagedVolume() * self.jobData.product.quantity
    216                 AddVolumeRow(self.tooltipPanel, volume)
    217 
    218     def GetScrollContent(self):
    219         entries = []
    220         for product in self.jobData.products:
    221             entry = GetFromClass(Item, {'typeID': product.typeID,
    222              'label': evetypes.GetName(product.typeID),
    223              'getIcon': True,
    224              'isCopy': not product.original,
    225              'hint': evetypes.GetDescription(product.typeID),
    226              'isSelected': self.jobData.GetProductTypeID() == product.typeID,
    227              'OnDblClick': self.OnNodeDblClick})
    228             entries.append(entry)
    229 
    230         entries = sorted(entries, key=lambda x: (x.typeID is not None, x.label))
    231         return entries
    232 
    233     def Confirm(self, *args):
    234         self.tooltipPanel.Close()
    235 
    236     def OnNodeDblClick(self, node):
    237         self.tooltipPanel.Close()
    238 
    239     def OnScrollSelectionChange(self, nodes):
    240         if not nodes:
    241             return
    242         self.jobData.product = nodes[0].typeID
    243 
    244 
    245 class MaterialGroupTooltipPanel:
    246 
    247     def __init__(self, materialsByGroupID, tooltipPanel, jobData):
    248         tooltipPanel.margin = MARGIN
    249         tooltipPanel.state = uiconst.UI_NORMAL
    250         tooltipPanel.columns = 2
    251         self.materialsByGroupID = materialsByGroupID
    252         self.jobData = jobData
    253         self.tooltipPanel = tooltipPanel
    254         self.Reconstruct()
    255         self.jobData.on_updated.connect(self.Reconstruct)
    256 
    257     def Reconstruct(self, *args):
    258         if self.tooltipPanel.destroyed:
    259             return
    260         self.tooltipPanel.Flush()
    261         for materialGroupID, materials in self.materialsByGroupID:
    262             AddMaterialGroupRow(materialGroupID, self.tooltipPanel)
    263             materials = filter(lambda m: m.typeID is not None, materials)
    264             if materials:
    265                 materials.sort(key=lambda x: x.GetName())
    266                 for materialData in materials:
    267                     AddItemRow(self.tooltipPanel, materialData)
    268 
    269                 self.tooltipPanel.AddCell(cellPadding=(0, 0, 0, 10), colSpan=2)
    270             else:
    271                 label = EveLabelMedium(align=uiconst.CENTERLEFT, left=16, text=localization.GetByLabel('UI/Industry/NoItemSelected'))
    272                 self.tooltipPanel.AddCell(label, cellPadding=(8, 0, 0, 10), colSpan=2)
    273 
    274         estPrice, volume = self.GetTotalEstimatedPriceAndVolume()
    275         AddPriceRow(self.tooltipPanel, estPrice)
    276         AddVolumeRow(self.tooltipPanel, volume)
    277         AddJobModifierRows(self.tooltipPanel, industry.MaterialModifier, self.jobData)
    278 
    279     def GetTotalEstimatedPriceAndVolume(self):
    280         totalPrice = 0.0
    281         totalVolume = 0.0
    282         for _, materials in self.materialsByGroupID:
    283             for material in materials:
    284                 if material.typeID:
    285                     totalPrice += material.GetEstimatedUnitPrice() * material.quantity
    286                     totalVolume += material.GetPackagedVolume() * material.quantity
    287 
    288         return (totalPrice, totalVolume)
    289 
    290 
    291 class MaterialIconAndLabel(ContainerAutoSize):
    292     default_state = uiconst.UI_NORMAL
    293 
    294     def ApplyAttributes(self, attributes):
    295         ContainerAutoSize.ApplyAttributes(self, attributes)
    296         self.materialData = attributes.materialData
    297         if isinstance(self.materialData, industry.Blueprint):
    298             bpData = self.materialData
    299         else:
    300             bpData = None
    301         icon = ItemIcon(parent=self, align=uiconst.CENTERLEFT, state=uiconst.UI_DISABLED, pos=(0, 0, 20, 20), typeID=self.materialData.typeID, bpData=bpData, showOmegaOverlay=False)
    302         label = EveLabelMedium(parent=self, align=uiconst.CENTERLEFT, text=self.materialData.GetName(), left=24)
    303 
    304     def GetMenu(self):
    305         menu = GetMenuService().GetMenuFromItemIDTypeID(None, self.materialData.typeID, includeMarketDetails=True)
    306         menu.insert(0, (MenuLabel('UI/Inventory/ItemActions/BuyThisType'), GetMenuService().QuickBuy, (self.materialData.typeID, self.materialData.missing)))
    307         return menu
    308 
    309     def GetHint(self):
    310         return evetypes.GetDescription(self.materialData.typeID)
    311 
    312 
    313 class SkillTooltipPanel:
    314 
    315     def __init__(self, skills, tooltipPanel):
    316         tooltipPanel.margin = 8
    317         tooltipPanel.state = uiconst.UI_NORMAL
    318         tooltipPanel.columns = 2
    319         self.tooltipPanel = tooltipPanel
    320         if not skills:
    321             description = EveLabelMedium(text=localization.GetByLabel('UI/Industry/NoSkillRequirements'), color=Color.GRAY, align=uiconst.TOPLEFT)
    322             tooltipPanel.AddCell(description, colSpan=2, cellPadding=(0, 0, 0, 8))
    323             return
    324         AddDescriptionRow(tooltipPanel, localization.GetByLabel('UI/Industry/RequiredSkills'), cellPadding=(0, 0, 0, 2))
    325         for typeID, level in skills:
    326             AddSkillRow(tooltipPanel, typeID, level, cellPadding=(0, 0, 0, 1))
    327 
    328         if self.IsCloneStateRestricted(skills):
    329             tooltipPanel.state = uiconst.UI_NORMAL
    330             tooltipPanel.AddRow(rowClass=OmegaTooltipPanelCell, cellPadding=(0, 6, 0, 6), text=GetByLabel('UI/CloneState/RequiresOmegaClone'), origin=ORIGIN_INDUSTRY)
    331 
    332     def IsCloneStateRestricted(self, skills):
    333         for typeID, level in skills:
    334             if get_clone_grade_svc().is_skill_level_restricted(typeID, level):
    335                 return True
    336 
    337         return False
    338 
    339 
    340 class SubmitButtonTooltipPanel:
    341 
    342     def __init__(self, status, errors, tooltipPanel):
    343         self.tooltipPanel = tooltipPanel
    344         self.Reconstruct(status, errors)
    345 
    346     def Reconstruct(self, status, errors):
    347         if self.tooltipPanel.destroyed:
    348             return
    349         self.tooltipPanel.margin = MARGIN
    350         numColumns = 2
    351         self.tooltipPanel.columns = numColumns
    352         self.tooltipPanel.Flush()
    353         if status == industry.STATUS_INSTALLED:
    354             self.tooltipPanel.AddLabelMedium(text=localization.GetByLabel('UI/Industry/StopJobHint'), wrapWidth=200, colSpan=numColumns)
    355             spacerHeight = 8
    356         else:
    357             if not errors:
    358                 return
    359             errors = {error[0].value:error for error in errors}.values()
    360             self.tooltipPanel.cellSpacing = (0, 4)
    361             for error, errorArgs in errors:
    362                 AddErrorRow(self.tooltipPanel, error, errorArgs)
    363 
    364             spacerHeight = 4
    365         self.tooltipPanel.AddSpacer(width=0, height=spacerHeight, colSpan=numColumns)
    366 
    367 
    368 class JobsSummaryTooltipPanel:
    369 
    370     def __init__(self, jobData, tooltipPanel):
    371         self.tooltipPanel = tooltipPanel
    372         tooltipPanel.state = uiconst.UI_NORMAL
    373         tooltipPanel.margin = 8
    374         tooltipPanel.columns = 2
    375         rangeLabel = industryUIConst.GetControlRangeLabel(jobData.max_distance)
    376         activityType = industryUIConst.GetActivityType(jobData.activityID)
    377         usedSlots = sm.GetService('industrySvc').GetJobCountForActivity(jobData.activityID)
    378         if activityType == industryUIConst.TYPE_MANUFACTURING:
    379             text = localization.GetByLabel('UI/Industry/JobSummaryManufacturing', used=usedSlots, max=jobData.max_slots)
    380             tooltipPanel.AddLabelMedium(text=text, width=320, cellPadding=(0, 0, 0, 2), colSpan=2)
    381             AddSkillRow(tooltipPanel, const.typeMassProduction, cellPadding=(0, 0, 0, 1))
    382             AddSkillRow(tooltipPanel, const.typeAdvancedMassProduction, cellPadding=(0, 0, 0, 1))
    383             text = localization.GetByLabel('UI/Industry/ControlRangeManufacturing', range=rangeLabel)
    384             tooltipPanel.AddLabelMedium(text=text, width=320, cellPadding=(0, 8, 0, 2), colSpan=2)
    385             AddSkillRow(tooltipPanel, const.typeSupplyChainManagement, cellPadding=(0, 0, 0, 1))
    386         elif activityType == industryUIConst.TYPE_SCIENCE:
    387             text = localization.GetByLabel('UI/Industry/JobSummaryScience', used=usedSlots, max=jobData.max_slots)
    388             tooltipPanel.AddLabelMedium(text=text, width=320, cellPadding=(0, 0, 0, 2), colSpan=2)
    389             AddSkillRow(tooltipPanel, const.typeLaboratoryOperation, cellPadding=(0, 0, 0, 1))
    390             AddSkillRow(tooltipPanel, const.typeAdvancedLaboratoryOperation, cellPadding=(0, 0, 0, 1))
    391             text = localization.GetByLabel('UI/Industry/ControlRangeScience', range=rangeLabel)
    392             tooltipPanel.AddLabelMedium(text=text, width=320, cellPadding=(0, 8, 0, 2), colSpan=2)
    393             AddSkillRow(tooltipPanel, const.typeScientificNetworking, cellPadding=(0, 0, 0, 1))
    394         elif activityType == industryUIConst.TYPE_REACTION:
    395             text = localization.GetByLabel('UI/Industry/JobSummaryReactions', used=usedSlots, max=jobData.max_slots)
    396             tooltipPanel.AddLabelMedium(text=text, width=320, cellPadding=(0, 0, 0, 2), colSpan=2)
    397             AddSkillRow(tooltipPanel, const.typeMassReactions, cellPadding=(0, 0, 0, 1))
    398             AddSkillRow(tooltipPanel, const.typeAdvancedMassReactions, cellPadding=(0, 0, 0, 1))
    399             text = localization.GetByLabel('UI/Industry/ControlRangeReactions', range=rangeLabel)
    400             tooltipPanel.AddLabelMedium(text=text, width=320, cellPadding=(0, 8, 0, 2), colSpan=2)
    401             AddSkillRow(tooltipPanel, const.typeRemoteReactions, cellPadding=(0, 0, 0, 1))
    402 
    403 
    404 class AllJobsSummaryTooltipPanel:
    405 
    406     def __init__(self, tooltipPanel):
    407         self.tooltipPanel = tooltipPanel
    408         tooltipPanel.state = uiconst.UI_NORMAL
    409         tooltipPanel.margin = 8
    410         tooltipPanel.columns = 2
    411         dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation()
    412         attributeValues = dogmaLocation.GetIndustryCharacterModifiers(session.charid)
    413         industrySvc = sm.GetService('industrySvc')
    414         for activityID, slotLimitAttributeID, labelPath, topPad, skills in [(industry.MANUFACTURING,
    415           attributeManufactureSlotLimit,
    416           'UI/Industry/JobSummaryManufacturing',
    417           0,
    418           (const.typeMassProduction, const.typeAdvancedMassProduction)), (industry.RESEARCH_TIME,
    419           attributeMaxLaborotorySlots,
    420           'UI/Industry/JobSummaryScience',
    421           8,
    422           (const.typeLaboratoryOperation, const.typeAdvancedLaboratoryOperation)), (industry.REACTION,
    423           attributeReactionSlotLimit,
    424           'UI/Industry/JobSummaryReactions',
    425           8,
    426           (const.typeMassReactions, const.typeAdvancedMassReactions))]:
    427             usedSlots = industrySvc.GetJobCountForActivity(activityID)
    428             maxSlots = int(attributeValues[slotLimitAttributeID])
    429             text = localization.GetByLabel(labelPath, used=usedSlots, max=maxSlots)
    430             tooltipPanel.AddLabelMedium(text=text, width=320, cellPadding=(0,
    431              topPad,
    432              0,
    433              2), colSpan=2)
    434             for eachSkillID in skills:
    435                 AddSkillRow(tooltipPanel, eachSkillID, cellPadding=(0, 0, 0, 1))
    436 
    437 
    438 class TimeContainerTooltipPanel:
    439 
    440     def __init__(self, jobData, tooltipPanel):
    441         tooltipPanel.state = uiconst.UI_NORMAL
    442         tooltipPanel.margin = (8, 8, 8, 7)
    443         tooltipPanel.columns = 2
    444         AddJobModifierRows(tooltipPanel, industry.TimeModifier, jobData)
    445         for typeID in jobData.GetTimeSkillTypes() or []:
    446             AddSkillRow(tooltipPanel, typeID, cellPadding=(0, 0, 0, 1))
    447 
    448 
    449 class CostContainerTooltipPanel:
    450     LABEL_OFFSET = 4
    451 
    452     def __init__(self, jobData, tooltipPanel):
    453         tooltipPanel.state = uiconst.UI_NORMAL
    454         tooltipPanel.margin = MARGIN
    455         numColumns = 3
    456         tooltipPanel.columns = numColumns
    457         tooltipPanel.cellPadding = (0, 0)
    458         header, abbrLabel, costPercentage = self.GetHeaderAndAbbreviation(jobData)
    459         percText = ''
    460         if costPercentage:
    461             estValueLabel = GetByLabel('UI/Industry/EstimatedItemValue', numRuns=jobData.runs)
    462             costWithoutPercentage = FmtISK(jobData.base_cost / costPercentage, 0)
    463             percText = '%.1f%%' % (costPercentage * 100.0)
    464             percText = GetByLabel('UI/Industry/PercentageOfEIV', percentage=percText, startColorTag='')
    465             self.AddAmountRow(tooltipPanel, estValueLabel, costWithoutPercentage, fontColor=TextColor.NORMAL)
    466             tooltipPanel.AddSpacer(width=0, height=10, colSpan=tooltipPanel.columns)
    467         baseCostAsText = FmtISK(jobData.base_cost, 0)
    468         self.AddAmountRow(tooltipPanel, header, baseCostAsText, percText)
    469         tooltipPanel.AddSpacer(width=0, height=10, colSpan=tooltipPanel.columns)
    470         self.AddBodyFramedCont(tooltipPanel, GetByLabel('UI/Industry/JobGrossCost'))
    471         tooltipPanel.AddSpacer(width=0, height=10, colSpan=tooltipPanel.columns)
    472         systemIndex = jobData.facility.GetCostIndex(jobData.activityID)
    473         systemIndexAsText = '%.2f%%' % (systemIndex * 100.0)
    474         if abbrLabel:
    475             systemIndexAsText = GetByLabel(abbrLabel, percentage=systemIndexAsText, startColorTag='')
    476         systemIndexCost = systemIndex * jobData.base_cost
    477         systemIndexCostAsText = FmtISK(systemIndexCost, 0)
    478         self.AddRow(tooltipPanel, GetByLabel('UI/Industry/SystemCostIndex'), systemIndexAsText, systemIndexCostAsText)
    479         jobModifiers = 1.0
    480         mulModifiers = [ x for x in jobData.GetModifiers(industry.CostModifier) if not x.additive ]
    481         if mulModifiers:
    482             tooltipPanel.AddSpacer(width=0, height=14, colSpan=tooltipPanel.columns)
    483             for m in mulModifiers:
    484                 jobModifiers *= m.amount
    485 
    486             bonusValue = systemIndexCost * (jobModifiers - 1.0)
    487             amountAsText, fontColor = self.GetAmountTextAndColor(bonusValue)
    488             self.AddAmountRow(tooltipPanel, GetByLabel('UI/Industry/CostBonuses'), amountAsText, fontColor=fontColor)
    489             for modifier in mulModifiers:
    490                 color = modifier.GetModifierColor()
    491                 self.AddRow(tooltipPanel, modifier.GetName(), modifier.GetPercentageLabel(), '', leftPadding=20, fontColor=color)
    492                 tooltipPanel.AddSpacer(width=0, height=2, colSpan=tooltipPanel.columns)
    493                 tooltipPanel.FillRow()
    494 
    495         tooltipPanel.AddSpacer(width=0, height=14, colSpan=tooltipPanel.columns)
    496         costWithoutAlphaTax = jobData.GetCostWithoutAlphaTax()
    497         costWithoutAlphaTaxAsText = FmtISK(costWithoutAlphaTax, 1)
    498         self.AddAmountRow(tooltipPanel, GetByLabel('UI/Industry/TotalJobGrossCost'), costWithoutAlphaTaxAsText, fontColor=TextColor.HIGHLIGHT)
    499         facilityTaxAmt = jobData.GetFacilityTax()
    500         taxRate = jobData.facility.tax
    501         if taxRate is None and jobData.activity:
    502             taxRate = jobData.activity.activity_tax(jobData.facility)
    503         totalTax = facilityTaxAmt or 0
    504         tooltipPanel.AddSpacer(width=0, height=10, colSpan=tooltipPanel.columns)
    505         self.AddBodyFramedCont(tooltipPanel, GetByLabel('UI/Industry/Taxes'))
    506         tooltipPanel.AddSpacer(width=0, height=10, colSpan=tooltipPanel.columns)
    507         if taxRate:
    508             taxLabel = '<color=%s>+%s%%</color>' % (Color.RGBtoHex(*eveColor.DANGER_RED), FormatNumeric(taxRate * 100, useGrouping=True, decimalPlaces=2))
    509             if abbrLabel:
    510                 taxLabel = GetByLabel(abbrLabel, percentage=taxLabel, startColorTag='')
    511             self.AddIskRow(tooltipPanel, GetByLabel('UI/Industry/FacilityTax'), taxLabel, facilityTaxAmt)
    512             tooltipPanel.AddSpacer(width=0, height=10, colSpan=tooltipPanel.columns)
    513         cloneStateModifier = GetCloneStateModifier(jobData)
    514         if cloneStateModifier:
    515             alphaTax = jobData.base_cost * (cloneStateModifier.amount - 1.0)
    516             totalTax += alphaTax
    517             percText = cloneStateModifier.GetPercentageLabel(decimals=2)
    518             if abbrLabel:
    519                 percText = GetByLabel(abbrLabel, percentage=percText, startColorTag='')
    520             self.AddIskRow(tooltipPanel, GetByLabel('UI/Industry/AlphaCloneTax'), percText, alphaTax)
    521             tooltipPanel.AddSpacer(width=0, height=10, colSpan=tooltipPanel.columns)
    522         scc_surcharge = jobData.scc_surcharge
    523         scc_surcharge_ratio = jobData.scc_surcharge_ratio
    524         if scc_surcharge:
    525             ratioText = '+%s%%' % FormatNumeric(scc_surcharge_ratio * 100, useGrouping=True, decimalPlaces=2)
    526             if abbrLabel:
    527                 ratioText = GetByLabel(abbrLabel, percentage=ratioText, startColorTag='')
    528             totalTax += scc_surcharge
    529             self.AddIskRow(tooltipPanel, GetByLabel('UI/Industry/SccSurcharge'), ratioText, scc_surcharge)
    530             tooltipPanel.AddSpacer(width=0, height=10, colSpan=tooltipPanel.columns)
    531         industrySecurityTax = int(getattr(jobData, 'industry_security_tax', 0) or 0)
    532         if industrySecurityTax:
    533             ratioText = '+%s%%' % FormatNumeric(INDUSTRY_SECURITY_TAX_RATE * 100, useGrouping=True, decimalPlaces=2)
    534             if abbrLabel:
    535                 ratioText = GetByLabel(abbrLabel, percentage=ratioText, startColorTag='')
    536             totalTax += industrySecurityTax
    537             self.AddIskRow(tooltipPanel, GetByLabel('UI/Industry/IndustrySecurityTax'), ratioText, industrySecurityTax)
    538             tooltipPanel.AddSpacer(width=0, height=10, colSpan=tooltipPanel.columns)
    539         totalTaxText = FmtISK(totalTax, 0)
    540         self.AddAmountRow(tooltipPanel, GetByLabel('UI/Industry/TotalTaxes'), totalTaxText, fontColor=TextColor.HIGHLIGHT)
    541         tooltipPanel.AddSpacer(width=0, height=6, colSpan=tooltipPanel.columns)
    542         totalCost = jobData.total_cost
    543         totalCostAsText = FmtISK(totalCost, 0)
    544         self.AddTotalFramedCont(tooltipPanel, totalCostAsText)
    545 
    546     def GetHeaderAndAbbreviation(self, jobData):
    547         costPercentage = None
    548         if jobData.activityID == industry.MANUFACTURING:
    549             return (GetByLabel('UI/Industry/EstimatedItemValue', numRuns=jobData.runs), 'UI/Industry/PercentageOfEIV', costPercentage)
    550         if jobData.activityID in (industry.RESEARCH_TIME, industry.RESEARCH_MATERIAL):
    551             return (GetByLabel('UI/Industry/ProcessTimeValue'), 'UI/Industry/PercentageOfPTV', costPercentage)
    552         if jobData.activityID in (industry.COPYING, industry.INVENTION):
    553             costPercentage = industry.COST_PERCENTAGE
    554             percText = FormatNumeric(industry.COST_PERCENTAGE * 100, useGrouping=True, decimalPlaces=1) + '%'
    555             return (GetByLabel('UI/Industry/HeaderPercentageOfEIV'), 'UI/Industry/PercentageOfPEIV', costPercentage)
    556         return (GetByLabel('UI/Industry/BaseItemCost'), '', costPercentage)
    557 
    558     def AddAmountRow(self, tooltipPanel, textLeft, amountAsText, percentage = '', fontColor = TextColor.NORMAL):
    559         return self.AddRow(tooltipPanel, textLeft, percentage, amountAsText, fontColor=fontColor)
    560 
    561     def AddBodyFramedCont(self, tooltipPanel, textLeft):
    562         fillColor = eveColor.WHITE[:3] + (0.1,)
    563         c, maxTextHeight = self._AddFramedCont(tooltipPanel, textLeft, '', bold=False, fillColor=fillColor)
    564         c.height = maxTextHeight + 10
    565 
    566     def AddTotalFramedCont(self, tooltipPanel, textRight):
    567         tooltipPanel.AddSpacer(width=0, height=4, colSpan=tooltipPanel.columns)
    568         c, maxTextHeight = self._AddFramedCont(tooltipPanel, GetByLabel('UI/Industry/JobCost'), textRight, EveHeaderLarge, bold=True, fillColor=eveColor.SMOKE_BLUE)
    569         c.height = maxTextHeight + 12
    570         tooltipPanel.AddSpacer(width=0, height=4, colSpan=tooltipPanel.columns)
    571 
    572     def _AddFramedCont(self, tooltipPanel, textLeft, textRight, rightLabelClass = EveLabelMedium, bold = False, fillColor = eveColor.WHITE):
    573         c = Container(align=uiconst.TOTOP)
    574         Fill(bgParent=c, color=fillColor)
    575         leftLabel = EveLabelMedium(parent=c, align=uiconst.CENTERLEFT, left=self.LABEL_OFFSET, text=textLeft)
    576         c.rightLabel = rightLabelClass(parent=c, align=uiconst.CENTERRIGHT, left=self.LABEL_OFFSET, text=textRight, bold=bold)
    577         textWidth = leftLabel.textwidth + c.rightLabel.textwidth + 20
    578         tooltipPanel.AddCell(cellObject=c, colSpan=tooltipPanel.columns)
    579         tooltipPanel.AddSpacer(width=textWidth, height=0, colSpan=tooltipPanel.columns)
    580         return (c, max(c.rightLabel.textheight, leftLabel.textheight))
    581 
    582     def AddIskRow(self, tooltipPanel, textLeft, textCenter, amount, fontColor = None):
    583         amountAsText, fc = self.GetAmountTextAndColor(amount)
    584         fontColor = fontColor or fc
    585         return self.AddRow(tooltipPanel, textLeft, textCenter, amountAsText, fontColor=fontColor)
    586 
    587     def GetAmountTextAndColor(self, amount):
    588         amountAsText = FmtISK(amount, 0)
    589         if amount > 0:
    590             amountAsText = '+%s' % amountAsText
    591             color = eveColor.DANGER_RED
    592         elif amount == 0:
    593             color = TextColor.NORMAL
    594         else:
    595             color = eveColor.SUCCESS_GREEN
    596         return (amountAsText, color)
    597 
    598     def AddRow(self, tooltipPanel, textLeft, textCenter, textRight, leftPadding = LABEL_OFFSET, fontColor = TextColor.NORMAL):
    599         tooltipPanel.AddLabelMedium(text=textLeft, align=uiconst.CENTERLEFT, left=leftPadding, color=fontColor)
    600         tooltipPanel.AddLabelMedium(text=textCenter, align=uiconst.CENTERRIGHT, cellPadding=(10, 0, 10, 1), color=fontColor)
    601         rowTotalLabel = tooltipPanel.AddLabelMedium(text=textRight, align=uiconst.CENTERRIGHT, color=fontColor, left=self.LABEL_OFFSET)
    602         tooltipPanel.FillRow()
    603         return rowTotalLabel
    604 
    605 
    606 def GetCloneStateModifier(jobData):
    607     for modifier in jobData.input_modifiers:
    608         if modifier.reference == industry.Reference.CLONE_STATE:
    609             return modifier
    610 
    611 
    612 class ProbabilityTooltipPanel:
    613 
    614     def __init__(self, jobData, tooltipPanel):
    615         tooltipPanel.state = uiconst.UI_NORMAL
    616         tooltipPanel.margin = MARGIN
    617         tooltipPanel.columns = 2
    618         AddJobModifierRows(tooltipPanel, industry.ProbabilityModifier, jobData)
    619         if jobData.activityID == industry.INVENTION:
    620             for skill in jobData.activity.skills:
    621                 AddSkillRow(tooltipPanel, skill.typeID, cellPadding=(0, 0, 0, 2))
    622 
    623             tooltipPanel.AddSpacer(width=0, height=6)
    624 
    625 
    626 class FacilityActivityTooltip:
    627 
    628     def __init__(self, facilityData, activityID, tooltipPanel):
    629         tooltipPanel.margin = MARGIN
    630         tooltipPanel.columns = 2
    631         tooltipPanel.state = uiconst.UI_NORMAL
    632         text = localization.GetByLabel(industryUIConst.ACTIVITY_NAMES.get(activityID))
    633         tooltipPanel.AddLabelMedium(text=text, cellPadding=PADBOTTOM, colSpan=2, bold=True)
    634         if activityID not in facilityData.activities:
    635             text = localization.GetByLabel('UI/Industry/ActivityNotSupported')
    636             tooltipPanel.AddLabelMedium(text=text, cellPadding=PADBOTTOM, colSpan=2)
    637             return
    638         if activityID in (industry.MANUFACTURING, industry.REACTION):
    639             activity = facilityData.activities[activityID]
    640             modifierData = self.GetTypesSupportedWithModifiers(activity, facilityData.modifiers)
    641             if modifierData:
    642                 label = IndustryCaptionLabel(text=localization.GetByLabel('UI/Industry/ManufacturingTypesHint'))
    643                 tooltipPanel.AddCell(label, colSpan=2)
    644                 self.scroll = ScrollContainer(align=uiconst.TOTOP, showUnderlay=True)
    645                 for text, modifierME, modifierTE in modifierData:
    646                     Label(parent=self.scroll, align=uiconst.TOTOP, text=text, padding=2)
    647                     if modifierME:
    648                         Label(parent=self.scroll, align=uiconst.TOTOP, text='<color=gray>%s:</color> %s (%s)' % (localization.GetByLabel('UI/Industry/MaterialConsumption'), modifierME.GetPercentageLabel(), localization.GetByLabel('UI/Industry/Specialities/Rigs')), fontsize=fontconst.EVE_SMALL_FONTSIZE, padding=(2, 0, 2, 2))
    649                     if modifierTE:
    650                         Label(parent=self.scroll, align=uiconst.TOTOP, text='<color=gray>%s</color> %s (%s)' % (localization.GetByLabel('UI/Industry/JobDuration'), modifierTE.GetPercentageLabel(), localization.GetByLabel('UI/Industry/Specialities/Rigs')), fontsize=fontconst.EVE_SMALL_FONTSIZE, padding=(2, -3, 2, 3))
    651 
    652                 self.scroll.height = min(150, len(modifierData) * 20)
    653                 tooltipPanel.AddCell(self.scroll, cellPadding=PADBOTTOM, colSpan=2)
    654         modifiers = facilityData.GetFacilityModifiersByActivityID().get(activityID, None)
    655         if modifiers:
    656             for modifierCls, label in ((industry.TimeModifier, 'UI/Industry/ModifierTimeCaption'), (industry.MaterialModifier, 'UI/Industry/ModifierMaterialCaption'), (industry.CostModifier, 'UI/Industry/ModifierCostCaption')):
    657                 clsModifiers = [ modifier for modifier in modifiers if isinstance(modifier, modifierCls) ]
    658                 if clsModifiers:
    659                     AddModifierRows(localization.GetByLabel(label), clsModifiers, tooltipPanel)
    660                     tooltipPanel.AddSpacer(0, 6, colSpan=2)
    661 
    662         costIndexes = facilityData.GetCostIndexByActivityID()
    663         costIndex = costIndexes.get(activityID, None)
    664         if costIndex:
    665             AddSystemCostIndexRow(activityID, facilityData, tooltipPanel)
    666         taxRates = facilityData.GetServiceTaxes()
    667         for serviceID in structures.INDUSTRY_SERVICES:
    668             if structures.GetActivityID(serviceID) != activityID:
    669                 taxRates.pop(serviceID, None)
    670 
    671         AddTaxRateRows(tooltipPanel, taxRates)
    672 
    673     def GetMETEModifiers(self, modifiers):
    674         modifierME = modifierTE = None
    675         for modifier in modifiers:
    676             if isinstance(modifier, industry.TimeModifier):
    677                 modifierTE = modifier
    678             elif isinstance(modifier, industry.MaterialModifier):
    679                 modifierME = modifier
    680 
    681         return (modifierME, modifierTE)
    682 
    683     def GetTypesSupportedWithModifiers(self, activity, modifiers):
    684         ret = []
    685         modifiersByCategoryID = self.GetModifiersByCategoryID(modifiers)
    686         for categoryID in activity['categories']:
    687             if categoryID == const.categoryBlueprint:
    688                 continue
    689             text = evetypes.GetCategoryNameByCategory(categoryID)
    690             modifierME, modifierTE = self.GetMETEModifiers(modifiersByCategoryID.get(categoryID, []))
    691             ret.append((text, modifierME, modifierTE))
    692 
    693         modifiersByGroupID = self.GetModifiersByGroupID(modifiers)
    694         for groupID in activity['groups']:
    695             text = evetypes.GetGroupNameByGroup(groupID)
    696             modifierME, modifierTE = self.GetMETEModifiers(modifiersByGroupID.get(groupID, []))
    697             if (modifierTE, modifierME) == (None, None):
    698                 categoryID = evetypes.GetCategoryIDByGroup(groupID)
    699                 if categoryID in modifiersByCategoryID:
    700                     modifierME, modifierTE = self.GetMETEModifiers(modifiersByCategoryID.get(categoryID, []))
    701             ret.append((text, modifierME, modifierTE))
    702 
    703         ret.sort()
    704         return ret
    705 
    706     def GetModifiersByCategoryID(self, modifiers):
    707         ret = defaultdict(list)
    708         for modifier in modifiers:
    709             if getattr(modifier, 'categoryID', None):
    710                 ret[modifier.categoryID].append(modifier)
    711 
    712         return ret
    713 
    714     def GetModifiersByGroupID(self, modifiers):
    715         ret = defaultdict(list)
    716         for modifier in modifiers:
    717             if getattr(modifier, 'groupID', None):
    718                 ret[modifier.groupID].append(modifier)
    719 
    720         return ret