~ubuntu-branches/ubuntu/raring/wxwidgets2.8/raring

« back to all changes in this revision

Viewing changes to .pc/xrced_bitmaps.dpatch/wxPython/wx/tools/XRCed/component.py

  • Committer: Package Import Robot
  • Author(s): Stéphane Graber
  • Date: 2012-01-07 13:59:25 UTC
  • mfrom: (1.1.9) (5.1.10 sid)
  • Revision ID: package-import@ubuntu.com-20120107135925-2601miy9ullcon9j
Tags: 2.8.12.1-6ubuntu1
* Resync from Debian, changes that were kept:
  - debian/rules: re-enable mediactrl. This allows libwx_gtk2u_media-2.8 to be
    built, as this is required by some applications (LP: #632984)
  - debian/control: Build-dep on libxt-dev for mediactrl.
  - Patches
    + fix-bashism-in-example
* Add conflict on python-wxgtk2.8 (<< 2.8.12.1-6ubuntu1~) to python-wxversion
  to guarantee upgrade ordering when moving from pycentral to dh_python2.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Name:         component.py
2
 
# Purpose:      base component classes
3
 
# Author:       Roman Rolinsky <rolinsky@femagsoft.com>
4
 
# Created:      31.05.2007
5
 
# RCS-ID:       $Id: component.py 64107 2010-04-22 14:05:36Z ROL $
6
 
 
7
 
"""
8
 
Component plugin classes.
9
 
 
10
 
This module defines base classes and for deriving component plugin
11
 
classes and contains some global variables used to register components
12
 
with XRCed.
13
 
 
14
 
Components are objects of Component class or one of the derived
15
 
classes used to define specialized components (such as sizers). After
16
 
a component object is constructed and configured it can be registered
17
 
using the Manager global object.
18
 
 
19
 
"""
20
 
 
21
 
 
22
 
import os,sys,bisect
23
 
import wx
24
 
from sets import Set
25
 
from globals import *
26
 
from model import Model
27
 
from attribute import *
28
 
import params
29
 
import view
30
 
import images
31
 
 
32
 
DEFAULT_POS = (1000,1000)
33
 
 
34
 
# Group compatibility specifications. 
35
 
# Key is the parent group, value is the list of child groups.
36
 
# !value means named main group is excluded from possible children.
37
 
# "root" is a special group for the tree root
38
 
parentChildGroups = {
39
 
    'root': ['top_level', 'component'],      # top-level objects
40
 
    'frame': ['toolbar', 'menubar', 'statusbar'],
41
 
    'mdi_parent_frame': ['toolbar', 'menubar', 'statusbar', 'mdi_child_frame'],
42
 
    'mdi_child_frame': ['toolbar', 'menubar', 'statusbar', 
43
 
                        'control', 'window', 'sizer', 'btnsizer', 
44
 
                        '!frame', '!mdi_child_frame'],
45
 
    'wizard': ['wizard_page'],
46
 
    'window': ['control', 'window', 'sizer', 'btnsizer', '!frame', '!mdi_child_frame'],
47
 
    'sizer': ['control', 'sizer', 'btnsizer', 'spacer'],
48
 
    'book': ['control', 'window', '!sizer', '!btnsizer'],
49
 
    'btnsizer': ['stdbtn'],
50
 
    'menubar': ['menu'],
51
 
    'toolbar': ['tool', 'separator'],
52
 
    'menu': ['menu', 'menu_item', 'separator'],
53
 
}
54
 
'''
55
 
Definition of compatibility of component groups as I{key}:I{group_list} pairs, where
56
 
I{key} is a parent group name and I{group_list} is a list of children group names or
57
 
group names prefixed with '!' character to exclude components having corresponding
58
 
primary group. This dictionary can be modified by component plugins directly
59
 
(some care must be taken not to redefine existing relations or breake them).
60
 
'''
61
 
 
62
 
 
63
 
class Component(object):
64
 
    '''Base class for component plugins.'''
65
 
    # Common window attributes
66
 
    windowAttributes = ['fg', 'bg', 'font', 'tooltip', 'help', 
67
 
                        'enabled', 'focused', 'hidden']
68
 
    '''Default window attributes for window-like components.'''
69
 
    genericStyles = [
70
 
        'wxSIMPLE_BORDER', 'wxSUNKEN_BORDER', 'wxDOUBLE_BORDER',
71
 
        'wxRAISED_BORDER', 'wxSTATIC_BORDER', 'wxNO_BORDER',
72
 
        'wxCLIP_CHILDREN', 'wxTRANSPARENT_WINDOW', 'wxWANTS_CHARS',
73
 
        'wxNO_FULL_REPAINT_ON_RESIZE', 'wxFULL_REPAINT_ON_RESIZE'
74
 
        ]
75
 
    '''Default generic styles.'''
76
 
    genericExStyles = [
77
 
        'wxWS_EX_VALIDATE_RECURSIVELY',
78
 
        'wxWS_EX_BLOCK_EVENTS',
79
 
#        'wxWS_EX_TRANSIENT',   # not processed by XRC (yet?)
80
 
        'wxWS_EX_PROCESS_IDLE',
81
 
        'wxWS_EX_PROCESS_UI_UPDATES'
82
 
        ]
83
 
    '''Default generic extended styles.'''
84
 
    genericEvents = [
85
 
        'EVT_WINDOW_CREATE', 'EVT_WINDOW_DESTROY',
86
 
        'EVT_MOVE', 'EVT_SIZE',
87
 
        'EVT_MOUSE_EVENTS', 'EVT_MOTION',
88
 
        'EVT_LEFT_DOWN', 'EVT_LEFT_DCLICK', 
89
 
        'EVT_MIDDLE_DOWN', 'EVT_MIDDLE_DCLICK', 
90
 
        'EVT_RIGHT_DOWN', 'EVT_RIGHT_DCLICK',  'EVT_MOUSEWHEEL',
91
 
        'EVT_ENTER_WINDOW', 'EVT_LEAVE_WINDOW', 
92
 
        'EVT_KEY_DOWN', 'EVT_KEY_UP', 'EVT_CHAR',
93
 
        'EVT_PAINT', 'EVT_ERASE_BACKGROUND',
94
 
        'EVT_CONTEXT_MENU', 'EVT_HELP',
95
 
        'EVT_SET_FOCUS', 'EVT_KILL_FOCUS', 'EVT_CHILD_FOCUS',
96
 
        'EVT_UPDATE_UI', 'EVT_IDLE',
97
 
        ]
98
 
    '''Default generic events.'''
99
 
    hasName = True                      # most elements have XRC IDs
100
 
    '''True if component has an XRC ID attribute.'''
101
 
    isTopLevel = False                  # if can be created as top level window
102
 
    '''True if component can be a top-level object in XML tree.'''
103
 
    renameDict = {}
104
 
    '''Dictionary of I{old_name}:I{new_name} for renaming some attributes
105
 
    in the Attribute Panel.'''
106
 
    hasCode = True
107
 
    '''True if component can generate code.'''
108
 
    
109
 
    def __init__(self, klass, groups, attributes, **kargs):
110
 
        '''
111
 
        Construct a new Component object. 
112
 
 
113
 
        @param klass: Interface element class name (e.g. C{'wxButton'}).
114
 
        @param groups: List of group names to which this component belongs.
115
 
        First group is considered to be the I{primary group}.
116
 
        @param attributes: List of XRC attribute names.
117
 
 
118
 
        B{Supported keyword parameters:}
119
 
 
120
 
        @keyword defaults: Dictionary of default attribute values for creating
121
 
        new items.
122
 
        @keyword specials: Dictionary of I{attribute_name}:I{attribute_class} pairs
123
 
         for specifying special attribute classes for some attributes, instead of
124
 
         using default Attribute class.
125
 
        @keyword params: Dictionary of pairs I{attribute_name}:I{param_class} where
126
 
         I{param_class} is a attribute interface class (one of classes in
127
 
         params.py or a custom class). If a param class is not specified, a default
128
 
         value defined by C{paramDict} dictionary is used.
129
 
        @keyword image,images: C{wx.Image} object or a list of C{wx.Image} objects
130
 
         for tree icons.
131
 
        @keyword events: List of event names for code generation panel.
132
 
        '''
133
 
        self.klass = klass
134
 
        self.groups = groups
135
 
        self.attributes = attributes
136
 
        self.styles = []
137
 
        self.exStyles = []
138
 
        self.defaults = kargs.pop('defaults', {})
139
 
        # Special Attribute classes if required
140
 
        self.specials = kargs.pop('specials', {})
141
 
        # Some default special attributes
142
 
        self.specials['font'] = FontAttribute
143
 
        self.specials['XRCED'] = CodeAttribute
144
 
        # Special Param classes if required
145
 
        self.params = kargs.pop('params', {})
146
 
        # Tree image
147
 
        if 'images' in kargs:
148
 
            self.images = kargs.pop('images')
149
 
        elif 'image' in kargs:
150
 
            self.images = [kargs.pop('image')]
151
 
        elif not 'image' in self.__dict__:
152
 
            self.images = []
153
 
        # Code generation data
154
 
        self.events = kargs.pop('events', [])
155
 
 
156
 
    def addStyles(self, *styles):
157
 
        '''Add more styles.'''
158
 
        self.styles.extend(styles)
159
 
 
160
 
    def addExStyles(self, *styles):
161
 
        '''Add more extra styles.'''
162
 
        self.exStyles.extend(styles)
163
 
 
164
 
    def setSpecial(self, attrName, attrClass):
165
 
        '''Set special attribute class for processing XML.
166
 
 
167
 
        @param attrName: Attribute name.
168
 
        @param attrClass: Attribute class.
169
 
        '''
170
 
        self.specials[attrName] = attrClass
171
 
 
172
 
    def setParamClass(self, attrName, paramClass):
173
 
        '''Set special attribute panel class for editing attribute value.
174
 
        
175
 
        @param attrName: Attribute name.
176
 
        @param paramClass: Param class.
177
 
        '''
178
 
        self.params[attrName] = paramClass
179
 
 
180
 
    def getTreeImageId(self, node):
181
 
        try:
182
 
            return self.images[0].Id
183
 
        except IndexError:
184
 
            if self.isContainer():
185
 
                return 1
186
 
            return 0
187
 
 
188
 
    def getTreeText(self, node):
189
 
        if node.nodeType == node.COMMENT_NODE:
190
 
            return node.data
191
 
        if node.tagName == 'object_ref':
192
 
            ref = node.getAttribute('ref')
193
 
            label = 'ref: %s' % ref
194
 
        else:
195
 
            label = node.getAttribute('subclass')
196
 
            if not label:
197
 
                label = node.getAttribute('class')
198
 
        if self.hasName:
199
 
            name = node.getAttribute('name')
200
 
            if name: label += ' "%s"' % name
201
 
        return label
202
 
 
203
 
    def addEvents(self, *events):
204
 
        '''Add more events.'''
205
 
        self.events.extend(events)
206
 
 
207
 
    # Order components having same index by group and klass
208
 
    def __cmp__(self, other):
209
 
        if self.groups < other.groups: return -1
210
 
        elif self.groups == other.groups: 
211
 
            if self.klass < other.klass: return -1
212
 
            elif self.klass == other.klass: return 0
213
 
            else: return 1
214
 
        else: return 1
215
 
 
216
 
    def __repr__(self):
217
 
        return "Component('%s', %s)" % (self.klass, self.attributes)
218
 
 
219
 
    def canHaveChild(self, component):
220
 
        '''True if the current component can have child of given type.
221
 
 
222
 
        This function is redefined by container classes.'''
223
 
        return False
224
 
 
225
 
    def canBeReplaced(self, component):
226
 
        '''True if the current component can be replaced by component.
227
 
 
228
 
        This function can be redefined by derived classes.'''
229
 
        return component.groups == groups
230
 
 
231
 
    def isContainer(self):
232
 
        '''True if component is a container (can have child nodes).'''
233
 
        return isinstance(self, Container)
234
 
 
235
 
    def getAttribute(self, node, attribute):
236
 
        attrClass = self.specials.get(attribute, Attribute)
237
 
        # 'object' means attribute is a text node without element tag,
238
 
        if attribute == 'object':
239
 
            return attrClass.get(node)
240
 
        elif issubclass(attrClass, AttributeAttribute):
241
 
            return attrClass.getAA(node, attribute)
242
 
        for n in node.childNodes:
243
 
            if n.nodeType == node.ELEMENT_NODE and n.tagName == attribute:
244
 
                return attrClass.get(n)
245
 
        return attrClass.get(None)
246
 
 
247
 
    def addAttribute(self, node, attribute, value):
248
 
        '''Add attribute element.'''
249
 
        attrClass = self.specials.get(attribute, Attribute)
250
 
        attrClass.add(node, attribute, value)
251
 
 
252
 
    def makeTestWin(self, res, name):
253
 
        '''Method can be overrided by derived classes to create custom test view.
254
 
 
255
 
        @param res: C{wx.xrc.XmlResource} object with current test resource.
256
 
        @param name: XRC ID of tested object.
257
 
        '''
258
 
        if not self.hasName: raise NotImplementedError
259
 
 
260
 
        testWin = view.testWin
261
 
        if self.isTopLevel:
262
 
            # Top-level window creates frame itself
263
 
            frame = None
264
 
            object = res.LoadObject(view.frame, STD_NAME, self.klass)
265
 
            object.Fit()
266
 
            testWin.size = object.GetSize()
267
 
        else:
268
 
            # Create MiniFrame to hold selected subtree
269
 
            frame = testWin.frame
270
 
            if not frame:
271
 
                frame = wx.MiniFrame(view.frame, -1, '%s: %s' % (self.klass, name), name=STD_NAME,
272
 
                                     style=wx.CAPTION|wx.CLOSE_BOX|wx.RESIZE_BORDER)
273
 
                frame.panel = wx.Panel(frame)
274
 
            object = res.LoadObject(frame.panel, STD_NAME, self.klass)
275
 
            if not object: raise NotImplementedError
276
 
            if not isinstance(object, wx.Window): raise NotImplementedError
277
 
            object.SetPosition((10,10))
278
 
            if g.conf.fitTestWin: 
279
 
                object.Fit()
280
 
                if frame:
281
 
                    frame.SetClientSize(object.GetSize()+(20,20))
282
 
                    testWin.size = frame.GetSize()
283
 
        return frame, object
284
 
 
285
 
    def getRect(self, obj):
286
 
        '''Return bounding box coordinates for C{obj}.'''
287
 
        # Object's rect must be relative to testWin.object
288
 
        if isinstance(obj, wx.Window):
289
 
            return [obj.GetRect()]
290
 
        elif isinstance(obj, wx.Rect): # spacer
291
 
            return [obj]
292
 
        else:
293
 
            return None
294
 
 
295
 
    def copyAttributes(self, srcNode, dstNode):
296
 
        '''Copy relevant attribute nodes from srcNode to dstNode.'''
297
 
        dstComp = Manager.getNodeComp(dstNode)
298
 
        if dstComp.hasName:
299
 
            dstNode.setAttribute('name', srcNode.getAttribute('name'))
300
 
        for n in srcNode.childNodes:
301
 
            if n.nodeType == n.ELEMENT_NODE:
302
 
                a = n.tagName
303
 
                # Check if attributes are compatible
304
 
                srcAttrClass = self.specials.get(a, Attribute)
305
 
                dstAttrClass = dstComp.specials.get(a, Attribute)
306
 
                if srcAttrClass is not dstAttrClass: continue
307
 
                srcParamClass = self.params.get(a, params.paramDict.get(a, params.ParamText))
308
 
                dstParamClass = dstComp.params.get(a, params.paramDict.get(a, params.ParamText))
309
 
                if srcParamClass is not dstParamClass: continue
310
 
                # Style and exstyle are not in attributes and can be treated specially
311
 
                if a == 'style':
312
 
                    styles = self.getAttribute(srcNode, a).split('|')
313
 
                    allStyles = dstComp.styles + self.genericStyles
314
 
                    dstStyles = [s for s in styles if s.strip() in allStyles]
315
 
                    if dstStyles:
316
 
                        dstComp.addAttribute(dstNode, a, '|'.join(dstStyles))
317
 
                elif a == 'exstyle':
318
 
                    styles = self.getAttribute(srcNode, a).split('|')
319
 
                    allStyles = dstComp.exStyles + self.genericExStyles
320
 
                    dstStyles = [s for s in styles if s.strip() in allStyles]
321
 
                    if dstStyles:
322
 
                        dstComp.addAttribute(dstNode, a, '|'.join(dstStyles))
323
 
                elif a in dstComp.attributes:
324
 
                    value = self.getAttribute(srcNode, a)
325
 
                    dstComp.addAttribute(dstNode, a, value)
326
 
 
327
 
    def copyImplicitAttributes(self, srcNode, dstNode, dstComp):
328
 
        '''Copy relevant implicit attribute nodes from srcNode to dstNode.'''
329
 
        for n in srcNode.childNodes:
330
 
            if n.nodeType == n.ELEMENT_NODE and not is_object(n):
331
 
                a = n.tagName
332
 
                if a in dstComp.implicitAttributes:
333
 
                    value = self.getAttribute(srcNode, a)
334
 
                    dstComp.addAttribute(dstNode, a, value)
335
 
 
336
 
 
337
 
class SimpleComponent(Component):
338
 
    '''Component without window attributes and styles.'''
339
 
    windowAttributes = []
340
 
    genericStyles = genericExStyles = []
341
 
 
342
 
 
343
 
class Container(Component):
344
 
    '''Base class for containers.'''
345
 
    def canHaveChild(self, component):
346
 
        # Test exclusion first
347
 
        for g in self.groups:
348
 
            if '!'+component.groups[0] in parentChildGroups.get(g, []): return False
349
 
        # Test for any possible parent-child
350
 
        groups = Set(component.groups)
351
 
        for g in self.groups:
352
 
            if groups.intersection(parentChildGroups.get(g, [])):
353
 
                return True
354
 
        return False
355
 
 
356
 
    def isSizer(self):
357
 
        '''If this container manages children positions and sizes.'''
358
 
        return False
359
 
 
360
 
    def requireImplicit(self, node):
361
 
        '''If there are implicit nodes for this particular node.'''
362
 
        return False
363
 
 
364
 
    def getTreeNode(self, node):
365
 
        '''Some containers may hide some internal elements.'''
366
 
        return node
367
 
 
368
 
    def getTreeOrImplicitNode(self, node):
369
 
        '''Return topmost child (implicit if exists).'''
370
 
        return node
371
 
 
372
 
    def appendChild(self, parentNode, node):
373
 
        '''Append child node. Can be overriden to create implicit nodes.'''
374
 
        parentNode.appendChild(node)
375
 
 
376
 
    def insertBefore(self, parentNode, node, nextNode):
377
 
        '''Insert node before nextNode. Can be overriden to create implicit nodes.'''
378
 
        parentNode.insertBefore(node, nextNode)
379
 
 
380
 
    def insertAfter(self, parentNode, node, prevNode):
381
 
        '''Insert node after prevNode. Can be overriden to create implicit nodes.'''
382
 
        parentNode.insertBefore(node, prevNode.nextSibling)
383
 
 
384
 
    def removeChild(self, parentNode, node):
385
 
        '''
386
 
        Remove node and the implicit node (if present). Return
387
 
        top-level removed child.
388
 
        '''
389
 
        return parentNode.removeChild(node)
390
 
 
391
 
    def copyObjects(self, srcNode, dstNode):
392
 
        # Copy child objects only for the same group
393
 
        dstComp = Manager.getNodeComp(dstNode)
394
 
        if self.groups[0] != dstComp.groups[0]: return
395
 
        children = []
396
 
        for n in filter(is_object, srcNode.childNodes):
397
 
            n = self.getTreeNode(n)
398
 
            if dstComp.canHaveChild(Manager.getNodeComp(n)):
399
 
                dstComp.appendChild(dstNode, n)
400
 
 
401
 
    def replaceChild(self, parentNode, newNode, oldNode):
402
 
        '''Replace oldNode by newNode keeping relevant attributes.'''
403
 
        # Keep compatible children
404
 
        oldComp = Manager.getNodeComp(oldNode)
405
 
        oldComp.copyAttributes(oldNode, newNode)
406
 
        if oldComp.isContainer():
407
 
            oldComp.copyObjects(oldNode, newNode)
408
 
        parentNode.replaceChild(newNode, oldNode)
409
 
 
410
 
    def getChildObject(self, node, obj, index):
411
 
        """Get index'th child of a tested interface element."""
412
 
        if isinstance(obj, wx.Window) and obj.GetSizer():
413
 
            return obj.GetSizer()
414
 
        try:
415
 
            return obj.GetChildren()[index]
416
 
        except IndexError:
417
 
            return None
418
 
 
419
 
 
420
 
class SimpleContainer(Container):
421
 
    '''Container without window attributes and styles.'''
422
 
    windowAttributes = []
423
 
    genericStyles = genericExStyles = []
424
 
 
425
 
 
426
 
class RootComponent(Container):
427
 
    '''Special root component.'''
428
 
    windowAttributes = []
429
 
    genericStyles = genericExStyles = []
430
 
    hasName = False
431
 
    hasCode = False
432
 
 
433
 
 
434
 
class SmartContainer(Container):
435
 
    '''Base class for containers with implicit nodes.'''
436
 
    implicitRenameDict = {}
437
 
    def __init__(self, klass, groups, attributes, **kargs):
438
 
        Container.__init__(self, klass, groups, attributes, **kargs)
439
 
        self.implicitKlass = kargs.pop('implicit_klass')
440
 
        self.implicitPageName = kargs.pop('implicit_page')
441
 
        self.implicitAttributes = kargs.pop('implicit_attributes')
442
 
        # This is optional
443
 
        self.implicitParams = kargs.pop('implicit_params', {})
444
 
 
445
 
    def getTreeNode(self, node):
446
 
        if node.getAttribute('class') == self.implicitKlass:
447
 
            for n in node.childNodes: # find first object
448
 
                if is_object(n): return n
449
 
        # Maybe some children are not implicit
450
 
        return node
451
 
 
452
 
    def getTreeOrImplicitNode(self, node):
453
 
        '''Return topmost child (implicit if exists).'''
454
 
        if node.parentNode.getAttribute('class') == self.implicitKlass:
455
 
            return node.parentNode
456
 
        else:
457
 
            return node
458
 
 
459
 
    def appendChild(self, parentNode, node):
460
 
        if self.requireImplicit(node):
461
 
            elem = Model.createObjectNode(self.implicitKlass)
462
 
            elem.appendChild(node)
463
 
            parentNode.appendChild(elem)
464
 
        else:
465
 
            parentNode.appendChild(node)
466
 
 
467
 
    def insertBefore(self, parentNode, node, nextNode):
468
 
        if self.requireImplicit(nextNode):
469
 
            nextNode = nextNode.parentNode
470
 
        if self.requireImplicit(node):
471
 
            elem = Model.createObjectNode(self.implicitKlass)
472
 
            elem.appendChild(node)
473
 
            parentNode.insertBefore(elem, nextNode)
474
 
        else:
475
 
            parentNode.insertBefore(node, nextNode)
476
 
 
477
 
    def insertAfter(self, parentNode, node, prevNode):
478
 
        if self.requireImplicit(prevNode):
479
 
            nextNode = prevNode.parentNode.nextSibling
480
 
        else:
481
 
            nextNode = prevNode.nextSibling
482
 
        if self.requireImplicit(node):
483
 
            elem = Model.createObjectNode(self.implicitKlass)
484
 
            elem.appendChild(node)
485
 
            parentNode.insertBefore(elem, nextNode)
486
 
        else:
487
 
            parentNode.insertBefore(node, nextNode)
488
 
 
489
 
    def removeChild(self, parentNode, node):
490
 
        if self.requireImplicit(node):
491
 
            implicitNode = node.parentNode
492
 
            return parentNode.removeChild(implicitNode)
493
 
        else:
494
 
            return parentNode.removeChild(node)
495
 
 
496
 
    def replaceChild(self, parentNode, newNode, oldNode):
497
 
        # Do similarly to Container for object child nodes
498
 
        oldComp = Manager.getNodeComp(oldNode)
499
 
        oldComp.copyAttributes(oldNode, newNode)
500
 
        if oldComp.isContainer():
501
 
            oldComp.copyObjects(oldNode, newNode)
502
 
        # Special treatment for implicit nodes
503
 
        if self.requireImplicit(oldNode):
504
 
            implicitNode = oldNode.parentNode
505
 
            if self.requireImplicit(newNode):
506
 
                implicitNode.replaceChild(newNode, oldNode)
507
 
            else:
508
 
                parentNode.replaceChild(newNode, implicitNode)
509
 
        else:
510
 
            if self.requireImplicit(newNode):
511
 
                elem = Model.createObjectNode(self.implicitKlass)
512
 
                elem.appendChild(newNode)
513
 
                parentNode.replaceChild(elem, oldNode)
514
 
            else:
515
 
                parentNode.replaceChild(newNode, oldNode)            
516
 
 
517
 
    def requireImplicit(self, node):
518
 
        # SmartContainer by default requires implicit
519
 
        return True
520
 
 
521
 
    def setImplicitParamClass(self, attrName, paramClass):
522
 
        '''Set special Param class.'''
523
 
        self.implicitParams[attrName] = paramClass
524
 
 
525
 
 
526
 
class Sizer(SmartContainer):
527
 
    '''Sizers are not windows and have common implicit node.'''
528
 
    windowAttributes = []
529
 
    hasName = False
530
 
    genericStyles = []
531
 
    genericExStyles = []    
532
 
    renameDict = {'orient':'orientation'}
533
 
    implicitRenameDict = {'option':'proportion'}
534
 
    def __init__(self, klass, groups, attributes, **kargs):
535
 
        kargs.setdefault('implicit_klass', 'sizeritem')
536
 
        kargs.setdefault('implicit_page', 'SizerItem')
537
 
        kargs.setdefault('implicit_attributes', ['option', 'flag', 'border', 'minsize', 'ratio'])
538
 
        kargs.setdefault('implicit_params', {'option': params.ParamInt, 
539
 
                                             'minsize': params.ParamPosSize, 
540
 
                                             'ratio': params.ParamPosSize})
541
 
        SmartContainer.__init__(self, klass, groups, attributes, **kargs)
542
 
 
543
 
    def isSizer(self):
544
 
        return True
545
 
 
546
 
    def requireImplicit(self, node):
547
 
        return node.getAttribute('class') != 'spacer'
548
 
 
549
 
    def getChildObject(self, node, obj, index):
550
 
        obj = obj.GetChildren()[index]
551
 
        if obj.IsSizer():
552
 
            return obj.GetSizer()
553
 
        elif obj.IsWindow():
554
 
            return obj.GetWindow()
555
 
        elif obj.IsSpacer():
556
 
            return obj.GetRect()
557
 
        return None                 # normally this is an error
558
 
 
559
 
    def getRect(self, obj):
560
 
        rects = [wx.RectPS(obj.GetPosition(), obj.GetSize())]
561
 
        for sizerItem in obj.GetChildren():
562
 
            rect = sizerItem.GetRect()
563
 
            rects.append(rect)
564
 
            # Add lines to show borders
565
 
            flag = sizerItem.GetFlag()
566
 
            border = sizerItem.GetBorder()
567
 
            if border == 0: continue
568
 
            x = (rect.GetLeft() + rect.GetRight()) / 2
569
 
            if flag & wx.TOP:
570
 
                rects.append(wx.Rect(x, rect.GetTop() - border, 0, border))
571
 
            if flag & wx.BOTTOM:
572
 
                rects.append(wx.Rect(x, rect.GetBottom() + 1, 0, border))
573
 
            y = (rect.GetTop() + rect.GetBottom()) / 2
574
 
            if flag & wx.LEFT:
575
 
                rects.append(wx.Rect(rect.GetLeft() - border, y, border, 0))
576
 
            if flag & wx.RIGHT:
577
 
                rects.append(wx.Rect(rect.GetRight() + 1, y, border, 0))
578
 
        return rects
579
 
 
580
 
 
581
 
class BoxSizer(Sizer):
582
 
    '''Sizers are not windows and have common implicit node.'''
583
 
 
584
 
    def __init__(self, klass, groups, attributes, **kargs):
585
 
        Sizer.__init__(self, klass, groups, attributes, **kargs)
586
 
 
587
 
    def getTreeImageId(self, node):
588
 
        if self.getAttribute(node, 'orient') == 'wxVERTICAL':
589
 
            return self.images[0].Id
590
 
        else:
591
 
            return self.images[1].Id
592
 
 
593
 
    def getRect(self, obj):
594
 
        rects = Sizer.getRect(self, obj)
595
 
        for sizerItem in obj.GetChildren():
596
 
            rect = sizerItem.GetRect()
597
 
            flag = sizerItem.GetFlag()
598
 
            if flag & wx.EXPAND:
599
 
                if obj.GetOrientation() == wx.VERTICAL:
600
 
                    y = (rect.GetTop() + rect.GetBottom()) / 2
601
 
                    rects.append(wx.Rect(rect.x, y, rect.width, 0))
602
 
                else:
603
 
                    x = (rect.GetLeft() + rect.GetRight()) / 2
604
 
                    rects.append(wx.Rect(x, rect.y, 0, rect.height))
605
 
        return rects
606
 
 
607
 
    def setDefaults(self, node):
608
 
        if node.tagName == 'object_ref': return
609
 
        if self.requireImplicit(node):
610
 
            comp = Manager.getNodeComp(node)
611
 
            sizerItem = self.getTreeOrImplicitNode(node)
612
 
            if comp.isContainer():
613
 
                for a,v in g.conf.defaultsContainer.items():
614
 
                    self.addAttribute(sizerItem, a, v)
615
 
            else:
616
 
                for a,v in g.conf.defaultsControl.items():
617
 
                    self.addAttribute(sizerItem, a, v)        
618
 
 
619
 
    def appendChild(self, parentNode, node):
620
 
        Sizer.appendChild(self, parentNode, node)
621
 
        self.setDefaults(node)
622
 
 
623
 
    def insertBefore(self, parentNode, node, nextNode):
624
 
        Sizer.insertBefore(self, parentNode, node, nextNode)
625
 
        self.setDefaults(node)
626
 
 
627
 
    def insertAfter(self, parentNode, node, prevNode):
628
 
        Sizer.insertAfter(self, parentNode, node, prevNode)
629
 
        self.setDefaults(node)
630
 
 
631
 
################################################################################
632
 
    
633
 
class _ComponentManager:
634
 
    '''Component manager used to register component plugins.'''
635
 
    def __init__(self):
636
 
        self.rootComponent = RootComponent('root', ['root'], ['encoding'], 
637
 
                                           specials={'encoding': EncodingAttribute},
638
 
                                           params={'encoding': params.ParamEncoding})
639
 
        self.components = {}
640
 
        self.ids = {}
641
 
        self.firstId = self.lastId = -1
642
 
        self.menus = {}
643
 
        self.panels = {}
644
 
        self.menuNames = ['TOP_LEVEL', 'ROOT', 'bar', 'control', 'button', 'box', 
645
 
                          'container', 'sizer', 'custom']
646
 
        self.panelNames = ['Windows', 'Panels', 'Controls', 'Sizers',  'Menus',
647
 
                           'Gizmos', 'Custom']
648
 
        self.panelImages = {}
649
 
 
650
 
    def init(self):
651
 
        self.firstId = self.lastId = wx.NewId()
652
 
        self.external = []      # external resources
653
 
        self.handlers = []      # registered XmlHandlers
654
 
 
655
 
    def register(self, component):
656
 
        '''Register component object.'''
657
 
        TRACE('register %s' % component.klass)
658
 
        self.components[component.klass] = component
659
 
        # unique wx ID for event handling
660
 
        component.id = self.lastId = wx.NewId()
661
 
        self.ids[component.id] = component
662
 
 
663
 
    def forget(self, klass):
664
 
        '''Remove registered component.'''
665
 
        del self.components[klass]
666
 
        for menu,iclh in self.menus.items():
667
 
            if iclh[1].klass == klass:
668
 
                self.menus[menu].remove(iclh)
669
 
        for panel,icb in self.panels.items():
670
 
            if icb[1].klass == klass:
671
 
                self.panels[panel].remove(icb)
672
 
 
673
 
    def getNodeComp(self, node, defaultClass='unknown'):
674
 
        # For ref nodes without class name, need to find ref element
675
 
        if node is Model.mainNode:
676
 
            return self.rootComponent
677
 
        elif node.nodeType == node.COMMENT_NODE:
678
 
            return self.components['comment']
679
 
        cls = node.getAttribute('class')
680
 
        if node.tagName == 'object_ref':
681
 
            if not cls:
682
 
                refNode = Model.findResource(node.getAttribute('ref'))
683
 
                if refNode:
684
 
                    cls = refNode.getAttribute('class')
685
 
                else:
686
 
                    cls = 'unknown'
687
 
        if defaultClass and cls not in self.components:
688
 
            cls = defaultClass
689
 
        return self.components[cls]
690
 
 
691
 
    def getMenuData(self, menu):
692
 
        return self.menus.get(menu, None)
693
 
 
694
 
    def setMenu(self, component, menu, label, help, index=999999):
695
 
        '''Set pulldown menu data.'''
696
 
        if menu not in self.menuNames: self.menuNames.append(menu)
697
 
        if menu not in self.menus: self.menus[menu] = []
698
 
        bisect.insort_left(self.menus[menu], (index, component, label, help))
699
 
 
700
 
    def getPanelData(self, panel):
701
 
        return self.panels.get(panel, None)
702
 
 
703
 
    def setTool(self, component, panel, bitmap=None, 
704
 
                pos=DEFAULT_POS, span=(1,1)):
705
 
        '''Set toolpanel data.'''
706
 
        if panel not in self.panelNames: self.panelNames.append(panel)
707
 
        if panel not in self.panels: self.panels[panel] = []
708
 
        # Auto-select bitmap if not provided
709
 
        if not bitmap:
710
 
            bmpPath = os.path.join('bitmaps', component.klass + '.png')
711
 
            if os.path.exists(bmpPath):
712
 
                bitmap = wx.Bitmap(bmpPath)
713
 
            else:
714
 
                bitmap = images.ToolDefault.GetBitmap()
715
 
        if g.conf.toolIconScale != 100:
716
 
            im = bitmap.ConvertToImage().Scale(
717
 
                bitmap.GetWidth() * g.conf.toolIconScale / 100,
718
 
                bitmap.GetHeight() * g.conf.toolIconScale / 100)
719
 
            bitmap = im.ConvertToBitmap()
720
 
        bisect.insort_left(self.panels[panel], (pos, span, component, bitmap))
721
 
 
722
 
    def addXmlHandler(self, h):
723
 
        '''
724
 
        Add an XML resource handler. h must be a class derived from
725
 
        XmlResourceHandler or a function loaded from a dynamic library
726
 
        using ctypes.
727
 
        '''
728
 
        self.handlers.append(h)
729
 
        
730
 
    def findById(self, id):
731
 
        return self.ids[id]
732
 
 
733
 
    def addXmlHandlers(self, res):
734
 
        '''Register XML handlers before creating a test window.'''
735
 
        for h in self.handlers:
736
 
            TRACE('registering Xml handler %s', h)
737
 
            if g._CFuncPtr and isinstance(h, g._CFuncPtr):
738
 
                try:
739
 
                    apply(h, ())
740
 
                except:
741
 
                    logger.exception('error calling DL func "%s"', h)
742
 
                    wx.LogError('error calling DL func "%s"' % h)
743
 
            else:               # assume a python class handler
744
 
                try:
745
 
                    res.AddHandler(apply(h, ()))
746
 
                except:
747
 
                    logger.exception('error adding XmlHandler "%s"', h)
748
 
                    wx.LogError('error adding XmlHandler "%s"' % h)
749
 
 
750
 
    def addExternal(self, f):
751
 
        '''Add an external resource file f to the list of preloaded
752
 
        resources.'''
753
 
        self.external.append(f)
754
 
        Model.addExternal(f)
755
 
 
756
 
    def preload(self, res):
757
 
        '''Preload external resources.'''
758
 
        for f in self.external:
759
 
            TRACE('Loading external resources: %s', f)
760
 
            res.Load(f)
761
 
        
762
 
 
763
 
# Singleton object
764
 
Manager = _ComponentManager()
765
 
'''Singleton global object of L{_ComponentManager} class.'''
766
 
 
767
 
c = SimpleComponent('comment', ['top_level', 'control'], ['comment'],
768
 
                    specials={'comment': CommentAttribute},
769
 
                    image=images.TreeComment.GetImage())
770
 
c.hasName = False
771
 
c.hasCode = False
772
 
c.setParamClass('comment', params.ParamMultilineText)
773
 
Manager.register(c)
774