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

fittingMgmtWindow.py (20382B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\fittingMgmtWindow.py
      2 import eveicon
      3 import evetypes
      4 import log
      5 import uthread2
      6 from carbon.common.script.sys.serviceConst import ROLE_WORLDMOD
      7 from carbonui import AxisAlignment, TextAlign, TextColor, uiconst
      8 from carbonui.control.combo import Combo
      9 from carbonui.control.comboEntryData import ComboEntryData
     10 from carbonui.control.contextMenu.menuData import MenuData
     11 from carbonui.control.singlelineedits.singleLineEditText import SingleLineEditText
     12 from carbonui.decorative.panelUnderlay import PanelUnderlay
     13 from carbonui.primitives.container import Container
     14 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     15 from carbonui.primitives.flowcontainer import FlowContainer
     16 from carbonui.util.various_unsorted import SortListOfTuples
     17 from eve.client.script.ui.control import eveScroll
     18 from carbonui.button.menu import MenuButtonIcon
     19 from eve.common.lib import appConst as const
     20 from carbonui.control.buttonIcon import ButtonIcon
     21 from carbonui.control.button import Button
     22 from eve.client.script.ui.control.divider import Divider
     23 from eve.client.script.ui.control.entries.fitting import FittingEntry
     24 from eve.client.script.ui.control.entries.generic import Generic
     25 from eve.client.script.ui.control.entries.header import Header
     26 from eve.client.script.ui.control.entries.util import GetFromClass
     27 from eve.client.script.ui.control.eveLabel import EveCaptionLarge
     28 from carbonui.control.window import Window
     29 from eve.client.script.ui.control.listgroup import ListGroup
     30 from eve.client.script.ui.shared.export import ExportFittingsWindow, ImportFittingsWindow
     31 from eve.client.script.ui.shared.fitting.fittingMgmtCont import FittingMgmtContInWnd, FittingMgmtContInStandalone
     32 from eve.client.script.ui.shared.fitting.fittingUtil import GetDeletableNodes, DeleteFittings
     33 from eve.common.script.sys.eveCfg import IsDocked
     34 from eveexceptions import ServiceNotFound, UserError
     35 from globalConfig.getFunctions import GetMaxShipsToFit
     36 from inventorycommon.const import numVisibleSubsystems
     37 from inventorycommon.util import IsModularShip, IsSubsystemFlag
     38 from localization import GetByLabel
     39 from menu import MenuLabel
     40 MENU_ICON_NO = 'ui_73_16_50'
     41 
     42 class FittingMgmt(Window):
     43     __guid__ = 'form.FittingMgmt'
     44     __notifyevents__ = ['OnRedrawFittingMgmt']
     45     default_windowID = 'FittingMgmt'
     46     default_captionLabelPath = 'UI/Fitting/FittingWindow/FittingManagement/WindowCaption'
     47     default_iconNum = 'res:/ui/Texture/WindowIcons/fittingManagement.png'
     48     default_minSize = (525, 400)
     49     default_width = 620
     50     default_height = 600
     51     _left_cont = None
     52     _word_filter = None
     53     _export_button = None
     54     _fitting_management_cont = None
     55     _search_field = None
     56     _scroll = None
     57     _left_underlay = None
     58 
     59     def __init__(self, **kwargs):
     60         self._selected_owner_id = session.charid
     61         super(FittingMgmt, self).__init__(**kwargs)
     62 
     63     def ApplyAttributes(self, attributes):
     64         super(FittingMgmt, self).ApplyAttributes(attributes)
     65         self.DrawLeftSide()
     66         self.DrawRightSide()
     67         self.HideRightPanel()
     68         self.on_content_padding_changed.connect(self._on_window_content_padding_changed)
     69 
     70     @property
     71     def _fitting_service(self):
     72         return sm.GetService('fittingSvc')
     73 
     74     def DrawLeftSide(self):
     75         self._left_cont = Container(name='leftside', parent=self.content, align=uiconst.TOLEFT, width=256)
     76         self._left_underlay = PanelUnderlay(bgParent=self._left_cont, align=uiconst.TOALL, padding=self._get_left_underlay_padding())
     77         options = [ComboEntryData(label=GetByLabel('UI/Fitting/FittingWindow/FittingManagement/PersonalFittings'), returnValue=session.charid), ComboEntryData(label=GetByLabel('UI/Fitting/FittingWindow/FittingManagement/CorporationFittings'), returnValue=session.corpid)]
     78         if session.allianceid:
     79             options.append(ComboEntryData(label=GetByLabel('UI/Fitting/FittingWindow/FittingManagement/AllianceFittings'), returnValue=session.allianceid))
     80         selected = settings.user.ui.Get('savedFittingsCombo', None)
     81         if selected not in [ x.returnValue for x in options ]:
     82             selected = session.charid
     83         self._selected_owner_id = selected
     84         Combo(name='savedFittingsCombo', parent=self._left_cont, align=uiconst.TOTOP, padding=(0, 8, 8, 0), options=options, select=selected, callback=self.ChangeOwnerFilter)
     85         search_cont = ContainerAutoSize(name='searchContainer', parent=self._left_cont, align=uiconst.TOTOP, alignMode=uiconst.TOTOP, top=8, padRight=8)
     86         MenuButtonIcon(parent=ContainerAutoSize(parent=search_cont, align=uiconst.TOLEFT, left=-4), align=uiconst.CENTER, widht=24, height=24, texturePath=eveicon.load, iconSize=16, hint=GetByLabel('UI/Commands/Import'), get_menu_func=self._get_import_menu)
     87         ButtonIcon(parent=ContainerAutoSize(parent=search_cont, align=uiconst.TOLEFT), align=uiconst.CENTER, widht=24, height=24, texturePath=eveicon.export, iconSize=16, hint=GetByLabel('UI/Commands/Export'), func=self.ExportFittings)
     88         ButtonIcon(name='collapse', parent=ContainerAutoSize(parent=search_cont, align=uiconst.TOLEFT, padRight=8), align=uiconst.CENTER, width=24, height=24, iconSize=12, texturePath='res:/UI/Texture/classes/Scroll/Collapse.png', func=self.CollapseAll, hint=GetByLabel('UI/Common/Buttons/CollapseAll'))
     89         self._search_field = SingleLineEditText(name='searchTextField', parent=search_cont, align=uiconst.TOTOP, maxLength=40, OnChange=self.Search, OnReturn=self.Search, hintText=GetByLabel('UI/Common/Buttons/Search'))
     90         self._search_field.ShowClearButton()
     91         self._scroll = eveScroll.Scroll(parent=self._left_cont, align=uiconst.TOALL, padding=(0, 8, 0, 0))
     92         self.DrawFittings()
     93         divider = Divider(name='divider', parent=self.content, align=uiconst.TOLEFT, width=8, padRight=8, state=uiconst.UI_NORMAL, cross_axis_alignment=AxisAlignment.START)
     94         divider.Startup(self._left_cont, 'width', 'x', 160, 350)
     95 
     96     def _get_left_underlay_padding(self):
     97         pad_left, _, _, pad_bottom = self.content_padding
     98         return (-pad_left,
     99          0,
    100          0,
    101          -pad_bottom)
    102 
    103     def _update_left_underlay_padding(self):
    104         if self._left_underlay:
    105             self._left_underlay.padding = self._get_left_underlay_padding()
    106 
    107     def _on_window_content_padding_changed(self, window):
    108         self._update_left_underlay_padding()
    109 
    110     def _get_import_menu(self):
    111         menu = MenuData()
    112         menu.AddEntry(text=GetByLabel('UI/Fitting/FittingWindow/FittingManagement/ImportFromFile'), func=self.ImportFittings)
    113         menu.AddEntry(text=GetByLabel('UI/Fitting/FittingWindow/FittingManagement/ImportFromClipboard'), func=lambda : sm.GetService('fittingSvc').ImportFittingFromClipboard(), hint=GetByLabel('UI/Fitting/FittingWindow/FittingManagement/ImportFromClipboardHint'))
    114         return menu
    115 
    116     def ConstructImportExportButtons(self):
    117         fitButtons = FlowContainer(name='buttonParent', parent=self._left_cont, align=uiconst.TOBOTTOM, padding=6, autoHeight=True, centerContent=True, contentSpacing=uiconst.BUTTONGROUPMARGIN, idx=0)
    118         self._export_button = Button(parent=fitButtons, label=GetByLabel('UI/Commands/Export'), func=self.ExportFittings, align=uiconst.NOALIGN)
    119         Button(parent=fitButtons, label=GetByLabel('UI/Commands/Import'), func=self.ImportFittings, align=uiconst.NOALIGN)
    120         importFromClipboardButton = Button(parent=fitButtons, align=uiconst.NOALIGN, label=GetByLabel('UI/Fitting/FittingWindow/FittingManagement/ImportFromClipboard'), func=lambda button: sm.GetService('fittingSvc').ImportFittingFromClipboard())
    121         importFromClipboardButton.hint = GetByLabel('UI/Fitting/FittingWindow/FittingManagement/ImportFromClipboardHint')
    122 
    123     def DrawRightSide(self):
    124         self.noContentHint = EveCaptionLarge(parent=self.content, align=uiconst.TOTOP, padding=(32, 32, 32, 0), color=TextColor.SECONDARY, text=GetByLabel('UI/Fitting/FittingWindow/FittingManagement/NoFittingSelected'), textAlign=TextAlign.CENTER)
    125         self._fitting_management_cont = FittingMgmtContInWnd(parent=self.content, align=uiconst.TOALL)
    126 
    127     def EnableExportButton(self, enable):
    128         if enable:
    129             self._export_button.SetLabel(GetByLabel('UI/Commands/Export'))
    130             self._export_button.state = uiconst.UI_NORMAL
    131         else:
    132             self._export_button.SetLabel(GetByLabel('UI/Fitting/FittingWindow/FittingManagement/ExportDisabled'))
    133             self._export_button.state = uiconst.UI_DISABLED
    134 
    135     def Search(self, *args):
    136         self._perform_search()
    137 
    138     @uthread2.debounce(wait=0.3)
    139     def _perform_search(self):
    140         query = self._search_field.GetValue()
    141         if self._word_filter != query:
    142             self._word_filter = query
    143             self.DrawFittings()
    144 
    145     def CollapseAll(self, *args):
    146         self._scroll.CollapseAll()
    147 
    148     def ChangeOwnerFilter(self, combo, label, ownerID, *args):
    149         if self._selected_owner_id != ownerID:
    150             self._selected_owner_id = ownerID
    151             settings.user.ui.Set('savedFittingsCombo', ownerID)
    152             self.DrawFittings()
    153             self.HideRightPanel()
    154 
    155     def DrawFittings(self, *args):
    156         scrolllist = []
    157         fittings = self._fitting_service.GetFittings(self._selected_owner_id).items()
    158         fittingsByGroupID = {}
    159         shipGroups = []
    160         if self._word_filter:
    161             query = self._word_filter.lower()
    162             for fittingID, fitting in fittings[:]:
    163                 if query in fitting.name.lower():
    164                     pass
    165                 elif evetypes.Exists(fitting.shipTypeID) and query in evetypes.GetName(fitting.shipTypeID).lower():
    166                     pass
    167                 else:
    168                     fittings.remove((fittingID, fitting))
    169 
    170         for fittingID, fitting in fittings:
    171             shipTypeID = fitting.shipTypeID
    172             if not evetypes.Exists(shipTypeID):
    173                 log.LogError('Ship in stored fittings does not exist, shipID=%s, fittingID=%s' % (shipTypeID, fittingID))
    174                 continue
    175             groupID = evetypes.GetGroupID(shipTypeID)
    176             if groupID not in fittingsByGroupID:
    177                 fittingsByGroupID[groupID] = []
    178             fittingsByGroupID[groupID].append(fitting)
    179             groupName = evetypes.GetGroupNameByGroup(groupID)
    180             if (groupName, groupID) not in shipGroups:
    181                 shipGroups.append((groupName, groupID))
    182 
    183         shipGroups.sort()
    184         if len(fittings) == 0 and self._word_filter:
    185             pass
    186         else:
    187             if self._selected_owner_id == session.charid:
    188                 maxFittings = const.maxCharFittings
    189             elif self._selected_owner_id == session.corpid:
    190                 maxFittings = const.maxCorpFittings
    191             else:
    192                 maxFittings = None
    193             if maxFittings is not None:
    194                 label = GetByLabel('UI/Fitting/FittingWindow/FittingManagement/FittingsListHeader', numFittings=len(fittings), maxFittings=maxFittings)
    195                 scrolllist.append(GetFromClass(Header, {'label': label,
    196                  'selectable': 0}))
    197             for groupName, groupID in shipGroups:
    198                 scrolllist.append(GetFromClass(ListGroup, {'GetSubContent': self.GetShipGroupSubContent,
    199                  'label': groupName,
    200                  'fittings': fittingsByGroupID[groupID],
    201                  'groupItems': fittingsByGroupID[groupID],
    202                  'id': ('fittingMgmtScrollWndGroup', groupName),
    203                  'showicon': 'hide',
    204                  'state': 'locked',
    205                  'BlockOpenWindow': 1,
    206                  'selectable': 0}))
    207 
    208         self._scroll.Load(contentList=scrolllist, noContentHint=GetByLabel('UI/Common/NothingFound'))
    209 
    210     def GetShipGroupSubContent(self, nodedata, *args):
    211         scrolllist = []
    212         fittingsByType = {}
    213         shipTypes = []
    214         for fitting in nodedata.fittings:
    215             shipTypeID = fitting.shipTypeID
    216             if not evetypes.Exists(shipTypeID):
    217                 log.LogError('Ship in stored fittings does not exist, shipID=%s, fittingID=%s' % (shipTypeID, fitting.fittingID))
    218                 continue
    219             if shipTypeID not in fittingsByType:
    220                 fittingsByType[shipTypeID] = []
    221             fittingsByType[shipTypeID].append(fitting)
    222             typeName = evetypes.GetName(shipTypeID)
    223             if (typeName, shipTypeID) not in shipTypes:
    224                 shipTypes.append((typeName, shipTypeID))
    225 
    226         shipTypes.sort()
    227         for typeName, typeID in shipTypes:
    228             scrolllist.append(GetFromClass(ListGroup, {'GetSubContent': self.GetFittingSubContent,
    229              'label': typeName,
    230              'groupItems': fittingsByType[typeID],
    231              'fittings': fittingsByType[typeID],
    232              'id': ('fittingMgmtScrollWndType', typeName),
    233              'sublevel': 1,
    234              'showicon': 'hide',
    235              'state': 'locked',
    236              'selectable': 0}))
    237 
    238         return scrolllist
    239 
    240     def GetFittingSubContent(self, nodedata, *args):
    241         scrolllist = []
    242         fittings = []
    243         for fitting in nodedata.fittings:
    244             fittings.append((fitting.name, fitting))
    245 
    246         fittings.sort()
    247         for fittingName, fitting in fittings:
    248             scrolllist.append((fittingName.lower(), GetFromClass(FittingEntry, {'label': fittingName,
    249               'fittingID': fitting.fittingID,
    250               'fitting': fitting,
    251               'ownerID': self._selected_owner_id,
    252               'showinfo': 1,
    253               'GetMenu': self.GetFittingMenu,
    254               'showicon': 'hide',
    255               'sublevel': 2,
    256               'OnClick': self.ClickEntry,
    257               'ignoreRightClick': 1})))
    258 
    259         scrolllist = SortListOfTuples(scrolllist)
    260         return scrolllist
    261 
    262     def GetFittingMenu(self, entry):
    263         m = []
    264         if not self or self.destroyed:
    265             return m
    266         node = entry.sr.node
    267         selectedNodes = node.scroll.GetSelectedNodes(node)
    268         multiSelected = len(selectedNodes) > 1
    269         fittingID = entry.sr.node.fittingID
    270         ownerID = entry.sr.node.ownerID
    271         maxShipsAllowed = GetMaxShipsToFit(sm.GetService('machoNet'))
    272         m = []
    273         if not multiSelected:
    274             if evetypes.GetCategoryID(entry.sr.node.fitting.shipTypeID) != const.categoryStructure:
    275                 m += [(GetByLabel('UI/Fitting/FittingWindow/FitFittingToActiveShip'), self._fitting_service.LoadFittingFromFittingID, [ownerID, fittingID])]
    276             if maxShipsAllowed and IsDocked():
    277                 m += [(GetByLabel('UI/Fitting/FittingWindow/FittingManagement/OpenMultifit'), self.DoBulkFit, [entry])]
    278             if session.role & ROLE_WORLDMOD:
    279                 try:
    280                     fitting_spawner = sm.GetService('fittingspawner')
    281                 except ServiceNotFound:
    282                     pass
    283                 else:
    284                     m.append(None)
    285                     m.append(('DEV Hax This Together!', fitting_spawner.SpawnFitting, [ownerID, entry.sr.node.fitting]))
    286                     m.append(('Mass DEV Hax This Together!', fitting_spawner.MassSpawnFitting, [ownerID, entry.sr.node.fitting]))
    287 
    288         m += [None]
    289         deletable = GetDeletableNodes(selectedNodes)
    290         if deletable:
    291             m += [(MenuLabel('UI/Fitting/FittingWindow/FittingManagement/DeleteFitting'), self.DeleteFitting, [entry, deletable])]
    292         return m
    293 
    294     def DeleteFitting(self, entry, selectedNodes):
    295         fittingID = entry.sr.node.fittingID
    296         fittingDeleted = DeleteFittings(selectedNodes)
    297         if not fittingDeleted:
    298             return
    299         loadedFitting = self.GetVisibleFitting()
    300         if loadedFitting is not None and loadedFitting.fittingID == fittingID:
    301             self.HideRightPanel()
    302 
    303     def DoBulkFit(self, entry):
    304         fitting = entry.sr.node.fitting
    305         OpenOrLoadMultiFitWnd(fitting)
    306 
    307     def ClickEntry(self, entry, *args):
    308         if not self or self.destroyed:
    309             return
    310         self.ShowRightPanel()
    311         fitting = self.GetVisibleFitting()
    312         if fitting is not None and fitting.fittingID == entry.sr.node.fitting.fittingID:
    313             return
    314         fitting = entry.sr.node.fitting
    315         self._fitting_management_cont.LoadNewFitting(fitting)
    316 
    317     def Fit(self, *args):
    318         visibleFitting = self.GetVisibleFitting()
    319         if visibleFitting is None:
    320             return
    321         self._fitting_service.LoadFittingFromFittingIDAndGetBuyOptionOnFailure(self._selected_owner_id, visibleFitting.fittingID)
    322 
    323     def HideRightPanel(self):
    324         self.noContentHint.Show()
    325         self._fitting_management_cont.HidePanel()
    326 
    327     def ShowRightPanel(self):
    328         self.noContentHint.Hide()
    329         self._fitting_management_cont.ShowPanel()
    330 
    331     def GetVisibleFitting(self):
    332         if self._fitting_management_cont:
    333             return self._fitting_management_cont.GetSelectedFitting()
    334 
    335     def ExportFittings(self, *args):
    336         ExportFittingsWindow.Open(ownerID=self._selected_owner_id)
    337 
    338     def ImportFittings(self, *args):
    339         ImportFittingsWindow.Open()
    340 
    341     def OnRedrawFittingMgmt(self, *args):
    342         self.DrawFittings()
    343 
    344 
    345 class ViewFitting(Window):
    346     __guid__ = 'form.ViewFitting'
    347     __nonpersistvars__ = []
    348     __notifyevents__ = ['OnSessionChanged']
    349     default_width = 410
    350     default_height = 580
    351     default_captionLabelPath = 'UI/Fitting/ShipFitting'
    352     default_minSize = (250, 300)
    353     default_windowID = 'ViewFitting'
    354     default_iconNum = 'res:/ui/Texture/WindowIcons/fittingManagement.png'
    355 
    356     def ApplyAttributes(self, attributes):
    357         Window.ApplyAttributes(self, attributes)
    358         self.fitting = attributes.fitting
    359         self.truncated = attributes.truncated
    360         self._fitting_service = sm.GetService('fittingSvc')
    361         self.ReconstructLayout()
    362         sm.RegisterNotify(self)
    363 
    364     def GetHeaderIconMenu(self, *args):
    365         m = MenuData()
    366         self._AddCustomMenuOptions(m)
    367         return m
    368 
    369     def GetMenuMoreOptions(self):
    370         menuData = super(ViewFitting, self).GetMenuMoreOptions()
    371         self._AddCustomMenuOptions(menuData)
    372         return menuData
    373 
    374     def _AddCustomMenuOptions(self, menu):
    375         if IsDocked():
    376             fittingcopy = self.fitting.copy()
    377             menu.AddEntry(GetByLabel('UI/Fitting/FittingWindow/FittingManagement/OpenMultifit'), lambda : OpenOrLoadMultiFitWnd(fittingcopy))
    378         menu.AddEntry(GetByLabel('UI/Fitting/FittingWindow/FittingManagement/ExportToClipboard'), self._fitting_management_cont.ExportFittingToClipboard, hint=GetByLabel('UI/Fitting/FittingWindow/FittingManagement/ExportToClipboardHint'))
    379 
    380     def ClickDragIcon(self, *args):
    381         subsystems = {}
    382         if IsModularShip(self.fitting.shipTypeID):
    383             for typeID, flag, qty in self.fitting.fitData:
    384                 if IsSubsystemFlag(flag):
    385                     subsystems[evetypes.GetGroupID(typeID)] = typeID
    386 
    387             if len(subsystems) != numVisibleSubsystems:
    388                 raise UserError('NotEnoughSubSystemsNotify', {})
    389         sm.GetService('preview').PreviewType(self.fitting.shipTypeID, subsystems)
    390 
    391     def ReloadWnd(self, windowID, fitting, truncated):
    392         self.fitting = fitting
    393         self.truncated = truncated
    394         self.windowID = windowID
    395         self.ReconstructLayout()
    396 
    397     def ReconstructLayout(self):
    398         self.content.Flush()
    399         self.UpdateHeader()
    400         self._fitting_management_cont = FittingMgmtContInStandalone(parent=self.content, saveCallback=self.CloseByUser)
    401         self._fitting_management_cont.LoadNewFitting(self.fitting, self.truncated)
    402 
    403     def UpdateHeader(self):
    404         self.UpdateCaption()
    405 
    406     def UpdateCaption(self):
    407         self.caption = GetByLabel(self.default_captionLabelPath) + ': %s' % evetypes.GetName(self.fitting.shipTypeID)
    408 
    409     def OnSessionChanged(self, isRemote, session, change):
    410         if 'stationid' in change or 'structureid' in change:
    411             self.UpdateHeader()
    412 
    413 
    414 def OpenOrLoadMultiFitWnd(fitting):
    415     sm.GetService('fittingSvc').CheckBusyFittingAndRaiseIfNeeded()
    416     from eve.client.script.ui.shared.fitting.multiFitWnd import MultiFitWnd
    417     wnd = MultiFitWnd.GetIfOpen()
    418     if wnd:
    419         wnd.LoadWindow(fitting)
    420         wnd.Maximize()
    421     else:
    422         wnd = MultiFitWnd.Open(fitting=fitting)
    423     return wnd