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

createContractWnd.py (103568B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\neocom\contracts\createContractWnd.py
      2 from __future__ import absolute_import
      3 import math
      4 import sys
      5 import eveLocalization
      6 import evetypes
      7 import localization
      8 import utillib
      9 from carbon.common.script.util import timerstuff
     10 from carbon.common.script.util.format import FmtAmt, FmtDate
     11 from carbonui.control.buttonIcon import ButtonIcon
     12 from carbonui.control.combo import Combo
     13 from carbonui.util.various_unsorted import SortListOfTuples, GetClipboardData
     14 from carbonui.button.group import ButtonGroup
     15 from carbonui.control.button import Button
     16 from eve.client.script.ui.control.entries.item import Item
     17 from eve.client.script.ui.control.entries.universe import SolarSystem
     18 from eve.client.script.ui.control.entries.util import GetFromClass
     19 from carbonui.control.window import Window
     20 from eve.client.script.ui.control.utilMenu import UtilMenu
     21 from eve.client.script.ui.services.menuMerge import MergeMenus
     22 from eve.client.script.ui.shared.neocom.contracts.contractentry import ContractItemSelect
     23 from eve.client.script.ui.util import uix
     24 import uthread
     25 import log
     26 import blue
     27 from bannedwords.client import bannedwords
     28 from carbon.common.script.util.linkUtil import GetShowInfoLink
     29 from carbonui import uiconst
     30 from carbonui.control.singlelineedits.singleLineEditInteger import SingleLineEditInteger
     31 from carbonui.control.singlelineedits.singleLineEditText import SingleLineEditText
     32 from carbonui.primitives.line import Line
     33 from carbonui.primitives.container import Container
     34 from contracts.contractStruct import ContractStruct
     35 from contracts.copyContracts import CopyContractObject, GetMissingTypesTxt, FindMissingBlueprints, FindMissingTypesAndQty
     36 from eve.client.script.ui.control import eveEdit, eveLabel, eveScroll
     37 from carbonui.control.checkbox import Checkbox
     38 from eve.client.script.ui.control.listgroup import ListGroup
     39 from eve.client.script.ui.control.listwindow import ListWindow
     40 from eve.client.script.ui.util import searchUtil, searchOld
     41 from eve.client.script.ui.util.form import FormWnd
     42 from eve.client.script.util.contractutils import FmtISKWithDescription, GetContractIcon, IsSearchStringLongEnough, MatchInvTypeName, SelectItemTypeDlg, TypeName
     43 from eve.common.script.search import const as search_const
     44 from eve.common.script.sys import eveCfg, idCheckers
     45 from eve.common.script.util.contractscommon import CREATECONTRACT_CONFIRM_CHARGESTOHANGAR, CalcContractFees, CanRequestType, EXPIRE_TIMES, GetContractTypeText, MAX_AMOUNT, MAX_NUM_CONTRACTS, MAX_NUM_ITEMS, MAX_TITLE_LENGTH, NUM_CONTRACTS_BY_SKILL, NUM_CONTRACTS_BY_ADVSKILL, NUM_CONTRACTS_BY_SKILL_CORP, const_conBidMinimum, const_conBrokersFeeMinimum, const_conCourierMaxVolume, const_conCourierWarningVolume, const_conDepositMinimum, const_conSalesTax
     46 from eve.common.script.util.eveFormat import FmtISK, RoundISK
     47 from eveexceptions import UserError
     48 from evePathfinder.pathfinderconst import UNREACHABLE_JUMP_COUNT
     49 from eveservices.menu import GetMenuService
     50 from globalConfig.getFunctions import GetMaxShipsToContract
     51 from inventorycommon.typeHelpers import GetAveragePrice
     52 from inventorycommon.const import categoryStructure, groupStation, typeBrokerRelations, typeAccounting
     53 from inventorycommon.util import GetItemVolume, IsNPC
     54 from localization import GetByLabel
     55 from menu import MenuLabel
     56 from player_messaging.client.ui_message import prompt_player_fsd_dialog
     57 from structures.types import IsFlexStructure
     58 from textImporting.importMultibuy import ImportMultibuy
     59 from threadutils import throttled
     60 MIN_CONTRACT_MONEY = 1000
     61 
     62 class CreateContractWnd(Window):
     63     __guid__ = 'form.CreateContract'
     64     __notifyevents__ = ['OnDeleteContracts']
     65     default_windowID = 'createcontract'
     66     default_iconNum = 'res:/ui/Texture/WindowIcons/contracts.png'
     67     default_captionLabelPath = 'UI/Inventory/ItemActions/CreateContract'
     68     default_minSize = (410, 520)
     69 
     70     def ApplyAttributes(self, attributes):
     71         super(CreateContractWnd, self).ApplyAttributes(attributes)
     72         self.topParent = Container(name='topParent', parent=self.GetMainArea(), align=uiconst.TOTOP, height=42)
     73         self.ResetWizard()
     74         contractItems = attributes.contractItems
     75         copyContract = attributes.copyContract
     76         copyContractCorpFlagID = attributes.copyContractDisvision
     77         stationID = attributes.stationID
     78         flag = attributes.flag
     79         itemIDs = attributes.itemIDs
     80         if stationID and not self.CanContractItemsFromLocation(stationID):
     81             stationID = None
     82         if stationID:
     83             self.data.startStation = stationID
     84             self.data.startStationDivision = const.flagHangar
     85         if flag:
     86             self.data.startStationDivision = flag
     87         if attributes.contractType:
     88             self.data.type = attributes.contractType
     89         else:
     90             self.contractType = const.conTypeNothing
     91         self.isMultiContract = False
     92         self.data.items = {}
     93         self.data.childItems = {}
     94         if stationID and itemIDs:
     95             foundItems = []
     96             itemsInStation = sm.GetService('contracts').GetItemsInDockableLocation(self.data.startStation, False)
     97             for item in itemsInStation:
     98                 if item.itemID in itemIDs:
     99                     foundItems.append(item)
    100 
    101             if foundItems:
    102                 self.data.startStationDivision = foundItems[0].flagID
    103                 for item in foundItems:
    104                     self.data.items[item.itemID] = [item.typeID, item.stacksize, item]
    105 
    106         self.currPage = 0
    107         self.NUM_STEPS = 4
    108         self.CREATECONTRACT_TITLES = {const.conTypeNothing: [GetByLabel('UI/Contracts/ContractsWindow/SelectContractType'),
    109                                 '',
    110                                 '',
    111                                 ''],
    112          const.conTypeAuction: [GetByLabel('UI/Contracts/ContractsWindow/SelectContractType'),
    113                                 GetByLabel('UI/Contracts/ContractsWindow/PickItems'),
    114                                 GetByLabel('UI/Contracts/ContractsWindow/SelectOptions'),
    115                                 GetByLabel('UI/Common/Confirm')],
    116          const.conTypeItemExchange: [GetByLabel('UI/Contracts/ContractsWindow/SelectContractType'),
    117                                      GetByLabel('UI/Contracts/ContractsWindow/PickItems'),
    118                                      GetByLabel('UI/Contracts/ContractsWindow/SelectOptions'),
    119                                      GetByLabel('UI/Common/Confirm')],
    120          const.conTypeCourier: [GetByLabel('UI/Contracts/ContractsWindow/SelectContractType'),
    121                                 GetByLabel('UI/Contracts/ContractsWindow/PickItems'),
    122                                 GetByLabel('UI/Contracts/ContractsWindow/SelectOptions'),
    123                                 GetByLabel('UI/Common/Confirm')]}
    124         self.isContractMgr = session.corprole & const.corpRoleContractManager == const.corpRoleContractManager
    125         self.marketTypes = sm.GetService('contracts').GetMarketTypes()
    126         self.sr.pageWnd = {}
    127         self.lockedItems = {}
    128         self.state = uiconst.UI_NORMAL
    129         self.blockconfirmonreturn = 1
    130         setattr(self, 'first', True)
    131         if contractItems is not None:
    132             self._SetStartVariablesInData(contractItems)
    133         self.buttons = [(GetByLabel('UI/Common/Cancel'),
    134           self.OnCancel,
    135           (),
    136           84), (GetByLabel('UI/Common/Previous'),
    137           self.OnStepChange,
    138           -1,
    139           84), (GetByLabel('UI/Common/Next'),
    140           self.OnStepChange,
    141           1,
    142           84)]
    143         self.sr.buttonWnd = ButtonGroup(btns=self.buttons, parent=self.sr.main)
    144         self.sr.buttonCancel = self.sr.buttonWnd.buttons[0]
    145         self.sr.buttonPrev = self.sr.buttonWnd.buttons[1]
    146         self.sr.buttonNext = self.sr.buttonWnd.buttons[2]
    147         if copyContract:
    148             self.CopyContract(copyContract, copyContractCorpFlagID)
    149         self.formWnd = FormWnd(name='form', align=uiconst.TOALL, pos=(0, 0, 0, 0), parent=self.sr.main)
    150         self.GotoPage(0)
    151 
    152     def SetMainIcon(self, texturePath):
    153         self.icon = texturePath
    154 
    155     def _SetStartVariablesInData(self, contractItems):
    156         firstItem = contractItems[0]
    157         startStation = sm.StartService('invCache').GetStationIDOfItem(firstItem)
    158         if not self.CanContractItemsFromLocation(startStation):
    159             return
    160         self.data.startStation = startStation
    161         dataItems = {}
    162         for item in contractItems:
    163             dataItems[item.itemID] = [item.typeID, item.stacksize, item]
    164             if item.ownerID == session.corpid:
    165                 if not self.isContractMgr:
    166                     raise UserError('ConNotContractManager')
    167                 self.data.forCorp = True
    168                 self.data.startStation = sm.StartService('invCache').GetStationIDOfItem(item)
    169                 self.data.startStationDivision = item.flagID
    170             elif item.ownerID != session.charid:
    171                 raise RuntimeError('Not your item!')
    172 
    173         self.data.items = dataItems
    174 
    175     def CopyContract(self, contract, corpFlagID = None):
    176         c = contract.contract
    177         self.data.type = c.type
    178         self.data.startStation = c.startStationID
    179         self.data.endStation = c.endStationID
    180         expireTime = 1440 * ((c.dateExpired - c.dateIssued) / const.DAY)
    181         self.data.expiretime = expireTime
    182         self.data.duration = c.numDays
    183         self.data.description = c.title
    184         self.data.assigneeID = c.assigneeID
    185         if c.availability == 0:
    186             self.data.avail = 0
    187         elif c.assigneeID == session.corpid:
    188             self.data.avail = 2
    189             self.data.assigneeID = None
    190         elif session.allianceid and c.assigneeID == session.allianceid:
    191             self.data.avail = 3
    192             self.data.assigneeID = None
    193         else:
    194             self.data.avail = 1
    195         if self.data.assigneeID:
    196             self.data.name = cfg.eveowners.Get(self.data.assigneeID).name
    197         self.data.price = c.price
    198         self.data.reward = c.reward
    199         self.data.collateral = c.collateral
    200         self.data.forCorp = c.forCorp
    201         self.data.reqItems = {}
    202         if self.data.type == const.conTypeCourier:
    203             uthread.new(prompt_player_fsd_dialog, 'ConCopyContractCourier')
    204         itemsInContract = contract.items
    205         if not itemsInContract:
    206             return
    207         copyContractsObject = CopyContractObject(startStation=self.data.startStation, forCorp=self.data.forCorp, contractItems=itemsInContract, getItemsInStationFunc=sm.GetService('contracts').GetItemsInDockableLocation, corpFlagID=corpFlagID)
    208         sellItems, sellBlueprintCopies, sellBlueprintOriginals, requestItems = copyContractsObject.GetSellAndRequestedItems()
    209         foundBlueprint = sellBlueprintCopies or sellBlueprintOriginals
    210         self.data.reqItems = dict(requestItems)
    211         self.data.items = {}
    212         self.data.childItems = {}
    213         if sellItems or sellBlueprintCopies or sellBlueprintOriginals:
    214             foundItems, foundBPC, foundBPO = copyContractsObject.FindItemToSellInHangar(sellItems, sellBlueprintCopies, sellBlueprintOriginals)
    215             notFound = FindMissingTypesAndQty(foundItems, sellItems)
    216             notFoundBPCs = FindMissingBlueprints(foundBPC, sellBlueprintCopies)
    217             notFoundBPOs = FindMissingBlueprints(foundBPO, sellBlueprintOriginals)
    218             if notFound or notFoundBPCs or notFoundBPOs:
    219                 missintTypesTxt = GetMissingTypesTxt(notFound)
    220                 missintTypesTxt += GetMissingTypesTxt(notFoundBPCs)
    221                 missintTypesTxt += GetMissingTypesTxt(notFoundBPOs)
    222                 uthread.new(prompt_player_fsd_dialog, 'ConCopyContractMissingItems', {'types': missintTypesTxt})
    223             allFoundItems = []
    224             for eachDict in (foundItems, foundBPC, foundBPO):
    225                 for eachItemList in eachDict.itervalues():
    226                     allFoundItems += eachItemList
    227 
    228             if allFoundItems:
    229                 self.data.startStationDivision = allFoundItems[0].flagID
    230                 for item in allFoundItems:
    231                     self.data.items[item.itemID] = [item.typeID, item.stacksize, item]
    232 
    233         if foundBlueprint:
    234             uthread.new(prompt_player_fsd_dialog, 'ConCopyContractBlueprint')
    235 
    236     def _OnClose(self, *args):
    237         self.UnlockItems()
    238 
    239     def Refresh(self):
    240         self.GotoPage(self.currPage)
    241 
    242     def Confirm(self, *args):
    243         pass
    244 
    245     def SetTitle(self):
    246         pageTitle = self.CREATECONTRACT_TITLES[self.data.type][self.currPage]
    247         titleLabel = GetByLabel('UI/Contracts/ContractsService/PageOfTotal', pageTitle=pageTitle, pageNum=self.currPage + 1, numPages=self.NUM_STEPS)
    248         title = titleLabel
    249         if self.sr.Get('windowCaption', None) is None:
    250             self.sr.windowCaption = eveLabel.EveCaptionMedium(parent=self.topParent, align=uiconst.CENTERLEFT, state=uiconst.UI_DISABLED, text=title)
    251         else:
    252             self.sr.windowCaption.text = title
    253 
    254     def UnlockItems(self):
    255         if not hasattr(self, 'lockedItems'):
    256             return
    257         for i in self.lockedItems.iterkeys():
    258             sm.StartService('invCache').UnlockItem(i)
    259 
    260         self.lockedItems = {}
    261 
    262     def LockItems(self, itemIDs):
    263         for i, item in itemIDs.iteritems():
    264             self.lockedItems[i] = i
    265             userErrorDict = {'typeID': item[0],
    266              'action': GetByLabel('UI/Contracts/ContractsWindow/CreatingContract').lower()}
    267             sm.StartService('invCache').TryLockItem(i, 'lockItemContractFunction', userErrorDict, 1)
    268 
    269     def GotoPage(self, n):
    270         uthread.Lock(self)
    271         try:
    272             if n < 2:
    273                 self.UnlockItems()
    274             self.currPage = n
    275             self.SetTitle()
    276             for p in self.sr.pageWnd:
    277                 self.sr.pageWnd[p].state = uiconst.UI_HIDDEN
    278 
    279             self.formWnd.Flush()
    280             format = []
    281             self.form = retfields = reqresult = errorcheck = []
    282             self.sr.buttonNext.SetLabel(GetByLabel('/Carbon/UI/Common/Next'))
    283             self.sr.buttonPrev.state = uiconst.UI_NORMAL
    284             contractIconType = const.conTypeNothing if n == 0 else self.data.type
    285             self.SetMainIcon(GetContractIcon(contractIconType))
    286             if n == 0:
    287                 retfields, reqresult, errorcheck = self.BuildPage0()
    288             elif n == 1:
    289                 self.WriteSelectItems(GetByLabel('UI/Contracts/ContractsWindow/SelectStationToGetItems'))
    290             elif n == 2:
    291                 errorcheck, reqresult, retfields = self.BuildPage2()
    292             elif n == 3:
    293                 self.BuildPage3()
    294             self.formdata = [retfields, reqresult, errorcheck]
    295         finally:
    296             uthread.UnLock(self)
    297 
    298     def BuildPage0(self):
    299         self.sr.buttonPrev.state = uiconst.UI_HIDDEN
    300         format = []
    301         format += self.Page0_GetContractTypeOptions()
    302         s = [0,
    303          0,
    304          0,
    305          0]
    306         s[getattr(self.data, 'avail', 0)] = 1
    307         format += self.Page0_GetAvailabilityOptions(s)
    308         forCorp = getattr(self.data, 'forCorp', 0)
    309         if self.isContractMgr:
    310             corpAcctName = sm.GetService('corp').GetMyCorpAccountName() or GetByLabel('UI/Common/None')
    311             behalfLabel = GetByLabel('UI/Contracts/ContractsService/OnBehalfOfCorp', corpName=cfg.eveowners.Get(session.corpid).name, corpAccountName=corpAcctName)
    312             format += [{'type': 'btline'}]
    313             format += [{'type': 'checkbox',
    314               'label': '_hide',
    315               'key': 'forCorp',
    316               'text': behalfLabel,
    317               'required': 1,
    318               'frame': 0,
    319               'setvalue': forCorp,
    320               'onchange': self.OnForCorpChange}]
    321         else:
    322             format += [{'type': 'data',
    323               'key': 'forCorp',
    324               'data': {'forCorp': 0}}]
    325         uthread.new(self.WriteNumContractsAndLimitsLazy)
    326         limitsParent = Container(name='limitsParent', parent=self.formWnd, align=uiconst.TOBOTTOM, height=10, idx=0, padBottom=8)
    327         outstandingContractsLabel = GetByLabel('UI/Contracts/ContractsService/OutstandingContractsUnknown')
    328         labelText = '<color=0xff999999>%s</color>' % outstandingContractsLabel
    329         self.limitTextWnd = eveLabel.EveLabelSmall(text=labelText, parent=limitsParent, left=6, state=uiconst.UI_DISABLED)
    330         self.form, retfields, reqresult, self.panels, errorcheck, refresh = sm.GetService('form').GetForm(format, self.formWnd)
    331         nameEditField = self.form.FindChild('edit_name')
    332         privateCheckbox = self.form.FindChild('privateCb')
    333         if nameEditField and privateCheckbox:
    334             nameEditField.OnChange = lambda *args: self.OnChangePrivateName(privateCheckbox, *args)
    335         fittedShipsCb = self.form.FindChild('contractTypeCb_fittedShip')
    336         if fittedShipsCb:
    337             self.SetFittedShipsCbEnableState(self.data.type or const.conTypeItemExchange)
    338             fittedShipsCb.padLeft = 16
    339             fittedShipsCb.hint = GetByLabel('UI/Contracts/FittedShipsHint')
    340         return (retfields, reqresult, errorcheck)
    341 
    342     def Page0_GetContractTypeOptions(self):
    343         selected = self.data.type
    344         if selected == const.conTypeNothing:
    345             selected = const.conTypeItemExchange
    346         selectedOptions = [selected]
    347         if self.IsFittedShipContract(selected):
    348             selectedOptions = [selected, 'fittedShip']
    349 
    350         def GetFormatLine(conType, textPath, required = 1, group = 'type'):
    351             return {'type': 'checkbox',
    352              'label': '_hide',
    353              'key': conType,
    354              'text': GetByLabel(textPath),
    355              'required': required,
    356              'frame': 0,
    357              'setvalue': conType in selectedOptions,
    358              'group': group,
    359              'OnChange': self.OnContractTypeOptionChanged,
    360              'name': 'contractTypeCb_%s' % conType}
    361 
    362         format = [{'type': 'header',
    363           'text': GetByLabel('UI/Contracts/ContractType')}]
    364         format += [GetFormatLine(const.conTypeAuction, 'UI/Contracts/Auction')]
    365         format += [GetFormatLine(const.conTypeCourier, 'UI/Contracts/ContractsWindow/Courier')]
    366         format += [GetFormatLine(const.conTypeItemExchange, 'UI/Contracts/ContractsWindow/ItemExchange')]
    367         if GetMaxShipsToContract(sm.GetService('machoNet')):
    368             format += [GetFormatLine('fittedShip', 'UI/Contracts/ContractsWindow/AssembledShips', 0, None)]
    369         format += [{'type': 'btline'}]
    370         return format
    371 
    372     def OnContractTypeOptionChanged(self, cb, *args):
    373         self.SetFittedShipsCbEnableState(cb.GetSettingsKey())
    374 
    375     def SetFittedShipsCbEnableState(self, cbConType):
    376         fittedShipsCb = self.form.FindChild('contractTypeCb_fittedShip')
    377         if fittedShipsCb:
    378             if cbConType in (const.conTypeItemExchange, 'fittedShip'):
    379                 fittedShipsCb.Enable()
    380             else:
    381                 fittedShipsCb.Disable()
    382 
    383     def Page0_GetAvailabilityOptions(self, s):
    384         n = getattr(self.data, 'name', '')
    385 
    386         def ChangeSearchBy(combo, *args):
    387             combo.UpdateSettings()
    388 
    389         searchTypePrefsKey = ('char', 'ui', 'contracts_searchNameBy')
    390         selectSearchBy = settings.char.ui.Get('contracts_searchNameBy', search_const.MatchBy.partial_terms)
    391         f = [{'type': 'header',
    392           'text': GetByLabel('UI/Contracts/ContractsWindow/Availability')},
    393          {'type': 'checkbox',
    394           'label': '_hide',
    395           'key': 0,
    396           'text': GetByLabel('UI/Generic/Public'),
    397           'required': 1,
    398           'frame': 0,
    399           'setvalue': s[0],
    400           'group': 'avail',
    401           'onchange': self.OnAvailChange},
    402          {'type': 'checkbox',
    403           'label': '_hide',
    404           'key': 1,
    405           'text': GetByLabel('UI/Generic/Private'),
    406           'required': 1,
    407           'frame': 0,
    408           'setvalue': s[1],
    409           'group': 'avail',
    410           'onchange': self.OnAvailChange,
    411           'name': 'privateCb'},
    412          {'type': 'edit',
    413           'width': 120,
    414           'label': GetByLabel('UI/Contracts/ContractsService/IndentedName'),
    415           'key': 'name',
    416           'required': 0,
    417           'frame': 0,
    418           'setvalue': n,
    419           'group': 'name',
    420           'isCharCorpOrAllianceField': True},
    421          {'type': 'combo',
    422           'width': 120,
    423           'label': GetByLabel('UI/Common/SearchBy'),
    424           'key': 'searchBy',
    425           'options': searchUtil.GetSearchByChoices(),
    426           'setvalue': selectSearchBy,
    427           'group': 'name',
    428           'callback': ChangeSearchBy,
    429           'prefskey': searchTypePrefsKey}]
    430         if not idCheckers.IsNPC(session.corpid):
    431             f += [{'type': 'checkbox',
    432               'label': '_hide',
    433               'key': 2,
    434               'text': GetByLabel('UI/Contracts/ContractsWindow/MyCorporation'),
    435               'required': 1,
    436               'frame': 0,
    437               'setvalue': s[2],
    438               'group': 'avail',
    439               'onchange': self.OnAvailChange}]
    440         if session.allianceid:
    441             f += [{'type': 'checkbox',
    442               'label': '_hide',
    443               'key': 3,
    444               'text': GetByLabel('UI/Contracts/ContractsWindow/MyAlliance'),
    445               'required': 1,
    446               'frame': 0,
    447               'setvalue': s[3],
    448               'group': 'avail',
    449               'onchange': self.OnAvailChange}]
    450         return f
    451 
    452     def BuildPage2(self):
    453         retfields = reqresult = errorcheck = []
    454         duration, expireTime, expireTimeOptions = self.GetDurationAndExpireTimes()
    455         contractType = self.data.type
    456         if contractType == const.conTypeAuction:
    457             errorcheck, reqresult, retfields = self.BuildPage2_AddAuctionOption(expireTime, expireTimeOptions, errorcheck, reqresult, retfields)
    458         elif self.IsFittedShipContract(contractType):
    459             errorcheck, reqresult, retfields = self.BuildPage2_AddFittedShipOptions(expireTime, expireTimeOptions)
    460         elif contractType == const.conTypeItemExchange:
    461             errorcheck, reqresult, retfields = self.BuildPage2_AddItemExchangeOptions(expireTime, expireTimeOptions)
    462         elif contractType == const.conTypeCourier:
    463             errorcheck, reqresult, retfields = self.BuildPage2_AddCourierOptions(duration, expireTime, expireTimeOptions)
    464         return (errorcheck, reqresult, retfields)
    465 
    466     def GetDurationAndExpireTimes(self):
    467         expireTime = getattr(self.data, 'expiretime', None)
    468         if expireTime is None:
    469             expireTime = settings.char.ui.Get('contracts_expiretime_%s' % self.data.type, 1440)
    470         duration = getattr(self.data, 'duration', None)
    471         if duration is None:
    472             duration = settings.char.ui.Get('contracts_duration_%s' % self.data.type, 1)
    473         expireTimeOptions = []
    474         for t in EXPIRE_TIMES:
    475             num = numDays = t / 1440
    476             txt = GetByLabel('UI/Contracts/ContractsService/QuantityDays', numDays=num)
    477             numWeeks = t / 10080
    478             if numWeeks >= 1:
    479                 num = numWeeks
    480                 txt = GetByLabel('UI/Contracts/ContractsService/QuantityWeeks', numWeeks=num)
    481             expireTimeOptions.append((txt, t))
    482 
    483         return (duration, expireTime, expireTimeOptions)
    484 
    485     def BuildPage2_AddAuctionOption(self, expireTime, expireTimeOptions, errorcheck, reqresult, retfields):
    486         del expireTimeOptions[-1]
    487         format = [{'type': 'push'},
    488          {'type': 'edit',
    489           'labelwidth': 150,
    490           'width': 120,
    491           'label': GetByLabel('UI/Contracts/ContractsWindow/StartingBid'),
    492           'key': 'price',
    493           'autoselect': 1,
    494           'required': 0,
    495           'frame': 0,
    496           'group': 'avail',
    497           'floatonly': [0, MAX_AMOUNT, 2],
    498           'setvalue': getattr(self.data, 'price', 0)},
    499          {'type': 'push'},
    500          {'type': 'edit',
    501           'labelwidth': 150,
    502           'width': 120,
    503           'label': GetByLabel('UI/Contracts/ContractsWindow/BuyoutPriceOptional'),
    504           'key': 'collateral',
    505           'autoselect': 1,
    506           'required': 0,
    507           'frame': 0,
    508           'group': 'avail',
    509           'floatonly': [0, MAX_AMOUNT, 2],
    510           'setvalue': getattr(self.data, 'collateral', 0)},
    511          {'type': 'push'},
    512          {'type': 'combo',
    513           'labelwidth': 150,
    514           'width': 120,
    515           'label': GetByLabel('UI/Contracts/ContractsWindow/AuctionTime'),
    516           'key': 'expiretime',
    517           'options': expireTimeOptions,
    518           'setvalue': getattr(self.data, 'expiretime', expireTime)},
    519          {'type': 'push'},
    520          {'type': 'edit',
    521           'labelwidth': 150,
    522           'width': 220,
    523           'label': GetByLabel('UI/Contracts/ContractEntry/DescriptionOptional'),
    524           'key': 'description',
    525           'required': 0,
    526           'frame': 0,
    527           'group': 'avail',
    528           'setvalue': getattr(self.data, 'description', '')}]
    529         self.form, retfields, reqresult, self.panels, errorcheck, refresh = sm.GetService('form').GetForm(format, self.formWnd)
    530         btnparent = Container(name='btnparent', parent=self.formWnd.sr.price.parent, align=uiconst.TOALL, padLeft=const.defaultPadding)
    531         Button(parent=btnparent, label=GetByLabel('UI/Contracts/ContractsWindow/EstPrice'), func=self.SetPriceToEstPrice, btn_default=0, align=uiconst.CENTERLEFT)
    532         return (errorcheck, reqresult, retfields)
    533 
    534     def BuildPage2_AddItemExchangeOptions(self, expireTime, expireTimeOptions):
    535         isRequestItems = not not self.data.reqItems
    536         format = [{'type': 'push'},
    537          {'type': 'edit',
    538           'labelwidth': 150,
    539           'width': 120,
    540           'label': GetByLabel('UI/Contracts/ContractEntry/IWillPay'),
    541           'key': 'reward',
    542           'autoselect': 1,
    543           'required': 0,
    544           'frame': 0,
    545           'group': 'avail',
    546           'floatonly': [0, MAX_AMOUNT, 2],
    547           'setvalue': getattr(self.data, 'reward', 0)},
    548          {'type': 'push'},
    549          {'type': 'edit',
    550           'labelwidth': 150,
    551           'width': 120,
    552           'label': GetByLabel('UI/Contracts/ContractEntry/IWillRecieve'),
    553           'key': 'price',
    554           'autoselect': 1,
    555           'required': 0,
    556           'frame': 0,
    557           'group': 'avail',
    558           'floatonly': [0, MAX_AMOUNT, 2],
    559           'setvalue': getattr(self.data, 'price', 0)},
    560          {'type': 'push'},
    561          {'type': 'combo',
    562           'labelwidth': 150,
    563           'width': 120,
    564           'label': GetByLabel('UI/Contracts/ContractsWindow/Expiration'),
    565           'key': 'expiretime',
    566           'options': expireTimeOptions,
    567           'setvalue': getattr(self.data, 'expiretime', expireTime)},
    568          {'type': 'push'},
    569          {'type': 'edit',
    570           'labelwidth': 150,
    571           'width': 220,
    572           'label': GetByLabel('UI/Contracts/ContractEntry/DescriptionOptional'),
    573           'key': 'description',
    574           'required': 0,
    575           'frame': 0,
    576           'maxLength': 50,
    577           'group': 'avail',
    578           'setvalue': getattr(self.data, 'description', '')},
    579          {'type': 'push',
    580           'height': 10},
    581          {'type': 'checkbox',
    582           'required': 1,
    583           'height': 16,
    584           'setvalue': isRequestItems,
    585           'key': 'requestitems',
    586           'label': '',
    587           'text': GetByLabel('UI/Contracts/ContractsWindow/AlsoReqItemsFromBuyer'),
    588           'frame': 0,
    589           'onchange': self.OnRequestItemsChange}]
    590         self.form, retfields, reqresult, self.panels, errorcheck, refresh = sm.GetService('form').GetForm(format, self.formWnd)
    591         self.ConstructReqItems()
    592         self.PopulateReqItemScroll()
    593         btnparent = Container(name='btnparent', parent=self.formWnd.sr.price.parent, align=uiconst.TOALL, padLeft=const.defaultPadding)
    594         Button(parent=btnparent, label=GetByLabel('UI/Contracts/ContractsWindow/EstPrice'), func=self.SetPriceToEstPrice, btn_default=0, align=uiconst.CENTERLEFT)
    595         self.ToggleShowRequestItems(isRequestItems)
    596         return (errorcheck, reqresult, retfields)
    597 
    598     def ConstructReqItems(self):
    599         self.sr.reqItemsParent = reqItemsParent = Container(name='reqItemsParent', parent=self.formWnd, align=uiconst.TOALL, pos=(0, 0, 0, 0), idx=50, state=uiconst.UI_NORMAL)
    600         topReqItemCont = Container(name='topReqItemCont', parent=self.sr.reqItemsParent, align=uiconst.TOTOP, height=50, top=30)
    601         importHint = '%s<br><br>%s' % (GetByLabel('UI/Contracts/ContractsWindow/ImportRequestedItemsListOption'), GetByLabel('UI/Market/MarketQuote/ImportShoppingListOptionHint', type1=const.typeVeldspar, type2=const.typeTritanium))
    602         importMenu = ButtonIcon(name='importMenu', menuAlign=uiconst.TOPLEFT, parent=topReqItemCont, align=uiconst.CENTERLEFT, pos=(0, 0, 32, 32), func=self.ImportRequestedList, texturePath='res:/UI/Texture/Shared/pasteFrom.png', iconSize=28, hint=importHint)
    603         addBtn = Button(parent=topReqItemCont, label=GetByLabel('UI/Contracts/ContractEntry/AddItem'), func=self.AddRequestItem, pos=(0, 0, 0, 0), align=uiconst.CENTERRIGHT)
    604         self.reqItemQtyWnd = c = SingleLineEditInteger(name='itemqty', parent=topReqItemCont, label=GetByLabel('UI/Inventory/ItemQuantity'), align=uiconst.CENTERRIGHT, width=50, setvalue=1, left=addBtn.width + 8)
    605         self.reqItemTypeWnd = SingleLineEditText(name='itemtype', parent=topReqItemCont, label=GetByLabel('UI/Contracts/ContractsWindow/ItemType'), align=uiconst.TOTOP, isTypeField=True, top=9, padLeft=importMenu.left + importMenu.width + 8, padRight=self.reqItemQtyWnd.left + self.reqItemQtyWnd.width + 8)
    606         self.reqItemTypeWnd.ShowClearButton()
    607         self.reqItemTypeWnd.OnFocusLost = self.ParseItemType
    608         self.reqItemTypeWnd.OnReturn = lambda *args: self.ParseItemType(self.reqItemTypeWnd)
    609         self.reqItemScroll = eveScroll.Scroll(name='reqItemScroll', parent=reqItemsParent, padding=const.defaultPadding, id='reqitemscroll')
    610 
    611     def ImportRequestedList(self, *args):
    612         localizedDecimal = eveLocalization.GetDecimalSeparator(localization.SYSTEM_LANGUAGE)
    613         localizedSeparator = eveLocalization.GetThousandSeparator(localization.SYSTEM_LANGUAGE)
    614         typeIDFinder = sm.GetService('contracts').GetContractTypeIDFinder()
    615         nameImporter = ImportMultibuy(typeIDFinder, localizedDecimal, localizedSeparator)
    616         text = GetClipboardData()
    617         toAdd, failedLines = nameImporter.GetTypesAndQty(text)
    618         if not toAdd:
    619             self.ShowFailedLines(failedLines)
    620             return
    621         for typeID, qty in toAdd.iteritems():
    622             if typeID not in self.data.reqItems:
    623                 self.data.reqItems[typeID] = 0
    624             self.data.reqItems[typeID] += qty
    625 
    626         self.PopulateReqItemScroll()
    627         self.ShowFailedLines(failedLines)
    628 
    629     def ShowFailedLines(self, failedLines):
    630         if failedLines:
    631             text = '%s<br>' % GetByLabel('UI/SkillQueue/CouldNotReadLines')
    632             text += '<br>'.join(failedLines)
    633             prompt_player_fsd_dialog('CustomInfo', {'info': text}, modal=False)
    634 
    635     def BuildPage2_AddFittedShipOptions(self, expireTime, expireTimeOptions):
    636         text = GetByLabel('UI/Contracts/ContractsWindow/AssembledShipOptionText', numShips=len(self.data.items))
    637         eveLabel.EveLabelMedium(text=text, parent=self.formWnd, state=uiconst.UI_DISABLED, align=uiconst.TOTOP, padding=(7, 0, 7, 0))
    638         format = [{'type': 'push'},
    639          {'type': 'edit',
    640           'labelwidth': 150,
    641           'width': 120,
    642           'label': GetByLabel('UI/Contracts/ContractEntry/IWillRecieve'),
    643           'key': 'price',
    644           'autoselect': 1,
    645           'required': 0,
    646           'frame': 0,
    647           'group': 'avail',
    648           'floatonly': [0, MAX_AMOUNT, 2],
    649           'setvalue': getattr(self.data, 'price', 0)},
    650          {'type': 'push'},
    651          {'type': 'combo',
    652           'labelwidth': 150,
    653           'width': 120,
    654           'label': GetByLabel('UI/Contracts/ContractsWindow/Expiration'),
    655           'key': 'expiretime',
    656           'options': expireTimeOptions,
    657           'setvalue': getattr(self.data, 'expiretime', expireTime)},
    658          {'type': 'push'},
    659          {'type': 'edit',
    660           'labelwidth': 150,
    661           'width': 220,
    662           'label': GetByLabel('UI/Contracts/ContractEntry/DescriptionOptional'),
    663           'key': 'description',
    664           'required': 0,
    665           'frame': 0,
    666           'maxLength': 50,
    667           'group': 'avail',
    668           'setvalue': getattr(self.data, 'description', '')},
    669          {'type': 'push',
    670           'height': 10}]
    671         self.form, retfields, reqresult, self.panels, errorcheck, refresh = sm.GetService('form').GetForm(format, self.formWnd)
    672         return (errorcheck, reqresult, retfields)
    673 
    674     def BuildPage2_AddCourierOptions(self, duration, expireTime, expireTimeOptions):
    675         if not self.data.endStation and len(self.data.items) == 1:
    676             myItemInfo = self.data.items.values()[0]
    677             if myItemInfo[0] == const.typePlasticWrap:
    678                 wrapItem = myItemInfo[2]
    679                 endStationID = sm.GetService('contracts').GetEndStationIDForCourierWrapID(wrapItem.itemID)
    680                 if endStationID:
    681                     self.data.endStation = endStationID
    682         format = [{'type': 'push'},
    683          {'type': 'edit',
    684           'label': GetByLabel('UI/Contracts/ContractsWindow/ShipTo'),
    685           'labelwidth': 150,
    686           'width': 120,
    687           'key': 'endStationName',
    688           'frame': 0,
    689           'maxLength': 80,
    690           'setvalue': ''},
    691          {'type': 'push'},
    692          {'type': 'edit',
    693           'labelwidth': 150,
    694           'width': 120,
    695           'label': GetByLabel('UI/Contracts/ContractsWindow/Reward'),
    696           'key': 'reward',
    697           'autoselect': 1,
    698           'required': 0,
    699           'frame': 0,
    700           'group': 'avail',
    701           'floatonly': [0, MAX_AMOUNT, 2],
    702           'setvalue': getattr(self.data, 'reward', 0)},
    703          {'type': 'push'},
    704          {'type': 'edit',
    705           'labelwidth': 150,
    706           'width': 120,
    707           'label': GetByLabel('UI/Contracts/ContractsWindow/Collateral'),
    708           'key': 'collateral',
    709           'autoselect': 1,
    710           'required': 0,
    711           'frame': 0,
    712           'group': 'avail',
    713           'floatonly': [0, MAX_AMOUNT, 2],
    714           'setvalue': getattr(self.data, 'collateral', 0)},
    715          {'type': 'push'},
    716          {'type': 'combo',
    717           'labelwidth': 150,
    718           'width': 120,
    719           'label': GetByLabel('UI/Contracts/ContractsWindow/Expiration'),
    720           'key': 'expiretime',
    721           'options': expireTimeOptions,
    722           'setvalue': expireTime},
    723          {'type': 'push'},
    724          {'type': 'edit',
    725           'labelwidth': 150,
    726           'width': 60,
    727           'label': GetByLabel('UI/Contracts/ContractsWindow/DaysToComplete'),
    728           'key': 'duration',
    729           'autoselect': 1,
    730           'required': 0,
    731           'frame': 0,
    732           'group': 'duration',
    733           'setvalue': duration,
    734           'intonly': [1, 365]},
    735          {'type': 'push'},
    736          {'type': 'edit',
    737           'labelwidth': 150,
    738           'width': 220,
    739           'label': GetByLabel('UI/Contracts/ContractEntry/DescriptionOptional'),
    740           'key': 'description',
    741           'required': 0,
    742           'frame': 0,
    743           'group': 'avail',
    744           'setvalue': getattr(self.data, 'description', '')}]
    745         self.form, retfields, reqresult, self.panels, errorcheck, refresh = sm.GetService('form').GetForm(format, self.formWnd)
    746         if self.data.endStation:
    747             self.SearchStation(self.data.endStation)
    748         endStationEdit = self.formWnd.sr.endStationName
    749         endStationEdit.OnFocusLost = self.SearchStationFromEdit
    750         endStationEdit.isLocationField = True
    751         endStationEdit.SetValueAfterDragging = self.EndStationSetValueAfterDragging
    752         btnparent = Container(name='btnparent', parent=endStationEdit.parent, align=uiconst.TOALL, padLeft=const.defaultPadding)
    753         btn = Button(parent=btnparent, label=GetByLabel('UI/Common/Search'), func=self.SearchStationFromEdit, args=(endStationEdit,), btn_default=0, align=uiconst.CENTERLEFT)
    754         Line(parent=self.formWnd, align=uiconst.TOTOP, padTop=10)
    755         self.sr.courierHint = eveLabel.EveLabelSmall(name='courierHint', text='', align=uiconst.TOTOP, parent=self.formWnd, state=uiconst.UI_DISABLED, padding=6)
    756         self.UpdateCourierHint()
    757         if not getattr(self, 'courierHint', None):
    758             self.courierHint = timerstuff.AutoTimer(1000, self.UpdateCourierHint)
    759         btnparent2 = Container(name='btnparent', parent=self.formWnd.sr.collateral.parent, align=uiconst.TOALL, padLeft=const.defaultPadding)
    760         btn2 = Button(parent=btnparent2, label=GetByLabel('UI/Contracts/ContractsWindow/EstPrice'), func=self.SetCollateralToEstPrice, btn_default=0, align=uiconst.CENTERLEFT)
    761         return (errorcheck, reqresult, retfields)
    762 
    763     def EndStationSetValueAfterDragging(self, name, locationID):
    764         if self.IsDockableLocation(locationID):
    765             SingleLineEditText.SetValueAfterDragging(self.formWnd.sr.endStationName, name, locationID)
    766             self.data.endStation = None
    767 
    768     def GetItemQtyLabel(self, iQty, iTypeID):
    769         return GetByLabel('UI/Contracts/ContractsService/NameXQuantity', typeID=iTypeID, quantity=FmtAmt(iQty))
    770 
    771     def BuildPage3(self):
    772         contractType = self.data.type
    773         if hasattr(self.data, 'expiretime'):
    774             settings.char.ui.Set('contracts_expiretime_%s' % contractType, self.data.expiretime)
    775         if hasattr(self.data, 'duration'):
    776             settings.char.ui.Set('contracts_duration_%s' % contractType, self.data.duration)
    777         rows = []
    778         contractTypeText = GetContractTypeText(contractType)
    779         if self.IsFittedShipContract(contractType):
    780             contractTypeText += ' / %s' % GetByLabel('UI/Contracts/ContractsWindow/AssembledShips')
    781         rows.append([GetByLabel('UI/Contracts/ContractType'), contractTypeText])
    782         desc = self.data.description
    783         if desc == '':
    784             desc = GetByLabel('UI/Contracts/ContractEntry/NoneParen')
    785         rows.append([GetByLabel('UI/Contracts/ContractsWindow/Description'), desc])
    786         avail = self.data.avail
    787         a = GetByLabel('UI/Generic/Public')
    788         if avail > 0:
    789             a = GetByLabel('UI/Generic/Private')
    790             assignee = cfg.eveowners.Get(self.data.assigneeID)
    791             a += ' (<a href=showinfo:%s//%s>%s</a>)' % (assignee.typeID, self.data.assigneeID, assignee.name)
    792         else:
    793             regionID = None
    794             if idCheckers.IsStation(self.data.startStation):
    795                 regionID = cfg.evelocations.Get(self.data.startStation).Station().regionID
    796             else:
    797                 structureInfo = sm.GetService('structureDirectory').GetStructureInfo(self.data.startStation)
    798                 if structureInfo is not None:
    799                     regionID = cfg.mapSystemCache.Get(structureInfo.solarSystemID).regionID
    800             if regionID:
    801                 regionLabel = GetByLabel('UI/Contracts/ContractsService/RegionName', region=regionID)
    802             else:
    803                 log.LogTraceback('No Region?')
    804                 regionLabel = ''
    805             a += regionLabel
    806         rows.append([GetByLabel('UI/Contracts/ContractsWindow/Availability'), a])
    807         loc = GetByLabel('UI/Contracts/ContractEntry/NoneParen')
    808         if self.data.startStation:
    809             loc = cfg.evelocations.Get(self.data.startStation).name
    810             if idCheckers.IsStation(self.data.startStation):
    811                 startStationTypeID = sm.GetService('ui').GetStationStaticInfo(self.data.startStation).stationTypeID
    812             else:
    813                 startStationTypeID = sm.GetService('structureDirectory').GetStructureInfo(self.data.startStation).typeID
    814             loc = GetShowInfoLink(startStationTypeID, loc, self.data.startStation)
    815         rows.append([GetByLabel('UI/Contracts/ContractsWindow/Location'), loc])
    816         expireLabel = GetByLabel('UI/Contracts/ContractsService/TimeLeftWithoutCaption', timeLeft=FmtDate(self.data.expiretime * const.MIN + blue.os.GetWallclockTime(), 'ss'), expireTime=FmtDate(self.data.expiretime * const.MIN))
    817         rows.append([GetByLabel('UI/Contracts/ContractsWindow/Expiration'), expireLabel])
    818         contractType = self.data.type
    819         if not self.IsFittedShipContract(contractType):
    820             rows += self.GetPaymentRows()
    821         rows.append([])
    822         if contractType == const.conTypeAuction:
    823             auctionRows = self.BuildPage3_AddAuctionOptions()
    824             rows += auctionRows
    825         elif self.IsFittedShipContract(contractType):
    826             rows += self.BuildPage3_AddItemExchangeFittedShips()
    827         elif contractType == const.conTypeItemExchange:
    828             rows += self.BuildPage3_AddItemExchangeOptions()
    829         elif contractType == const.conTypeCourier:
    830             courierRows = self.BuildPage3_AddCourierOptions()
    831             rows += courierRows
    832         else:
    833             raise RuntimeError('Contract type not implemented!')
    834         self.WriteConfirm(rows)
    835         self.sr.buttonNext.SetLabel(GetByLabel('UI/Contracts/ContractsWindow/Finish'))
    836 
    837     def GetPaymentRows(self, numContract = 1):
    838         feeTextAndAmounts = self.GetFeeTextAndAmounts()
    839         depositText = feeTextAndAmounts['depositText']
    840         brokersFeeText = feeTextAndAmounts['brokersFeeText']
    841         salesTaxText = feeTextAndAmounts['salesTaxText']
    842         if numContract > 1:
    843             totalDepoitText = FmtISK(numContract * feeTextAndAmounts['depositAmount'], 0)
    844             totalBrokersFeeText = FmtISK(numContract * feeTextAndAmounts['brokersFeeAmount'], 0)
    845             totalSalesTaxText = FmtISK(numContract * feeTextAndAmounts['salesTaxAmount'], 0)
    846             brokersFeeText = GetByLabel('UI/Contracts/ContractsWindow/PerContract', amountText=brokersFeeText, totalText=totalBrokersFeeText, totalColor='red')
    847             depositText = GetByLabel('UI/Contracts/ContractsWindow/PerContract', amountText=depositText, totalText=totalDepoitText, totalColor='red')
    848             salesTaxText = GetByLabel('UI/Contracts/ContractsWindow/PerContract', amountText=salesTaxText, totalText=totalSalesTaxText, totalColor='red')
    849         rows = [[GetByLabel('UI/Contracts/ContractsWindow/SalesTax'), salesTaxText]]
    850         rows.append([GetByLabel('UI/Contracts/ContractsWindow/BrokersFee'), brokersFeeText])
    851         rows.append([GetByLabel('UI/Contracts/ContractsWindow/Deposit'), depositText])
    852         return rows
    853 
    854     def GetFeeTextAndAmounts(self):
    855         brokersFeeAmount = 0
    856         depositAmount = 0
    857         salesTaxAmount = 0
    858         if self.data.avail > 0:
    859             salesTax = GetByLabel('UI/Contracts/ContractEntry/NoneParen')
    860             brokersFeeAmount = const_conBrokersFeeMinimum
    861             brokersFee = FmtISK(brokersFeeAmount, 0)
    862             deposit = FmtISK(0.0, 0)
    863         else:
    864             skillsSvc = sm.GetService('skills')
    865             skillLevels = utillib.KeyVal()
    866             skillLevels.brokerRelations = skillsSvc.GetEffectiveLevel(typeBrokerRelations) or 0
    867             skillLevels.accounting = skillsSvc.GetEffectiveLevel(typeAccounting) or 0
    868             ret = CalcContractFees(self.data.Get('price', 0), self.data.Get('reward', 0), self.data.type, self.data.avail, self.data.expiretime, skillLevels, session.solarsystemid2, session.warfactionid)
    869             salesTaxAmount = ret.salesTaxAmt
    870             brokersFeeAmount = ret.brokersFeeAmt
    871             depositAmount = ret.depositAmt
    872             salesTax = GetByLabel('UI/Contracts/ContractsService/ISKFollowedByPercent', numISK=FmtISK(salesTaxAmount, 0), percentage=ret.salesTax * 100.0)
    873             if brokersFeeAmount == const_conBrokersFeeMinimum:
    874                 brokersFee = GetByLabel('UI/Contracts/ContractsService/ISKMinimumQuantity', numISK=FmtISK(brokersFeeAmount, 0))
    875             else:
    876                 brokersFee = GetByLabel('UI/Contracts/ContractsService/ISKFollowedByPercent', numISK=FmtISK(brokersFeeAmount, 0), percentage=ret.brokersFee * 100.0)
    877             minDeposit = const_conDepositMinimum
    878             if self.data.type == const.conTypeCourier:
    879                 minDeposit = minDeposit / 10
    880             if depositAmount == minDeposit:
    881                 deposit = GetByLabel('UI/Contracts/ContractsService/ISKMinimumQuantity', numISK=FmtISK(depositAmount, 0))
    882             else:
    883                 deposit = GetByLabel('UI/Contracts/ContractsService/ISKFollowedByPercent', numISK=FmtISK(depositAmount, 0), percentage=ret.deposit * 100.0)
    884             if self.data.type == const.conTypeAuction:
    885                 p = const_conSalesTax - float(skillLevels.accounting) * 0.001
    886                 buyout = self.data.collateral
    887                 if buyout < self.data.Get('price', 0):
    888                     buyout = MAX_AMOUNT
    889                 ret2 = CalcContractFees(buyout, self.data.Get('reward', 0), self.data.type, self.data.avail, self.data.expiretime, skillLevels, session.solarsystemid2, session.warfactionid)
    890                 maxSalesTax = ret2.salesTaxAmt
    891                 salesTax = GetByLabel('UI/Contracts/ContractsService/PercentOfSalesPriceAtTax', percent=100.0 * p, formattedISKWithUnits=FmtISKWithDescription(maxSalesTax, True))
    892             elif self.data.type == const.conTypeCourier:
    893                 salesTax = GetByLabel('UI/Contracts/ContractEntry/NoneParen')
    894         return {'brokersFeeText': brokersFee,
    895          'depositText': deposit,
    896          'salesTaxText': salesTax,
    897          'brokersFeeAmount': brokersFeeAmount,
    898          'depositAmount': depositAmount,
    899          'salesTaxAmount': salesTaxAmount}
    900 
    901     def BuildPage3_AddAuctionOptions(self):
    902         rows = []
    903         rows.append([GetByLabel('UI/Contracts/ContractsWindow/StartingBid'), FmtISKWithDescription(self.data.price)])
    904         buyout = GetByLabel('UI/Contracts/ContractEntry/NoneParen')
    905         if self.data.collateral > 0:
    906             buyout = FmtISKWithDescription(self.data.collateral)
    907         rows.append([GetByLabel('UI/Contracts/ContractsWindow/BuyoutPrice'), buyout])
    908         itemList = []
    909         for i in self.data.items:
    910             item = self.data.items[i]
    911             itemLabel = self.GetItemQtyLabel(item[1], item[0])
    912             itemList.append(itemLabel)
    913             chld = self.data.childItems.get(i, [])
    914             for c in chld:
    915                 childItemLabel = GetByLabel('UI/Contracts/ContractsService/NameXQuantityChild', typeID=c[1], quantity=c[2])
    916                 itemList.append(childItemLabel)
    917 
    918         items = '<br>'.join(itemList)
    919         rows.append([GetByLabel('UI/Contracts/ContractEntry/ItemsForSale'), items])
    920         return rows
    921 
    922     def BuildPage3_AddItemExchangeOptions(self):
    923         rows = [[GetByLabel('UI/Contracts/ContractEntry/IWillPay'), FmtISKWithDescription(self.data.reward)], [GetByLabel('UI/Contracts/ContractEntry/IWillRecieve'), FmtISKWithDescription(self.data.price)]]
    924         itemList = []
    925         for i in self.data.items:
    926             itemLabel = self.GetItemQtyLabel(self.data.items[i][1], self.data.items[i][0])
    927             itemList.append(itemLabel)
    928             chld = self.data.childItems.get(i, [])
    929             for c in chld:
    930                 childItemLabel = GetByLabel('UI/Contracts/ContractsService/NameXQuantityChild', typeID=c[1], quantity=c[2])
    931                 itemList.append(childItemLabel)
    932 
    933         items = '<br>'.join(itemList)
    934         rows.append([GetByLabel('UI/Contracts/ContractEntry/ItemsForSale'), items])
    935         listToJoin = []
    936         for typeID, qty in self.data.reqItems.iteritems():
    937             labelToAppend = self.GetItemQtyLabel(qty, typeID)
    938             listToJoin.append(labelToAppend)
    939 
    940         itemStr = '<br>'.join(listToJoin)
    941         rows.append([GetByLabel('UI/Contracts/ContractEntry/ItemsRequired'), itemStr])
    942         return rows
    943 
    944     def BuildPage3_AddItemExchangeFittedShips(self):
    945         numContracts = len(self.data.items)
    946         rows = []
    947         rows += self.GetPaymentRows(numContracts)
    948         pricePerContract = self.data.price
    949         pricePerContractText = FmtISKWithDescription(pricePerContract)
    950         if numContracts > 1:
    951             totalPriceText = FmtISK(numContracts * pricePerContract, 0)
    952             pricePerContractText = GetByLabel('UI/Contracts/ContractsWindow/PerContract', amountText=pricePerContractText, totalText=totalPriceText, totalColor='green')
    953         rows += [[GetByLabel('UI/Contracts/ContractEntry/IWillRecieve'), pricePerContractText]]
    954         rows.append([])
    955         itemList = []
    956         rows.append([GetByLabel('UI/Contracts/ContractsWindow/NumberOfContracts'), numContracts])
    957         numByContainerItemIDs = getattr(self.data, 'numByContainerItemIDs', {})
    958         for itemID, item in self.data.items.iteritems():
    959             iTypeID, iQty, iRow = item
    960             if iQty > 1:
    961                 itemLabel = self.GetItemQtyLabel(iQty, iTypeID)
    962             else:
    963                 itemLabel = evetypes.GetName(iTypeID)
    964             shipLocationData = cfg.evelocations.GetIfExists(itemID)
    965             if shipLocationData:
    966                 itemLabel += ' (%s)' % shipLocationData.name
    967             numContained = numByContainerItemIDs.get(itemID, 0)
    968             if numContained:
    969                 itemLabel += '<br><font color=grey>%s</font>' % GetByLabel('UI/Contracts/ContractsWindow/ContainsItemsPostText', numItems=numContained)
    970             itemList.append('<li>%s</li>' % itemLabel)
    971 
    972         items = ''.join(itemList)
    973         if items:
    974             items = '<ol>%s</ol>' % items
    975         rows.append([GetByLabel('UI/Contracts/ContractEntry/ItemsForSale'), items])
    976         return rows
    977 
    978     def BuildPage3_AddCourierOptions(self):
    979         rows = []
    980         rows.append([GetByLabel('UI/Contracts/ContractsWindow/Reward'), FmtISKWithDescription(self.data.reward)])
    981         rows.append([GetByLabel('UI/Contracts/ContractsWindow/Collateral'), FmtISKWithDescription(self.data.collateral)])
    982         loc = ''
    983         if self.data.startStation:
    984             loc = cfg.evelocations.Get(self.data.endStation).name
    985             if idCheckers.IsStation(self.data.endStation):
    986                 typeID = sm.GetService('ui').GetStationStaticInfo(self.data.endStation).stationTypeID
    987             else:
    988                 typeID = sm.GetService('structureDirectory').GetStructureInfo(self.data.endStation).typeID
    989             loc = GetShowInfoLink(typeID, loc, itemID=self.data.endStation)
    990         rows.append([GetByLabel('UI/Common/Destination'), loc])
    991         rows.append([GetByLabel('UI/Contracts/ContractsWindow/DaysToComplete'), self.data.duration])
    992         volume = 0
    993         for i in self.data.items:
    994             volume += GetItemVolume(self.data.items[i][2], int(self.data.items[i][1]))
    995 
    996         if 0 < volume < 1:
    997             significantDigits = 2
    998             decimalPlaces = int(-math.ceil(math.log10(volume)) + significantDigits)
    999         else:
   1000             decimalPlaces = 2
   1001         volumeLabel = GetByLabel('UI/Contracts/ContractsWindow/NumericVolume', volume=FmtAmt(volume, showFraction=decimalPlaces))
   1002         rows.append([GetByLabel('UI/Inventory/ItemVolume'), volumeLabel])
   1003         items = ''
   1004         itemList = []
   1005         for i in self.data.items:
   1006             item = self.data.items[i]
   1007             itemLabel = self.GetItemQtyLabel(item[1], item[0])
   1008             itemList.append(itemLabel)
   1009             chld = self.data.childItems.get(i, [])
   1010             for c in chld:
   1011                 childItemLabel = GetByLabel('UI/Contracts/ContractsService/NameXQuantityChild', typeID=c[1], quantity=c[2])
   1012                 itemList.append(childItemLabel)
   1013 
   1014         items = '<br>'.join(itemList)
   1015         rows.append([GetByLabel('UI/Contracts/ContractEntry/Items'), items])
   1016         return rows
   1017 
   1018     def OnChangePrivateName(self, privateCheckbox, text):
   1019         if text:
   1020             privateCheckbox.ToggleState()
   1021 
   1022     def OnRequestItemsChange(self, chkbox, *args):
   1023         if not chkbox.GetValue():
   1024             self.reqItemScroll.Clear()
   1025             self.data.reqItems = {}
   1026         self.ToggleShowRequestItems(not not chkbox.GetValue())
   1027 
   1028     def ToggleShowRequestItems(self, isIt):
   1029         if isIt:
   1030             self.sr.reqItemsParent.Show()
   1031         else:
   1032             self.sr.reqItemsParent.Hide()
   1033 
   1034     def WriteNumContractsAndLimitsLazy(self):
   1035         try:
   1036             n, maxNumContracts = self.GetNumContractsAndLimits(getattr(self.data, 'forCorp', 0), getattr(self.data, 'assigneeID', 0))
   1037             outstandingLabel = GetByLabel('UI/Contracts/ContractsService/OutstandingContractsDisplay', numContracts=n, maxContracts=maxNumContracts)
   1038             self.limitTextWnd.text = '<color=0xff999999>%s</color>' % outstandingLabel
   1039         except:
   1040             sys.exc_clear()
   1041 
   1042     def OnForCorpChange(self, wnd, *args):
   1043         self.data.forCorp = wnd.GetValue()
   1044         self.OnAvailChange(None)
   1045         self.data.items = {}
   1046 
   1047     def OnAvailChange(self, wnd, *args):
   1048         uthread.new(self._OnAvailChange, wnd)
   1049 
   1050     def _OnAvailChange(self, wnd, *args):
   1051         if self.destroyed:
   1052             return
   1053         if not wnd:
   1054             key = getattr(self.data, 'lastAvailKey', 0)
   1055         else:
   1056             key = wnd.data.get('key')
   1057             self.data.lastAvailKey = key
   1058         if key == 2:
   1059             n, maxNumContracts = self.GetNumContractsAndLimits(getattr(self.data, 'forCorp', 0), session.corpid)
   1060         elif key == 1:
   1061             n, maxNumContracts = self.GetNumContractsAndLimits(getattr(self.data, 'forCorp', 0), getattr(self.data, 'assigneeID', 0))
   1062         else:
   1063             n, maxNumContracts = self.GetNumContractsAndLimits(getattr(self.data, 'forCorp', 0), 0)
   1064         try:
   1065             outstandingLabel = GetByLabel('UI/Contracts/ContractsService/OutstandingContractsDisplay', numContracts=n, maxContracts=maxNumContracts)
   1066             self.limitTextWnd.text = '<color=0xff999999>%s</color>' % outstandingLabel
   1067         except:
   1068             sys.exc_clear()
   1069 
   1070     def CreateContract(self):
   1071         sm.GetService('loading').ProgressWnd(GetByLabel('UI/Contracts/ContractsWindow/CreatingContract'), '', 2, 10)
   1072         try:
   1073             self.itemsCurrentlyBeingContracted = None
   1074             self.state = uiconst.UI_HIDDEN
   1075             contractType = getattr(self.data, 'type', const.conTypeNothing)
   1076             assigneeID = getattr(self.data, 'assigneeID', 0)
   1077             startStationID = getattr(self.data, 'startStation', 0)
   1078             startStationDivision = getattr(self.data, 'startStationDivision', None)
   1079             endStationID = getattr(self.data, 'endStation', 0)
   1080             forCorp = getattr(self.data, 'forCorp', 0) > 0
   1081             if not forCorp:
   1082                 startStationDivision = 4
   1083             price = getattr(self.data, 'price', 0)
   1084             reward = getattr(self.data, 'reward', 0)
   1085             collateral = getattr(self.data, 'collateral', 0)
   1086             expiretime = getattr(self.data, 'expiretime', 0)
   1087             duration = getattr(self.data, 'duration', 0)
   1088             title = getattr(self.data, 'description', '')
   1089             description = getattr(self.data, 'body', '')
   1090             items = []
   1091             itemsReq = map(list, self.data.reqItems.items())
   1092             for i in self.data.items:
   1093                 items.append([i, self.data.items[i][1]])
   1094 
   1095             isPrivate = not not assigneeID
   1096             contractIDs = None
   1097             isMultiContract = self.IsFittedShipContract(contractType)
   1098             expectedNumContracts = len(items) if isMultiContract else 1
   1099             contractInfo = ContractStruct(contractType=contractType, isPrivate=isPrivate, assignedToID=assigneeID, minutesExpire=expiretime, numDays=duration, startStationID=startStationID, destinationID=endStationID, price=price, reward=reward, collateral=collateral, title=title, description=description, itemList=items, startStationDivision=startStationDivision, requestItemTypeList=itemsReq, forCorp=forCorp, multiContract=isMultiContract)
   1100             try:
   1101                 if isMultiContract:
   1102                     self.itemsCurrentlyBeingContracted = (expectedNumContracts, {x[0] for x in items})
   1103                     sm.RegisterForNotifyEvent(self, 'OnItemChange')
   1104                     sm.GetService('loading').ProgressWnd(GetByLabel('UI/Contracts/ContractsWindow/CreatingContract'), '%s / %s ' % (0, expectedNumContracts), 2, expectedNumContracts + 2)
   1105                 contractIDs = sm.GetService('contracts').CreateContract(contractInfo)
   1106             except UserError as e:
   1107                 if e.msg == 'NotEnoughCargoSpace':
   1108                     if prompt_player_fsd_dialog('ConAskRemoveToHangar', {}, uiconst.YESNO) == uiconst.ID_YES:
   1109                         contractID = sm.GetService('contracts').CreateContract(contractInfo, confirm=CREATECONTRACT_CONFIRM_CHARGESTOHANGAR)
   1110                     else:
   1111                         raise
   1112                 elif e.msg == 'ConMultiContractAbortedDueToTidi':
   1113                     contractIDs = e.dict.get('createdContractIDs', [])
   1114                     self.UpdateContractItemsAfterFailedMultiContract()
   1115                     self.ShowCreatedContracts(contractIDs, expectedNumContracts, isMultiContract)
   1116                     raise
   1117                 else:
   1118                     raise
   1119 
   1120             self.ShowCreatedContracts(contractIDs, expectedNumContracts, isMultiContract)
   1121         finally:
   1122             sm.UnregisterForNotifyEvent(self, 'OnItemChange')
   1123             self.state = uiconst.UI_NORMAL
   1124             if self.itemsCurrentlyBeingContracted:
   1125                 expectedNumContracts, itemsLeft = self.itemsCurrentlyBeingContracted
   1126                 itemsProcessed = expectedNumContracts - len(itemsLeft)
   1127                 sub = '%s / %s ' % (itemsProcessed, expectedNumContracts)
   1128                 steps = expectedNumContracts + 2
   1129             else:
   1130                 sub = ''
   1131                 steps = 10
   1132             sm.GetService('loading').ProgressWnd(GetByLabel('UI/Contracts/ContractsWindow/CreatingContract'), sub, steps, steps)
   1133             self.itemsCurrentlyBeingContracted = None
   1134 
   1135     def ShowCreatedContracts(self, contractIDs, expectedNumContracts = None, isMultiContract = False):
   1136         if contractIDs:
   1137             if len(contractIDs) == 1 or not isMultiContract:
   1138                 contractID = contractIDs[0]
   1139                 sm.GetService('contracts').ShowContract(contractID)
   1140             else:
   1141                 sm.GetService('contracts').ShowManyContracts(contractIDs)
   1142             if expectedNumContracts is None or len(contractIDs) >= expectedNumContracts:
   1143                 self.Close()
   1144 
   1145     def UpdateContractItemsAfterFailedMultiContract(self):
   1146         oldItems = set(self.data.items)
   1147         itemsInStation = sm.GetService('contracts').GetItemsInDockableLocation(self.data.startStation, getattr(self.data, 'forCorp', 0))
   1148         itemIDsInStation = {x.itemID for x in itemsInStation}
   1149         toRemove = oldItems - itemIDsInStation
   1150         for toRemoveID in toRemove:
   1151             self.data.items.pop(toRemoveID)
   1152 
   1153         self.Refresh()
   1154 
   1155     def OnItemChange(self, item, change, location):
   1156         itemsCurrentlyBeingContracted = getattr(self, 'itemsCurrentlyBeingContracted', None)
   1157         if not itemsCurrentlyBeingContracted:
   1158             return
   1159         expectedNumContracts, itemsLeft = self.itemsCurrentlyBeingContracted
   1160         if item.itemID in itemsLeft:
   1161             itemsLeft.discard(item.itemID)
   1162         itemsProcessed = expectedNumContracts - len(itemsLeft)
   1163         sm.GetService('loading').ProgressWnd(GetByLabel('UI/Contracts/ContractsWindow/CreatingContract'), '%s / %s ' % (itemsProcessed, expectedNumContracts), 2 + itemsProcessed, expectedNumContracts + 2)
   1164 
   1165     def WriteConfirm(self, rows):
   1166         body = ''
   1167         for r in rows:
   1168             if r == []:
   1169                 body += '<tr><td colspan=2><hr></td></tr>'
   1170             elif len(r) == 1:
   1171                 body += '<tr><td colspan=2>%s</td></tr>' % r[0]
   1172             else:
   1173                 boldKey = GetByLabel('UI/Contracts/ContractsService/BoldGenericLabel', labelText=r[0])
   1174                 val = r[1]
   1175                 body += '<tr><td width=100 valign=top>%(key)s</td><td>%(val)s</td></tr>' % {'key': boldKey,
   1176                  'val': val}
   1177 
   1178         message = '\n              <TABLE width="98%%">%s</TABLE>\n        ' % body
   1179         messageArea = eveEdit.Edit(parent=self.formWnd, readonly=1, hideBackground=1)
   1180         messageArea.SetValue(message)
   1181 
   1182     def ParseItemType(self, editField, *args):
   1183         if self.destroyed or getattr(self, 'parsingItemType', False):
   1184             return
   1185         try:
   1186             self.parsingItemType = True
   1187             txt = editField.GetValue()
   1188             if len(txt) == 0 or not IsSearchStringLongEnough(txt):
   1189                 return
   1190             types = []
   1191             for t in self.marketTypes:
   1192                 if MatchInvTypeName(txt, t.typeID):
   1193                     if CanRequestType(t.typeID):
   1194                         types.append(t.typeID)
   1195 
   1196             typeID = SelectItemTypeDlg(types)
   1197             if typeID:
   1198                 editField.SetValue(TypeName(typeID))
   1199                 self.parsedItemType = typeID
   1200             return typeID
   1201             self.parsedItemType = typeID
   1202         finally:
   1203             self.parsingItemType = False
   1204 
   1205     def AddRequestItem(self, *args):
   1206         typeID = getattr(self, 'parsedItemType', None) or self.ParseItemType(self.reqItemTypeWnd)
   1207         self.parsedItemType = None
   1208         if not typeID:
   1209             return
   1210         qty = self.reqItemQtyWnd.GetValue()
   1211         if qty < 1:
   1212             return
   1213         self.data.reqItems[typeID] = qty
   1214         self.reqItemTypeWnd.SetValue('')
   1215         self.reqItemQtyWnd.SetValue(1)
   1216         self.PopulateReqItemScroll()
   1217 
   1218     def RemoveRequestItem(self, nodes, *args):
   1219         for node in nodes:
   1220             self.data.reqItems.pop(node.typeID, None)
   1221 
   1222         self.PopulateReqItemScroll()
   1223 
   1224     def PopulateReqItemScroll(self):
   1225         scrolllist = []
   1226         self.reqItemScroll.Clear()
   1227         for typeID, qty in self.data.reqItems.iteritems():
   1228             data = utillib.KeyVal(label='%s<t>%s' % (evetypes.GetName(typeID), qty), typeID=typeID, getIcon=True, showinfo=False, GetMenu=self.GetReqItemMenu, ignoreRightClick=True)
   1229             entry = GetFromClass(Item, data)
   1230             scrolllist.append(entry)
   1231 
   1232         self.reqItemScroll.Load(contentList=scrolllist, headers=[GetByLabel('UI/Inventory/InvItemNameShort'), GetByLabel('UI/Common/Quantity')], noContentHint=GetByLabel('UI/Contracts/ContractsWindow/AddItemsAbove'))
   1233 
   1234     def GetReqItemMenu(self, entry, *args):
   1235         node = entry.sr.node
   1236         selectedNodes = node.scroll.GetSelectedNodes(node)
   1237         menus = []
   1238         for node in selectedNodes:
   1239             m = [(MenuLabel('UI/Generic/RemoveItem'), self.RemoveRequestItem, (selectedNodes,))]
   1240             menus.append(m)
   1241 
   1242         if len(selectedNodes) == 1:
   1243             menus += [GetMenuService().GetMenuFromItemIDTypeID(None, node.typeID, includeMarketDetails=True)]
   1244         return MergeMenus(menus)
   1245 
   1246     def SearchStationFromEdit(self, editField):
   1247         stationID = editField.draggedValue
   1248         self.SearchStation(stationID)
   1249 
   1250     @staticmethod
   1251     def GetSearchScrollEntryData(searchResults, resultType):
   1252         entryList = []
   1253         if resultType not in (search_const.ResultType.structure_with_inlined_data, search_const.ResultType.station):
   1254             log.LogWarn('resultType %d not recognized')
   1255             return entryList
   1256         for entry in searchResults:
   1257             if resultType == search_const.ResultType.structure_with_inlined_data:
   1258                 itemID = entry.structureID
   1259                 solarsystemID = entry.solarSystemID
   1260                 typeID = entry.typeID
   1261                 name = entry.itemName
   1262             elif resultType == search_const.ResultType.station:
   1263                 itemID = entry
   1264                 station = cfg.stations.Get(itemID)
   1265                 solarsystemID = station.solarSystemID
   1266                 typeID = station.stationTypeID
   1267                 name = cfg.evelocations.Get(itemID).name
   1268             else:
   1269                 continue
   1270             data = {'sublevel': 1,
   1271              'showinfo': True,
   1272              'itemID': itemID,
   1273              'listvalue': (itemID, name),
   1274              'solarSystemID': solarsystemID,
   1275              'typeID': typeID,
   1276              'label': name}
   1277             entryList.append(data)
   1278 
   1279         return entryList
   1280 
   1281     def SearchStation(self, stationID = None, *args):
   1282         searchstr = self.form.sr.endStationName.GetValue().strip()
   1283         if stationID is None and not IsSearchStringLongEnough(searchstr):
   1284             return
   1285         if stationID and not self.IsDockableLocation(stationID):
   1286             stationID = None
   1287         if stationID is None:
   1288             with sm.GetService('loading').LoadCycle(GetByLabel('UI/Common/Searching'), GetByLabel('UI/Contracts/ContractsService/LoadingSearchHint', searchStr=searchstr)):
   1289                 searchResult = self.SearchGetStationID(searchstr)
   1290                 if searchResult is None:
   1291                     return
   1292                 stationID, locationName = searchResult
   1293         else:
   1294             locationName = cfg.evelocations.Get(stationID).name
   1295         if stationID:
   1296             self.courierDropLocation = stationID
   1297             self.form.sr.endStationName.SetValue(locationName)
   1298         self.data.endStation = stationID
   1299 
   1300     def SearchGetStationID(self, searchstr):
   1301         resultsByType = searchUtil.GetResultsByGroupID(searchstr.lower(), [search_const.ResultType.station, search_const.ResultType.structure_with_inlined_data])
   1302         if search_const.ResultType.structure_with_inlined_data in resultsByType:
   1303             resultsByType[search_const.ResultType.structure_with_inlined_data] = [ structureInfo for structureInfo in resultsByType.get(search_const.ResultType.structure_with_inlined_data, []) if self.IsDockableType(structureInfo.typeID) ]
   1304         totalResults = len(resultsByType.get(search_const.ResultType.station, [])) + len(resultsByType.get(search_const.ResultType.structure_with_inlined_data, []))
   1305         if totalResults == 1:
   1306             stationID = resultsByType.get(search_const.ResultType.station, [None])[0]
   1307             if stationID:
   1308                 return (stationID, cfg.evelocations.Get(stationID).name)
   1309             else:
   1310                 structureInfo = resultsByType.get(search_const.ResultType.structure_with_inlined_data)[0]
   1311                 return (structureInfo.structureID, structureInfo.itemName)
   1312         scroll = []
   1313         for resultType, results in resultsByType.iteritems():
   1314             if not results:
   1315                 continue
   1316             entryList = self.GetSearchScrollEntryData(results, resultType)
   1317             label = GetByLabel('UI/Common/LocationTypes/Stations') if resultType == search_const.ResultType.station else GetByLabel('UI/Common/LocationTypes/Structures')
   1318             scroll.append(GetFromClass(ListGroup, {'GetSubContent': searchUtil.GetSearchSubContent,
   1319              'label': GetByLabel('UI/Search/UniversalSearch/SectionHeader', resultType=label, numberReturned=len(entryList)),
   1320              'groupItems': (SolarSystem, entryList),
   1321              'id': ('search_cat', resultType),
   1322              'sublevel': 0,
   1323              'showlen': 0,
   1324              'showicon': 'hide',
   1325              'cat': resultType,
   1326              'state': 'locked'}))
   1327 
   1328         header = GetByLabel('UI/Common/SearchResults')
   1329         top = GetByLabel('UI/Search/UniversalSearch/WindowHeaderNumResults', numResults=totalResults, searchStr=searchstr)
   1330         if totalResults >= search_const.max_result_count:
   1331             top = GetByLabel('UI/Search/UniversalSearch/WindowHeaderOverMax', maxNumber=search_const.max_result_count, searchStr=searchstr)
   1332         return ListWindow.ShowList(scroll, 'generic', header, top, 0, isModal=1, windowName='contractEndpointSearch', lstDataIsGrouped=1, unstackable=1, noContentHint=GetByLabel('UI/Search/UniversalSearch/NoResultsReturned', searchStr=searchstr))
   1333 
   1334     @staticmethod
   1335     def IsDockableLocation(dockableItemID):
   1336         if idCheckers.IsStation(dockableItemID):
   1337             return True
   1338         structureInfo = sm.GetService('structureDirectory').GetStructureInfo(dockableItemID)
   1339         return structureInfo is not None and not IsNPC(structureInfo.ownerID) and not IsFlexStructure(structureInfo.typeID)
   1340 
   1341     @staticmethod
   1342     def IsDockableType(typeID):
   1343         if evetypes.GetGroupID(typeID) == groupStation:
   1344             return True
   1345         if evetypes.GetCategoryID(typeID) == categoryStructure:
   1346             if IsFlexStructure(typeID):
   1347                 return False
   1348             return True
   1349         return False
   1350 
   1351     def UpdateCourierHint(self):
   1352         if self.data.endStation and self.data.startStation and self.sr.courierHint and not self.sr.courierHint.destroyed:
   1353             hintList = []
   1354             startSolarSystemID = sm.GetService('contracts').GetSolarSystemIDForStationOrStructure(self.data.startStation)
   1355             endSolarSystemID = sm.GetService('contracts').GetSolarSystemIDForStationOrStructure(self.data.endStation)
   1356             numJumps = sm.GetService('clientPathfinderService').GetAutopilotJumpCount(endSolarSystemID, startSolarSystemID)
   1357             perJump = self.formWnd.sr.reward.GetValue() / (numJumps or 1)
   1358             sec = sm.GetService('contracts').GetRouteSecurityWarning(startSolarSystemID, endSolarSystemID)
   1359             volumeHint = ''
   1360             if numJumps == 0:
   1361                 hint = GetByLabel('UI/Contracts/ContractsService/CourierHintNoJumps', numISK=FmtISK(perJump))
   1362             elif numJumps == 1:
   1363                 hint = GetByLabel('UI/Contracts/ContractsService/CourierHintOneJump', numISK=FmtISK(perJump))
   1364             else:
   1365                 hint = GetByLabel('UI/Contracts/ContractsService/CourierHintManyJumps', numJumps=numJumps, numISK=FmtISK(perJump))
   1366             if numJumps != UNREACHABLE_JUMP_COUNT:
   1367                 hintList.append('- ' + hint)
   1368             num, totalVolume = self.GetVolumeOfAllItems()
   1369             if totalVolume:
   1370                 perM3 = self.formWnd.sr.reward.GetValue() / totalVolume
   1371                 v = FmtAmt(totalVolume, showFraction=1)
   1372                 hintList.append('- ' + GetByLabel('UI/Contracts/ContractsService/CourierHintIskM3', volume=v, numISK=FmtISK(perM3)))
   1373             if sec:
   1374                 hintList.append('- ' + sec)
   1375             hintText = '<br>'.join(hintList)
   1376             self.sr.courierHint.SetText(hintText)
   1377 
   1378     def GetNumContractsAndLimits(self, forCorp, assigneeID):
   1379         skills = sm.GetService('skills').GetSkills()
   1380         skillTypeID = [const.typeContracting, const.typeCorporationContracting][forCorp]
   1381         skill = skills.get(skillTypeID, None)
   1382         advSkill = skills.get(const.typeAdvancedContracting, None)
   1383         if skill is None:
   1384             lvl = 0
   1385         else:
   1386             lvl = skill.effectiveSkillLevel
   1387         if advSkill is None:
   1388             advlvl = 0
   1389         else:
   1390             advlvl = advSkill.effectiveSkillLevel
   1391         if forCorp:
   1392             maxNumContracts = NUM_CONTRACTS_BY_SKILL_CORP[lvl]
   1393         else:
   1394             maxNumContracts = NUM_CONTRACTS_BY_SKILL[lvl] + NUM_CONTRACTS_BY_ADVSKILL[advlvl]
   1395         innerCorp = False
   1396         if assigneeID > 0:
   1397             if idCheckers.IsCorporation(assigneeID):
   1398                 if assigneeID == session.corpid:
   1399                     innerCorp = True
   1400             elif idCheckers.IsCharacter(assigneeID):
   1401                 corpID = sm.RemoteSvc('corpmgr').GetCorporationIDForCharacter(assigneeID)
   1402                 if corpID == session.corpid and not idCheckers.IsNPC(session.corpid):
   1403                     innerCorp = True
   1404         n = 0
   1405         try:
   1406             if getattr(self, 'numOutstandingContracts', None) is None:
   1407                 self.numOutstandingContracts = sm.GetService('contracts').NumOutstandingContracts()
   1408             if forCorp:
   1409                 if innerCorp:
   1410                     n = self.numOutstandingContracts.myCorpTotal
   1411                 else:
   1412                     n = self.numOutstandingContracts.nonCorpForMyCorp
   1413             elif innerCorp:
   1414                 n = self.numOutstandingContracts.myCharTotal
   1415             else:
   1416                 n = self.numOutstandingContracts.nonCorpForMyChar
   1417         finally:
   1418             return (n, [maxNumContracts, MAX_NUM_CONTRACTS][innerCorp])
   1419 
   1420     def FinishStep(self, step):
   1421         uthread.Lock(self)
   1422         try:
   1423             result = sm.GetService('form').ProcessForm(self.formdata[0], self.formdata[1])
   1424             lastType = getattr(self.data, 'type', None)
   1425             if step == 0:
   1426                 contractType = result.get('type', None)
   1427                 if contractType is None:
   1428                     return False
   1429                 if contractType != lastType and not getattr(self, 'first', False):
   1430                     self.ResetWizard()
   1431             setattr(self, 'first', False)
   1432             for i in result:
   1433                 setattr(self.data, str(i), result[i])
   1434 
   1435             if step == 0:
   1436                 doContinue = self.FinishPage0()
   1437                 if not doContinue:
   1438                     return False
   1439             elif step == 1:
   1440                 doContinue = self.FinishStep1()
   1441                 if not doContinue:
   1442                     return False
   1443             else:
   1444                 if step == 2:
   1445                     return self.FinishStep2()
   1446                 raise RuntimeError('Illegal step (%s)' % step)
   1447             return True
   1448         finally:
   1449             uthread.UnLock(self)
   1450 
   1451     def FinishPage0(self):
   1452         self.isMultiContract = False
   1453         if self.data.type == const.conTypeItemExchange:
   1454             fittedShipsCb = self.form.FindChild('contractTypeCb_fittedShip')
   1455             if fittedShipsCb and fittedShipsCb.checked:
   1456                 self.isMultiContract = True
   1457         ownerID = None
   1458         if self.data.avail == 1:
   1459             if IsSearchStringLongEnough(self.data.name):
   1460                 exact = getattr(self.data, 'searchBy', search_const.MatchBy.partial_terms)
   1461                 ownerID = searchOld.Search(self.data.name.lower(), const.groupCharacter, const.categoryOwner, exact=exact, hideNPC=1, searchWndName='contractFinishStepSearch', hideDustChars=True)
   1462             if not ownerID:
   1463                 return False
   1464             if self.data.type == const.conTypeAuction:
   1465                 owner = cfg.eveowners.Get(ownerID)
   1466                 if owner.IsCharacter() or owner.IsCorporation() and ownerID != session.corpid:
   1467                     raise UserError('CustomInfo', {'info': GetByLabel('UI/Contracts/ContractsService/UserErrorCannotCreatePrivateAuction')})
   1468         elif self.data.name != '':
   1469             raise UserError('CustomInfo', {'info': GetByLabel('UI/Contracts/ContractsService/UserErrorPrivateNameAndStateDontMatch')})
   1470         elif self.data.avail == 2:
   1471             ownerID = session.corpid
   1472         elif self.data.avail == 3:
   1473             ownerID = session.allianceid
   1474             if not ownerID:
   1475                 raise UserError('CustomInfo', {'info': GetByLabel('UI/CapitalNavigation/CapitalNavigationWindow/CorporationNotInAllianceMessage')})
   1476         self.data.assigneeID = ownerID
   1477         forCorp = bool(self.data.forCorp)
   1478         n, maxNumContracts = self.GetNumContractsAndLimits(forCorp, self.data.assigneeID)
   1479         if n >= maxNumContracts >= 0:
   1480             if forCorp is True:
   1481                 skillLabel = GetByLabel('UI/Contracts/ContractsService/IncreaseSkillInfo', skillType=const.typeCorporationContracting)
   1482             else:
   1483                 skillLabel = GetByLabel('UI/Contracts/ContractsService/IncreaseSkillInfo', skillType=const.typeContracting)
   1484             errorLabel = GetByLabel('UI/Contracts/ContractsService/UserErrorMaxContractsReached', numContracts=maxNumContracts, skillInfo=skillLabel)
   1485             raise UserError('CustomInfo', {'info': errorLabel})
   1486         return True
   1487 
   1488     def FinishStep1(self):
   1489         ILLEGAL_ITEMGROUPS = [const.groupCapsule]
   1490         contractType = self.data.type
   1491         self.data.numByContainerItemIDs = {}
   1492         if self.IsFittedShipContract(contractType):
   1493             numShips = len(self.data.items)
   1494             isForCorp = self.data.forCorp
   1495             currentNum, maxNumContracts = self.GetNumContractsAndLimits(isForCorp, getattr(self.data, 'assigneeID', 0))
   1496             maxMultiContracts = GetMaxShipsToContract(sm.GetService('machoNet'))
   1497             maxMultiContract = min(maxMultiContracts, maxNumContracts - currentNum)
   1498             if numShips > maxMultiContract:
   1499                 raise UserError('ConMultiContractExceedAvailable')
   1500             if not numShips:
   1501                 raise UserError('ConMultiContractSelectShip')
   1502             for itemID, item in self.data.items.iteritems():
   1503                 itemTypeID = item[0]
   1504                 itemGroupID = evetypes.GetGroupID(itemTypeID)
   1505                 itemRow = item[2]
   1506                 if evetypes.GetCategoryID(itemTypeID) != const.categoryShip or not itemRow.singleton:
   1507                     raise UserError('ConMultiContractNotShip')
   1508                 if itemGroupID in ILLEGAL_ITEMGROUPS:
   1509                     raise UserError('ConIllegalType')
   1510                 if itemID in (session.shipid, eveCfg.GetActiveShip()):
   1511                     raise UserError('ConCannotTradeCurrentShip')
   1512 
   1513             div = self.data.startStationDivision if isForCorp else const.flagHangar
   1514             numByContainerItemIDs = sm.GetService('contracts').GetNumItemsInContainers(self.data.startStation, self.data.items.keys(), isForCorp, div)
   1515             self.data.numByContainerItemIDs = numByContainerItemIDs
   1516             totalItems = sum(numByContainerItemIDs.itervalues())
   1517             if totalItems:
   1518                 if prompt_player_fsd_dialog('ConMultiContractConfirmShipNumAndContent', {'numShips': len(numByContainerItemIDs),
   1519                  'numItems': totalItems}, buttons=uiconst.YESNO, modal=True, suppress=uiconst.ID_YES) != uiconst.ID_YES:
   1520                     return
   1521         else:
   1522             self.data.childItems = {}
   1523             for i in self.data.items:
   1524                 typeID = self.data.items[i][0]
   1525                 groupID = evetypes.GetGroupID(typeID)
   1526                 categoryID = evetypes.GetCategoryID(typeID)
   1527                 if groupID in ILLEGAL_ITEMGROUPS:
   1528                     raise UserError('ConIllegalType')
   1529                 if i in (session.shipid, eveCfg.GetActiveShip()):
   1530                     raise UserError('ConCannotTradeCurrentShip')
   1531                 isContainer = categoryID == const.categoryShip or groupID in (const.groupCargoContainer,
   1532                  const.groupSecureCargoContainer,
   1533                  const.groupAuditLogSecureContainer,
   1534                  const.groupFreightContainer)
   1535                 if isContainer:
   1536                     div = const.flagHangar
   1537                     if self.data.forCorp:
   1538                         div = self.data.startStationDivision
   1539                     itemNameList = []
   1540                     totalVolume = 0
   1541                     for l in sm.GetService('contracts').GetItemsInContainer(self.data.startStation, i, self.data.forCorp, div) or []:
   1542                         itemNameList.append(TypeName(l.typeID))
   1543                         val = [l.itemID, l.typeID, l.stacksize]
   1544                         if i not in self.data.childItems:
   1545                             self.data.childItems[i] = []
   1546                         self.data.childItems[i].append(val)
   1547                         totalVolume += GetItemVolume(l, l.stacksize)
   1548 
   1549                     if itemNameList:
   1550                         itemsText = ', '.join(itemNameList)
   1551                         msg = 'ConNonEmptyShip' if evetypes.GetCategoryID(typeID) == const.categoryShip else 'ConNonEmptyContainer2'
   1552                         if prompt_player_fsd_dialog(msg, {'container': typeID,
   1553                          'items': itemsText,
   1554                          'volume': totalVolume}, uiconst.YESNO, suppress=uiconst.ID_YES) != uiconst.ID_YES:
   1555                             return False
   1556 
   1557         if contractType == const.conTypeCourier:
   1558             volume = 0
   1559             for i in self.data.items:
   1560                 evetypes.RaiseIFNotExists(self.data.items[i][0])
   1561                 item = self.data.items[i][2]
   1562                 volume += GetItemVolume(item, int(self.data.items[i][1]))
   1563 
   1564             if volume > const_conCourierMaxVolume:
   1565                 raise UserError('ConExceedsMaxVolume', {'max': const_conCourierMaxVolume,
   1566                  'vol': volume})
   1567             elif volume > const_conCourierWarningVolume:
   1568                 if prompt_player_fsd_dialog('ConNeedFreighter', {'vol': volume}, uiconst.YESNO, suppress=uiconst.ID_YES) != uiconst.ID_YES:
   1569                     return False
   1570         if not self.data.startStation or self.data.startStation == 0:
   1571             raise UserError('CustomInfo', {'info': GetByLabel('UI/Contracts/ContractsService/UserErrorSelectStartingPointForContract')})
   1572         elif contractType in [const.conTypeCourier, const.conTypeAuction] and len(self.data.items) == 0:
   1573             raise UserError('CustomInfo', {'info': GetByLabel('UI/Contracts/ContractsService/UserErrorSelectItemsForContract')})
   1574         if contractType in [const.conTypeAuction, const.conTypeItemExchange]:
   1575             insuranceContracts = []
   1576             for i in self.data.items:
   1577                 item = self.data.items[i]
   1578                 categoryID = evetypes.GetCategoryID(item[0])
   1579                 if item[2].singleton and categoryID == const.categoryShip:
   1580                     contract = sm.GetService('insurance').GetContractForShip(i)
   1581                     if contract:
   1582                         insuranceContracts.append([contract, item[0]])
   1583 
   1584             for contract in insuranceContracts:
   1585                 example = contract[1]
   1586                 if (contract[0].ownerID == session.corpid or self.data.forCorp) and self.data.avail != const.conAvailPublic:
   1587                     if prompt_player_fsd_dialog('ConInsuredShipCorp', {'example': example}, uiconst.YESNO) != uiconst.ID_YES:
   1588                         doContinue = False
   1589                         return doContinue
   1590                 elif prompt_player_fsd_dialog('ConInsuredShip', {'example': example}, uiconst.YESNO) != uiconst.ID_YES:
   1591                     return False
   1592 
   1593             for i in self.data.items:
   1594                 item = self.data.items[i]
   1595                 categoryID = evetypes.GetCategoryID(item[0])
   1596                 if categoryID == const.categoryShip and item[2].singleton:
   1597                     sm.GetService('invCache').RemoveInventoryContainer(item[2])
   1598 
   1599         self.LockItems(self.data.items)
   1600         return True
   1601 
   1602     def FinishStep2(self):
   1603         if hasattr(self.data, 'price'):
   1604             self.data.price = int(self.data.price)
   1605         if hasattr(self.data, 'reward'):
   1606             self.data.reward = int(self.data.reward)
   1607         if hasattr(self.data, 'collateral'):
   1608             self.data.collateral = int(self.data.collateral)
   1609         if len(self.data.description) > MAX_TITLE_LENGTH:
   1610             errorLabel = GetByLabel('UI/Contracts/ContractsService/UserErrorContractTitleTooLong', length=len(self.data.description), maxLength=MAX_TITLE_LENGTH)
   1611             raise UserError('CustomInfo', {'info': errorLabel})
   1612         if self.data.description:
   1613             bannedwords.check_words_allowed(self.data.description)
   1614         contractType = self.data.type
   1615         if contractType == const.conTypeAuction:
   1616             self.FinishStep2_AuctionContracts()
   1617         elif contractType == const.conTypeCourier:
   1618             doContinue = self.FinishStep2_CourierContract()
   1619             if not doContinue:
   1620                 return False
   1621         elif self.IsFittedShipContract(contractType):
   1622             self.FinishStep2_FittedShip()
   1623         elif contractType == const.conTypeItemExchange:
   1624             self.FinishStep2_ItemExchangeContract()
   1625         else:
   1626             raise RuntimeError('Not implemented!')
   1627         return True
   1628 
   1629     def FinishStep2_AuctionContracts(self):
   1630         if self.data.price < const_conBidMinimum and self.data.avail == 0:
   1631             errorLabel = GetByLabel('UI/Contracts/ContractsWindow/ErrorMinimumBidTooLow', minimumBid=const_conBidMinimum)
   1632             raise UserError('CustomInfo', {'info': errorLabel})
   1633         elif self.data.price > self.data.collateral and self.data.collateral > 0:
   1634             errorLabel = GetByLabel('UI/Contracts/ContractsService/UserErrorCannotSpecifyBidOverBuyout')
   1635             raise UserError('CustomInfo', {'info': errorLabel})
   1636 
   1637     def FinishStep2_CourierContract(self):
   1638         if not self.data.endStation and len(self.form.sr.endStationName.GetValue()) > 0:
   1639             self.SearchStationFromEdit(self.form.sr.endStationName)
   1640             if not self.data.endStation:
   1641                 return False
   1642         if not self.data.endStation:
   1643             errorLabel = GetByLabel('UI/Contracts/ContractsService/UserErrorMustSpecifyContractDestination')
   1644             raise UserError('CustomInfo', {'info': errorLabel})
   1645         if not self.data.assigneeID:
   1646             if self.data.reward < MIN_CONTRACT_MONEY:
   1647                 errorLabel = GetByLabel('UI/Contracts/ContractsService/UserErrorMinimumRewardNotMet', minimum=MIN_CONTRACT_MONEY)
   1648                 raise UserError('CustomInfo', {'info': errorLabel})
   1649             if self.data.collateral < MIN_CONTRACT_MONEY:
   1650                 errorLabel = GetByLabel('UI/Contracts/ContractsService/UserErrorMinimumCollateralNotMet', minimum=MIN_CONTRACT_MONEY)
   1651                 raise UserError('CustomInfo', {'info': errorLabel})
   1652         return True
   1653 
   1654     def FinishStep2_ItemExchangeContract(self):
   1655         if self.data.price != 0.0 and self.data.reward != 0.0:
   1656             errorLabel = GetByLabel('UI/Contracts/ContractsService/UserErrorCannotGiveAndReceiveISK')
   1657             raise UserError('CustomInfo', {'info': errorLabel})
   1658         if not self.data.assigneeID:
   1659             if self.data.reqItems == {} and self.data.price < MIN_CONTRACT_MONEY:
   1660                 errorLabel = GetByLabel('UI/Contracts/ContractsService/UserErrorExchangeContractRequestNotMet', minimum=MIN_CONTRACT_MONEY)
   1661                 raise UserError('CustomInfo', {'info': errorLabel})
   1662         if self.reqItemTypeWnd.GetValue() != '':
   1663             errorLabel = GetByLabel('UI/Contracts/ContractsService/UserErrorMustClickAddItem')
   1664             raise UserError('CustomInfo', {'info': errorLabel})
   1665         if not self.data.assigneeID:
   1666             if (self.data.reqItems != {} or self.data.price > 0) and len(self.data.items) == 0 and self.data.reward == 0:
   1667                 errorLabel = GetByLabel('UI/Contracts/ContractsService/UserErrorCannotCreateOneSidedExchangeContract')
   1668                 raise UserError('CustomInfo', {'info': errorLabel})
   1669 
   1670     def FinishStep2_FittedShip(self):
   1671         if getattr(self.data, 'reward', 0):
   1672             raise UserError('ConMultiContracCannotOfferIsk')
   1673         if self.data.reqItems:
   1674             raise UserError('ConMultiContractCannotAskForItems')
   1675         if not self.data.assigneeID:
   1676             if self.data.price < MIN_CONTRACT_MONEY:
   1677                 errorLabel = GetByLabel('UI/Contracts/ContractsService/UserErrorExchangeContractRequestNotMet', minimum=MIN_CONTRACT_MONEY)
   1678                 raise UserError('CustomInfo', {'info': errorLabel})
   1679         if not len(self.data.items):
   1680             raise UserError('CustomInfo', {'info': 'You need to include at least 1 ship'})
   1681 
   1682     def OnComboChange(self, wnd, *args):
   1683         if wnd.name == 'startStation' or wnd.name == 'startStationDivision':
   1684             if wnd.name == 'startStation':
   1685                 self.data.startStation = wnd.GetValue()
   1686             else:
   1687                 self.data.startStationDivision = wnd.GetValue()
   1688             self.data.items = {}
   1689             self.GotoPage(self.currPage)
   1690 
   1691     def OnStepChange(self, move, *args):
   1692         if self.currPage + move >= 4:
   1693             if prompt_player_fsd_dialog('ConConfirmCreateContract', {}, uiconst.YESNO, suppress=uiconst.ID_YES) == uiconst.ID_YES:
   1694                 try:
   1695                     self.CreateContract()
   1696                 except:
   1697                     self.Maximize()
   1698                     raise
   1699 
   1700         elif self.currPage + move >= 0 and (move < 0 or self.FinishStep(self.currPage)):
   1701             self.GotoPage(self.currPage + move)
   1702 
   1703     def ClickItem(self, *args):
   1704         pass
   1705 
   1706     def OnDeleteContracts(self, *args):
   1707         self.numOutstandingContracts = None
   1708 
   1709     def OnCancel(self, *args):
   1710         if prompt_player_fsd_dialog('ConAreYouSureYouWantToCancel', None, uiconst.YESNO, suppress=uiconst.ID_YES) == uiconst.ID_YES:
   1711             self.Close()
   1712 
   1713     def ResetWizard(self):
   1714         self.data = utillib.KeyVal()
   1715         self.data.items = {}
   1716         self.data.reqItems = {}
   1717         self.data.startStation = None
   1718         self.data.endStation = None
   1719         self.data.startStationDivision = 0
   1720         self.data.type = const.conTypeNothing
   1721         self.SetMainIcon(GetContractIcon(const.conTypeNothing))
   1722 
   1723     def WriteSelectItems(self, title):
   1724         contractType = self.data.type
   1725         forCorp = self.data.forCorp
   1726         deliveriesRoles = const.corpRoleAccountant | const.corpRoleJuniorAccountant | const.corpRoleTrader
   1727         if self.IsFittedShipContract(contractType):
   1728             currentNum, maxNumContracts = self.GetNumContractsAndLimits(forCorp, getattr(self.data, 'assigneeID', 0))
   1729             maxMultiContracts = GetMaxShipsToContract(sm.GetService('machoNet'))
   1730             maxMultiContract = min(maxMultiContracts, maxNumContracts - currentNum)
   1731             text = GetByLabel('UI/Contracts/ContractsWindow/ItemExchangeSelectShips', numShips=maxMultiContract)
   1732             eveLabel.EveLabelMedium(text=text, parent=self.formWnd, state=uiconst.UI_DISABLED, align=uiconst.TOTOP, padding=(7, 0, 7, 0))
   1733             itemIDsToRemove = set()
   1734             for itemID, item in self.data.items.iteritems():
   1735                 itemRow = item[2]
   1736                 if evetypes.GetCategoryID(item[0]) != const.categoryShip or not itemRow.singleton:
   1737                     itemIDsToRemove.add(itemID)
   1738 
   1739             for eachItemID in itemIDsToRemove:
   1740                 self.data.items.pop(eachItemID)
   1741 
   1742         if forCorp:
   1743             rows = sm.RemoteSvc('corpmgr').GetAssetInventory(session.corpid, 'offices')
   1744             sortops = []
   1745             for r in rows:
   1746                 stationName = cfg.evelocations.Get(r.locationID).name
   1747                 sortops.append((stationName, (stationName, r.locationID)))
   1748 
   1749             if session.corprole & deliveriesRoles > 0:
   1750                 rows = sm.RemoteSvc('corpmgr').GetAssetInventory(session.corpid, 'deliveries')
   1751                 for r in rows:
   1752                     stationName = cfg.evelocations.Get(r.locationID).name
   1753                     stat = (stationName, (stationName, r.locationID))
   1754                     if stat not in sortops:
   1755                         sortops.append(stat)
   1756 
   1757             ops = [(title, 0)] + SortListOfTuples(sortops)
   1758         else:
   1759             stations = sm.GetService('invCache').GetInventory(const.containerGlobal).ListStations()
   1760             primeloc = []
   1761             for station in stations:
   1762                 primeloc.append(station.stationID)
   1763 
   1764             if len(primeloc):
   1765                 cfg.evelocations.Prime(primeloc)
   1766             sortops = []
   1767             for station in stations:
   1768                 stationName = cfg.evelocations.Get(station.stationID).name
   1769                 itemCount = station.itemCount
   1770                 if station.stationID in (session.stationid, session.structureid):
   1771                     itemCount -= 1
   1772                 itemsInStationLabel = GetByLabel('UI/Contracts/ContractsService/ItemsInStation', station=station.stationID, numItems=itemCount)
   1773                 sortops.append((stationName.lower(), (itemsInStationLabel, station.stationID)))
   1774 
   1775             ops = [(title, 0)] + SortListOfTuples(sortops)
   1776         comboParent = Container(name='comboParent', parent=self.formWnd, align=uiconst.TOTOP, top=const.defaultPadding)
   1777         Container(name='push', parent=comboParent, align=uiconst.TOLEFT, width=1)
   1778         Container(name='push', parent=comboParent, align=uiconst.TORIGHT, width=1)
   1779         combo = Combo(label=None, parent=comboParent, options=ops, name='startStation', select=None, callback=self.OnComboChange, align=uiconst.TOALL)
   1780         comboParent.height = Combo.default_height
   1781         if self.data.startStation is None:
   1782             if session.stationid:
   1783                 self.data.startStation = session.stationid
   1784             elif session.structureid:
   1785                 self.data.startStation = session.structureid
   1786         if forCorp:
   1787             divs = [(GetByLabel('UI/Contracts/ContractsWindow/SelectDivision'), 0)]
   1788             divisions = sm.GetService('corp').GetDivisionNames()
   1789             for flagID in const.flagCorpSAGs:
   1790                 divisionID = const.corpDivisionsByFlag[flagID]
   1791                 divisionName = divisions[divisionID + 1]
   1792                 divs.append((divisionName, flagID))
   1793 
   1794             if session.corprole & deliveriesRoles > 0:
   1795                 divs.append((GetByLabel('UI/Contracts/ContractsWindow/Deliveries'), const.flagCorpDeliveries))
   1796             comboDiv = Combo(label='', parent=comboParent, options=divs, name='startStationDivision', select=None, callback=self.OnComboChange, align=uiconst.TORIGHT, width=100, pos=(1, 0, 0, 0), idx=0)
   1797             Container(name='push', parent=comboParent, align=uiconst.TORIGHT, width=4, idx=1)
   1798             nudge = 18
   1799         for i, entry in enumerate(combo.entries):
   1800             if entry.returnValue == self.data.startStation:
   1801                 combo.SelectItemByIndex(i)
   1802                 break
   1803 
   1804         if forCorp:
   1805             for i, entry in enumerate(comboDiv.entries):
   1806                 if entry.returnValue == self.data.startStationDivision:
   1807                     comboDiv.SelectItemByIndex(i)
   1808                     break
   1809 
   1810         c = Container(name='volume', parent=self.formWnd, align=uiconst.TOBOTTOM, width=6, height=20)
   1811         numVolLabel = GetByLabel('UI/Contracts/ContractsService/NumberOfWithVolume', number=0, volume=0)
   1812         self.sr.volumeText = eveLabel.EveLabelMedium(text=numVolLabel, parent=c, state=uiconst.UI_DISABLED, align=uiconst.CENTERRIGHT)
   1813         self.UpdateNumberOfItems()
   1814         self.sr.itemsScroll = eveScroll.Scroll(parent=self.formWnd, padTop=const.defaultPadding, padBottom=const.defaultPadding)
   1815         self.toggleSelectAllCheckbox = Checkbox(parent=self.sr.itemsScroll, align=uiconst.TOPLEFT, checked=0, left=7, callback=self.OnSelectAllCheckboxClicked, idx=0, top=-1)
   1816         self.toggleSelectAllCheckbox.display = False
   1817         if forCorp:
   1818             if not self.data.startStation:
   1819                 self.sr.itemsScroll.ShowHint(GetByLabel('UI/Search/SelectStation'))
   1820                 if self.data.startStationDivision == 0:
   1821                     self.sr.itemsScroll.ShowHint(GetByLabel('UI/Contracts/ContractEntry/SelectStationDivision'))
   1822             elif self.data.startStationDivision == 0:
   1823                 self.sr.itemsScroll.ShowHint(GetByLabel('UI/Contracts/ContractsWindow/SelectDivision'))
   1824         validStartStation = self.data.startStation and self.CanContractItemsFromLocation(self.data.startStation)
   1825         if validStartStation and (not forCorp or self.data.startStationDivision):
   1826             self.sr.itemsScroll.Load(contentList=[], headers=[], noContentHint=GetByLabel('UI/Contracts/ContractsWindow/NoItemsFound'))
   1827             self.sr.itemsScroll.ShowHint(GetByLabel('UI/Contracts/ContractsWindow/GettingItems'))
   1828             items = sm.GetService('contracts').GetItemsInDockableLocation(self.data.startStation, forCorp)
   1829             self.sr.itemsScroll.hiliteSorted = 0
   1830             scrolllist = []
   1831 
   1832             def sameFlag(itemFlag):
   1833                 if itemFlag == self.data.startStationDivision:
   1834                     return True
   1835                 if set((itemFlag, self.data.startStationDivision)) == set((const.flagHangar, const.flagCorpSAG1)):
   1836                     return True
   1837                 return False
   1838 
   1839             isFittedShipContract = self.IsFittedShipContract(contractType)
   1840             uix.PrimeEveLocationsBeforeGetItemName(items)
   1841             contractType = self.data.type
   1842             for item in items:
   1843                 if forCorp and not sameFlag(item.flagID):
   1844                     continue
   1845                 data = uix.GetItemData(item, 'list', viewOnly=1)
   1846                 volume = GetItemVolume(item)
   1847                 itemName = ''
   1848                 isContainer = item.groupID in (const.groupCargoContainer,
   1849                  const.groupSecureCargoContainer,
   1850                  const.groupAuditLogSecureContainer,
   1851                  const.groupFreightContainer) and item.singleton
   1852                 if item.categoryID == const.categoryShip or isContainer:
   1853                     if item.itemID == eveCfg.GetActiveShip():
   1854                         continue
   1855                     shipName = cfg.evelocations.GetIfExists(item.itemID)
   1856                     if shipName is not None:
   1857                         itemName = shipName.locationName
   1858                 if isFittedShipContract and item.categoryID != const.categoryShip:
   1859                     continue
   1860                 scrolllist.append(GetFromClass(ContractItemSelect, {'info': item,
   1861                  'stationID': self.data.startStation,
   1862                  'forCorp': forCorp,
   1863                  'flag': item.flagID,
   1864                  'itemID': item.itemID,
   1865                  'typeID': item.typeID,
   1866                  'isCopy': item.categoryID == const.categoryBlueprint and item.singleton == const.singletonBlueprintCopy,
   1867                  'label': '%s<t>%s<t>%s<t>%s' % (evetypes.GetName(item.typeID),
   1868                            item.stacksize,
   1869                            volume,
   1870                            itemName),
   1871                  'quantity': item.stacksize,
   1872                  'getIcon': 1,
   1873                  'item': item,
   1874                  'checked': item.itemID in self.data.items,
   1875                  'cfgname': item.itemID,
   1876                  'itemData': (item,
   1877                               item.itemID,
   1878                               item.typeID,
   1879                               item.stacksize),
   1880                  'OnChange': self.OnItemSelectedChanged,
   1881                  'GetSortValue': self.GetItemNodeSortValue}))
   1882 
   1883             if self.sr.itemsScroll is not None:
   1884                 if scrolllist:
   1885                     self.sr.itemsScroll.ShowHint()
   1886                 self.sr.itemsScroll.sr.id = 'itemsScroll'
   1887                 self.sr.itemsScroll.sr.lastSelected = None
   1888                 headers = [GetByLabel('UI/Common/Type'),
   1889                  GetByLabel('UI/Inventory/ItemQuantityShort'),
   1890                  GetByLabel('UI/Common/Volume'),
   1891                  GetByLabel('UI/Common/Details')]
   1892                 if combo.GetValue() == 0:
   1893                     noContentHint = GetByLabel('UI/Search/SelectStation')
   1894                 elif isFittedShipContract:
   1895                     noContentHint = GetByLabel('UI/Contracts/ContractsWindow/NoShipFound')
   1896                 else:
   1897                     noContentHint = GetByLabel('UI/Contracts/ContractsWindow/NoItemsFound')
   1898                 self.sr.itemsScroll.GetEffectiveColumnOffset = lambda *args: 24
   1899                 self.toggleSelectAllCheckbox.display = bool(scrolllist)
   1900                 self.sr.itemsScroll.Load(contentList=scrolllist, headers=headers, noContentHint=noContentHint)
   1901                 self.UpdateSelectAllCheckbox()
   1902 
   1903     def GetItemNodeSortValue(self, node, by, sortDirection, idx):
   1904         ret = self.sr.itemsScroll._GetSortValue(by, node, idx)
   1905         if node.checked:
   1906             return (sortDirection, ret)
   1907         return (not sortDirection, ret)
   1908 
   1909     def IsFittedShipContract(self, contractType):
   1910         if not GetMaxShipsToContract(sm.GetService('machoNet')):
   1911             return False
   1912         return contractType == const.conTypeItemExchange and self.isMultiContract
   1913 
   1914     def CanContractItemsFromLocation(self, locationID):
   1915         if idCheckers.IsStation(locationID):
   1916             return True
   1917         return sm.GetService('structureDirectory').CanContractFrom(locationID)
   1918 
   1919     def UpdateSelectAllCheckbox(self):
   1920         self.UpdateSelectAllCheckbox_throttled()
   1921 
   1922     @throttled(0.5)
   1923     def UpdateSelectAllCheckbox_throttled(self):
   1924         if not self or self.destroyed or not self.sr.itemsScroll or self.sr.itemsScroll.destroyed:
   1925             return
   1926         selectedValues = {x.checked for x in self.sr.itemsScroll.GetNodes()}
   1927         if selectedValues == {1}:
   1928             self.toggleSelectAllCheckbox.SetChecked(True, False)
   1929             self.toggleSelectAllCheckbox.hint = GetByLabel('UI/Common/DeselectAll')
   1930         else:
   1931             self.toggleSelectAllCheckbox.SetChecked(False, False)
   1932             self.toggleSelectAllCheckbox.hint = GetByLabel('UI/Common/SelectAll')
   1933 
   1934     def OnSelectAllCheckboxClicked(self, cb, *args):
   1935         if cb.GetValue():
   1936             self.SelectAll()
   1937         else:
   1938             self.DeselectAll()
   1939         self.UpdateSelectAllCheckbox()
   1940 
   1941     def DeselectAll(self, *args):
   1942         self.ChangeCheckboxStates(onoff=False)
   1943 
   1944     def SelectAll(self, *args):
   1945         self.ChangeCheckboxStates(onoff=True)
   1946 
   1947     def ChangeCheckboxStates(self, onoff):
   1948         if not self.sr.itemsScroll:
   1949             return
   1950         for node in self.sr.itemsScroll.GetNodes():
   1951             if node:
   1952                 node.checked = onoff
   1953                 if node.panel:
   1954                     node.panel.sr.checkbox.SetChecked(onoff, 0)
   1955                 self.ChangeItemSelection(onoff, updateNumber=False, *node.itemData)
   1956 
   1957         self.UpdateNumberOfItems()
   1958 
   1959     def OnItemSelectedChanged(self, isChecked, itemData, *args):
   1960         item, itemID, typeID, qty = itemData
   1961         self.ChangeItemSelection(isChecked, item, itemID, typeID, qty)
   1962         self.UpdateSelectAllCheckbox()
   1963 
   1964     def ChangeItemSelection(self, checkboxSelected, item, itemID, typeID, qty, updateNumber = True):
   1965         if checkboxSelected:
   1966             self.data.items[itemID] = [typeID, qty, item]
   1967         elif itemID in self.data.items:
   1968             del self.data.items[itemID]
   1969         if updateNumber:
   1970             self.UpdateNumberOfItems()
   1971 
   1972     def UpdateNumberOfItems(self):
   1973         num, totalVolume = self.GetVolumeOfAllItems()
   1974         numVolLabel = GetByLabel('UI/Contracts/ContractsService/NumberOfWithVolume', number=num, volume=FmtAmt(totalVolume))
   1975         maxItems = MAX_NUM_ITEMS
   1976         if self.IsFittedShipContract(self.data.type):
   1977             maxItems = GetMaxShipsToContract(sm.GetService('machoNet'))
   1978             currentNum, maxNumContracts = self.GetNumContractsAndLimits(self.data.forCorp, getattr(self.data, 'assigneeID', 0))
   1979             maxItems = min(maxItems, maxNumContracts - currentNum)
   1980         if num > maxItems:
   1981             numVolLabel = '<color=red>' + numVolLabel + '</color>'
   1982         self.sr.volumeText.text = numVolLabel
   1983 
   1984     def GetVolumeOfAllItems(self):
   1985         totalVolume = 0
   1986         num = 0
   1987         for itemID, item in self.data.items.iteritems():
   1988             totalVolume += GetItemVolume(item[2])
   1989             num += 1
   1990 
   1991         return (num, totalVolume)
   1992 
   1993     def GetTypePrice(self, typeID):
   1994         return GetAveragePrice(typeID) or evetypes.GetBasePrice(typeID)
   1995 
   1996     def CalcEstPrice(self):
   1997         price = 0
   1998         for item in self.data.items.itervalues():
   1999             price += int(float(self.GetTypePrice(item[0])) / evetypes.GetPortionSize(item[0]) * item[1])
   2000 
   2001         price = RoundISK(price)
   2002         if price == 0:
   2003             raise UserError('ConNoEstPrice')
   2004         return price
   2005 
   2006     def SetCollateralToEstPrice(self, *args):
   2007         price = self.CalcEstPrice()
   2008         if prompt_player_fsd_dialog('ConEstPrice', {'price': FmtISK(price, 0)}, uiconst.YESNO, suppress=uiconst.ID_YES) == uiconst.ID_YES:
   2009             self.formWnd.sr.collateral.SetValue(price)
   2010 
   2011     def SetPriceToEstPrice(self, *args):
   2012         price = self.CalcEstPrice()
   2013         if prompt_player_fsd_dialog('ConEstPrice', {'price': FmtISK(price, 0)}, uiconst.YESNO, suppress=uiconst.ID_YES) == uiconst.ID_YES:
   2014             self.formWnd.sr.price.SetValue(price)
   2015 
   2016     def OnItemSplit(self, itemID, amountRemoved):
   2017         if itemID in self.data.items:
   2018             self.data.items[itemID][1] -= amountRemoved
   2019             if self.data.items[itemID][1] <= 0:
   2020                 del self.data.items[itemID]