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

svc_copycat2.py (48177B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\devtools\script\svc_copycat2.py
      2 from __future__ import print_function
      3 from future.utils import raise_
      4 import cPickle
      5 import sys
      6 import blue
      7 import triui
      8 import evetypes
      9 import os
     10 import random
     11 import types
     12 import urllib
     13 import uthread
     14 import xml.parsers.expat
     15 from carbon.common.script.sys.service import Service, ROLE_WORLDMOD, ROLEMASK_ELEVATEDPLAYER, ROLE_GML, ROLE_PLAYER, ROLE_HEALSELF, ROLE_SPAWN, SERVICE_RUNNING, SERVICE_START_PENDING
     16 from carbon.common.script.util import timerstuff
     17 from carbonui import uiconst
     18 from carbonui.control import tabGroup
     19 from carbonui.control.window import Window
     20 from carbonui.primitives.container import Container
     21 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     22 from carbonui.primitives.fill import Fill
     23 from carbonui.primitives.line import Line
     24 from carbonui.primitives.sprite import Sprite
     25 from carbonui.uicore import uicore
     26 from eve.client.script.ui.control import eveLabel, eveScroll, eveWindowDropDownMenu
     27 from eve.client.script.ui.control.entries.generic import Generic
     28 from eve.client.script.ui.control.entries.util import GetFromClass
     29 from eve.client.script.ui.control.listgroup import ListGroup
     30 from eve.client.script.ui.util import utilWindows
     31 from eve.common.lib import appConst as const
     32 from eve.common.script.sys import eveCfg
     33 from eve.devtools.script import dna, spiffy, param
     34 from eveprefs import Handler
     35 from eveprefs.iniformat import IniIniFile
     36 from player_messaging.client.ui_message import prompt_player
     37 CFG_USEONLINE = 0
     38 myversion = '2.4'
     39 SERVICENAME = 'copycat'
     40 DEFAULTDBNAME = 'copycat.xml'
     41 VIEW_USER = 0
     42 VIEW_GROUPED = 1
     43 VIEW_MARKET = 2
     44 
     45 def GetTypeName(typeID):
     46     return evetypes.GetName(typeID)
     47 
     48 
     49 Progress = lambda title, text, current, total: sm.GetService('loading').ProgressWnd(title, text, current, total)
     50 Slash = lambda command: sm.RemoteSvc('slash').SlashCmd(command)
     51 Message = lambda title, body, icon = triui.INFO: prompt_player(body, title, options=uiconst.OK, icon=icon)
     52 AUTOSAVE_IMMEDIATE = 3
     53 AUTOSAVE_TIMED = 2
     54 AUTOSAVE_EXITONLY = 1
     55 AUTOSAVE_DISABLED = 0
     56 UI_WBIG = 'copycat_width_big'
     57 UI_WSMALL = 'copycat_width_small'
     58 UI_WINFO = 'copycat_width_infopanel'
     59 UI_USEINFO = 'copycat_infopanel'
     60 
     61 def IsRepeatable(module):
     62     dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation()
     63     if module.IsOnline() is False:
     64         return False
     65     for effectID in dogmaLocation.dogmaStaticMgr.effectsByType[module.typeID]:
     66         effect = dogmaLocation.dogmaStaticMgr.effects[effectID]
     67         if effect.effectCategory not in [const.dgmEffActivation, const.dgmEffTarget]:
     68             continue
     69         if effect.durationAttributeID is None:
     70             continue
     71         if effectID == const.effectOnline:
     72             continue
     73         return True
     74 
     75     return False
     76 
     77 
     78 def AskFile(title = 'Title goes here', body = 'Enter name of file:', setvalue = '', mustexist = True):
     79     while True:
     80         ret = utilWindows.NamePopup(caption=title, label=body, setvalue=setvalue, maxLength=256)
     81         if not ret:
     82             return None
     83         filename = ret
     84         if mustexist:
     85             if os.path.exists(filename):
     86                 return filename
     87             Message('File not found', 'Could not locate the file:<br>  %s' % filename, icon=triui.WARNING)
     88             continue
     89         return filename
     90 
     91 
     92 def EscapeString(unescaped):
     93     unescaped = str(unescaped)
     94     escaped = unescaped.replace('&', '&amp;')
     95     escaped = escaped.replace('<', '&lt;')
     96     escaped = escaped.replace('>', '&gt;')
     97     return escaped
     98 
     99 
    100 def UnEscapeString(escaped):
    101     escaped = str(escaped)
    102     unescaped = escaped.replace('&lt;', '<')
    103     unescaped = unescaped.replace('&gt;', '>')
    104     unescaped = unescaped.replace('&amp;', '&')
    105     return unescaped
    106 
    107 
    108 class CCItem(object):
    109 
    110     def __init__(self):
    111         self.parent = None
    112 
    113     def __repr__(self):
    114         if hasattr(self, 'name'):
    115             return '<Item name="%s">' % self.name
    116         return '<Item name=Unspecified>'
    117 
    118     def Remove(self):
    119         if self.parent:
    120             self.parent.RemoveItem(self)
    121 
    122 
    123 class CCFolder(CCItem):
    124 
    125     def __init__(self):
    126         CCItem.__init__(self)
    127         self.content = []
    128 
    129     def AddItem(self, this):
    130         self.content.append(this)
    131         this.parent = self
    132 
    133     def RemoveItem(self, this):
    134         self.content.remove(this)
    135         this.parent = None
    136 
    137     def ContainsItem(self, this):
    138         while this.parent:
    139             this = this.parent
    140             if this == self:
    141                 return True
    142 
    143         return False
    144 
    145     def __repr__(self):
    146         return '<CCFolder>'
    147 
    148 
    149 def CreateTreeFromDict(dict):
    150     f = CCFolder()
    151     for k, v in dict.iteritems():
    152         if type(v) == types.DictType:
    153             node = CreateTreeFromDict(v)
    154         else:
    155             node = CCItem()
    156             node.dna = v
    157         node.name = k
    158         f.AddItem(node)
    159 
    160     return f
    161 
    162 
    163 racial = {'Caldari': 1,
    164  'Minmatar': 2,
    165  'Gallente': 3,
    166  'Amarr': 4}
    167 
    168 class CopycatService(Service):
    169     __slashhook__ = True
    170     __notifyevents__ = ['ProcessRestartUI']
    171     __dependencies__ = []
    172     __guid__ = 'svc.copycat'
    173     __servicename__ = SERVICENAME
    174     __displayname__ = SERVICENAME.capitalize()
    175 
    176     def cmd_cc(self, p):
    177         try:
    178             dnaKey, qty = p.Parse('s?i')
    179             if qty is None:
    180                 qty = 1
    181             dna.Ship(dnaKey=dnaKey).AssembleMany(qty)
    182             return 'Ok'
    183         except param.Error:
    184             if not p.line.strip():
    185                 uthread.new(self.Show)
    186                 return 'Ok'
    187             raise
    188 
    189     def cmd_getshipsetup(self, p):
    190         dnaKey = sm.RemoteSvc('slash').SlashCmd('/getshipsetup ' + p.line)
    191         dna.Ship(dnaKey=dnaKey).ShowInfo()
    192         return 'Ok'
    193 
    194     def igb(self, action = '', key = '', name = None):
    195         if not session.role & ROLEMASK_ELEVATEDPLAYER:
    196             return
    197         action = action.lower()
    198         if action == 'open':
    199             dna.Popup(key, name)
    200 
    201     def AddDestroyedShip(self, dnaKey, name):
    202         now = blue.os.GetWallclockTime()
    203         for info in self.last10killed:
    204             if info[0] == dnaKey:
    205                 self.last10killed.remove(info)
    206                 break
    207 
    208         self.last10killed = [(dnaKey, now, name)] + self.last10killed[:9]
    209         self.WriteKilled()
    210 
    211     def GetSetupsByTypeID(self, typeID):
    212         hits = []
    213         typeID = str(typeID)
    214 
    215         def Scan(folder):
    216             for item in folder.content:
    217                 if isinstance(item, CCFolder):
    218                     Scan(item)
    219                 else:
    220                     parts = item.dna.split(':', 2)
    221                     if len(parts) == 3 and typeID == parts[1]:
    222                         hits.append((item.name, item.dna))
    223 
    224         Scan(self.tree)
    225         return hits
    226 
    227     def ShowInfo(self, what = None, name = None):
    228         dna.Popup(what, name, buttons=False)
    229 
    230     def GetInsiderDir(self):
    231         return sm.GetService('insider').GetInsiderDir()
    232 
    233     def Run(self, memStream = None):
    234         self.state = SERVICE_START_PENDING
    235         try:
    236             self.lockout = False
    237             self.wnd = None
    238             self.dbchanged = False
    239             INSIDERDIR = self.GetInsiderDir()
    240             self.prefs = Handler(IniIniFile('copycat', INSIDERDIR))
    241             dbpath = os.path.join(INSIDERDIR, DEFAULTDBNAME)
    242             for key, value in [('database', DEFAULTDBNAME), ('autosave', AUTOSAVE_IMMEDIATE)]:
    243                 if not self.prefs.HasKey(key):
    244                     self.prefs.SetValue(key, value)
    245 
    246             TARGET = os.path.join(INSIDERDIR, self.prefs.database)
    247             self.dbfilename = TARGET
    248             self.dbautosave = self.prefs.autosave
    249             self.last10killed = self.ReadKilled()
    250             if not os.path.exists(dbpath):
    251                 self.RetrieveDefaultDatabase(dbpath)
    252             self.tree = self.ReadDatabase()
    253             if not self.tree:
    254                 self.tree = CreateTreeFromDict({'There is no data to display': {'': ''}})
    255                 self.SetAutoSave(0)
    256             self.SetAutoSave(self.dbautosave)
    257         finally:
    258             self.state = SERVICE_RUNNING
    259 
    260     def Stop(self, memStream = None):
    261         self.Hide()
    262         if self.dbautosave:
    263             self.SaveDatabaseIfChanged()
    264         Service.Stop(self, memStream)
    265 
    266     def ReadKilled(self):
    267         self.last10killed = []
    268         try:
    269             for line in open(os.path.join(self.GetInsiderDir(), 'destroyed.txt'), 'r'):
    270                 line = line.strip()
    271                 if line.startswith('DNA:'):
    272                     dnaKey, lostAt, name = line.split('=', 2)
    273                     self.last10killed.append([dnaKey, long(lostAt), name])
    274 
    275         except:
    276             sys.exc_clear()
    277 
    278         return self.last10killed
    279 
    280     def WriteKilled(self):
    281         try:
    282             out = open(os.path.join(self.GetInsiderDir(), 'destroyed.txt'), 'w')
    283             for line in self.last10killed:
    284                 print('%s=%d=%s' % line, file=out)
    285 
    286             out.close()
    287         except:
    288             sys.exc_clear()
    289 
    290     def SetAutoSave(self, mode):
    291         self.dbautosave = mode
    292         if mode == AUTOSAVE_TIMED:
    293             self.dbautosavetimer = timerstuff.AutoTimer(300000, self.SaveDatabaseIfChanged)
    294         else:
    295             self.dbautosavetimer = None
    296         self.prefs.autosave = self.dbautosave = mode
    297         self.SaveDatabaseIfChanged()
    298         self.UpdateStatus()
    299 
    300     def SaveDatabaseIfChanged(self):
    301         if self.dbchanged:
    302             self.WriteDatabase(self.tree)
    303             self.dbchanged = False
    304             self.UpdateStatus()
    305 
    306     def ReadDatabaseOLD(self, filename):
    307         folder = CCFolder()
    308         db = blue.classes.CreateInstance('blue.ResFile')
    309         if db.Open(filename) and db.size > 0:
    310             crud = str(db.Read())
    311             try:
    312                 stuff = cPickle.loads(crud)
    313                 for k, v in stuff:
    314                     item = CCItem()
    315                     item.name = k
    316                     item.dna = v
    317                     folder.AddItem(item)
    318 
    319             except:
    320                 for line in crud.replace('\r', '').split('\n'):
    321                     if len(line) > 2:
    322                         if line[0] == '[' and line[-1] == ']':
    323                             pass
    324                         elif line[:4] == 'DNA:':
    325                             line = line.split('=')
    326                             item = CCItem()
    327                             item.name = line[1]
    328                             item.dna = line[0]
    329                             folder.AddItem(item)
    330 
    331         if folder.content:
    332             return folder
    333 
    334     def ReadDatabase(self, filename = None):
    335         self._root = None
    336         if filename is None or filename == '':
    337             filename = self.dbfilename
    338 
    339         def XMLTagStart(tag, attrs):
    340             if tag == 'data':
    341                 if self._root:
    342                     raise RuntimeError('Craptastic!')
    343                 self._root = self.currentfolder = self.currentitem = CCFolder()
    344             elif tag == 'group':
    345                 folder = CCFolder()
    346                 self.currentfolder.AddItem(folder)
    347                 self.currentfolder = self.currentitem = folder
    348             elif tag == 'item':
    349                 self.currentitem = CCItem()
    350             else:
    351                 self.currenttag = tag
    352 
    353         def XMLTagEnd(tag):
    354             if tag == 'group':
    355                 if not hasattr(self.currentfolder, 'name'):
    356                     self.currentfolder.name = 'Unnamed group'
    357                 self.currentfolder = self.currentfolder.parent
    358             elif tag == 'item':
    359                 if not hasattr(self.currentitem, 'name'):
    360                     self.currentitem.name = 'Unnamed setup'
    361                 if hasattr(self.currentitem, 'dna'):
    362                     self.currentfolder.AddItem(self.currentitem)
    363 
    364         def XMLTagData(data):
    365             if not self.currenttag:
    366                 return
    367             data = data.replace('\n', '').replace('\r', '')
    368             if not data:
    369                 return
    370             if self.currenttag == 'name':
    371                 self.currentitem.name = UnEscapeString(eval("'" + data.replace("'", "\\'") + "'"))
    372             elif self.currenttag == 'dna':
    373                 self.currentitem.dna = data
    374             self.currenttag = None
    375 
    376         try:
    377             f = open(filename, 'r')
    378         except:
    379             return
    380 
    381         self.currenttag = 'Kittens!'
    382         self.currentfolder = None
    383         p = xml.parsers.expat.ParserCreate()
    384         p.StartElementHandler = XMLTagStart
    385         p.EndElementHandler = XMLTagEnd
    386         p.CharacterDataHandler = XMLTagData
    387         p.buffer_text = True
    388         p.returns_unicode = False
    389         p.ParseFile(f)
    390         root = self._root
    391         self._root = None
    392         return root
    393 
    394     def WriteDatabase(self, tree, filename = None):
    395         if not filename:
    396             filename = self.dbfilename
    397         list = []
    398 
    399         def XMLTag(key, value):
    400             return '<%s>%s</%s>\r\n' % (key, repr(EscapeString(value))[1:-1], key)
    401 
    402         def XMLDumpItem(f, item, tag, indent):
    403             deeper = indent + '\t'
    404             f.write('%s<%s>\r\n' % (indent, tag))
    405             f.write(deeper + XMLTag('name', item.name))
    406             f.write(deeper + XMLTag('dna', item.dna))
    407             f.write('%s</%s>\r\n' % (indent, tag))
    408             if item in list:
    409                 (RuntimeError, 'Endless cycle detected: %s' % item)
    410             list.append(item)
    411 
    412         def XMLDumpFolder(f, folder, tag = 'data', indent = ''):
    413             deeper = indent + '\t'
    414             if not indent:
    415                 f.write("<?xml version='1.0' encoding='utf-8'?>\r\n")
    416             if hasattr(folder, 'name') and folder.name:
    417                 f.write('%s<%s>\r\n' % (indent, tag))
    418                 f.write(deeper + XMLTag('name', folder.name))
    419             else:
    420                 f.write('%s<%s>\r\n' % (indent, tag))
    421             for item in folder.content:
    422                 if isinstance(item, CCFolder):
    423                     XMLDumpFolder(f, item, 'group', deeper)
    424                 else:
    425                     XMLDumpItem(f, item, 'item', deeper)
    426 
    427             f.write('%s</%s>\r\n' % (indent, tag))
    428 
    429         XMLDumpFolder(open(filename, 'wb'), tree)
    430 
    431     def ProcessRestartUI(self):
    432         if self.wnd:
    433             self.Hide()
    434             self.Show()
    435 
    436     def Hide(self, *args):
    437         if self.wnd:
    438             self.wnd.Close()
    439             self.wnd = None
    440             self.infotemplate = None
    441 
    442     def RetrieveDefaultDatabase(self, dbpath):
    443         try:
    444             src = 'http://content.eveonline.com/QA/insider/copycat.xml'
    445             urllib.urlretrieve(src, dbpath)
    446             self.ReadDatabase()
    447         except urllib.URLError:
    448             prompt_player('There was a problem retrieving the default copycat db', 'Error', options=uiconst.OK, icon=triui.ERROR)
    449 
    450     def Show(self):
    451         if not (hasattr(session, 'solarsystemid') and session.solarsystemid2):
    452             Message('Hold your horses!', 'The copycat UI requires you to be logged in.')
    453             return
    454         if self.wnd:
    455             self.wnd.Maximize()
    456             return
    457         initing = True
    458         self.wnd = wnd = Window.Open(windowID=SERVICENAME)
    459         wnd._OnClose = self.Hide
    460         wnd.SetCaption('Copycat')
    461         wnd.sr.main = main = wnd.GetChild('main')
    462         top = Container(name='push', align=uiconst.TOTOP, height=14, parent=main)
    463         Line(parent=top, align=uiconst.TOBOTTOM)
    464         push = Container(name='push', parent=top, align=uiconst.TOLEFT, width=const.defaultPadding, state=uiconst.UI_DISABLED)
    465         Line(parent=push, align=uiconst.TORIGHT)
    466         for label, func in [('File', self.GetMenu_File), ('My Ship', self.GetMenu_Ship)]:
    467             menu = eveWindowDropDownMenu.WindowDropDownMenu(name='menu', parent=top, align=uiconst.TOLEFT, state=uiconst.UI_NORMAL)
    468             menu.Setup(label, func)
    469 
    470         Container(name='bottompush', parent=main, state=uiconst.UI_DISABLED, align=uiconst.TOBOTTOM, height=const.defaultPadding)
    471         c = ContainerAutoSize(name='', parent=main, state=uiconst.UI_PICKCHILDREN, align=uiconst.TOBOTTOM)
    472         wnd.sr.filenamelabel = eveLabel.Label(text='', parent=c, align=uiconst.TOPLEFT, padLeft=const.defaultPadding, color=None, state=uiconst.UI_NORMAL)
    473         self.UpdateStatus()
    474         wnd.sr.tabs = tabGroup.TabGroup(name='tabsparent', parent=main)
    475         body = Container(name='scroll', parent=main, pos=(const.defaultPadding,
    476          const.defaultPadding,
    477          const.defaultPadding,
    478          const.defaultPadding))
    479         wnd.sr.info = info = dna.InfoPanel(name='infopanel', parent=body, align=uiconst.TORIGHT)
    480         info.Setup(readonly=True)
    481         info.width = settings.user.ui.Get(UI_WINFO, 288)
    482         self.scaling = 0
    483         wnd.sr.div = Container(name='meow', parent=body, align=uiconst.TORIGHT)
    484         Fill(parent=body, color=(1.0, 1.0, 1.0, 0.5), width=2, align=uiconst.TORIGHT)
    485         wnd.sr.scroll = eveScroll.Scroll(parent=body)
    486         wnd.sr.scroll.sr.sortBy = 'Name'
    487         wnd.sr.scroll.Startup()
    488         wnd.sr.scroll.sr.id = 'Copycat2Scroll'
    489         wnd.sr.scroll.sr.content.OnDropData = self.OnDropData
    490         wnd.OnDropData = self.OnDropData
    491 
    492         def toggleview(*args):
    493             x = settings.user.ui.Get(UI_USEINFO, 0)
    494             if not initing:
    495                 x = not x
    496             settings.user.ui.Set(UI_USEINFO, x)
    497             b.state = uiconst.UI_HIDDEN
    498             self.wnd.width = (self.smallwidth, self.bigwidth)[x]
    499             self.UpdateInfoPanelStuff()
    500 
    501         icon = Sprite(parent=main, width=16, height=16, align=uiconst.TOPRIGHT, top=14, left=const.defaultPadding, texturePath='res:/UI/Texture/classes/Browser/backIdle.png')
    502         icon.OnClick = toggleview
    503         self.wnd.sr.infotoggle = b = icon
    504         self.viewmode = VIEW_USER
    505         self.fixedgroups = {}
    506         wnd.sr.tabs.Startup([['Normal',
    507           self.wnd.sr.scroll,
    508           self,
    509           VIEW_USER], ['Grouped',
    510           self.wnd.sr.scroll,
    511           self,
    512           VIEW_GROUPED], ['Market',
    513           self.wnd.sr.scroll,
    514           self,
    515           VIEW_MARKET]], 'copycattabs')
    516         self.smallwidth = settings.user.ui.Get(UI_WSMALL, 200)
    517         self.bigwidth = settings.user.ui.Get(UI_WBIG, 400)
    518         self.lastdna = None
    519         toggleview()
    520         if info.state != uiconst.UI_HIDDEN:
    521             wnd.width = self.bigwidth
    522         else:
    523             wnd.width = self.smallwidth
    524         wnd.Maximize(1)
    525         wnd.OnEndScale_ = self.UpdateInfoPanelStuff
    526         if session.role > 1:
    527             self.ShowContent()
    528         initing = False
    529 
    530     def ScaleStart(self, *args):
    531         self.wnd.sr.info.state = uiconst.UI_DISABLED
    532         self.scaling = 1
    533         self.ss_w = self.wnd.sr.info.width
    534         self.ss_x = uicore.uilib.x
    535 
    536     def ScaleMove(self, *args):
    537         if self.scaling:
    538             minW = 256
    539             maxW = max(self.wnd.width - 100, minW)
    540             diffx = uicore.uilib.x - self.ss_x
    541             try:
    542                 self.wnd.sr.info.width = min(maxW, max(minW, self.ss_w - diffx))
    543             except:
    544                 pass
    545 
    546     def ScaleEnd(self, *args):
    547         settings.user.ui.Set(UI_WINFO, self.wnd.sr.info.width)
    548         self.UpdateInfoPanelStuff()
    549 
    550     def UpdateInfoPanelStuff(self, *args):
    551         self.scaling = 0
    552         b = self.wnd.sr.infotoggle
    553         info = self.wnd.sr.info
    554         info.state = self.wnd.sr.div.state = (uiconst.UI_HIDDEN, uiconst.UI_NORMAL)[settings.user.ui.Get(UI_USEINFO, 0)]
    555         info.hideBackground = 0
    556         if info.state == uiconst.UI_HIDDEN:
    557             b.flag = b.hint = b.name = 'Show Info Panel'
    558             b.texturePath = 'res:/UI/Texture/classes/Browser/nextIdle.png'
    559             self.wnd.SetMinSize((256, 220))
    560             self.smallwidth = self.wnd.width
    561             settings.user.ui.Set(UI_WSMALL, self.smallwidth)
    562         else:
    563             b.flag = b.hint = b.name = 'Hide Info Panel'
    564             b.texturePath = 'res:/UI/Texture/classes/Browser/backIdle.png'
    565             self.wnd.SetMinSize((max(400, info.width + 100), 220))
    566             self.bigwidth = self.wnd.width
    567             settings.user.ui.Set(UI_WBIG, self.bigwidth)
    568         b.state = uiconst.UI_HIDDEN
    569         b.state = uiconst.UI_NORMAL
    570         self.ShowDNA(force=True)
    571 
    572     def ShowInfo(self, dnaKey = None, shipID = None, name = None):
    573         if dnaKey:
    574             dna.Ship(dnaKey=dnaKey, name=name).ShowInfo()
    575         elif shipID:
    576             dna.Ship(shipID=shipID).ShowInfo()
    577 
    578     def ShowDNA(self, name = None, dnaKey = None, force = False):
    579         info = self.wnd.sr.info
    580         if not force:
    581             if info.state == uiconst.UI_HIDDEN:
    582                 return
    583             if self.lastdna == dnaKey:
    584                 return
    585         self.lastdna = dnaKey
    586         t = None
    587         if name is None and dnaKey is None:
    588             t = getattr(self, 'infotemplate', None)
    589         elif dnaKey:
    590             self.infotemplate = t = dna.Ship(dnaKey=dnaKey, name=name)
    591         if t:
    592             info.Load(t)
    593 
    594     def Load(self, viewmode):
    595         s = self.wnd.sr.scroll
    596         if self.viewmode != viewmode:
    597             self.viewmode = viewmode
    598             self.ShowContent()
    599 
    600     def Refresh(self):
    601         self.wnd.sr.scroll.Refresh()
    602 
    603     def ShowContent(self, resetPos = True):
    604         if not self.wnd:
    605             return
    606         s = self.wnd.sr.scroll
    607         p = s.sr.position
    608         tree = self.tree
    609         if self.viewmode == VIEW_USER:
    610             h = ['Name', 'Type', 'Group']
    611         elif self.viewmode == VIEW_GROUPED:
    612             tree = self.ReorganisedByInvGroup(self.tree)
    613             h = ['Name', 'Type']
    614         elif self.viewmode == VIEW_MARKET:
    615             tree = self.ReorganisedByMarketGroup(self.tree)
    616             h = ['Name', 'Type']
    617         else:
    618             raise RuntimeError('Unrecognized View Mode: %s' % tree)
    619         self.LogInfo('ShowContent calling self.BuildTreeList')
    620         s.Load(contentList=self.BuildTreeList(tree), fixedEntryHeight=None, headers=h)
    621         if not resetPos:
    622             s.sr.position = p
    623             s.UpdatePosition()
    624 
    625     def UpdateStatus(self):
    626         if not self.wnd:
    627             return
    628         self.wnd.sr.filenamelabel.text = 'File: <color=0xff%s>%s' % (('00ff00', 'ff0000')[self.dbchanged], os.path.basename(self.dbfilename))
    629         status = 'Current database:<br>  %s<br><br>' % os.path.abspath(self.dbfilename)
    630         if self.dbautosave != AUTOSAVE_IMMEDIATE:
    631             status += '%sModified.<br><br>' % ('Not ', '')[self.dbchanged]
    632         if self.dbautosave == AUTOSAVE_DISABLED:
    633             status += "Changes to the database are not automatically saved. Select 'SAVE' from the database menu to make any changes permanent."
    634         elif self.dbautosave == AUTOSAVE_IMMEDIATE:
    635             status += 'Any changes to the database are permanent.'
    636         elif self.dbautosave == AUTOSAVE_TIMED:
    637             status += 'Changes to the database are automatically saved every 5 minutes.'
    638         elif self.dbautosave == AUTOSAVE_EXITONLY:
    639             status += "Changes to the database are saved at exit or by selecting 'SAVE' from the database menu."
    640         else:
    641             status += 'wtf?'
    642         self.wnd.sr.filenamelabel.hint = status
    643 
    644     def UpdateContent(self, everything = True):
    645         self.ShowContent(False)
    646         self.dbchanged = True
    647         if self.dbautosave == AUTOSAVE_IMMEDIATE:
    648             self.SaveDatabaseIfChanged()
    649         self.UpdateStatus()
    650 
    651     def CopyFromItem(self, fromItem):
    652         item = CCItem()
    653         item.name = fromItem.name
    654         item.dna = fromItem.dna
    655         item.fromItem = fromItem
    656         return item
    657 
    658     def ReorganisedByMarketGroup(self, sourcetree):
    659         temp = {}
    660 
    661         def _CollectItem(fromItem):
    662             if isinstance(fromItem, CCFolder):
    663                 for x in fromItem.content:
    664                     _CollectItem(x)
    665 
    666                 return
    667             item = self.CopyFromItem(fromItem)
    668             typeID = int(item.dna.split(':')[1])
    669             if typeID in temp:
    670                 temp[typeID].append(item)
    671             else:
    672                 temp[typeID] = [item]
    673 
    674         _CollectItem(sourcetree)
    675 
    676         def _WalkMarket(marketGroups):
    677             items = []
    678             for info in marketGroups:
    679                 grouplist = sm.GetService('marketutils').GetMarketGroups()[info.marketGroupID]
    680                 if grouplist:
    681                     subitems = _WalkMarket(grouplist)
    682                     if subitems:
    683                         folder = CCFolder()
    684                         folder.name = info.marketGroupName
    685                         for item in subitems:
    686                             folder.AddItem(item)
    687 
    688                         items.append(folder)
    689                 else:
    690                     for typeID in info.types:
    691                         if typeID in temp:
    692                             folder = CCFolder()
    693                             folder.name = info.marketGroupName
    694                             for item in temp[typeID]:
    695                                 folder.AddItem(item)
    696 
    697                             items.append(folder)
    698                             del temp[typeID]
    699 
    700             return items
    701 
    702         tree = _WalkMarket(sm.GetService('marketutils').GetMarketGroups()[None])[0]
    703         if temp:
    704             folder = CCFolder()
    705             folder.name = 'Other'
    706             for itemlist in temp.itervalues():
    707                 for item in itemlist:
    708                     folder.AddItem(item)
    709 
    710             tree.AddItem(folder)
    711         return tree
    712 
    713     def ReorganisedByInvGroup(self, sourcetree):
    714 
    715         def _CollectItem(fromItem):
    716             if isinstance(fromItem, CCFolder):
    717                 for x in fromItem.content:
    718                     _CollectItem(x)
    719 
    720                 return
    721             typeID = int(fromItem.dna.split(':')[1])
    722             if evetypes.Exists(typeID):
    723                 groupname = evetypes.GetGroupName(typeID)
    724             else:
    725                 groupname = 'Unknown'
    726             if groupname in self.fixedgroups:
    727                 folder = self.fixedgroups[groupname]
    728             else:
    729                 folder = self.fixedgroups[groupname] = CCFolder()
    730                 folder.name = groupname
    731             folder.AddItem(self.CopyFromItem(fromItem))
    732 
    733         for groupname in self.fixedgroups.iterkeys():
    734             self.fixedgroups[groupname].content = []
    735 
    736         _CollectItem(sourcetree)
    737         newtree = CCFolder()
    738         for groupname in self.fixedgroups.iterkeys():
    739             folder = self.fixedgroups[groupname]
    740             if folder.content:
    741                 newtree.AddItem(folder)
    742 
    743         return newtree
    744 
    745     def BuildTreeList(self, folder, sublevel = 0):
    746         ret = []
    747         self.LogInfo('folder: %s' % folder)
    748         for this in folder.content:
    749             self.LogInfo('this: %s' % this)
    750             if isinstance(this, CCFolder):
    751                 raceID = racial.get(this.name, 0)
    752                 node = {'raceID': raceID,
    753                  'label': this.name,
    754                  'iconMargin': 18,
    755                  'showlen': self.viewmode != VIEW_MARKET or raceID,
    756                  'groupItems': this.content,
    757                  'state': 0,
    758                  'allowCopy': 0,
    759                  'allowGuids': ['listentry.DNAGroup', 'listentry.DNAEntry'],
    760                  'RefreshScroll': self.ShowContent,
    761                  'GetSubContent': self.GroupGetSubContent,
    762                  'typeID': 3296,
    763                  'selected': 0,
    764                  'hideFill': True,
    765                  'id': ('dna', this),
    766                  'sublevel': sublevel}
    767                 if self.viewmode == VIEW_USER:
    768                     node.update({'ChangeLabel': self.GroupChangeLabel,
    769                      'DeleteFolder': self.GroupDelete,
    770                      'DropData': self.GroupAdoptNodes})
    771                 entry = GetFromClass(DNAGroup, node)
    772             else:
    773                 typeID = int(this.dna.split(':')[1])
    774                 tgroupname = 'Unknown'
    775                 okay = False
    776                 if session.role & ROLE_PLAYER:
    777                     typeName = evetypes.GetNameOrNone(typeID)
    778                     if typeName:
    779                         tname = typeName
    780                         tgroupname = evetypes.GetGroupName(typeID)
    781                         okay = True
    782                     else:
    783                         tname = '[%s]' % typeID
    784                         typeID = 0
    785                 else:
    786                     inv = None
    787                     tname = '%s' % typeID
    788                 node = {'typename': 'Type:%s' % typeID,
    789                  'typeID': typeID,
    790                  'invitem': typeName,
    791                  'showinfo': okay,
    792                  'id': ('dna', this),
    793                  'sublevel': sublevel}
    794                 if self.viewmode == VIEW_USER:
    795                     node['label'] = '%s<t>%s<t>%s' % (this.name, tname, tgroupname)
    796                 else:
    797                     node['label'] = '%s<t>%s' % (this.name, tname)
    798                 entry = GetFromClass(DNAEntry, node)
    799             node['sublevel'] = sublevel
    800             node['id'] = (str(id(this)), this)
    801             node['locked'] = self.viewmode
    802             node[SERVICENAME] = None
    803             self.LogInfo('node: %s' % node)
    804             ret.append(entry)
    805 
    806         return ret
    807 
    808     def GroupGetSubContent(self, node, newitems = 0):
    809         if not len(node.groupItems):
    810             return []
    811         sublevel = node.get('sublevel', 0)
    812         self.LogInfo('GroupGetSubContent calling self.BuildTreeList')
    813         self.LogInfo('node: %s' % node)
    814         if not isinstance(node.id[1], CCFolder):
    815             return []
    816         return self.BuildTreeList(node.id[1], sublevel + 1)
    817 
    818     def GroupChangeLabel(self, id, newlabel):
    819         id[1].name = newlabel
    820         self.UpdateContent()
    821 
    822     def GroupAdd(self, name = None, parent = None):
    823         if not name:
    824             ret = utilWindows.NamePopup(caption='New folder', label='Enter name for new folder:', setvalue='New Folder', maxLength=64)
    825             if ret:
    826                 name = ret
    827         if not name:
    828             return None
    829         f = CCFolder()
    830         f.name = name
    831         if not parent:
    832             parent = self.tree
    833         parent.AddItem(f)
    834         self.UpdateContent()
    835         return f
    836 
    837     def GroupDelete(self, id):
    838         id[1].Remove()
    839         self.UpdateContent()
    840 
    841     def GroupAdoptNodes(self, folderid, these, *args):
    842         self.FolderAdoptNodes(folderid[1], these)
    843 
    844     def OnDropData(self, dragObj, these):
    845         self.FolderAdoptNodes(self.tree, these)
    846 
    847     def FolderAdoptNodes(self, targetfolder, these):
    848         changed = False
    849         for node in these:
    850             if node.Get(SERVICENAME, None):
    851                 item = node.id[1]
    852                 if isinstance(item, CCFolder):
    853                     if item == targetfolder or item.ContainsItem(targetfolder):
    854                         continue
    855                 elif item.parent == targetfolder:
    856                     continue
    857                 item.Remove()
    858                 targetfolder.AddItem(item)
    859                 changed = True
    860 
    861         if changed:
    862             self.UpdateContent()
    863 
    864     def GetMenu_File(self):
    865         mutexgfx = (('ui_9_64_14', 1.0 / 4.0), ('ui_38_16_193', 1.0 / 16.0))
    866         m = [(('Open...', 'ui_34_64_5'), self.DoImport, ('open',)),
    867          None,
    868          (('New Folder', 'res:/ui/Texture/WindowIcons/smallfolder.png', 0.125), (None, self.GroupAdd)[self.viewmode == VIEW_USER]),
    869          None,
    870          (('Save', 'ui_41_64_1'), self.DoSave),
    871          (('Save As...', 'ui_41_64_1'), self.DoSave, ('as',)),
    872          None,
    873          (('Import', 'ui_7_64_16'), [(('DNA from clipboard', 'ui_10_64_16'), self.DoImport, ('clipboard',)),
    874            None,
    875            (('XML Database (Copycat 2.x)', 'ui_34_64_5'), self.DoImport, ('import',)),
    876            (('DNA Database (Copycat 1.x)', 'ui_34_64_6'), self.DoImport, ('importold',))]),
    877          None,
    878          (('Autosave?', 'res:/ui/Texture/WindowIcons/assets.png'), [(('Immediately on every change',) + mutexgfx[self.dbautosave == AUTOSAVE_IMMEDIATE], self.SetAutoSave, (AUTOSAVE_IMMEDIATE,)),
    879            (('Every 5 minutes, if changed',) + mutexgfx[self.dbautosave == AUTOSAVE_TIMED], self.SetAutoSave, (AUTOSAVE_TIMED,)),
    880            (('At copycat/client exit only',) + mutexgfx[self.dbautosave == AUTOSAVE_EXITONLY], self.SetAutoSave, (AUTOSAVE_EXITONLY,)),
    881            (('Never (Manual only)',) + mutexgfx[self.dbautosave == AUTOSAVE_DISABLED], self.SetAutoSave, (AUTOSAVE_DISABLED,))])]
    882         return m
    883 
    884     def DoImport(self, mode):
    885         if mode == 'clipboard':
    886             self.DoStore(dnaKey=blue.clipboard.GetClipboardString())
    887             return
    888         verb = 'Import'
    889         importfunc = self.ReadDatabase
    890         if mode == 'importold':
    891             filename = 'copycat.dna'
    892             title = 'Import Copycat 1.x DNA Database'
    893             importfunc = self.ReadDatabaseOLD
    894         elif mode == 'import':
    895             filename = 'copycat.xml'
    896             title = 'Import XML Database'
    897         elif mode == 'reload':
    898             filename = self.dbfilename
    899             verb = 'Reload'
    900         elif mode == 'open':
    901             filename = self.dbfilename
    902             title = 'Open Database'
    903             verb = 'Open'
    904         else:
    905             raise_(ValueError, "'%s' is not a DoImport mode" % mode)
    906         if mode != 'reload':
    907             filename = AskFile(title, 'Enter name of file to %s' % verb, setvalue=filename)
    908             if not filename:
    909                 return
    910         item = importfunc(filename)
    911         if item:
    912             if mode in ('reload', 'open'):
    913                 append = False
    914                 text = 'This operation will clear the currently loaded database!<br><br>Proceed?'
    915                 result = prompt_player(title='%s Database' % verb, text=text, options=uiconst.OKCANCEL, icon=uiconst.WARNING)
    916             else:
    917                 append = True
    918                 text = 'Do you want to append to current database, or replace it?<br><br>Select <color=0xff00ff00>YES<color=0xffffffff> to <color=0xff00ff00>APPEND<color=0xffffffff> to current database.<br>Select <color=0xffff0000>NO<color=0xffffffff> to <color=0xffff0000>REPLACE<color=0xffffffff> current database.<br>Select <color=0xffffff00>CANCEL<color=0xffffffff> to abort the import.'
    919                 result = prompt_player(title='Import Database', text=text, options=uiconst.YESNOCANCEL)
    920             if result in (uiconst.ID_CANCEL, uiconst.ID_CLOSE):
    921                 return
    922             if result == uiconst.ID_NO:
    923                 append = False
    924             if append:
    925                 item.name = 'Imported (%s)' % os.path.basename(filename)
    926                 self.tree.AddItem(item)
    927             else:
    928                 self.tree = item
    929             if mode == 'open':
    930                 self.prefs.database = self.dbfilename = filename
    931             self.dbchanged = not append
    932             self.UpdateContent()
    933         else:
    934             Message('Database %s Error' % verb, 'The file is not a valid Copycat DNA Database, or contained no entries.', icon=triui.WARNING)
    935 
    936     def DoSave(self, mode = 'default'):
    937         if mode == 'default':
    938             self.WriteDatabase(self.tree)
    939             self.dbchanged = False
    940         elif mode == 'as':
    941             ret = utilWindows.NamePopup(caption='Save database as...', label='Enter filename', setvalue=self.dbfilename, maxLength=256)
    942             if ret:
    943                 self.WriteDatabase(self.tree, ret)
    944                 self.dbchanged = False
    945                 self.prefs.database = self.dbfilename = ret
    946         elif mode == 'selection':
    947             pass
    948         else:
    949             raise_(ValueError, 'Unknown save mode: %s' % mode)
    950         self.UpdateStatus()
    951 
    952     def GetMenu_Ship(self):
    953         HaveShip = eveCfg.GetActiveShip() is not None
    954         HaveGML = session.role & ROLE_GML
    955         CanSpawn = session.role & (ROLE_WORLDMOD | ROLE_SPAWN)
    956         HaveHEALSELF = session.role & ROLE_HEALSELF
    957         s = dna.Ship(shipID=eveCfg.GetActiveShip())
    958         dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation()
    959         ship = dogmaLocation.GetDogmaItem(eveCfg.GetActiveShip())
    960         pod = False
    961         if HaveShip:
    962             if ship.groupID == 29:
    963                 if not CanSpawn:
    964                     uthread.new(Message, "Haha! You're in a pod :)", "How did that happen?<br>Wait, never mind, I don't care.<br><br><br>Menu not available because you:<br>- are in a capsule<br>- don't have WORLDMOD or SPAWN role.")
    965                     return
    966                 pod = True
    967                 m = s.GetMenuInline(disabled=True, info=False, store=False, assemble=False, fit=False, refit=False)
    968             else:
    969                 m = s.GetMenuInline(disabled=not HaveShip, store=True, assemble=False, refit=True)
    970         else:
    971             uthread.new(Message, 'No ship... No pod...', "You might want to get into a ship, it's cold out there.")
    972             return
    973 
    974         def cf(func, condition = True):
    975             if condition and HaveShip:
    976                 return func
    977             return False
    978 
    979         if not pod:
    980             m += [None,
    981              ('Clone', cf(self.DoClone, HaveGML and not pod)),
    982              ('<color=0xff708090>Clone Perfect', cf(self.DoClonePerfect, session.role & ROLE_WORLDMOD and session.stationid and not pod)),
    983              None,
    984              ('Online Modules', cf(self.DoOnline, (HaveHEALSELF or session.stationid) and not pod)),
    985              ('Autorepeat Modules', cf(self.DoRepeat, not pod))]
    986             m.insert(0, None)
    987             m.insert(0, ('<color=0xff708090>My %s' % evetypes.GetName(s.typeID), None))
    988         m += [None,
    989          ('<color=0xff708090>Extras', None),
    990          None,
    991          ('Recover Ship', cf(('isDynamic', self.ResurrectMenu, ()), CanSpawn or HaveShip and session.role & ROLE_GML))]
    992         m += [None, ('T3 Subsystems', cf(('isDynamic', self.T3SubsystemsMenu, ()), CanSpawn or session.role & ROLE_GML))]
    993         return m
    994 
    995     def T3SubsystemsMenu(self):
    996         m = []
    997         m.append(('/Load me subsystems for...', None))
    998 
    999         def _LoadRandomT3Subsystems(shipTypeID):
   1000             slashSvc = sm.RemoteSvc('slash')
   1001             godmaSvc = sm.GetService('godma')
   1002             for subsystemGroupID in evetypes.GetGroupIDsByCategory(const.categorySubSystem):
   1003                 validTypes = []
   1004                 for typeID in evetypes.GetTypeIDsByGroup(subsystemGroupID):
   1005                     validShipTypeID = int(godmaSvc.GetTypeAttribute2(typeID, const.attributeFitsToShipType))
   1006                     if validShipTypeID == shipTypeID and evetypes.IsPublished(typeID):
   1007                         validTypes.append(typeID)
   1008 
   1009                 choice = random.choice(validTypes)
   1010                 slashSvc.SlashCmd('/load me %d' % choice)
   1011 
   1012         for typeID in evetypes.GetTypeIDsByGroup(const.groupStrategicCruiser):
   1013             m.append((evetypes.GetName(typeID), _LoadRandomT3Subsystems, (typeID,)))
   1014 
   1015         return m
   1016 
   1017     def ResurrectMenu(self):
   1018         m = []
   1019 
   1020         def _Resurrect(dnaKey, name):
   1021             dna.Ship().ImportFromDNA(dnaKey, name).Assemble()
   1022 
   1023         for dnaKey, lostAt, name in self.last10killed:
   1024             ship = dna.Ship().ImportFromDNA(dnaKey, name)
   1025             m.append((name, _Resurrect, (dnaKey, name)))
   1026 
   1027         if m:
   1028             m.insert(0, ('Recently lost ships', None))
   1029         return m
   1030 
   1031     def DoOnline(self, shipID = None):
   1032         if CFG_USEONLINE:
   1033             if session.role & ROLE_GML:
   1034                 sm.RemoteSvc('slash').SlashCmd('/online me')
   1035                 return
   1036             roles = 'ROLE_GML or ROLE_HEALSELF'
   1037         else:
   1038             roles = 'ROLE_HEALSELF'
   1039         if shipID is None:
   1040             shipID = eveCfg.GetActiveShip()
   1041         if shipID is None:
   1042             return
   1043         dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation()
   1044         ship = dogmaLocation.GetDogmaItem(shipID)
   1045         onlined = 0
   1046         offlinemods = []
   1047         if session.stationid:
   1048             title = 'Turbo Power Up'
   1049         else:
   1050             if not session.role & ROLE_HEALSELF:
   1051                 prompt_player('To use this function in space, you need %s.' % roles, 'Function not available', options=uiconst.OK, icon=triui.INFO)
   1052                 return
   1053             title = 'Power up'
   1054         c = 0
   1055         for slot in (const.flagLoSlot0, const.flagMedSlot0, const.flagHiSlot0):
   1056             for flag in xrange(slot + 7, slot - 1, -1):
   1057                 c += 1
   1058                 Progress(title, 'Checking for offline modules...', c, 24)
   1059                 for module in ship.GetFittedItems().itervalues():
   1060                     if module.flagID == flag and module.categoryID is not const.categoryCharge:
   1061                         if not module.IsOnline():
   1062                             offlinemods.append(module)
   1063                         break
   1064 
   1065         def ONLINE(state, control):
   1066             try:
   1067                 dogmaLocation.OnlineModule(control.itemID)
   1068             except:
   1069                 pass
   1070 
   1071             state[0] += 1
   1072             Progress(title, 'Activating module %s of %s' % (state[0], state[1]), state[0], state[1])
   1073 
   1074         offlinemodcount = len(offlinemods)
   1075         if offlinemodcount > 0:
   1076             state = [0, offlinemodcount]
   1077             w = sm.RemoteSvc('slash')
   1078             if session.stationid:
   1079                 parallelCalls = []
   1080                 for control in offlinemods:
   1081                     parallelCalls.append((ONLINE, (state, control)))
   1082 
   1083                 uthread.parallel(parallelCalls)
   1084             else:
   1085                 for control in offlinemods:
   1086                     w.SlashCmd('/heal me capac=1')
   1087                     ONLINE(state, control)
   1088 
   1089                 w.SlashCmd('/heal me capac=1')
   1090         Progress(title, 'Done', 1, 1)
   1091         return offlinemodcount
   1092 
   1093     def DoRepeat(self, *args):
   1094         dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation()
   1095         ship = dogmaLocation.GetDogmaItem(eveCfg.GetActiveShip())
   1096         try:
   1097             modules = uicore.layer.shipui.slotsContainer.modulesByID
   1098         except:
   1099             modules = []
   1100 
   1101         for module in ship.GetFittedItems().itervalues():
   1102             if module and IsRepeatable(module):
   1103                 if not session.stationid and module.itemID in modules:
   1104                     modules[module.itemID].SetRepeat(1000)
   1105                 else:
   1106                     settings.char.autorepeat.Set(module.itemID, 1000)
   1107 
   1108     def DoStore(self, dnaKey = None, name = None, shipID = None):
   1109         if dnaKey:
   1110             template = dna.Ship(dnaKey=dnaKey)
   1111             action = 'Import'
   1112             if not name:
   1113                 name = 'Imported %s' % GetTypeName(template.typeID)
   1114         else:
   1115             template = dna.Ship(shipID=shipID)
   1116             dnaKey = template.ExportAsDNA()
   1117             action = 'Store'
   1118             name = template.name
   1119         ret = utilWindows.NamePopup(caption='%s %s DNA' % (action, GetTypeName(template.typeID)), label='Enter name for this entry', setvalue=name, maxLength=64)
   1120         if ret:
   1121             item = CCItem()
   1122             item.name = ret
   1123             item.dna = dnaKey
   1124             self.tree.AddItem(item)
   1125             self.UpdateContent()
   1126             return 1
   1127 
   1128     def DoClonePerfect(self):
   1129         if session.stationid:
   1130             if session.role & ROLE_WORLDMOD:
   1131                 ship = sm.GetService('clientDogmaIM').GetDogmaLocation().GetDogmaItem(eveCfg.GetActiveShip())
   1132                 if ship is None:
   1133                     return
   1134                 tname = GetTypeName(ship.typeID)
   1135                 Progress('Clone %s' % tname, 'Executing copyship command...', 0, 1)
   1136                 Slash('/copyship %s 1' % eveCfg.GetActiveShip())
   1137                 Progress('Clone %s' % tname, 'Done', 1, 1)
   1138                 return
   1139 
   1140     def DoClone(self):
   1141         dna.Ship().ImportFromShip().Assemble(clone=1)
   1142 
   1143     def DoShowInfo(self, node = None):
   1144         dna.Ship().ImportFromShip().ShowInfo()
   1145 
   1146 
   1147 class DNAGroup(ListGroup):
   1148     __guid__ = 'listentry.DNAGroup'
   1149     __update_on_reload__ = 1
   1150 
   1151     def Load(self, node):
   1152         ListGroup.Load(self, node)
   1153         if node.raceID:
   1154             self.sr.icon.LoadIcon('ui_19_128_%d' % node.raceID, ignoreSize=True)
   1155             self.sr.icon.SetSize(16, 16)
   1156             self.sr.icon.state = uiconst.UI_DISABLED
   1157 
   1158     def GetMenu(self):
   1159         node = self.sr.node
   1160         m = []
   1161         if not node.open:
   1162             m += [(('Expand', '25_11'), self.Toggle, ())]
   1163         else:
   1164             m += [(('Collapse', '25_12'), self.Toggle, ())]
   1165         if node.Get('state', None) != 'locked':
   1166             m += [None,
   1167              (('New Folder', 'res:/ui/Texture/WindowIcons/smallfolder.png', 0.125), (None, self.CreateFolder)[not node.locked]),
   1168              None,
   1169              (('Change Label', 'ui_7_64_15'), (None, self.ChangeLabel)[not node.locked]),
   1170              (('Delete Folder', '04_16'), self.DeleteFolder)]
   1171         spiffy.CreateMenu(m)
   1172 
   1173     def GetDragData(self, *args):
   1174         if self.sr.node.locked:
   1175             return []
   1176         nodes = self.sr.node.scroll.GetSelectedNodes(self.sr.node)
   1177         if session.role & ROLE_PLAYER:
   1178             for node in nodes:
   1179                 node.__guid__ = 'xtriui.TypeIcon'
   1180 
   1181         else:
   1182             for node in nodes:
   1183                 node.__guid__ = DNAGroup.__guid__
   1184 
   1185         if len(nodes) == 1:
   1186             return nodes
   1187         return []
   1188 
   1189     def CreateFolder(self):
   1190         node = self.sr.node
   1191         if not node.open:
   1192             self.Toggle()
   1193         sm.GetService(SERVICENAME).GroupAdd(parent=node.id[1])
   1194 
   1195 
   1196 class DNAEntry(Generic):
   1197     __guid__ = 'listentry.DNAEntry'
   1198     __update_on_reload__ = 1
   1199 
   1200     def Load(self, *args, **kwargs):
   1201         Generic.Load(self, *args, **kwargs)
   1202         self.hint = ''
   1203 
   1204     def GetDragData(self, *args):
   1205         if self.sr.node.locked:
   1206             return []
   1207         nodes = self.sr.node.scroll.GetSelectedNodes(self.sr.node)
   1208         if session.role & ROLE_PLAYER:
   1209             for node in nodes:
   1210                 node.__guid__ = 'xtriui.TypeIcon'
   1211 
   1212         else:
   1213             for node in nodes:
   1214                 node.__guid__ = DNAEntry.__guid__
   1215 
   1216         if len(nodes):
   1217             return filter(lambda n: not isinstance(n.panel, ListGroup), nodes)
   1218         else:
   1219             return nodes
   1220 
   1221     def GetMenu(self):
   1222 
   1223         def cf(func, condition = True):
   1224             if condition:
   1225                 return func
   1226             return False
   1227 
   1228         node = self.sr.node
   1229         selected = node.scroll.GetSelectedNodes(node)
   1230         multi = len(selected) > 1
   1231         m = []
   1232         if multi:
   1233             m += [(('Delete Setups...', '04_16'), self.Delete, (selected,))]
   1234         else:
   1235             item = node.id[1]
   1236             m += dna.Ship(dnaKey=item.dna, name=item.name).GetMenuInline(store=False)
   1237             m += [None, (('Rename Setup...', 'ui_7_64_15'), self.Rename), (('Delete Setup...', '04_16'), self.Delete)]
   1238         spiffy.CreateMenu(m)
   1239 
   1240     def OnMouseUp(self, *args):
   1241         try:
   1242             item = self.sr.node.id[1]
   1243             sm.GetService(SERVICENAME).ShowDNA(name=item.name, dnaKey=item.dna)
   1244         except:
   1245             sys.exc_clear()
   1246 
   1247     def Delete(self, nodes = None):
   1248         if not nodes:
   1249             nodes = [self.sr.node]
   1250         text = 'You are about to delete the setup(s):<br>'
   1251         for node in nodes:
   1252             text += '  %s<br>' % node.id[1].name
   1253 
   1254         text += '<br>Are you sure?'
   1255         result = prompt_player(title='Delete Stuff', text=text, options=uiconst.OKCANCEL, icon=uiconst.WARNING)
   1256         if result != uiconst.ID_OK:
   1257             return
   1258         for node in nodes:
   1259             if node.locked:
   1260                 node.id[1].fromItem.Remove()
   1261             else:
   1262                 node.id[1].Remove()
   1263 
   1264         sm.GetService(SERVICENAME).UpdateContent()
   1265 
   1266     def Rename(self):
   1267         item = self.sr.node.id[1]
   1268         if self.sr.node.locked:
   1269             item = item.fromItem
   1270         ret = utilWindows.NamePopup(caption='Rename %s DNA' % self.sr.node.typename, label='Enter new name', setvalue=item.name, maxLength=32)
   1271         if ret:
   1272             newname = ret
   1273             if item.name != newname:
   1274                 item.name = newname
   1275                 sm.GetService(SERVICENAME).UpdateContent()
   1276 
   1277     def OnDropData(self, dragObj, these, *args):
   1278         if self.sr.node.locked:
   1279             return
   1280         sm.GetService(SERVICENAME).FolderAdoptNodes(self.sr.node.id[1].parent, these)