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

main.py (42070B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\UITree\main.py
      2 from __future__ import print_function
      3 __author__ = 'fridrik'
      4 import os
      5 import sys
      6 import weakref
      7 import blue
      8 import carbonui
      9 import eveicon
     10 import trinity
     11 import uthread
     12 from carbon.common.script.util import timerstuff
     13 from carbonui import TextColor, uiconst
     14 from carbonui.control.buttonIcon import ButtonIcon
     15 from carbonui.control.contextMenu.menuData import MenuData
     16 from carbonui.control.contextMenu.menuEntryData import MenuEntryDataRadioButton
     17 from carbonui.control.dragResizeCont import DragResizeCont
     18 from clientsettings.setting import UserSettingBool, UserSettingEnum
     19 from carbonui.uicore import uicore
     20 from carbonui.primitives.base import Base
     21 from carbonui.primitives.desktop import UIRoot
     22 from carbonui.control.layer import LayerCore
     23 from carbonui.control.singlelineedits.singleLineEditText import SingleLineEditText
     24 from carbonui.primitives.container import Container
     25 from carbonui.primitives.containerAutoSize import ContainerAutoSize
     26 from carbonui.primitives.frame import Frame
     27 from carbonui.primitives.fill import Fill
     28 from carbonui.primitives.sprite import Sprite
     29 from carbonui.control.scrollentries import SE_BaseClassCore
     30 from carbonui.control.scrollentries import ScrollEntryNode
     31 from carbonui.util.various_unsorted import GetAttrs, GetWindowAbove, SortListOfTuples
     32 from eve.client.script.ui import eveColor
     33 from eve.client.script.ui.control import eveLabel
     34 from eve.client.script.ui.control.eveLabel import EveLabelMedium as Label
     35 from eve.client.script.ui.control.eveScroll import Scroll
     36 from eve.client.script.ui.shared.mapView.dockPanel import DockablePanel
     37 LAYER_DEFAULT = 1
     38 LAYER_MODAL = 2
     39 LAYER_DESKTOP = 3
     40 debugger_parent_setting = UserSettingEnum('uitreeParentLayer', LAYER_DEFAULT, (LAYER_DEFAULT, LAYER_MODAL, LAYER_DESKTOP))
     41 show_pick_hilite_setting = UserSettingBool('uitreeShowPickHilite', True)
     42 ignore_full_screen_pick_setting = UserSettingBool('uitreeIgnoreFullScreenPick', False)
     43 RESROOT = os.path.dirname(__file__) + '\\res\\'
     44 VALUEOFFSET = 160
     45 IGNORE_PROPERTIES = ['dead']
     46 ALIGNMENTNAMES = {val:key for key, val in carbonui.Align.__dict__.items() if not key.startswith('_')}
     47 PICKSTATENAMES = {}
     48 for k, v in uiconst.__dict__.iteritems():
     49     if k.startswith('TR2_SPS'):
     50         PICKSTATENAMES[v] = k
     51 
     52 SPRITEEFFECTNAMES = {}
     53 BLENDMODENAMES = {}
     54 OUTPUTMODENAMES = {uiconst.OUTPUT_COLOR: 'OUTPUT_COLOR',
     55  uiconst.OUTPUT_GLOW: 'OUTPUT_GLOW',
     56  uiconst.OUTPUT_COLOR_AND_GLOW: 'OUTPUT_COLOR_AND_GLOW'}
     57 for k, v in trinity.__dict__.iteritems():
     58     if k.startswith('TR2_SFX'):
     59         SPRITEEFFECTNAMES[v] = k
     60     if k.startswith('TR2_SBM'):
     61         BLENDMODENAMES[v] = k
     62 
     63 COMBOPROPERTIES = {'align': ALIGNMENTNAMES,
     64  'pickState': PICKSTATENAMES,
     65  'spriteEffect': SPRITEEFFECTNAMES,
     66  'blendMode': BLENDMODENAMES,
     67  'outputMode': OUTPUTMODENAMES}
     68 COMBOPROPERTIES_NAMESPACE = {'align': 'uiconst',
     69  'pickState': 'uiconst',
     70  'spriteEffect': 'trinity',
     71  'blendMode': 'trinity',
     72  'outputMode': 'uiconst'}
     73 PROPERTYCAPS = {'opacity': (0.0, 5.0),
     74  'color.r': (0.0, 5.0),
     75  'color.g': (0.0, 5.0),
     76  'color.b': (0.0, 5.0),
     77  'color.a': (0.0, 5.0),
     78  'color': (0.0, 5.0),
     79  'pickRadius': (-1, sys.maxint)}
     80 PYOBJECT_SUBLISTS = ('children', 'background')
     81 PYOBJECT_SUBDICTS = ()
     82 
     83 class UITree(DockablePanel):
     84     default_name = 'UITree'
     85     default_windowID = 'uitree'
     86     default_caption = 'UI Tree'
     87     default_width = 400
     88     default_height = 400
     89     default_minSize = (300, default_height)
     90     default_left = '__center__'
     91     default_top = '__center__'
     92     filterString = None
     93     panelID = default_windowID
     94 
     95     def ApplyAttributes(self, attributes):
     96         super(UITree, self).ApplyAttributes(attributes)
     97         mainArea = self.GetMainArea()
     98         mainArea.padding = 6
     99         mainArea.padTop = 28
    100         self._selectedObject = None
    101         self._infoContainer = Container(parent=mainArea, align=uiconst.TOTOP, height=72)
    102         self.searchInput = SingleLineEditText(parent=self._infoContainer, align=uiconst.BOTTOMRIGHT, left=4, top=8, width=100, OnChange=self.OnSearchInputChange, hintText='Search')
    103         self.searchInput.SetHistoryVisibility(False)
    104         self.searchInput.ShowClearButton()
    105         menuBtn = ButtonIcon(parent=self._infoContainer, align=uiconst.TOPRIGHT, texturePath=eveicon.settings)
    106         menuBtn.expandOnLeft = True
    107         menuBtn.GetMenu = self.GetSettingMenu
    108         self._infoLabel = Label(parent=self._infoContainer, state=uiconst.UI_DISABLED, color=(1.0, 1.0, 1.0, 0.75), left=2)
    109         self.searchResultParent = ContainerAutoSize(parent=mainArea, align=uiconst.TOTOP_NOPUSH, padding=(26, -6, 4, 0), bgColor=(0.5, 0.5, 0.5, 1))
    110         resizeCont = DragResizeCont(parent=mainArea, align=uiconst.TOBOTTOM, settingsID='UITree_ResizeCont', minSize=50, maxSize=500)
    111         self.attributeScroll = Scroll(parent=resizeCont.mainCont, name='attributeScroll', align=uiconst.TOALL)
    112         self.scroll = Scroll(parent=mainArea, name='treeScroll')
    113         self._hiliteFrame = Fill(parent=uicore.desktop, align=uiconst.ABSOLUTE, color=(0, 1, 0, 0.3), idx=0, state=uiconst.UI_DISABLED)
    114         self._selectedFrame = Frame(parent=uicore.desktop, align=uiconst.ABSOLUTE, color=(0, 1, 0, 0.3), idx=0, state=uiconst.UI_DISABLED)
    115         self.ReloadUIRoots()
    116         uthread.new(self.UpdateInfo)
    117         debugger_parent_setting.on_change.connect(self._UpdateParent)
    118         if not debugger_parent_setting.is_equal(LAYER_DEFAULT):
    119             uthread.new(self._UpdateParent)
    120         self._keyDownCookie = uicore.uilib.RegisterForTriuiEvents([uiconst.UI_KEYDOWN], self.OnGlobalKeyDown)
    121 
    122     def OnSearchInputChange(self, *args):
    123         self.searchThread = timerstuff.AutoTimer(250, self.SearchTree)
    124 
    125     def SearchTree(self):
    126         self.searchThread = None
    127         self.filterString = self.searchInput.GetValue()
    128         if not self.filterString:
    129             self.searchResultParent.Hide()
    130             self.searchResultParent.Flush()
    131             return
    132         self.searchResultParent.Flush()
    133         res = []
    134         searchFor = self.filterString.lower()
    135 
    136         def Crawl(obj, path):
    137             if obj is self:
    138                 return
    139             if searchFor in obj.name.lower():
    140                 if path:
    141                     res.append((obj, path + '/ <b>' + obj.name + '</b>'))
    142                 else:
    143                     res.append((obj, '<b>' + obj.name + '</b>'))
    144             if hasattr(obj, 'children'):
    145                 for each in obj.children:
    146                     if path:
    147                         Crawl(each, path + '/' + obj.name)
    148                     else:
    149                         Crawl(each, obj.name)
    150 
    151         for root in uicore.uilib.rootObjects:
    152             Crawl(root, '')
    153 
    154         if res:
    155             for obj, path in res[:20]:
    156                 label = Label(parent=self.searchResultParent, align=uiconst.TOTOP, text=path, state=uiconst.UI_NORMAL, padding=(10, 2, 10, 2))
    157                 label._searchObj = obj
    158                 label.hint = path
    159                 label.OnClick = (self.OnSearchResultClick, obj)
    160 
    161             if len(res) > 20:
    162                 Label(parent=self.searchResultParent, align=uiconst.TOTOP, text='and even more... (%s found)' % len(res), padding=(10, 2, 10, 2))
    163         else:
    164             Label(parent=self.searchResultParent, align=uiconst.TOTOP, text='No Match!', padding=(10, 3, 10, 3))
    165         self.searchResultParent.Show()
    166 
    167     def OnSearchResultClick(self, obj):
    168         self.searchResultParent.Hide()
    169         self.ShowUIObject(obj)
    170 
    171     def GetSettingMenu(self, *args):
    172         menuData = MenuData()
    173         menuData.AddCheckbox('Show pick hilite', setting=show_pick_hilite_setting)
    174         menuData.AddCheckbox('Ignore fullscreen hilite', setting=ignore_full_screen_pick_setting)
    175         menuData.AddEntry('Configure parent', subMenuData=[MenuEntryDataRadioButton('Default', LAYER_DEFAULT, setting=debugger_parent_setting), MenuEntryDataRadioButton('uicore.layer.modal (above most things)', LAYER_MODAL, setting=debugger_parent_setting), MenuEntryDataRadioButton('uicore.desktop (above everything)', LAYER_DESKTOP, setting=debugger_parent_setting)])
    176         menuData.AddEntry('Reload Textures', self.ReloadTextures, texturePath=eveicon.refresh)
    177         return menuData
    178 
    179     def ReloadTextures(self):
    180         for resPath in blue.motherLode.GetNonCachedKeys() + blue.motherLode.GetCachedKeys():
    181             if resPath.lower().startswith('res:/ui'):
    182                 res = blue.motherLode.Lookup(resPath)
    183                 if res:
    184                     if hasattr(res, 'Reload'):
    185                         res.Reload()
    186 
    187     def FinalizeModeChange(self, registerSettings = True):
    188         super(UITree, self).FinalizeModeChange(registerSettings)
    189         self._UpdateParent()
    190 
    191     def _UpdateParent(self, *args):
    192         if debugger_parent_setting.is_equal(LAYER_DEFAULT):
    193             if self.IsFloating():
    194                 self.SetParent(uicore.layer.main, idx=0)
    195             else:
    196                 self.SetParent(uicore.layer.sidePanels, idx=0)
    197         elif debugger_parent_setting.is_equal(LAYER_MODAL):
    198             self.SetParent(uicore.layer.modal, idx=0)
    199         else:
    200             self.SetParent(uicore.desktop, idx=0)
    201 
    202     def ToggleCheckboxSetting(self, settingName, *args):
    203         checked = settings.user.ui.Get('uitree' + settingName, True)
    204         settings.user.ui.Set('uitree' + settingName, not checked)
    205 
    206     def Close(self, *args, **kwds):
    207         frame = getattr(self, '_hiliteFrame', None)
    208         if frame:
    209             frame.Close()
    210         if getattr(self, '_selectedFrame', None):
    211             self._selectedFrame.Close()
    212         super(UITree, self).Close(*args, **kwds)
    213 
    214     def OnGlobalKeyDown(self, *args, **kwds):
    215         if self.destroyed:
    216             return False
    217         ctrl = uicore.uilib.Key(uiconst.VK_CONTROL)
    218         alt = uicore.uilib.Key(uiconst.VK_MENU)
    219         if ctrl and not (uicore.uilib.mouseOver.IsUnder(self) or uicore.uilib.mouseOver is self):
    220             self.ShowUIObject(uicore.uilib.mouseOver)
    221         return True
    222 
    223     def UpdateInfo(self):
    224         while not self.destroyed:
    225             mo = uicore.uilib.mouseOver
    226             if mo:
    227                 infoText = 'MouseOver: %s' % mo.name
    228             else:
    229                 infoText = 'MouseOver: None'
    230             active = uicore.registry.GetActive()
    231             if active:
    232                 infoText += '<br>Active: %s' % active.name
    233             else:
    234                 infoText += '<br>Active: None'
    235             focus = uicore.registry.GetFocus()
    236             if focus:
    237                 infoText += '<br>Focus: %s' % (focus.name or getattr(focus, '__guid__', focus.__class__.__name__))
    238             else:
    239                 infoText += '<br>Focus: None'
    240             capture = uicore.uilib.GetMouseCapture()
    241             if capture:
    242                 infoText += '<br>Capture: %s' % (capture.name or getattr(capture, '__guid__', capture.__class__.__name__))
    243             else:
    244                 infoText += '<br>Capture: None'
    245             infoText += '<br>MouseX/Y: %s, %s' % (uicore.uilib.x, uicore.uilib.y)
    246             showHilite = settings.user.ui.Get('uitreeShowPickHilite', True)
    247             ignoreFullScreenPick = settings.user.ui.Get('uitreeIgnoreFullScreenPick', False)
    248             hiliteUIObject = None
    249             if showHilite:
    250                 if mo.IsUnder(self):
    251                     if mo.IsUnder(self.scroll):
    252                         uiObjectGetAbs = GetAttrs(mo, 'sr', 'node', 'uiObject', 'GetAbsolute')
    253                         if uiObjectGetAbs:
    254                             hiliteUIObject = GetAttrs(mo, 'sr', 'node', 'uiObject')
    255                 elif mo is not uicore.desktop and mo is not self and not mo.IsUnder(self) and self.state == uiconst.UI_NORMAL:
    256                     hiliteUIObject = mo
    257             if hiliteUIObject and ignoreFullScreenPick and isinstance(hiliteUIObject, (LayerCore, UIRoot)):
    258                 hiliteUIObject = None
    259             self.ShowHilitedObjectInUI(hiliteUIObject)
    260             selectedObject = None
    261             if self._selectedObject:
    262                 selectedObject = self._selectedObject()
    263                 if getattr(selectedObject, 'destroyed', False):
    264                     selectedObject = None
    265             for node in self.scroll.sr.nodes:
    266                 if node.panel:
    267                     node.panel.UpdateSelected(selectedObject)
    268                     node.panel.UpdatePickHilite(hiliteUIObject)
    269                     if getattr(node.uiObject, 'destroyed', False):
    270                         node.panel.CheckDestroyed()
    271 
    272             self.ShowSelectedObjectInUI(selectedObject)
    273             self._infoLabel.text = infoText
    274             self._infoContainer.height = self._infoLabel.textheight + 5
    275             blue.pyos.synchro.SleepWallclock(100)
    276 
    277     def ShowSelectedObjectInUI(self, uiObject):
    278         if uiObject and hasattr(uiObject, 'GetAbsolute'):
    279             self._selectedFrame.pos = uiObject.GetAbsolute()
    280             self._selectedFrame.display = True
    281         else:
    282             self._selectedFrame.display = False
    283 
    284     def ShowHilitedObjectInUI(self, uiObject):
    285         if self.destroyed:
    286             return
    287         if uiObject:
    288             self._hiliteFrame.pos = uiObject.GetAbsolute()
    289             self._hiliteFrame.display = True
    290         else:
    291             self._hiliteFrame.display = False
    292 
    293     def ReloadUIRoots(self, *args):
    294         startRoot = uicore.uilib.rootObjects
    295         scrollNodes = []
    296         for root in startRoot:
    297             self._CrawlUIObject(root, scrollNodes, 0)
    298 
    299         self.scroll.LoadContent(contentList=scrollNodes)
    300 
    301     def _AddUIObject(self, uiObject, scrollList, lvl, isExpanded = False, objectLabel = None, isExpandable = False):
    302         if len(scrollList) > 1 and lvl:
    303             for i in xrange(1, len(scrollList) - 1):
    304                 last = scrollList[-i]
    305                 if last.level < lvl:
    306                     break
    307                 if lvl not in last.connectLevels:
    308                     last.connectLevels.append(lvl)
    309 
    310         node = ScrollEntryNode(decoClass=UITreeEntry, uiObject=uiObject, level=lvl, isExpanded=isExpanded, objectLabel=objectLabel, isExpandable=isExpandable, connectLevels=[lvl])
    311         scrollList.append(node)
    312 
    313     def IsExpanded(self, uiObject):
    314         desktopObjectID = id(uicore.desktop)
    315         allPrefs = settings.user.ui.Get('UITreeExpandedObjects', {})
    316         if desktopObjectID not in allPrefs:
    317             return False
    318         uiObjectID = id(uiObject)
    319         if getattr(uiObject, 'parent', None):
    320             uiObjectID = (id(uiObject.parent), uiObjectID)
    321         return uiObjectID in allPrefs[desktopObjectID]
    322 
    323     def ExpandUIObject(self, uiObject):
    324         uiObjectID = id(uiObject)
    325         if getattr(uiObject, 'parent', None):
    326             uiObjectID = (id(uiObject.parent), uiObjectID)
    327         desktopObjectID = id(uicore.desktop)
    328         allPrefs = settings.user.ui.Get('UITreeExpandedObjects', {})
    329         if desktopObjectID not in allPrefs:
    330             allPrefs[desktopObjectID] = []
    331         if uiObjectID not in allPrefs[desktopObjectID]:
    332             allPrefs[desktopObjectID].append(uiObjectID)
    333         settings.user.ui.Set('UITreeExpandedObjects', allPrefs)
    334 
    335     def ToggleExpandedObject(self, uiObject):
    336         uiObjectID = id(uiObject)
    337         if getattr(uiObject, 'parent', None):
    338             uiObjectID = (id(uiObject.parent), uiObjectID)
    339         desktopObjectID = id(uicore.desktop)
    340         allPrefs = settings.user.ui.Get('UITreeExpandedObjects', {})
    341         if desktopObjectID not in allPrefs:
    342             allPrefs[desktopObjectID] = []
    343         if uiObjectID in allPrefs[desktopObjectID]:
    344             allPrefs[desktopObjectID].remove(uiObjectID)
    345         else:
    346             allPrefs[desktopObjectID].append(uiObjectID)
    347         settings.user.ui.Set('UITreeExpandedObjects', allPrefs)
    348         self.ReloadUIRoots()
    349 
    350     def _CrawlUIObject(self, uiObject, scrollNodes, lvl, objectLabel = None):
    351         isExpandable = UITree._IsExpandable(uiObject)
    352         if isExpandable:
    353             isExpanded = self.IsExpanded(uiObject)
    354         else:
    355             isExpanded = False
    356         self._AddUIObject(uiObject, scrollNodes, lvl, isExpanded, objectLabel=objectLabel, isExpandable=isExpandable)
    357         if isExpanded:
    358             if isinstance(uiObject, Base):
    359                 allPy = dir(uiObject)
    360                 for propertyName in allPy:
    361                     if propertyName.startswith('_') or propertyName in IGNORE_PROPERTIES:
    362                         continue
    363                     try:
    364                         prop = getattr(uiObject, propertyName, None)
    365                     except:
    366                         print('Failed on property', propertyName)
    367                         continue
    368 
    369                     try:
    370                         if getattr(prop, 'TypeInfo', None):
    371                             self._CrawlUIObject(prop, scrollNodes, lvl + 1, objectLabel=propertyName)
    372                     except (KeyError, RuntimeError):
    373                         pass
    374 
    375                 for dictName in PYOBJECT_SUBDICTS:
    376                     dct = getattr(uiObject, dictName, {})
    377                     if dct:
    378                         for k, v in dct.iteritems():
    379                             self._CrawlUIObject(v, scrollNodes, lvl + 1, objectLabel='%s: %s' % (dictName.lstrip('_'), k))
    380 
    381                 for listName in PYOBJECT_SUBLISTS:
    382                     lst = getattr(uiObject, listName, [])
    383                     if lst:
    384                         for objChild in lst:
    385                             if listName == 'background':
    386                                 self._CrawlUIObject(objChild, scrollNodes, lvl + 1, objectLabel=listName)
    387                             else:
    388                                 self._CrawlUIObject(objChild, scrollNodes, lvl + 1)
    389 
    390             else:
    391                 allC = dir(uiObject)
    392                 for propertyName in allC:
    393                     if propertyName.startswith('_'):
    394                         continue
    395                     prop = getattr(uiObject, propertyName, None)
    396                     if callable(prop):
    397                         continue
    398                     if getattr(prop, '__bluetype__', None) == 'blue.List':
    399                         isExpanded = self.IsExpanded(prop)
    400                         self._AddUIObject(prop, scrollNodes, lvl + 1, isExpanded, isExpandable=True, objectLabel='%s (%s)' % (propertyName, len(prop)))
    401                         if isExpanded:
    402                             for each in prop:
    403                                 self._CrawlUIObject(each, scrollNodes, lvl + 2)
    404 
    405                         continue
    406                     elif getattr(prop, '__bluetype__', None) == 'blue.Dict':
    407                         isExpanded = self.IsExpanded(prop)
    408                         self._AddUIObject(prop, scrollNodes, lvl + 1, isExpanded, isExpandable=True, objectLabel=propertyName)
    409                         if isExpanded:
    410                             for k, v in prop.items():
    411                                 self._CrawlUIObject(v, scrollNodes, lvl + 2, objectLabel=k)
    412 
    413                         continue
    414                     elif hasattr(prop, 'TypeInfo'):
    415                         self._CrawlUIObject(prop, scrollNodes, lvl + 1, objectLabel=propertyName)
    416 
    417     @classmethod
    418     def _IsExpandable(cls, uiObject):
    419         if isinstance(uiObject, Base):
    420             return True
    421         allC = dir(uiObject)
    422         for propertyName in allC:
    423             if propertyName.startswith('_'):
    424                 continue
    425             prop = getattr(uiObject, propertyName, None)
    426             if getattr(prop, '__bluetype__', None) in ('blue.List', 'blue.Dict'):
    427                 return True
    428             if hasattr(prop, 'TypeInfo'):
    429                 return True
    430 
    431         return False
    432 
    433     def ShowUIObject(self, uiObject):
    434         traceUp = [uiObject]
    435         parent = uiObject.parent
    436         while parent:
    437             traceUp.insert(0, parent)
    438             parent = parent.parent
    439 
    440         for each in traceUp:
    441             self.ExpandUIObject(each)
    442 
    443         self.ShowPropertiesForObject(uiObject)
    444         for node in self.scroll.sr.nodes:
    445             if node.uiObject is uiObject:
    446                 self.scroll.ShowNodeIdx(node.idx)
    447                 break
    448 
    449     def ShowPropertiesForObject(self, uiObject):
    450         if uiObject is None:
    451             return
    452         try:
    453             len(uiObject)
    454             self.attributeScroll.LoadContent(contentList=[])
    455             return
    456         except:
    457             pass
    458 
    459         self._selectedObject = weakref.ref(uiObject)
    460         level = 0
    461         newNodes = []
    462         if isinstance(uiObject, Base):
    463             combined = []
    464             if hasattr(uiObject, 'color'):
    465                 combined.append(('color', ('color.r', 'color.g', 'color.b', 'color.a')))
    466             for propertyName, subs in combined:
    467                 propertyNode = ScrollEntryNode(decoClass=UITreePropertyEntry, uiObject=uiObject, level=level, propertyName=propertyName, combineProperties=subs)
    468                 newNodes.append(propertyNode)
    469 
    470             basics = ['__class__',
    471              'name',
    472              'display',
    473              'opacity',
    474              'align',
    475              'pickState',
    476              'pos',
    477              'padding',
    478              'uniqueUiName',
    479              'elementId',
    480              'windowID',
    481              'windowInstanceID',
    482              'displayRect',
    483              'blendMode',
    484              'spriteEffect',
    485              'outputMode',
    486              'clipChildren',
    487              'pickRadius',
    488              'absoluteCoordinates',
    489              'cacheContents',
    490              'text',
    491              'effectAmount',
    492              'effectAmount2',
    493              'glowBrightness',
    494              'useSizeFromTexture',
    495              'rotation',
    496              'rotationCenter',
    497              'scale',
    498              'scalingCenter',
    499              'scalingRotation',
    500              '',
    501              '__guid__']
    502             for propertyName in basics:
    503                 prop = getattr(uiObject, propertyName, '_!_')
    504                 if prop == '_!_':
    505                     continue
    506                 propertyNode = ScrollEntryNode(decoClass=UITreePropertyEntry, uiObject=uiObject, level=level, propertyName=propertyName)
    507                 newNodes.append(propertyNode)
    508 
    509         else:
    510             for attr in dir(uiObject):
    511                 if attr[0] == attr[0].upper():
    512                     continue
    513                 if attr[0] == '_':
    514                     continue
    515                 if attr in ('children', 'background'):
    516                     continue
    517                 propertyNode = ScrollEntryNode(decoClass=UITreePropertyEntry, uiObject=uiObject, level=level, propertyName=attr)
    518                 newNodes.append((attr, propertyNode))
    519 
    520             newNodes = SortListOfTuples(newNodes)
    521         self.ReloadUIRoots()
    522         self.attributeScroll.LoadContent(contentList=newNodes)
    523 
    524 
    525 class UITreeNumberInput(Container):
    526     default_state = uiconst.UI_NORMAL
    527     _downPos = 0
    528 
    529     def ApplyAttributes(self, attributes):
    530         Container.ApplyAttributes(self, attributes)
    531         self._value = attributes.value
    532         self._propertyName = attributes.propertyName
    533         self._propertyIndex = attributes.propertyIndex
    534         self._uiObject = attributes.uiObject
    535         self._editType = type(self._value)
    536         self._label = eveLabel.EveLabelMedium(parent=self, state=uiconst.UI_DISABLED, align=uiconst.CENTERLEFT, text=str(self._value))
    537         if self._propertyName in COMBOPROPERTIES:
    538             self._label.text = COMBOPROPERTIES[self._propertyName].get(self._value, 'Unknown value!')
    539             self.expandOnLeft = True
    540         elif self._editType == float:
    541             self.hint = 'MouseDown and Drag to change value<br>ALT for 1/100, ALT-SHIFT for 1/1000'
    542             if -1 <= self._value <= 1:
    543                 self._label.text = '%.3f' % self._value
    544         elif self._editType == int:
    545             self.hint = 'MouseDown and Drag to change value'
    546         elif self._editType == type:
    547             try:
    548                 text = repr(self._value).replace("<class '", '').replace("'>", '')
    549                 text = text.split('.')[-1] + ' (%s)' % text
    550                 self._label.text = text
    551             except:
    552                 pass
    553 
    554         self.UpdateLabelColor()
    555         self.UpdateSize()
    556 
    557     def UpdateLabelColor(self):
    558         if self._propertyName == 'display' and self._value is False:
    559             color = TextColor.DANGER
    560         elif self._propertyName == 'opacity' and self._value == 0.0:
    561             color = TextColor.DANGER
    562         elif self._propertyName == 'opacity' and self._value > 1.0:
    563             color = TextColor.WARNING
    564         else:
    565             color = TextColor.NORMAL
    566         self._label.rgba = color
    567 
    568     def GetMenu(self):
    569         m = []
    570         if self._propertyName in COMBOPROPERTIES:
    571             m += self.GetComboOptions()
    572         if m:
    573             m.append(None)
    574         m.append(('Copy value', self.CopyValue))
    575         return m
    576 
    577     def CopyValue(self):
    578         blue.pyos.SetClipboardData(repr(self._value))
    579 
    580     def GetComboOptions(self, *args):
    581         m = []
    582         for value, name in COMBOPROPERTIES[self._propertyName].iteritems():
    583             m.append((name, self.ChangeComboOption, (value,)))
    584 
    585         return m
    586 
    587     def ChangeComboOption(self, newValue, *args):
    588         self._value = newValue
    589         self._label.text = COMBOPROPERTIES[self._propertyName].get(self._value, 'Unknown value!')
    590         self.UpdateSize()
    591         self.UpdateLabelColor()
    592         self.SetPropertyValue(self._value)
    593 
    594     def UpdateSize(self):
    595         self.width = self._label.textwidth + 6
    596 
    597     def OnClick(self, *args):
    598         if self._propertyName in COMBOPROPERTIES:
    599             return
    600         if self._propertyName == '__class__':
    601             OpenInEditor(self._uiObject)
    602         elif self._editType in (bool,):
    603             self._value = not self._value
    604             self.SetPropertyValue(self._value)
    605             self._label.text = str(self._value)
    606             self.UpdateLabelColor()
    607             self.UpdateSize()
    608 
    609     def OnMouseDown(self, *args):
    610         if self._propertyName in COMBOPROPERTIES:
    611             return
    612         if self._editType in (int, float):
    613             self._downPos = uicore.uilib.x
    614             self._updateValue = timerstuff.AutoTimer(10, self.ScrollValue)
    615 
    616     def OnMouseUp(self, *args):
    617         if self._propertyName in COMBOPROPERTIES:
    618             return
    619         if self._editType in (int, float):
    620             self._updateValue = None
    621             self.ScrollValue(register=True)
    622 
    623     def ScrollValue(self, register = False):
    624         posDiff = (uicore.uilib.x - self._downPos) / 2
    625         alt = uicore.uilib.Key(uiconst.VK_MENU)
    626         shift = uicore.uilib.Key(uiconst.VK_SHIFT)
    627         if self._editType == float:
    628             if shift:
    629                 newValue = round(self._value + posDiff / 1000.0, 3)
    630             elif alt:
    631                 newValue = round(self._value + posDiff / 100.0, 3)
    632             else:
    633                 newValue = round(self._value + posDiff / 10.0, 3)
    634         elif shift:
    635             newValue = self._value + posDiff / 4
    636         elif alt:
    637             newValue = self._value + posDiff / 2
    638         else:
    639             newValue = self._value + posDiff
    640         PROPERTYCAPS = {'opacity': (0.0, 5.0),
    641          'color.r': (0.0, 5.0),
    642          'color.g': (0.0, 5.0),
    643          'color.b': (0.0, 5.0),
    644          'color.a': (0.0, 5.0),
    645          'color': (0.0, 5.0),
    646          'pickRadius': (-1, sys.maxint)}
    647         for capName, (mn, mx) in PROPERTYCAPS.iteritems():
    648             if capName.lower() in self._propertyName.lower():
    649                 newValue = min(max(newValue, mn), mx)
    650 
    651         self.SetPropertyValue(newValue)
    652         if self._editType in (float,) and -1 <= newValue <= 1:
    653             self._label.text = '%.3f' % newValue
    654         else:
    655             self._label.text = str(newValue)
    656         if register:
    657             self._value = newValue
    658         self.UpdateLabelColor()
    659         self.UpdateSize()
    660 
    661     def SetPropertyValue(self, value):
    662         obj = self._uiObject
    663         propertyName = self._propertyName
    664         if '.' in self._propertyName:
    665             trace = self._propertyName.split('.')
    666             for each in trace[:-1]:
    667                 obj = getattr(obj, each, None)
    668                 if not obj:
    669                     return
    670 
    671             propertyName = trace[-1]
    672         if self._propertyIndex is not None:
    673             current = list(getattr(obj, propertyName))
    674             current[self._propertyIndex] = value
    675             value = tuple(current)
    676         try:
    677             setattr(obj, propertyName, value)
    678         except:
    679             pass
    680 
    681 
    682 INDENTSIZE = 18
    683 
    684 def GetClassPath(obj):
    685     ret = None
    686     module = obj.__module__
    687     exec 'import %s' % module in globals()
    688     exec 'ret = %s.__file__' % module
    689     return ret
    690 
    691 
    692 def OpenInEditor(uiObjectOrRenderObject):
    693     blue.os.ShellExecute(GetClassPath(uiObjectOrRenderObject), 'edit')
    694 
    695 
    696 class UITreeEntry(SE_BaseClassCore):
    697     ENTRYHEIGHT = 18
    698 
    699     def ApplyAttributes(self, attributes):
    700         SE_BaseClassCore.ApplyAttributes(self, attributes)
    701         self._expandIcon = Sprite(parent=self, align=uiconst.CENTERLEFT, pos=(0, 0, 18, 18))
    702         self._expandIcon.OnClick = self.ExpandUIObject
    703         self._label = Label(parent=self, state=uiconst.UI_DISABLED, align=uiconst.CENTERLEFT, left=20, color=(1.0, 1.0, 1.0, 1))
    704         self._pickHilite = Frame(name='_pickHilite', bgParent=self, texturePath=RESROOT + 'hiliteBackground.png', color=(0, 1, 0, 0.3), state=uiconst.UI_HIDDEN)
    705         self._hilite = Fill(name='_hilite', bgParent=self, color=(1, 1, 1, 0.125), state=uiconst.UI_HIDDEN)
    706         self.loaded = False
    707 
    708     def Load(self, node):
    709         self.CheckDestroyed()
    710         if self.loaded:
    711             return
    712         self.loaded = True
    713         if isinstance(node.uiObject, Base):
    714             self._label.text = node.uiObject.name or node.uiObject.__class__
    715             if node.objectLabel:
    716                 self._label.text = '%s: %s' % (node.objectLabel, self._label.text)
    717         else:
    718             self._label.italic = True
    719             if node.objectLabel:
    720                 self._label.text = node.objectLabel
    721             elif getattr(node.uiObject, 'name', None):
    722                 self._label.text = '%s (%s)' % (node.uiObject.name, node.uiObject.__typename__)
    723             else:
    724                 self._label.text = node.uiObject.__typename__
    725             self._label.color = (1.0, 1.0, 1.0, 0.5)
    726         self._label.left = INDENTSIZE * node.level + 20
    727         if hasattr(node.uiObject, 'Reload') or 'Reload' in getattr(node.uiObject, '__methods__', []):
    728             reloadIcon = Sprite(texturePath=RESROOT + 'tinyReload.png', pos=(3, 0, 16, 16), parent=self, align=uiconst.CENTERRIGHT, idx=0)
    729             reloadIcon.OnClick = self.ReloadUIObject
    730         if node.isExpandable:
    731             if node.isExpanded:
    732                 self._expandIcon.texturePath = RESROOT + 'containerExpanded_down.png'
    733             else:
    734                 self._expandIcon.texturePath = RESROOT + 'containerCollapsed.png'
    735         else:
    736             self._expandIcon.texturePath = RESROOT + 'nonContainer.png'
    737             self._expandIcon.state = uiconst.UI_DISABLED
    738         self._expandIcon.left = INDENTSIZE * node.level
    739         nextNode = node.scroll.GetNode(node.idx + 1)
    740         if node.connectLevels is not None:
    741             for i, connectLevel in enumerate(node.connectLevels):
    742                 if connectLevel == node.level:
    743                     if not nextNode or connectLevel not in nextNode.connectLevels:
    744                         texturePath = RESROOT + 'connectionL.png'
    745                     else:
    746                         texturePath = RESROOT + 'connectionT.png'
    747                 else:
    748                     texturePath = RESROOT + 'connectionI.png'
    749                 Sprite(parent=self, texturePath=texturePath, align=uiconst.CENTERLEFT, pos=((connectLevel - 1) * INDENTSIZE,
    750                  0,
    751                  INDENTSIZE,
    752                  INDENTSIZE))
    753 
    754     def ReloadUIObject(self, *args):
    755         if hasattr(self.sr.node.uiObject, 'Reload'):
    756             self.CallUIObjectAndReload('Reload', True, self.sr.node.uiObject)
    757         elif 'Reload' in self.sr.node.uiObject.__methods__:
    758             self.CallUIObjectAndReload('Reload')
    759 
    760     def CheckDestroyed(self):
    761         if isinstance(self.sr.node.uiObject, Base) and self.sr.node.uiObject.destroyed:
    762             if not getattr(self, '_destroyedIndicator', None):
    763                 self._destroyedIndicator = Fill(parent=self, idx=0, color=(1, 0, 0, 0.3))
    764             self.state = uiconst.UI_DISABLED
    765 
    766     def UpdateSelected(self, selectedObject):
    767         if self.sr.node.uiObject and selectedObject is self.sr.node.uiObject:
    768             if not getattr(self, '_selectedSprite', None):
    769                 self._selectedSprite = Frame(bgParent=self, color=(0, 1, 0, 0.3), padding=(1, 0, 1, 0))
    770             self._selectedSprite.display = True
    771         elif getattr(self, '_selectedSprite', None):
    772             self._selectedSprite.display = False
    773 
    774     def UpdatePickHilite(self, hilitedObject):
    775         if hilitedObject and self.sr.node.uiObject:
    776             wnd = UITree.GetIfOpen()
    777             if hilitedObject is self.sr.node.uiObject:
    778                 self._pickHilite.display = True
    779                 self._pickHilite.spriteEffect = trinity.TR2_SFX_FILL
    780             elif hilitedObject.IsUnder(self.sr.node.uiObject) and not (wnd and uicore.uilib.mouseOver.IsUnder(wnd.scroll)):
    781                 self._pickHilite.display = True
    782                 self._pickHilite.spriteEffect = trinity.TR2_SFX_COPY
    783             else:
    784                 self._pickHilite.display = False
    785         else:
    786             self._pickHilite.display = False
    787 
    788     def GetMenu(self):
    789         m = MenuData()
    790         if isinstance(self.sr.node.uiObject, Base):
    791             if hasattr(self.sr.node.uiObject, 'Reload'):
    792                 m.AddEntry('Reload', lambda : self.CallUIObjectAndReload('Reload', True, self.sr.node.uiObject))
    793             if hasattr(self.sr.node.uiObject, 'Play'):
    794                 m.AddEntry('Play', lambda : self.CallUIObjectAndReload('Play', True))
    795             m.AddEntry('Close', lambda : self.CallUIObjectAndReload('Close'), texturePath=eveicon.close)
    796             if hasattr(self.sr.node.uiObject, 'Flush'):
    797                 m.AddEntry('Flush', lambda : self.CallUIObjectAndReload('Flush'))
    798             m.AddEntry('Move Up', lambda : self.MoveUIObject(-1))
    799             m.AddEntry('Move Down', lambda : self.MoveUIObject(1))
    800             m.AddSeparator()
    801             m.AddEntry('Open In Jessica', lambda : self.OpenInJessica(self.sr.node.uiObject), texturePath=eveicon.look_at)
    802             m.AddEntry('Open In Graphite', lambda : self.OpenInGraphite(self.sr.node.uiObject), texturePath=eveicon.look_at)
    803             m.AddSeparator()
    804             m.AddEntry('Open Class In Editor', lambda : OpenInEditor(self.sr.node.uiObject), texturePath=eveicon.open_window)
    805             m.AddEntry('Copy Name', lambda : self.CopyName(self.sr.node.uiObject.name))
    806         elif self.sr.node.uiObject:
    807             if 'Reload' in self.sr.node.uiObject.__methods__:
    808                 m.AddEntry('Reload', lambda : self.CallUIObjectAndReload('Reload'))
    809             if hasattr(self.sr.node.uiObject, 'name') and self.sr.node.uiObject.name not in ('children', 'background'):
    810                 m.AddEntry('Open In Jessica', lambda : self.OpenInJessica(self.sr.node.uiObject))
    811                 m.AddEntry('Open In Graphite', lambda : self.OpenInGraphite(self.sr.node.uiObject))
    812         return m
    813 
    814     def CallUIObjectAndReload(self, functionName, setFocus = False, *args):
    815         func = getattr(self.sr.node.uiObject, functionName, None)
    816         if func:
    817             returnValue = func(*args)
    818             self.ReloadUITree()
    819             if setFocus and returnValue:
    820                 wnd = UITree.GetIfOpen()
    821                 if wnd:
    822                     uthread.new(wnd.ShowUIObject, returnValue)
    823 
    824     def MoveUIObject(self, direction):
    825         uiObject = self.sr.node.uiObject
    826         currentIndex = uiObject.parent.children.index(uiObject)
    827         uiObject.SetParent(uiObject.parent, direction + currentIndex)
    828         self.ReloadUITree()
    829 
    830     def ReloadUITree(self):
    831         wnd = GetWindowAbove(self)
    832         if wnd:
    833             wnd.ReloadUIRoots()
    834 
    835     def CopySourceToClipboard(self):
    836         uiObject = self.sr.node.uiObject
    837         objClass = uiObject.__class__
    838         wrap = '%s = %s(\n' % (uiObject.name, uiObject.__class__.__name__)
    839         defaultAttrs = [ each for each in dir(objClass) if each.startswith('default_') ]
    840         for parentClass in objClass.__bases__:
    841             for each in dir(parentClass):
    842                 if each.startswith('default_') and each not in defaultAttrs:
    843                     defaultAttrs.append(each)
    844 
    845         for defaultAttr in defaultAttrs:
    846             attr = defaultAttr[8:]
    847             defaultAttrValue = getattr(objClass, defaultAttr)
    848             objAttrValue = getattr(uiObject, attr, '__attr_notset__')
    849             if objAttrValue != '__attr_notset__' and objAttrValue != defaultAttrValue:
    850                 if isinstance(objAttrValue, basestring):
    851                     wrap += '    %s="%s",\n' % (attr, objAttrValue)
    852                 elif attr == 'color':
    853                     wrap += '    %s=%s,\n' % (attr, objAttrValue.GetRGBA())
    854                 elif attr == 'parent':
    855                     wrap += '    %s=%s,\n' % (attr, objAttrValue.name)
    856                 elif attr in ('state',):
    857                     continue
    858                 elif attr in COMBOPROPERTIES:
    859                     objAttrValue = COMBOPROPERTIES[attr].get(objAttrValue, 'Unknown value!')
    860                     nameSpace = COMBOPROPERTIES_NAMESPACE.get(attr, 'Unknown namespace')
    861                     wrap += '    %s=%s.%s,\n' % (attr, nameSpace, objAttrValue)
    862                 else:
    863                     wrap += '    %s=%s,\n' % (attr, objAttrValue)
    864 
    865         wrap += '    )'
    866 
    867     def ExpandUIObject(self, *args):
    868         wnd = GetWindowAbove(self)
    869         if wnd:
    870             wnd.ToggleExpandedObject(self.sr.node.uiObject)
    871 
    872     def OnClick(self, *args):
    873         wnd = GetWindowAbove(self)
    874         uiObject = self.sr.node.uiObject
    875         if uicore.uilib.Key(uiconst.VK_MENU):
    876             uiObject.display = not uiObject.display
    877         if wnd:
    878             wnd.ShowPropertiesForObject(uiObject)
    879 
    880     def OpenInJessica(self, uiObjectOrRenderObject):
    881         try:
    882             import wx
    883             tree = wx.FindWindowByName('TreeView2')
    884             tree.AddUITree(uiObjectOrRenderObject.name, uiObjectOrRenderObject)
    885         except ImportError:
    886             pass
    887 
    888     def OpenInGraphite(self, obj):
    889         try:
    890             from platformtools.compatibility.exposure.graphite.graphiteutils import IsGraphiteAttached, createTreeViewForItem
    891             if IsGraphiteAttached():
    892                 createTreeViewForItem(obj)
    893         except ImportError:
    894             pass
    895 
    896     def CopyName(self, name):
    897         blue.pyos.SetClipboardData(name)
    898 
    899     def OnMouseExit(self, *args):
    900         self._hilite.display = False
    901 
    902     def OnMouseEnter(self, *args):
    903         self._hilite.display = True
    904 
    905 
    906 class UITreePropertyEntry(SE_BaseClassCore):
    907     ENTRYHEIGHT = 18
    908 
    909     def ApplyAttributes(self, attributes):
    910         SE_BaseClassCore.ApplyAttributes(self, attributes)
    911         cnt = Container(parent=self, pos=(0, 0, 0, 0), state=uiconst.UI_PICKCHILDREN)
    912         self._label = eveLabel.EveLabelMedium(parent=cnt, state=uiconst.UI_DISABLED, align=uiconst.CENTERLEFT)
    913         self._valueContainer = Container(parent=cnt, state=uiconst.UI_PICKCHILDREN, align=uiconst.TOALL, padLeft=VALUEOFFSET)
    914         self._hilite = Fill(bgParent=self, color=(1, 1, 1, 0.125), state=uiconst.UI_HIDDEN)
    915         self._mainContainer = cnt
    916 
    917     def Load(self, node):
    918         self._mainContainer.padLeft = 6 + 16 * node.level
    919         self._label.text = node.propertyName
    920         self._valueContainer.Flush()
    921         if node.combineProperties:
    922             for propertyName in node.combineProperties:
    923                 currentValue = GetAttrs(node.uiObject, *tuple(propertyName.split('.')))
    924                 inpt = UITreeNumberInput(parent=self._valueContainer, value=currentValue, uiObject=node.uiObject, propertyName=propertyName, align=uiconst.TOLEFT)
    925 
    926         else:
    927             currentValue = GetAttrs(node.uiObject, *tuple(node.propertyName.split('.')))
    928             if isinstance(currentValue, (tuple, list)):
    929                 for i in xrange(len(currentValue)):
    930                     inpt = UITreeNumberInput(parent=self._valueContainer, value=currentValue[i], uiObject=node.uiObject, propertyName=node.propertyName, propertyIndex=i, align=uiconst.TOLEFT)
    931 
    932             else:
    933                 inpt = UITreeNumberInput(parent=self._valueContainer, value=currentValue, uiObject=node.uiObject, propertyName=node.propertyName, align=uiconst.TOLEFT)
    934 
    935     def OnMouseExit(self, *args):
    936         self._hilite.display = False
    937 
    938     def OnMouseEnter(self, *args):
    939         self._hilite.display = True
    940 
    941     def GetMenu(self):
    942         m = []
    943         m.append(('Copy', self.CopyItem))
    944         m.append(('Copy value', self.CopyValue))
    945         m.append(('Copy All Properties', self.CopyAll))
    946         return m
    947 
    948     def CopyAll(self):
    949         allNodes = self.sr.node.scroll.sr.nodes
    950         allCopyStrings = []
    951         for node in allNodes:
    952             allCopyStrings.append(self._GetCopyString(node))
    953 
    954         blue.pyos.SetClipboardData('\n'.join(allCopyStrings))
    955 
    956     def CopyItem(self):
    957         blue.pyos.SetClipboardData(self._GetCopyString(self.sr.node))
    958 
    959     def CopyValue(self):
    960         blue.pyos.SetClipboardData(self._GetValueString(self.sr.node))
    961 
    962     def _GetCopyString(self, node):
    963         return '%s = %s' % (node.propertyName, self._GetValueString(node))
    964 
    965     def _GetValueString(self, node):
    966         propertyName = node.propertyName
    967         currentValue = getattr(node.uiObject, propertyName, '__notfound__')
    968         if propertyName in COMBOPROPERTIES:
    969             objAttrValue = COMBOPROPERTIES[propertyName].get(currentValue, 'Unknown value!')
    970             nameSpace = COMBOPROPERTIES_NAMESPACE.get(propertyName, 'Unknown namespace')
    971             currentValue = '%s.%s' % (nameSpace, objAttrValue)
    972         return str(currentValue)