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

sellMulti.py (28772B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\market\sellMulti.py
      2 import blue
      3 import eveLocalization
      4 import localization
      5 import uthread
      6 import uthread2
      7 from carbon.common.script.util.format import FmtAmt
      8 from carbon.common.script.util.mathCommon import FloatCloseEnough
      9 from carbonui import const as uiconst
     10 from carbonui.control.combo import Combo
     11 from carbonui.primitives.container import Container
     12 from carbonui.primitives.layoutGrid import LayoutGrid
     13 from carbonui.uicore import uicore
     14 from carbonui.util.various_unsorted import GetClipboardData
     15 from eve.client.script.ui import eveColor
     16 from eve.client.script.ui.control.eveLabel import EveLabelSmall, EveLabelMedium, EveLabelMediumBold, EveLabelLargeBold
     17 from eve.client.script.ui.control.eveLoadingWheel import LoadingWheel
     18 from eve.client.script.ui.services.menuSvcExtras.invItemFunctions import CheckRepackageItems
     19 from eve.client.script.ui.shared.market import INVENTORY_GUIDS
     20 from eve.client.script.ui.shared.market.buySellMultiBase import SellBuyItemsWindow, COL_RED, COL_GREEN, COL_WHITE
     21 from eve.client.script.ui.shared.market.sellItemEntry import SellItemContainer
     22 from eve.client.script.ui.tooltips.tooltipUtil import SetTooltipHeaderAndDescription
     23 from eve.client.script.ui.util.qtyTooltipUtil import AssignQtyTooltipFunc
     24 from eve.common.script.sys.idCheckers import IsStation
     25 from eve.common.script.util.eveFormat import FmtISK
     26 from eveexceptions import UserError
     27 from inventorycommon import const as invconst
     28 from inventorycommon.const import flagCorpSAGs, flagHangar, typePlex
     29 from localization import GetByLabel
     30 from marketutil.const import MAX_ORDER_PRICE
     31 from menucheckers import ItemChecker
     32 from menucheckers.sessionChecker import SessionChecker
     33 from player_messaging.client.ui_message import message_player, prompt_player_fsd_dialog
     34 from plex.common.const import InvoiceType
     35 from qtyTooltip.qtyConst import EDIT_INPUT_TYPE_FLOAT
     36 from textImporting import GetNameFunc, CleanText
     37 from textImporting.importMultiSell import ImportMultiSell
     38 from utillib import KeyVal
     39 from vgs.common.listeners import PlexWithdrawalListener
     40 from eveuniverse.security import get_solar_system_security_class, securityClassSafeSec
     41 from security.common.const import MARKET_SECURITY_TAX_RATE
     42 ACTION_ICON = 'res:/UI/Texture/classes/UtilMenu/BulletIcon.png'
     43 
     44 class SellItems(SellBuyItemsWindow):
     45     default_windowID = 'SellItemsWindow'
     46     default_name = 'SellItems'
     47     default_iconNum = 'res:/ui/Texture/WindowIcons/market.png'
     48     captionTextPath = 'UI/Inventory/ItemActions/MultiSell'
     49     scrollId = 'MultiSellScroll'
     50     durationSettingConfig = 'multiSellDuration'
     51     tradeForCorpSettingConfig = 'sellUseCorp'
     52     tradeTextPath = 'UI/Market/MarketQuote/CommandSell'
     53     orderCap = 'MultiSellOrderCap'
     54     badDeltaWarningPath = 'UI/Market/MarketQuote/MultiSellTypesBelowAverage'
     55     salesButtonCantAffordBrokersFeeTooltipHeaderLabelPath = 'UI/Market/MarketQuote/SalesButtonCantAffordBrokersFeeTooltipHeader'
     56     salesButtonCantAffordBrokersFeeDescriptionLabelPath = 'UI/Market/MarketQuote/SalesButtonCantAffordBrokersFeeTooltipDescription'
     57     importHintLabelPath = 'UI/Market/MarketQuote/ImportPrices'
     58     exportHintLabelPath = 'UI/Market/MarketQuote/ExportItems'
     59 
     60     def __init__(self, **kwargs):
     61         self.orderCountLabel = None
     62         self.maxCount = None
     63         self.myOrderCount = None
     64         self.itemDict = {}
     65         self.sellItemList = None
     66         self.itemAlreadyInList = None
     67         self.baseStationID = None
     68         self.durationCombo = None
     69         self.errorItemList = None
     70         self.itemsNeedRepackaging = []
     71         super(SellItems, self).__init__(**kwargs)
     72 
     73     def ApplyAttributes(self, attributes):
     74         super(SellItems, self).ApplyAttributes(attributes)
     75         top = 62 if self.useCorp else 44
     76         self.orderCountLabel = EveLabelSmall(parent=self.bottomLeft, top=top, left=2)
     77         self.maxCount = self.GetMaxOrderCount()
     78         self.myOrderCount = self.GetCurrentOrderCount()
     79         self.UpdateOrderCount()
     80         self.DrawDurationCombo()
     81         self.StartAddItemsThread()
     82 
     83     def InitializeVariables(self, attributes):
     84         SellBuyItemsWindow.InitializeVariables(self, attributes)
     85         self.itemDict = {}
     86         self.sellItemList = []
     87         self.itemAlreadyInList = []
     88         self.itemsNeedRepackaging = []
     89 
     90     def DrawNumbers(self):
     91         self.numbersGrid = LayoutGrid(parent=self.bottomRight, columns=2, align=uiconst.TORIGHT, top=self.numbersGridTop, state=uiconst.UI_PICKCHILDREN)
     92         self.sccRow = self.numbersGrid.AddRow(name='sccRow')
     93         self.sccSurchargeLabel = EveLabelMedium(parent=self.sccRow, text='', padRight=4, state=uiconst.UI_NORMAL)
     94         self.sccSurchargeAmtLabel = EveLabelMediumBold(parent=self.sccRow, text='', align=uiconst.CENTERRIGHT, padLeft=4, state=uiconst.UI_NORMAL)
     95         self.brokersFee = EveLabelMedium(text='', padRight=4, state=uiconst.UI_NORMAL)
     96         self.numbersGrid.AddCell(self.brokersFee)
     97         self.brokersFeeAmt = EveLabelMediumBold(text='', align=uiconst.CENTERRIGHT, padLeft=4, state=uiconst.UI_NORMAL)
     98         self.numbersGrid.AddCell(self.brokersFeeAmt)
     99         self.salesTax = EveLabelMedium(text='', padRight=4, state=uiconst.UI_NORMAL)
    100         self.numbersGrid.AddCell(self.salesTax)
    101         self.salesTaxAmt = EveLabelMediumBold(text='', align=uiconst.CENTERRIGHT, padLeft=4, state=uiconst.UI_NORMAL)
    102         self.numbersGrid.AddCell(self.salesTaxAmt)
    103         self.securityTax = EveLabelMedium(text='', padRight=4, state=uiconst.UI_NORMAL)
    104         self.numbersGrid.AddCell(self.securityTax)
    105         self.securityTaxAmt = EveLabelMediumBold(text='', align=uiconst.CENTERRIGHT, padLeft=4, state=uiconst.UI_NORMAL)
    106         self.numbersGrid.AddCell(self.securityTaxAmt)
    107         self.SetupSalesTaxTooltips()
    108         self.SetupBrokersFeeTooltips()
    109         spacer = Container(align=uiconst.TOTOP, height=12)
    110         self.numbersGrid.AddCell(spacer, colSpan=2)
    111         self.totalAmt = EveLabelLargeBold(text='', align=uiconst.CENTERRIGHT, padLeft=4, state=uiconst.UI_NORMAL)
    112         self.numbersGrid.AddCell(self.totalAmt, colSpan=2)
    113 
    114     def AddPreItems(self, preItems):
    115         itemLocations = set([])
    116         preItems = self.CheckOrderAvailability(preItems)
    117         for item in preItems:
    118             itemStationID = sm.GetService('invCache').GetStationIDOfItem(item)
    119             itemLocations.add(itemStationID)
    120 
    121         if len(itemLocations) > 1:
    122             prompt_player_fsd_dialog('CustomNotify', {'notify': GetByLabel('UI/Market/MarketQuote/AddingFromDifferentLocations')})
    123             return
    124         for item in preItems:
    125             if self.CheckItemLocation(item):
    126                 self.AddItem(item)
    127 
    128     def CheckItemLocation(self, item):
    129         if not self.CheckStation(item) and len(self.itemList) > 0:
    130             prompt_player_fsd_dialog('CustomNotify', {'notify': GetByLabel('UI/Market/MarketQuote/LocationNotShared')})
    131             return False
    132         if item.flagID not in flagCorpSAGs + (flagHangar,):
    133             prompt_player_fsd_dialog('CustomNotify', {'notify': GetByLabel('UI/Market/MarketQuote/NotValidLocation')})
    134             return False
    135         return True
    136 
    137     def GetErrorHints(self):
    138         hintTextList = SellBuyItemsWindow.GetErrorHints(self)
    139         alreadyInList = self.BuildHintTextList(self.itemAlreadyInList, 'UI/Market/MarketQuote/AlreadyInList')
    140         if hintTextList and alreadyInList:
    141             hintTextList.append('')
    142         hintTextList += alreadyInList
    143         needsRepackaging = self.BuildHintTextList(self.itemsNeedRepackaging, 'UI/Market/MarketQuote/NeedsRepackaging')
    144         if hintTextList and needsRepackaging:
    145             hintTextList.append('')
    146         hintTextList += needsRepackaging
    147         return hintTextList
    148 
    149     def AddItems(self, items):
    150         self.ClearErrorLists()
    151         items = self.CheckOrderAvailability(items)
    152         sellItems = []
    153         OkWithDumpingStuff = None
    154         for item in items:
    155             if item.singleton and cfg.IsContainer(item):
    156                 try:
    157                     itemsInside = {i for i in sm.GetService('invCache').GetInventoryFromId(item.itemID).List() if i.flagID != invconst.flagHiddenModifers}
    158                 except UserError:
    159                     continue
    160 
    161                 if itemsInside:
    162                     if item.categoryID == invconst.categoryShip:
    163                         rigs = {i for i in itemsInside if const.flagRigSlot0 <= i.flagID <= const.flagRigSlot7}
    164                         if rigs:
    165                             self.itemsNeedRepackaging.append(item)
    166                             continue
    167                     if OkWithDumpingStuff is None:
    168                         if prompt_player_fsd_dialog('RepackageContainers', {}, uiconst.YESNO) != uiconst.ID_YES:
    169                             OkWithDumpingStuff = False
    170                             continue
    171                         OkWithDumpingStuff = True
    172                     if OkWithDumpingStuff is False:
    173                         continue
    174             sellItems.append(item)
    175 
    176         sellItems = CheckRepackageItems(sellItems, OkWithDumpingStuff is None)
    177         for item in sellItems:
    178             self.AddItem(item)
    179 
    180         self.DisplayErrorHints()
    181 
    182     def AddItem(self, item):
    183         plex_being_added = item.typeID == typePlex
    184         order_empty = len(self.itemList) == 0
    185         order_already_contains_plex = any((x.typeID == typePlex for x in self.itemList))
    186         if plex_being_added and order_already_contains_plex:
    187             prompt_player_fsd_dialog('StackPlexBeforeSelling')
    188             return
    189         if plex_being_added and not order_empty or order_already_contains_plex:
    190             prompt_player_fsd_dialog('CannotPutPlexInMarketMultiSell')
    191             return
    192         if not self.IsSellable(item):
    193             return
    194         self.itemDict[item.itemID] = item
    195         itemEntry = self.DoAddItem(item)
    196         if len(self.itemList) == 1:
    197             self.UpdateStationInfo(itemEntry.stationID)
    198         self.CheckItemSize()
    199         uicore.registry.SetFocus(itemEntry.priceEdit)
    200 
    201     def GetItemEntry(self, item):
    202         solarSystemID = self._GetSolarSystemIDForItem(item)
    203         marketQuote = sm.GetService('marketQuote')
    204         bestPrice = marketQuote.GetBestPrice(item.typeID, item, item.stacksize, solarSystemID)
    205         bestBid = marketQuote.GetBestBidWithStationID(item, locationID=solarSystemID)
    206         stationID = sm.GetService('invCache').GetStationIDOfItem(item)
    207         itemEntry = SellItemContainer(item=item, editFunc=self.OnEntryEdit, align=uiconst.TOTOP, parentFunc=self.RemoveItem, bestPrice=bestPrice, bestBid=bestBid, stationID=stationID, solarSystemID=solarSystemID, isReadOnly=self.isReadOnly)
    208         return itemEntry
    209 
    210     @staticmethod
    211     def _GetSolarSystemIDForItem(item):
    212         if IsStation(item.locationID):
    213             station = sm.StartService('ui').GetStationStaticInfo(item.locationID)
    214             return station.solarSystemID
    215         structureInfo = sm.GetService('structureDirectory').GetStructureInfo(item.locationID)
    216         if structureInfo and structureInfo.solarSystemID:
    217             return structureInfo.solarSystemID
    218         return session.solarsystemid2
    219 
    220     def AddItemToCollection(self, item, itemEntry):
    221         self.itemDict[item.itemID] = item
    222 
    223     def CheckItemSize(self):
    224         if not len(self.itemList):
    225             return
    226         firstItem = self.itemList[0]
    227         if len(self.itemList) == 1:
    228             firstItem.MakeSingle()
    229         elif len(self.itemList) == 2:
    230             firstItem.RemoveSingle()
    231 
    232     def UpdateStationInfo(self, stationID):
    233         self.baseStationID = stationID
    234         if self.baseStationID:
    235             self.UpdateHeaderCount()
    236         else:
    237             self.SetCaption(GetByLabel(self.captionTextPath))
    238         if not self.baseStationID or IsStation(self.baseStationID):
    239             self.sccRow.Hide()
    240         else:
    241             self.sccRow.Show()
    242 
    243     def RemoveItem(self, itemEntry):
    244         SellBuyItemsWindow.RemoveItem(self, itemEntry)
    245         self.CheckItemSize()
    246         if len(self.itemList) == 0:
    247             self.baseStationID = None
    248             self.UpdateStationInfo(None)
    249 
    250     def RemoveItemFromCollection(self, itemEntry):
    251         self.itemDict.pop(itemEntry.itemID)
    252 
    253     def CheckStation(self, item):
    254         itemStationID = sm.GetService('invCache').GetStationIDOfItem(item)
    255         if itemStationID != self.baseStationID:
    256             return False
    257         return True
    258 
    259     def ClearErrorLists(self):
    260         SellBuyItemsWindow.ClearErrorLists(self)
    261         self.itemsNeedRepackaging = []
    262         self.itemAlreadyInList = []
    263 
    264     def IsSellable(self, item):
    265         if item.itemID in self.itemDict:
    266             self.itemAlreadyInList.append(item)
    267             return False
    268         sessionChecker = SessionChecker(session, sm)
    269         itemChecker = ItemChecker(item, sessionChecker)
    270         if not itemChecker.OfferSellThisItem():
    271             self.cannotTradeItemList.append(item)
    272             return False
    273         return True
    274 
    275     def OnEntryEdit(self, *args):
    276         uthread.new(self.UpdateNumbers)
    277 
    278     def DropItems(self, dragObj, nodes):
    279         if not self.IsAllowedDragData(nodes):
    280             return
    281         if not self.CheckItemLocation(self.GetDragNodeItem(nodes[0])):
    282             return
    283         self.AddItems([ self.GetDragNodeItem(n) for n in nodes ])
    284 
    285     @staticmethod
    286     def GetDragNodeItem(node):
    287         if hasattr(node, 'WithdrawPlex') and callable(node.WithdrawPlex):
    288             item = node.item
    289             item.locationID = session.stationid or session.structureid
    290             item.flagID = invconst.flagHangar
    291             return item
    292         else:
    293             return node.item
    294 
    295     def DrawDurationCombo(self):
    296         if self.isReadOnly:
    297             durations = [[GetByLabel('UI/Market/MarketQuote/Immediate'), 0]]
    298         else:
    299             durations = [[GetByLabel('UI/Market/MarketQuote/Immediate'), 0],
    300              [GetByLabel('UI/Common/DateWords/Day'), 1],
    301              [GetByLabel('UI/Market/MarketQuote/ThreeDays'), 3],
    302              [GetByLabel('UI/Common/DateWords/Week'), 7],
    303              [GetByLabel('UI/Market/MarketQuote/TwoWeeks'), 14],
    304              [GetByLabel('UI/Common/DateWords/Month'), 30],
    305              [GetByLabel('UI/Market/MarketQuote/ThreeMonths'), 90]]
    306         durationValue = settings.user.ui.Get(self.durationSettingConfig, 0)
    307         self.durationCombo = Combo(parent=self.bottomLeft, options=durations, select=durationValue, top=6, callback=self.OnDurationChange, maxVisibleEntries=len(durations))
    308 
    309     def UpdateNumbers(self):
    310         brokersFee, sccSurchargeFee, salesTax, totalSum = self.GetSums()
    311         totalShown = totalSum - salesTax - brokersFee - sccSurchargeFee
    312         if totalSum > 0:
    313             brokersPerc = round(brokersFee / totalSum * 100, 2)
    314             sccSurcharge = round(sccSurchargeFee / totalSum * 100, 2)
    315             salesPerc = round(salesTax / totalSum * 100, 2)
    316         else:
    317             brokersPerc = 0.0
    318             sccSurcharge = 0.0
    319             salesPerc = 0.0
    320         useCorp = self.TradingForCorp()
    321         walletAmount = sm.GetService('wallet').GetWealth()
    322         if useCorp:
    323             walletAmount = sm.GetService('wallet').GetCorpWealth(None)
    324         self.brokersFeeAmt.text = FmtISK(-brokersFee)
    325         self._ConfigureFeeLabelBasedOnFee(walletAmount, brokersFee, self.brokersFee, self.brokersFeeAmt)
    326         self.brokersFee.text = GetByLabel('UI/Market/MarketQuote/BrokersFeePerc', percentage=brokersPerc)
    327         self.sccSurchargeAmtLabel.text = FmtISK(-sccSurchargeFee)
    328         self._ConfigureFeeLabelBasedOnFee(walletAmount, sccSurchargeFee, self.sccSurchargeLabel, self.sccSurchargeAmtLabel)
    329         self.sccSurchargeLabel.text = GetByLabel('UI/Market/MarketQuote/SccSurchargeFeePerc', percentage=sccSurcharge)
    330         self._ConfigureTradeButtonBasedOnFees(walletAmount, brokersFee + sccSurchargeFee)
    331         if sccSurchargeFee:
    332             self.sccRow.Show()
    333         self.salesTaxAmt.text = FmtISK(-salesTax)
    334         self.salesTax.text = GetByLabel('UI/Market/MarketQuote/SalesTaxPerc', percentage=salesPerc)
    335         securityTax = 0.0
    336         isImmediate = self.durationCombo.GetValue() == 0
    337         if not isImmediate and totalSum > 0 and self.itemList:
    338             solarSystemID = self.itemList[0].solarSystemID
    339             if solarSystemID and get_solar_system_security_class(solarSystemID) == securityClassSafeSec:
    340                 securityTax = round(totalSum * MARKET_SECURITY_TAX_RATE, 2)
    341         totalShown -= securityTax
    342         if totalSum > 0:
    343             securityPerc = round(securityTax / totalSum * 100, 2)
    344         else:
    345             securityPerc = 0.0
    346         self.securityTax.text = '%s (%.2f%%)' % (GetByLabel('UI/Market/MarketQuote/MarketSecurityTax'), securityPerc)
    347         self.securityTaxAmt.text = FmtISK(-securityTax)
    348         self.totalAmt.text = FmtISK(totalShown)
    349         AssignQtyTooltipFunc(self.totalAmt, totalShown, inputType=EDIT_INPUT_TYPE_FLOAT)
    350         if totalShown < 0:
    351             self.totalAmt.SetRGB(*COL_RED)
    352         else:
    353             self.totalAmt.SetRGB(*COL_GREEN)
    354 
    355     def _ConfigureFeeLabelBasedOnFee(self, walletAmt, feeAmt, feeLabel, amtLabel):
    356         color = COL_RED if walletAmt < feeAmt else COL_WHITE
    357         feeLabel.SetRGB(*color)
    358         amtLabel.SetRGB(*color)
    359 
    360     def _ConfigureTradeButtonBasedOnFees(self, walletAmt, feeAmt):
    361         if walletAmt < feeAmt:
    362             cantAffordText = GetByLabel(self.salesButtonCantAffordBrokersFeeDescriptionLabelPath, walletBalance=walletAmt, brokersFee=feeAmt)
    363             SetTooltipHeaderAndDescription(self.tradeButton, GetByLabel(self.salesButtonCantAffordBrokersFeeTooltipHeaderLabelPath), cantAffordText)
    364             self.tradeButton.Disable()
    365         else:
    366             self.tradeButton.Enable()
    367             self.tradeButton.tooltipPanelClassInfo = None
    368 
    369     def GetSums(self):
    370         brokersFee = 0.0
    371         sccSurcharge = 0.0
    372         salesTax = 0
    373         totalSum = 0
    374         isImmediate = self.durationCombo.GetValue() == 0
    375         for item in self.GetItems():
    376             if item:
    377                 if not isImmediate:
    378                     brokersFee += item.brokersFee
    379                     sccSurcharge += item.sccSurcharge
    380                 if isImmediate and item.bestBid is None:
    381                     pass
    382                 else:
    383                     salesTax += item.salesTax
    384                 totalSum += item.totalSum
    385 
    386         return (brokersFee,
    387          sccSurcharge,
    388          salesTax,
    389          totalSum)
    390 
    391     def PerformTrade(self, *args):
    392         return self._PerformTrade()
    393 
    394     def _PerformTrade(self):
    395         self.sellItemList = []
    396         unitCount = self.GetUnitCount()
    397         allItems = self.GetItems()
    398         if unitCount == 0:
    399             prompt_player_fsd_dialog('uiwarning03')
    400             return
    401         if prompt_player_fsd_dialog('ConfirmSellingItems', {'noOfItems': int(unitCount)}, uiconst.OKCANCEL, suppress=uiconst.ID_OK) != uiconst.ID_OK:
    402             return
    403         self.errorItemList = []
    404         useCorp = self.TradingForCorp()
    405         duration = self.durationCombo.GetValue()
    406         expectedBrokerFeePercentage = None
    407         for item in allItems:
    408             if duration == 0:
    409                 if item.bestBid:
    410                     validatedItem = self.GetValidatedItem(item)
    411                 else:
    412                     continue
    413             else:
    414                 validatedItem = self.GetValidatedItem(item)
    415             if validatedItem:
    416                 if duration != 0:
    417                     if expectedBrokerFeePercentage is None:
    418                         expectedBrokerFeePercentage = validatedItem.rawBrokerFeePercentage
    419                     elif not FloatCloseEnough(expectedBrokerFeePercentage, validatedItem.rawBrokerFeePercentage):
    420                         brokerFeeNow = sm.GetService('marketQuote').GetBrokersFeeCommissionFromStationID(validatedItem.stationID)
    421                         raise UserError('MktBrokersFeeUnexpected2', {'actualBrokerFeePerc': 100 * brokerFeeNow})
    422                 self.sellItemList.append(validatedItem)
    423 
    424         if not len(self.sellItemList):
    425             return
    426         if duration > 0:
    427             brokerFeeNow = sm.GetService('marketQuote').GetBrokersFeeCommissionFromStationID(self.sellItemList[0].stationID)
    428             if not FloatCloseEnough(brokerFeeNow, expectedBrokerFeePercentage):
    429                 raise UserError('MktBrokersFeeUnexpected2', {'actualBrokerFeePerc': 100 * brokerFeeNow,
    430                  'expectedBrokerFeePercentage': expectedBrokerFeePercentage})
    431         if not self.ContinueAfterWarning(self.sellItemList):
    432             return
    433         selling_plex = any((x.typeID == typePlex for x in self.sellItemList))
    434         single_sell = len(self.sellItemList) == 1
    435         if not selling_plex:
    436             wereOrdersCreated = sm.GetService('marketQuote').SellMulti(self.sellItemList, useCorp, duration, expectedBrokerFeePercentage)
    437         elif single_sell:
    438             wereOrdersCreated = sm.GetService('marketQuote').SellPlex(self.sellItemList[0], useCorp, duration, expectedBrokerFeePercentage)
    439         else:
    440             wereOrdersCreated = False
    441             prompt_player_fsd_dialog('CannotPutPlexInMarketMultiSell')
    442         if wereOrdersCreated:
    443             self.Close()
    444 
    445     def GetUnitCount(self):
    446         unitCount = 0
    447         isImmediate = self.durationCombo.GetValue() == 0
    448         for item in self.itemList:
    449             if isImmediate:
    450                 unitCount += item.estimatedSellCount
    451             else:
    452                 unitCount += item.GetQtyToSell()
    453 
    454         return unitCount
    455 
    456     def GetValidatedItem(self, item):
    457         invItem = item.item
    458         if item.isUpdating:
    459             return
    460         price = round(item.GetPrice(), 2)
    461         if price > MAX_ORDER_PRICE:
    462             return
    463         if self.durationCombo.GetValue() == 0:
    464             if not item.bestBid:
    465                 return
    466             if not item.estimatedSellCount:
    467                 return
    468         self.CheckAndHandlePlexVaultItem(item)
    469         qty = item.GetQtyToSell()
    470         officeID = None
    471         if invItem.locationID != item.stationID and invItem.flagID in invconst.flagCorpSAGs:
    472             officeID = invItem.locationID
    473         validatedItem = KeyVal(stationID=int(item.stationID), typeID=int(item.typeID), itemID=item.itemID, price=price, quantity=int(qty), officeID=officeID, delta=item.GetDelta(), rawBrokerFeePercentage=item.rawBrokerFeePercentage)
    474         return validatedItem
    475 
    476     def CheckAndHandlePlexVaultItem(self, item):
    477         if item.typeID != invconst.typePlex or item.itemID is not None:
    478             return
    479         quantity = item.GetQtyToSell()
    480         item.locationID = session.stationid or session.structureid
    481         reference = sm.GetService('invCache').GetInventoryMgr().WithdrawPlexFromVault(quantity, item.locationID, InvoiceType.VaultToMarket, item.item.flagID)
    482         listener = PlexWithdrawalListener(session.userid, quantity, reference)
    483         loading_wheel = LoadingWheel(parent=self.mainCont, align=uiconst.CENTER)
    484         loading_wheel.Show()
    485         while not listener.received_event:
    486             uthread2.Sleep(1)
    487 
    488         loading_wheel.Hide()
    489         if listener.received_item_id:
    490             item.itemID = listener.received_item_id
    491 
    492     def IsAllowedDragData(self, dragData):
    493         for entry in dragData:
    494             guid = getattr(entry, '__guid__', None)
    495             if guid is not None and guid not in INVENTORY_GUIDS:
    496                 return False
    497 
    498         return True
    499 
    500     def OnDurationChange(self, *args):
    501         newDuration = self.durationCombo.GetValue()
    502         settings.user.ui.Set(self.durationSettingConfig, newDuration)
    503         self.UpdateNumbers()
    504         for item in self.GetItems():
    505             item.OnDurationChanged(newDuration)
    506 
    507     def UpdateOrderCount(self):
    508         self.orderCountLabel.text = GetByLabel('UI/Market/MarketQuote/OpenOrdersRemaining', orders=FmtAmt(self.maxCount - self.myOrderCount), maxOrders=FmtAmt(self.maxCount))
    509 
    510     def OnUseCorp(self, *args):
    511         SellBuyItemsWindow.OnUseCorp(self, *args)
    512         self.UpdateOrderCount()
    513 
    514     def GetMaxOrderCount(self):
    515         limits = self.marketQuoteSvc.GetSkillLimits(self.baseStationID)
    516         maxCount = limits['cnt']
    517         return maxCount
    518 
    519     def GetCurrentOrderCount(self):
    520         myOrders = self.marketQuoteSvc.GetMyOrders()
    521         return len(myOrders)
    522 
    523     def GetItemsWithBadDelta(self, buyItemList):
    524         lowItems = []
    525         for item in buyItemList:
    526             if item.delta < -0.5:
    527                 lowItems.append((item.delta, item))
    528 
    529         return lowItems
    530 
    531     def GetImportMenu(self, menuParent):
    532         localizedDecimal = eveLocalization.GetDecimalSeparator(localization.SYSTEM_LANGUAGE)
    533         localizedSeparator = eveLocalization.GetThousandSeparator(localization.SYSTEM_LANGUAGE)
    534         for decimalType, decimal, separator in (('UI/Market/MarketQuote/DecimalPoint', '.', ','), ('UI/Market/MarketQuote/DecimalComma', ',', '.')):
    535             if localizedDecimal == decimal and localizedSeparator == separator:
    536                 extraHint = GetByLabel('UI/Market/MarketQuote/SameAsYourNumberFormat', hexColor=eveColor.LEAFY_GREEN_HEX)
    537             else:
    538                 extraHint = GetByLabel('UI/Market/MarketQuote/NotSameAsYourNumberFormat', hexColor=eveColor.WARNING_ORANGE_HEX)
    539             colorText = '<color=%s>' % eveColor.CRYO_BLUE_HEX
    540             veldsparPrice = '2<b>%s%s</color></b>01' % (colorText, decimal)
    541             priceRifter = '400<b>%s%s</color></b>000<b>%s%s</color></b>01' % (colorText,
    542              separator,
    543              colorText,
    544              decimal)
    545             hint = GetByLabel('UI/Market/MarketQuote/ImportItemPriceListOptionHint', type1=const.typeVeldspar, type2=587, priceType1=veldsparPrice, priceType2=priceRifter)
    546             hint += '<br><br>%s' % extraHint
    547             text = '%s (%s) ' % (GetByLabel('UI/Market/MarketQuote/ImportPricesFromClipboard'), GetByLabel(decimalType))
    548             menuParent.AddIconEntry(icon=ACTION_ICON, text=text, hint=hint, callback=lambda d = decimal, s = separator: self.ImportItemPrices(d, s))
    549 
    550     def GetExportMenu(self, menuParent):
    551         menuParent.AddIconEntry(icon=ACTION_ICON, text=GetByLabel('UI/Market/MarketQuote/CopyItemsToClipboard'), callback=self.CopyContentToClipboardLocalized)
    552         if self.AreExtraExportOptionsAvailable():
    553             menuParent.AddIconEntry(icon=ACTION_ICON, text=GetByLabel('UI/Market/MarketQuote/CopyItemsToClipboardEnglish'), callback=self.CopyContentToClipboard)
    554 
    555     def ImportItemPrices(self, decimal, separator):
    556         marketImporter = sm.GetService('marketutils').GetMarketTypeIDFinder()
    557         multiSellImporter = ImportMultiSell(marketImporter, decimal, separator)
    558         text = GetClipboardData()
    559         newPrices, failedLines = multiSellImporter.GetTypesAndPrice(text)
    560         self.UpdatePrices(newPrices)
    561         if failedLines:
    562             text = '%s<br>' % GetByLabel('UI/SkillQueue/CouldNotReadLines')
    563             text += '<br>'.join(failedLines)
    564             prompt_player_fsd_dialog('CustomInfo', {'info': text}, modal=False)
    565 
    566     def UpdatePrices(self, newPrices):
    567         itemsUpdated = 0
    568         for item in self.GetItemsIterator():
    569             newTypePrice = newPrices.get(item.typeID, None)
    570             if newTypePrice:
    571                 item.SetPrice(newTypePrice)
    572                 itemsUpdated += 1
    573 
    574         if itemsUpdated > 0:
    575             text = GetByLabel('UI/Market/MarketQuote/UpdatedPriceForItems', numItems=itemsUpdated)
    576         else:
    577             text = GetByLabel('UI/Market/MarketQuote/UpdatedNoPriceForItems')
    578         message_player(text)
    579 
    580     def CopyContentToClipboard(self, *args):
    581         self._CopyContentToClipboard(isLocalized=False)
    582 
    583     def CopyContentToClipboardLocalized(self, *args):
    584         self._CopyContentToClipboard()
    585 
    586     def _CopyContentToClipboard(self, isLocalized = True, *args):
    587         listOfLines = []
    588         nameFunc = GetNameFunc(isLocalized)
    589         for item in self.GetItemsIterator():
    590             line = self._GetExportLine(item.typeID, item.GetQty(), item.GetPrice(), nameFunc)
    591             listOfLines.append(line)
    592 
    593         listOfLines.reverse()
    594         exportText = '\r\n'.join(listOfLines)
    595         blue.pyos.SetClipboardData(exportText)
    596 
    597     def _GetExportLine(self, typeID, qty, price, nameFunc):
    598         decimals = 2
    599         if not price:
    600             itemPriceText = '-'
    601             totalPriceText = '-'
    602         else:
    603             itemPriceText = unicode(round(price, decimals))
    604             totalPriceText = unicode(round(qty * price, decimals))
    605         lineParts = [unicode(typeID),
    606          CleanText(nameFunc(typeID)),
    607          str(qty),
    608          itemPriceText,
    609          totalPriceText]
    610         line = '\t'.join(lineParts)
    611         return line