~toolpart/+junk/pythoncard

« back to all changes in this revision

Viewing changes to tools/resourceEditor/modules/multipropertyEditor.py

  • Committer: Bazaar Package Importer
  • Author(s): Sandro Tosi
  • Date: 2010-03-04 23:55:10 UTC
  • mfrom: (3.1.3 sid)
  • Revision ID: james.westby@ubuntu.com-20100304235510-3v6lbhzwrgm0pcca
Tags: 0.8.2-1
* QA upload.
* New upstream release
* debian/control
  - set maintainer to QA group
  - set Homepage field, removing the URL from packages description
  - bump versioned b-d-i on python-support, to properly support Python module
  - replace b-d on python-all-dev with python-all, since building only
    arch:all packages
  - replace Source-Version substvar with source:Version
  - add ${misc:Depends} to binary packages Depends
* debian/watch
  - updated to use the SourceForge redirector; thanks to Raphael Geissert for
    the report and to Dario Minnucci for the patch; Closes: #449904
* debian/{pythoncard-doc, python-pythoncard}.install
  - use wildcards instead of site-packages to fix build with python 2.6;
    thanks to Ilya Barygin for the report and patch; Closes: #572332
* debian/pythoncard-doc.doc-base
  - set section to Programmin/Python
* debian/pythoncard-tools.menu
  - set menu main section to Applications
* debian/pythoncard-tools.postinst
  - removed, needed only to update the menu, but it's now created by debhelper

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
 
 
3
"""
 
4
__version__ = "$Revision: 1.5 $"
 
5
__date__ = "$Date: 2005/10/27 22:54:41 $"
 
6
"""
 
7
 
 
8
from PythonCard import dialog, font, model, registry, util
 
9
from PythonCard.event import ChangeListener
 
10
import multiresourceOutput
 
11
import time
 
12
import os
 
13
import string
 
14
 
 
15
import wx
 
16
 
 
17
DO_TRICKY_NAME_DERIVATIONS = True
 
18
 
 
19
# KEA this is a load of dingos' kidneys and needs to be rewritten
 
20
# 2002-02-22
 
21
# now I'm compounding the problem by porting from the original
 
22
# Property Editor to a PythonCard background
 
23
class PropertyEditor(model.Background, ChangeListener):
 
24
 
 
25
    def on_initialize(self, event):
 
26
        self._parent = self.GetParent()
 
27
        self._comp = self._parent.components
 
28
        self._updatingComponent = 0
 
29
        ##self.components.addChangeEventListener(self)
 
30
        self._comp.addChangeEventListener(self)
 
31
 
 
32
        self.checkItems = ['enabled', 'visible', 'editable', 'checked', 'default', \
 
33
                            'allowNameLabelVariation', \
 
34
                            'rules', 'labels', 'ticks', 'horizontalScrollbar']
 
35
        self.popItems = ['layout', 'border', 'style', 'alignment', 'stringSelection']
 
36
        self.cantModify = ['id', 'name', 'alignment', 'layout', 'style', 'border', \
 
37
            'horizontalScrollbar', 'min', 'max', 'columns', 'rules', 'labels', 'ticks']
 
38
 
 
39
        self.standardProps = ['name', 'allowNameLabelVariation', \
 
40
                            'enabled', 'visible', 'checked', 'backgroundColor', 'foregroundColor',
 
41
                            'font', 'position', 'size', 'label', 'text']
 
42
 
 
43
        self.standardPrefixes = ["txt", "fld", "btn", "chk", "pop", "clr", "fnt"]
 
44
        
 
45
        self.compOrderControls = ['SendToBack', 'MoveBack', 
 
46
                                  'reLayer', 'MoveTogether', 
 
47
                                  'MoveForward', 'SendToFront']
 
48
        
 
49
        self.multiCompControls = ['stbAlign', 
 
50
                                'alignRight', 'alignLeft', 'alignTop', 'alignBottom', 
 
51
                                'alignVerticalCentres','alignHorizontalCentres',
 
52
                                'stbEqualize',
 
53
                                'equalWidth', 'equalHeight', 'equalBoth',
 
54
                                'stbDistribute', 
 
55
                                'distHorizFirstLast', 'distHorizEdge', 
 
56
                                'distVertFirstLast', 'distVertEdge', ]
 
57
##                                'stbNudge', 'nudgeDistance',
 
58
##                                'nudgeLeft', 'nudgeRight',
 
59
##                                'nudgeUp', 'nudgeDown'
 
60
##                                ]
 
61
 
 
62
        # KEA 2001-08-14
 
63
        # this was causing an assertion error with the hybrid wxPython
 
64
        #self.components.wComponentList.SetSelection(0)
 
65
        if self.components.wComponentList.stringSelection == "":
 
66
#multicol        if self.components.wComponentList.getStringSelection() == []:
 
67
            wClass = ""
 
68
        else:
 
69
            wName, wClass = self.components.wComponentList.stringSelection.split("  :  ")
 
70
#multicol            wName, wClass = self.components.wComponentList.getStringSelection()[0].split("  :  ")
 
71
        self.setValidProps(wClass)
 
72
                
 
73
        #self.displayComponents(self.components)
 
74
        self.displayComponents(self._comp)
 
75
        self.visible = True
 
76
 
 
77
    # these functions for adding dynamically any property fields
 
78
    # not used for "standard" items, and used once only for others
 
79
    #  thereafter, use hiding and repositioning. 
 
80
    def add_field(self, pType, name, pos, siz, text, align):
 
81
        self.components[name] = {'type':pType,
 
82
                                 'name':name,
 
83
                                 'position':pos,
 
84
                                 'size':siz,
 
85
                                 'alignment':align,
 
86
                                 'text':text}
 
87
 
 
88
    def add_chk(self, name, pos, siz):
 
89
        # NB - set the command option to trigger the event
 
90
        self.components[name] = {'type':"CheckBox",
 
91
                                 'name':name,
 
92
                                 'position':pos,
 
93
                                 'command':'checkedProperty',
 
94
                                 'size':siz}
 
95
 
 
96
    def add_pop(self, name, pos, siz):
 
97
        #rint "add_pop", name, pos, siz
 
98
        self.components[name] = {'type':"Choice",
 
99
                                 'name':name,
 
100
                                 'position':pos,
 
101
                                 'size':siz}
 
102
 
 
103
    def add_btnFile(self, name, pos, siz):
 
104
        # NB - set the command option to trigger the event
 
105
        self.components[name] = {'type':"Button",
 
106
                                 'name':name,
 
107
                                 'position':pos,
 
108
                                 'label':"File...",
 
109
                                 'command':'btnFile',
 
110
                                 'size':siz}
 
111
 
 
112
 
 
113
    def on_componentSendBack_command(self, event):
 
114
        self._parent.on_componentSendBack_command(event)
 
115
        
 
116
    def on_componentMoveBack_command(self, event):
 
117
        self._parent.on_componentMoveBack_command(event)
 
118
 
 
119
    def on_componentMoveTogether_command(self, event):
 
120
        self._parent.on_componentMoveTogether_command(event)
 
121
        
 
122
    def on_componentRelayer_command(self, event):
 
123
        self._parent.on_componentRelayer_command(event)
 
124
 
 
125
    def on_componentMoveForward_command(self, event):
 
126
        self._parent.on_componentMoveForward_command(event)
 
127
        
 
128
    def on_componentBringFront_command(self, event):
 
129
        self._parent.on_componentBringFront_command(event)
 
130
        
 
131
 
 
132
    # KEA 2004-08-23
 
133
    # support updating of attributes without the need
 
134
    # for clicking the (no longer existent) Update button   
 
135
    def on_closeField(self, event):
 
136
        which = event.target.name.replace("fld", "")
 
137
        self.updateComponent(which)
 
138
 
 
139
    def on_checkedProperty_command(self, event):
 
140
        which = event.target.name.replace("chk", "")
 
141
        self.updateComponent(which)
 
142
 
 
143
    def on_select(self, event):
 
144
        which = event.target.name.replace("pop", "")
 
145
        self.updateComponent(which)
 
146
 
 
147
    def on_color_command(self, event):
 
148
        which = event.target.name.replace("clr", "")
 
149
        result = dialog.colorDialog(self, color=util.colorFromString(self.components["fld"+which].text))
 
150
        if result.accepted:
 
151
            self.components["fld"+which].text = str(result.color)
 
152
            self.components["clr"+which].backgroundColor = util.colorFromString(self.components["fld"+which].text)
 
153
            self.updateComponent(which)
 
154
 
 
155
    def on_changeFont_command(self, event):
 
156
        which = event.target.name.replace("fnt", "")
 
157
        wName, wClass = self.components.wComponentList.stringSelection.split("  :  ")
 
158
#multicol        wName, wClass = self.components.wComponentList.getStringSelection()[0].split("  :  ")
 
159
        widget = self._comp[wName]
 
160
        f = widget.font
 
161
        if f is None:
 
162
            desc = font.fontDescription(widget.GetFont())
 
163
            f = font.Font(desc)
 
164
        result = dialog.fontDialog(self, f)
 
165
        if result.accepted:
 
166
            f = result.font
 
167
            self.components["fld"+which].text = "%s" % f
 
168
            self.updateComponent(which)
 
169
 
 
170
    def on_btnFile_command(self, event):
 
171
        which = event.target.name.replace("btn", "")
 
172
        path, filename = os.path.split(self.components["fld"+which].text)
 
173
        result = dialog.openFileDialog(self, directory=path, filename=filename)
 
174
        #rint result.paths[0]
 
175
        if result.accepted:
 
176
            self.components["fld"+which].text = util.relativePath(self._parent.filename, result.paths[0])
 
177
            self.updateComponent(which)
 
178
 
 
179
 
 
180
    def addWidgetToComponentList(self, widget):
 
181
        wName = widget.name
 
182
        wClass = widget.__class__.__name__
 
183
        self.components.wComponentList.Append(wName + "  :  " + wClass)
 
184
 
 
185
    # KEA 2002-02-23
 
186
    # need to redo the logic below to avoid asserts in hybrid
 
187
    # versions of wxPython, but also be cleaner
 
188
    def deleteWidgetFromComponentList(self, wName, wClass):
 
189
        i = self.components.wComponentList.GetSelection()
 
190
        j = self.components.wComponentList.FindString(wName + "  :  " + wClass)
 
191
        if i == -1 or i != j:
 
192
            if j != -1: 
 
193
                self.components.wComponentList.Delete(j)
 
194
        else:
 
195
            if j > 0:
 
196
                self.components.wComponentList.SetSelection(j - 1)
 
197
            if j != -1:
 
198
                self.components.wComponentList.Delete(j)
 
199
            if self.components.wComponentList.GetSelection() == -1:
 
200
                self.setValidProps("")
 
201
            else:
 
202
                wName, wClass = self.components.wComponentList.stringSelection.split("  :  ")
 
203
                # deselect the name from properties list
 
204
                self.setValidProps(wClass)
 
205
                self.displayProperties(wName, wClass)
 
206
 
 
207
    def updateComponentList(self):
 
208
        if not self._parent.multipleSelected:
 
209
            return
 
210
        self.components.alphabetizeComponents.enabled = False
 
211
######  multicol
 
212
######        lastList = self.components.wComponentList.getStringSelection()
 
213
######        if lastList: 
 
214
######            for last in lastList:
 
215
######                self.components.wComponentList.SetStringSelection(last, 0)
 
216
######                #print "un setting", self.components.wComponentList.SetStringSelection(last, 0)
 
217
######        self.components.wComponentList.SetStringSelection(wName + "  :  " + wClass)
 
218
######        #print "setting", self.components.wComponentList.SetStringSelection(wName + "  :  " + wClass)
 
219
        self.components.wComponentList.Clear()
 
220
        self.PropertyListClear()
 
221
        for c in self.multiCompControls:
 
222
            self.components[c].visible = True
 
223
        for c,pref in self._parent.multipleComponents:
 
224
            self.addWidgetToComponentList(self._comp[c])                
 
225
 
 
226
 
 
227
    def selectComponentList(self, wName, wClass):
 
228
        if self._parent.multipleSelected:
 
229
            for c in self.multiCompControls:
 
230
                self.components[c].visible = False
 
231
             
 
232
        self.components.wComponentList.stringSelection = wName + "  :  " + wClass
 
233
######  multicol
 
234
######        lastList = self.components.wComponentList.getStringSelection()
 
235
######        if lastList: 
 
236
######            for last in lastList:
 
237
######                self.components.wComponentList.SetStringSelection(last, 0)
 
238
######                #print "un setting", self.components.wComponentList.SetStringSelection(last, 0)
 
239
######        self.components.wComponentList.SetStringSelection(wName + "  :  " + wClass)
 
240
######        #print "setting", self.components.wComponentList.SetStringSelection(wName + "  :  " + wClass)
 
241
        self.setValidProps(wClass)
 
242
        
 
243
        self.components.chkallowNameLabelVariation.checked = not DO_TRICKY_NAME_DERIVATIONS
 
244
        self.displayProperties(wName, wClass)
 
245
        c = self._parent.components[wName]
 
246
        self._parent.setToolTipDrag(wName, c.position, c.size)
 
247
 
 
248
    def changed(self, event):
 
249
        ##comp = self.components
 
250
        if self._updatingComponent:
 
251
            # KEA 2003-01-04
 
252
            # hack to speed up updates in place
 
253
            return
 
254
 
 
255
        comp = self._comp
 
256
        wName, wClass = event.getOldValue().split(",")
 
257
        if wName in comp:
 
258
            # new item added
 
259
            if not self._parent.isSizingHandle(wName):
 
260
                self.addWidgetToComponentList(comp[wName])
 
261
        else:
 
262
            # item deleted
 
263
            self.deleteWidgetFromComponentList(wName, wClass)
 
264
 
 
265
    """
 
266
    def on_wCopy_mouseClick(self, event):
 
267
        wName, wClass = self.components.wComponentList.stringSelection.split("  :  ")
 
268
        # what needs to happen here is to have a method for the Widget class that
 
269
        # will provide a valid resource description, each subclass of widget would
 
270
        # override the method to deal with their specific resource attributes
 
271
        # the Widget class should provide some ordering so that 'type',
 
272
        # 'position', 'size' comes before less commonly used items, the actual
 
273
        # ordering could just be defined in a list, so it is easy to change
 
274
        # also, if the current values match the defaults for a widget attribute
 
275
        # then that attribute should not be provided as part of the output
 
276
        print "this is just a placeholder method right now,"
 
277
        print "the resource is not actually copied to the clipboard yet"
 
278
        pprint.pprint(self._comp[wName])
 
279
    """
 
280
 
 
281
    def on_chkallowNameLabelVariation_mouseClick(self, event):
 
282
        if not self.components.chkallowNameLabelVariation.checked:
 
283
            # no longer allow them to be different
 
284
            # should we allow user to choose which one to keep ?
 
285
            # resolve if they are currently different
 
286
            # or simply leave as is ?
 
287
            wName, wClass = self.components.wComponentList.stringSelection.split("  :  ")
 
288
            if 'label'  in self.propertyList:
 
289
                propName = 'label'
 
290
                deriveName = self._parent.convertToValidName(self._comp[wName].label)            
 
291
            else:
 
292
                propName = 'text'
 
293
                deriveName = self._parent.convertToValidName(self._comp[wName].text)
 
294
            # do they already match ?
 
295
            if wName == deriveName:
 
296
                self.components.chkallowNameLabelVariation.checked = False
 
297
                return
 
298
                
 
299
            result = dialog.messageDialog(self, 
 
300
                                'Do you want '+propName+' to revert to reflect the name: '+wName,
 
301
                                'Empty '+propName,
 
302
                                wx.ICON_QUESTION | wx.YES_NO | wx.NO_DEFAULT)
 
303
            if result.accepted:
 
304
                self.components['fld'+propName].text = wName
 
305
                # temporarily allow diff name/label to allow update to happen, then revert
 
306
                self.components.chkallowNameLabelVariation.checked = True
 
307
                self.updateComponent(propName)
 
308
                self.components.chkallowNameLabelVariation.checked = False
 
309
            else:
 
310
                self.components.chkallowNameLabelVariation.checked = True
 
311
 
 
312
    def updateComponent(self, which):
 
313
        wName, wClass = self.components.wComponentList.stringSelection.split("  :  ")
 
314
        propName = which
 
315
        
 
316
        if propName in self.checkItems:
 
317
            value = self.components["chk"+propName].checked
 
318
        elif propName in self.popItems:
 
319
            value = self.components["pop"+propName].stringSelection
 
320
        elif propName in ('items', 'userdata') or (wClass == 'TextArea' and propName == 'text'):
 
321
            value = self.components["fld"+propName].text
 
322
        else:
 
323
            value = self.components["fld"+propName].text
 
324
 
 
325
        if propName == "textArea": propName = 'text'
 
326
        if propName not in ['name', 'label', 'stringSelection', 'text', 'toolTip', 'userdata']:
 
327
            try:
 
328
                value = eval(value)
 
329
            except:
 
330
                pass
 
331
 
 
332
        # KEA 2004-05-10
 
333
        # need to figure out where to stick validation code
 
334
        # but for now just need to make sure that if we're changing the name
 
335
        # attribute that it is valid, but similar checks will be necessary for
 
336
        # integer fields, a list of items, etc.
 
337
        # also maybe each attribute should have a doc or help string displayed
 
338
        # saying what the attribute does, example values, etc.
 
339
        if propName == 'name':
 
340
            badValue = False
 
341
            # if it isn't valid then display an alert and exit
 
342
            # must start with a letter and only contain alphanumeric characters
 
343
            if value == "" or value[0] not in string.ascii_letters:
 
344
                badValue = True
 
345
            else:
 
346
                alphanumeric = string.ascii_letters + string.digits
 
347
                for c in value:
 
348
                    if c not in alphanumeric:
 
349
                        badValue = True
 
350
                        break
 
351
            if badValue:
 
352
                dialog.alertDialog(None, "Name must start with a letter and only contain letters and numbers.", 
 
353
                                         'Error: Name is invalid')
 
354
                self.components["fld"+which].text = wName
 
355
                self.components["fld"+which].setFocus()
 
356
                self.components["fld"+which].setSelection(-1, -1)
 
357
                return
 
358
            # check for duplicate names is done below
 
359
 
 
360
        ##widget = self.components[wName]
 
361
        widget = self._comp[wName]
 
362
 
 
363
        # KEA 2002-02-23
 
364
        # I can't remember why this is actually necessary
 
365
        if propName == 'size':
 
366
            width, height = value
 
367
            if wClass not in ['BitmapCanvas', 'HtmlWindow']:
 
368
                bestWidth, bestHeight = widget.GetBestSize()
 
369
                if width == -1:
 
370
                    width = bestWidth
 
371
                if height == -1:
 
372
                    height = bestHeight
 
373
            widget.size = (width, height)
 
374
        else:
 
375
            if (propName in self.cantModify) or \
 
376
               (propName == 'items' and wClass == 'RadioGroup') or \
 
377
               (propName in ['label', 'text']):
 
378
                if (propName == 'layout'):
 
379
                    xx,yy = widget.size
 
380
                    widget.size = yy, xx
 
381
                    
 
382
                order = self._comp.order.index(wName)
 
383
                desc = multiresourceOutput.widgetAttributes(self._parent, widget)
 
384
                if desc.endswith(',\n'):
 
385
                    desc = eval(desc[:-2])
 
386
                else:
 
387
                    desc = eval(desc)
 
388
 
 
389
                if propName == 'name':
 
390
                    if value == wName:
 
391
                        # user didn't actually change the name
 
392
                        return
 
393
                    elif value in self._comp:
 
394
                        # we already have a component with that name
 
395
                        dialog.alertDialog(self, 'Another component already exists with the name ' + value,
 
396
                                           'Error: unable to rename component')
 
397
                        self.components["fldname"].text = wName
 
398
                        self.components["fldname"].setFocus()
 
399
                        self.components["fldname"].setSelection(-1, -1)
 
400
                        return
 
401
                if propName in ['label', 'text']:
 
402
                    if not self.components.chkallowNameLabelVariation.checked: 
 
403
                        if value == "":
 
404
                            result = dialog.messageDialog(self, 
 
405
                                'To set '+propName+' to be empty, you must allow Name and '+propName+' to differ.\n'+
 
406
                                'Do you want to allow that ?', 'Empty '+propName,
 
407
                                wx.ICON_QUESTION | wx.YES_NO | wx.NO_DEFAULT)
 
408
                            if result.accepted:
 
409
                                self.components.chkallowNameLabelVariation.checked = True
 
410
                                desc[propName] = value
 
411
                            else:
 
412
                                # don't allow this change
 
413
                                #self.components["fldname"].text = wName
 
414
                                self.components["fld"+propName].text = desc[propName]      
 
415
                                self.components["fld"+propName].setFocus()
 
416
                                self.components["fld"+propName].setSelection(-1, -1)   
 
417
                                return
 
418
                        else:
 
419
                            oldval = desc[propName]
 
420
                            desc[propName] = value
 
421
                            self._parent.deriveNameFromLabel(desc)
 
422
                                
 
423
                            if desc['name'] in self._comp:
 
424
                                # we already have a component with that name
 
425
                                dialog.alertDialog(self, 'Another component already exists with the name ' + value,
 
426
                                                   'Error: unable to rename component')
 
427
                                desc['name'] = wName
 
428
                                desc[propName] = oldval
 
429
                                self.components["fldname"].text = wName
 
430
                                self.components["fld"+propName].text = desc[propName]      
 
431
                                self.components["fld"+propName].setFocus()
 
432
                                self.components["fld"+propName].setSelection(-1, -1)   
 
433
                                return
 
434
                
 
435
                if value is None:
 
436
                    desc[propName] = 'none'
 
437
                elif propName in ['min', 'max']:
 
438
                    desc[propName] = int(value)
 
439
                else:
 
440
                    desc[propName] = value
 
441
                    
 
442
                    
 
443
                # need to experiment with freeze and thaw to avoid
 
444
                # a lot of update events
 
445
                startTime = time.time()
 
446
 
 
447
                # this is going to trigger a changed event
 
448
                # as we delete the old component
 
449
                self._updatingComponent = True
 
450
                del self._comp[wName]
 
451
                if propName in ['name']:
 
452
                    if not self.components.chkallowNameLabelVariation.checked: self._parent.deriveLabelFromName(desc)
 
453
                    wName = desc['name']
 
454
                elif propName in ['label', 'text']:
 
455
                    if not self.components.chkallowNameLabelVariation: self._parent.deriveNameFromLabel(desc)
 
456
                    wName = desc['name']
 
457
 
 
458
                # this is going to trigger another changed event
 
459
                # as we create a new component with the changed attribute
 
460
                self._comp[wName] = desc
 
461
                c = self._comp[wName]
 
462
                wx.EVT_LEFT_DOWN(c, self._parent.on_mouseDown)
 
463
                wx.EVT_LEFT_UP(c, self._parent.on_mouseUp)
 
464
                wx.EVT_MOTION(c, self._parent.on_mouseDrag)
 
465
 
 
466
                # now restore the order of the component
 
467
                # have to update the startName in case the name was updated
 
468
                if propName == 'name':
 
469
                    self._parent.startName = wName
 
470
                self._comp.order.remove(wName)
 
471
                self._comp.order.insert(order, wName)
 
472
 
 
473
                self._parent.fixComponentOrder(wName)
 
474
 
 
475
                self._updatingComponent = False
 
476
                
 
477
                endTime = time.time()
 
478
                #print "attribute change took:", endTime - startTime
 
479
            else:
 
480
                if wClass in ['Image', 'ImageButton'] and propName == 'file':
 
481
                    cwd = os.getcwd()
 
482
                    try:
 
483
                        os.chdir(self._parent.filename)
 
484
                    except:
 
485
                        pass
 
486
                    setattr(widget, propName, value)
 
487
                    os.chdir(cwd)
 
488
                else:
 
489
                    setattr(widget, propName, value)
 
490
                    #print propName, value
 
491
        # KEA 2002-02-23
 
492
        self._parent.showSizingHandles(wName)
 
493
        
 
494
        # and check if we now have matching name/label, and if so, assume they now maintain derivation
 
495
        self.determineNameLabelState(wName)
 
496
        
 
497
    def determineNameLabelState(self, wName):
 
498
        if 'label' in self.propertyList:
 
499
            deriveName = self._parent.convertToValidName(self._comp[wName].label)
 
500
            #rint wName, (self._comp[wName].label)
 
501
        elif 'text' in self.propertyList: 
 
502
            deriveName = self._parent.convertToValidName(self._comp[wName].text)
 
503
            #rint wName, (self._comp[wName].text)
 
504
        else:
 
505
            return
 
506
        if wName <> deriveName or not DO_TRICKY_NAME_DERIVATIONS:
 
507
            self.components["chkallowNameLabelVariation"].checked = True
 
508
            
 
509
 
 
510
    def setValidProps(self, wClass):
 
511
        if wClass == "":
 
512
            self.PropertyListClear()
 
513
        else:
 
514
            # get the property (attribute) list from the spec
 
515
            klass = registry.Registry.getInstance().getComponentClass(wClass)
 
516
            props = klass._spec.getAttributes().keys()
 
517
 
 
518
            # KEA 2002-03-24
 
519
            # only show the 'id' attribute for Button
 
520
            # and only when displaying dialog properties
 
521
            if not (self._parent.editingDialog and wClass == 'Button'):
 
522
                props.remove('id')
 
523
            props.sort()
 
524
            ##print "spec props", specProps
 
525
            
 
526
            self.PropertyListClear()
 
527
            self.PropertyListInsertItems(props)
 
528
 
 
529
    def PropertyListClear(self):
 
530
        self.propertyList = []
 
531
        for name, c in self.components.iteritems():
 
532
            prefix = name[:3]
 
533
            if prefix in self.standardPrefixes:
 
534
                self.components[name].visible = False
 
535
                self.components[name].enabled = False
 
536
 
 
537
    def PropertyListInsertItems(self, props):
 
538
        self.propertyList = props
 
539
               
 
540
 
 
541
    def updateSingleProperty(self, wName, propName, newValue):
 
542
        wClass = wName.__class__.__name__
 
543
        if propName in ['label', 'stringSelection', 'text', 'toolTip'] or propName in self.checkItems:
 
544
            newValue = newValue #getattr(widget, propName)
 
545
        else:
 
546
            newValue = str(newValue) #str(getattr(widget, propName))
 
547
 
 
548
        if propName in self.checkItems:
 
549
            self.components["chk"+propName].checked = newValue
 
550
        elif propName in self.popItems:
 
551
            self.components["pop"+propName].Clear()
 
552
            if propName == 'stringSelection':
 
553
                for v in widget.items:
 
554
                    self.components["pop"+propName].Append(v)
 
555
            else:
 
556
                for v in widget._spec.getAttributes()[propName].values:
 
557
                    self.components["pop"+propName].Append(v)
 
558
            try:
 
559
                self.components["pop"+propName].stringSelection = newValue
 
560
            except:
 
561
                # if value is empty or doesn't already exist
 
562
                pass
 
563
        elif propName in ('items', 'userdata') or (wClass == 'TextArea' and propName == 'text'):
 
564
            self.components["fld"+propName].text = newValue
 
565
        else:
 
566
            self.components["fld"+propName].text = newValue
 
567
 
 
568
        
 
569
    def displayProperties(self, wName, wClass):
 
570
        maxy = 0
 
571
        for theprop in self.standardProps:
 
572
            prop = theprop
 
573
            if prop == "text" and wClass == "TextArea": prop = "textArea"
 
574
            if prop in self.propertyList:
 
575
                vis = True
 
576
            else:
 
577
                vis = False
 
578
            for prefix in self.standardPrefixes:
 
579
                if prefix+prop in self.components.iterkeys():
 
580
                    self.components[prefix+prop].visible = vis
 
581
                    self.components[prefix+prop].enabled = vis
 
582
                    maxy = max(maxy, self.components[prefix+prop].position[1])
 
583
                    #rint self.components[prefix+prop].name, self.components[prefix+prop].position, self.components[prefix+prop].size, self.components[prefix+prop].GetBestSize()
 
584
        
 
585
        x,y = self.components.Properties.position
 
586
        
 
587
        # get values from one of the standard fields - use backgroundCcolor
 
588
        #  for text, field and button sizes
 
589
        
 
590
        # get the size as defined in the resource file, adjust for BestSize
 
591
        tx, ty = self.components.txtbackgroundColor.position
 
592
        tdefx, tdefy = self.components.txtbackgroundColor.size
 
593
        tsx, tsy = self.components.txtbackgroundColor.GetBestSize() ## + (20,10)
 
594
        tx = tx + tdefx - tsx
 
595
        
 
596
        fx, fy = self.components.fldbackgroundColor.position
 
597
        fdefx, fdefy = self.components.fldbackgroundColor.size
 
598
        fsx, fsy = self.components.fldbackgroundColor.GetBestSize() ## + (20,10)
 
599
        fx = fx + fdefx - fsx
 
600
        #rint fx, fy, fsx, fsy
 
601
        
 
602
        # Color buttons are odd, so use the font button for button positioning
 
603
        bx, by = self.components.fntfont.position
 
604
        bdefx, bdefy = self.components.fntfont.size
 
605
        bsx, bsy = self.components.fntfont.GetBestSize() ## + (20,10)
 
606
        #rint bx, by, bdefx, bdefy, bsx, bsy
 
607
        # bx = bx + bdefx - bsx
 
608
        bsx = bdefx
 
609
        #rint bx, by, bsx, bsy
 
610
 
 
611
        y = maxy+fsy 
 
612
        
 
613
        if DO_TRICKY_NAME_DERIVATIONS and ('label' in self.propertyList or 'text' in self.propertyList):
 
614
            self.components["chkallowNameLabelVariation"].visible = True
 
615
            self.components["chkallowNameLabelVariation"].enabled = True
 
616
            self.determineNameLabelState(wName)
 
617
        else:
 
618
            self.components["chkallowNameLabelVariation"].visible = False
 
619
            self.components["chkallowNameLabelVariation"].enabled = True
 
620
            self.determineNameLabelState(wName)
 
621
                        
 
622
        for propName in self.propertyList:
 
623
            widget = self._comp[wName]
 
624
            if propName in ['label', 'stringSelection', 'text', 'toolTip'] or propName in self.checkItems:
 
625
                value = getattr(widget, propName)
 
626
            else:
 
627
                value = str(getattr(widget, propName))
 
628
 
 
629
            if propName in self.checkItems:
 
630
                if not propName in self.standardProps:
 
631
                    if not "chk"+propName in self.components.iterkeys():
 
632
                        self.add_chk("chk"+propName, (fx, y), (fsx,fsy))
 
633
                    else:
 
634
                        self.components["chk"+propName].position = (fx, y)
 
635
                        self.components["chk"+propName].size = (fsx, fsy)
 
636
                    y += fsy + 5
 
637
                    self.components["chk"+propName].label = propName
 
638
                    self.components["chk"+propName].visible = True
 
639
                    self.components["chk"+propName].enabled = True
 
640
                    self.components["chk"+propName].checked = value
 
641
                else:
 
642
                    self.components["chk"+propName].visible = True
 
643
                    self.components["chk"+propName].checked = value
 
644
            elif propName in self.popItems:
 
645
                if not propName in self.standardProps:
 
646
                    if not "txt"+propName in self.components.iterkeys():
 
647
                        self.add_field("StaticText", "txt"+propName, (tx, y), (tsx,tsy), propName, "right")
 
648
                    else:
 
649
                        self.components["txt"+propName].position = (tx, y)
 
650
                        self.components["txt"+propName].size = (tsx, tsy)
 
651
                    if not "pop"+propName in self.components.iterkeys():
 
652
                        self.add_pop("pop"+propName, (fx, y), (fsx,fsy))
 
653
                    else:
 
654
                        self.components["pop"+propName].position = (fx, y)
 
655
                        self.components["pop"+propName].size = (fsx, fsy)
 
656
                    y += max(tsy, fsy) + 5    
 
657
                self.components["txt"+propName].visible = True
 
658
                self.components["txt"+propName].enabled = True                    
 
659
                self.components["pop"+propName].visible = True
 
660
                self.components["pop"+propName].enabled = True
 
661
                self.components["pop"+propName].Clear()
 
662
                if propName == 'stringSelection':
 
663
                    for v in widget.items:
 
664
                        self.components["pop"+propName].Append(v)
 
665
                else:
 
666
                    for v in widget._spec.getAttributes()[propName].values:
 
667
                        self.components["pop"+propName].Append(v)
 
668
                try:
 
669
                    self.components["pop"+propName].stringSelection = value
 
670
                except:
 
671
                    # if value is empty or doesn't already exist
 
672
                    pass
 
673
            elif propName in ('items', 'userdata') or (wClass == 'TextArea' and propName == 'text'):
 
674
                if not propName in self.standardProps:
 
675
                    if not "txt"+propName in self.components.iterkeys():
 
676
                        self.add_field("StaticText", "txt"+propName, (tx, y), (tsx,tsy), propName, "right")
 
677
                    else:
 
678
                        self.components["txt"+propName].position = (tx, y)
 
679
                        self.components["txt"+propName].size = (tsx, tsy)    
 
680
                    if not "fld"+propName in self.components.iterkeys():
 
681
                        self.add_field("TextArea", "fld"+propName, (fx, y), (fsx,fsy*3), value, "left")
 
682
                    else:
 
683
                        self.components["fld"+propName].position = (fx, y)
 
684
                        self.components["fld"+propName].size = (fsx, fsy*3)    
 
685
                    y += 3*fsy+5
 
686
                if wClass == 'TextArea' and propName == 'text':
 
687
                    propName = 'textArea'
 
688
                self.components["txt"+propName].visible = True
 
689
                self.components["txt"+propName].enabled = True
 
690
    
 
691
                self.components["fld"+propName].visible = True
 
692
                self.components["fld"+propName].enabled = True
 
693
                self.components["fld"+propName].text = value
 
694
            else:
 
695
                if not propName in self.standardProps:
 
696
                    if not "txt"+propName in self.components.iterkeys():
 
697
                        self.add_field("StaticText", "txt"+propName, (tx, y), (tsx,tsy), propName, "right")
 
698
                    else:
 
699
                        self.components["txt"+propName].position = (tx, y)
 
700
                        self.components["txt"+propName].size = (tsx, tsy)    
 
701
                    if not "fld"+propName in self.components.iterkeys():
 
702
                        self.add_field("TextField", "fld"+propName, (fx, y), (fsx,fsy), value, "left")
 
703
                    else:
 
704
                        self.components["fld"+propName].position = (fx, y)
 
705
                        self.components["fld"+propName].size = (fsx, fsy)
 
706
                        
 
707
                    if propName == 'file':
 
708
                        if not "btn"+propName in self.components.iterkeys():
 
709
                            self.add_btnFile("btn"+propName, (bx, y), (bsx,bsy))
 
710
                        else:
 
711
                            self.components["btn"+propName].position = (bx, y)
 
712
                            self.components["btn"+propName].size = (bsx, bsy)    
 
713
                        print "btn"+propName, bx, y, bsx, bsy
 
714
                        self.components["btn"+propName].visible = True
 
715
                        self.components["btn"+propName].enabled = True
 
716
                        # allow extra space for the "file" button
 
717
                        y += max(tsy, fsy, bsy) - max(tsy,fsy)
 
718
 
 
719
                    y += max(tsy, fsy) + 5
 
720
                else:
 
721
                    # all colors and fonts are "standard" items
 
722
                    if propName == 'foregroundColor' or propName == 'backgroundColor':
 
723
                        self.components["clr"+propName].visible = True
 
724
                        #rint propName, self.components["fld"+propName].text, value
 
725
                        self.components["clr"+propName].backgroundColor = util.colorFromString(value)
 
726
 
 
727
                self.components["txt"+propName].visible = True
 
728
                self.components["txt"+propName].enabled = True
 
729
    
 
730
                if propName == "font":
 
731
                    self.components["fld"+propName].visible = False 
 
732
                else:
 
733
                    self.components["fld"+propName].visible = True
 
734
                self.components["fld"+propName].enabled = True
 
735
                self.components["fld"+propName].text = value
 
736
                
 
737
##                self.components.wName.text = propName + ":"
 
738
                        
 
739
                # KEA 2002-02-23
 
740
                # I can't remember why this is actually necessary
 
741
                if propName == 'size':
 
742
                    width, height = getattr(widget, propName)
 
743
                    if wClass not in ['BitmapCanvas', 'HtmlWindow']:
 
744
                        bestWidth, bestHeight = widget.GetBestSize()
 
745
                        if width == bestWidth:
 
746
                            width = -1
 
747
                        if height == bestHeight:
 
748
                            height = -1
 
749
                    size = (width, height)
 
750
                    value = str(size)
 
751
    
 
752
                    self.components["fld"+propName].text = value
 
753
            # this should only display if the attribute is settable
 
754
            # so name, alignment, and others are read-only
 
755
 
 
756
    def on_wComponentList_select(self, event):
 
757
        #print 'selectComponentListEvent: %s\n' % event.GetString()
 
758
#multicol        print self.components.wComponentList.getStringSelection()
 
759
#multicol        print self.components.wComponentList.getStringSelection()[0]
 
760
#multicol        wName, wClass = self.components.wComponentList.getStringSelection()[0].split("  :  ")
 
761
        wName, wClass = event.GetString().split("  :  ")
 
762
 
 
763
        if self._parent.multipleSelected: return   # cannot select from list while in multi-mode
 
764
 
 
765
        self.setValidProps(wClass)
 
766
 
 
767
        self.components.chkallowNameLabelVariation.checked = not DO_TRICKY_NAME_DERIVATIONS
 
768
        self.displayProperties(wName, wClass)
 
769
        self._parent.showSizingHandles(wName)
 
770
        c = self._parent.components[wName]
 
771
        self._parent.startName = wName
 
772
        self._parent.setToolTipDrag(wName, c.position, c.size)
 
773
 
 
774
    def clearComponentList(self):
 
775
        self.components.wComponentList.Clear()
 
776
        self.statusBar.text = ''
 
777
 
 
778
    def clearPropertyList(self):
 
779
        self.PropertyListClear()
 
780
        
 
781
    def on_alphabetizeComponents_mouseClick(self, event):
 
782
        self.displayComponents(self._comp)
 
783
 
 
784
 
 
785
    def displayComponents(self, components):
 
786
        self.components.wComponentList.Freeze()
 
787
        self.components.wComponentList.Clear()
 
788
        if self._parent.multipleSelected:
 
789
            self.components.alphabetizeComponents.enabled = False
 
790
            for c in self.multiCompControls:
 
791
                self.components[c].visible = True
 
792
            for c,pref in self._parent.multipleComponents:
 
793
                    self.addWidgetToComponentList(self._comp[c])
 
794
                    
 
795
        else:
 
796
            self.components.alphabetizeComponents.enabled = True
 
797
            for c in self.multiCompControls:
 
798
                self.components[c].visible = False
 
799
            self._comp = components
 
800
            #rint "here ", self.components.alphabetizeComponents.checked
 
801
            if self.components.alphabetizeComponents.checked:
 
802
                for c in self.compOrderControls:
 
803
                    self.components[c].enabled = False
 
804
                xxx = []
 
805
                for c in components.order:
 
806
                    #print "display", c, self._parent.isSizingHandle(c)
 
807
                    if c not in self._parent.sizingHandleNames and not self._parent.isSizingHandle(c):
 
808
                        xxx.append(components[c])
 
809
                xxx.sort()
 
810
                for c in xxx:
 
811
                    self.addWidgetToComponentList(c)
 
812
            else:
 
813
                for c in self.compOrderControls:
 
814
                    self.components[c].enabled = True
 
815
                for c in components.order:
 
816
                    #print "display", c, self._parent.isSizingHandle(c)
 
817
                    if c not in self._parent.sizingHandleNames and not self._parent.isSizingHandle(c):
 
818
                        self.addWidgetToComponentList(components[c])
 
819
        self.components.wComponentList.Thaw()
 
820
        self.components.wComponentList.Refresh()
 
821
        self.components.wComponentList.Update()
 
822
 
 
823
    def on_close(self, event):
 
824
        self.visible = False
 
825
        parent = self.GetParent()
 
826
        parent.menuBar.setChecked('menuViewPropertyEditor', 0)
 
827
            
 
828
    def on_align_command(self, event):
 
829
        # offset is two x,y pairs
 
830
        # first pair is how far (whether) to move this dimension
 
831
        # second is how much of width to use
 
832
        #   all numbers used to multiply the difference between moving comp and base
 
833
        #   so (1,0,0,0) means make left X same as base, leave Y unchanged
 
834
        #     (1,0,1,0) means make right X same as base, leave Y unchanged
 
835
        #     (0,1,0,0.5) means make centre Y same as base, leave X unchanged
 
836
        alignOffsets = {'alignLeft': (1,0,0,0), 'alignRight': (1,0,1, 0), 
 
837
                        'alignTop': (0,1,0,0), 'alignBottom': (0,1,0,1),
 
838
                        'alignHorizontalCentres': (1,0,0.5,0), 
 
839
                        'alignVerticalCentres': (0,1,0,0.5)}
 
840
        if not self._parent.multipleComponents:
 
841
            return
 
842
        if not event.target.name in alignOffsets.keys():
 
843
            return
 
844
        BX, BY, SX, SY = alignOffsets[event.target.name]
 
845
        count = 0
 
846
        for c, pre in self._parent.multipleComponents:
 
847
            x,y = self._comp[c].position
 
848
            sx,sy = self._comp[c].size
 
849
            if count == 0:
 
850
                finalx = x + SX * sx
 
851
                finaly = y + SY * sy
 
852
            else:
 
853
                newx = x + BX * (finalx - SX*sx - x)
 
854
                newy = y + BY * (finaly - SY*sy - y)
 
855
                self._comp[c].position = (newx, newy)
 
856
            count += 1
 
857
        if count > 0:
 
858
            self._parent.showMultiSizingHandles()
 
859
            
 
860
    def on_equal_command(self, event):
 
861
        # settings is a single X,Y pair
 
862
        # specifies how much of final result comes from base (remainder from original)
 
863
        # so (1,0) means make comp's X size (i.e. width) be same as base's
 
864
        equalSettings = {'equalWidth': (1,0),
 
865
                         'equalHeight': (0,1), 
 
866
                         'equalBoth': (1,1)}
 
867
        if not self._parent.multipleComponents:
 
868
            return
 
869
        if not event.target.name in equalSettings.keys():
 
870
            return
 
871
        # set up Base fraction and Own fraction
 
872
        BX, BY = equalSettings[event.target.name]
 
873
        OX, OY = (1-BX, 1-BY)
 
874
        count = 0
 
875
        for c, pre in self._parent.multipleComponents:
 
876
            w,h = self._comp[c].size
 
877
            if count == 0:
 
878
                basew = w
 
879
                baseh = h
 
880
            else:
 
881
                neww,newh = (BX*basew + OX*w, BY*baseh+OY*h)
 
882
                self._comp[c].size = (neww, newh)
 
883
            count += 1
 
884
        if count > 0:
 
885
            self._parent.showMultiSizingHandles()
 
886
 
 
887
    def on_nudge_command(self, event):
 
888
        # Offsets are distance to move in each of X,Y
 
889
        # distance = 1 = 1, 2(,3) = gridsize, 4 = gridsize * gridsize
 
890
        nudgeOffsets = {'nudgeUp': (0,-1), 'nudgeDown': (0,1),
 
891
                        'nudgeLeft': (-1,0), 'nudgeRight': (1,0)}
 
892
 
 
893
        # can be applied in single component mode
 
894
##        if not self._parent.multipleComponents:
 
895
##            return
 
896
 
 
897
        if not event.target.name in nudgeOffsets.keys():
 
898
            return
 
899
        self._parent.nudge( nudgeOffsets[event.target.name], self.components.nudgeDistance.value)
 
900
            
 
901
    def on_distribute_command(self, event):
 
902
        # setting is x,y for which is to change, plus style
 
903
 
 
904
        distributeSettings = {'distHorizEdge': (1,0,'E'), 'distHorizFirstLast': (1,0,'F'), 
 
905
                              'distVertEdge': (0,1,'E'), 'distVertFirstLast': (0,1,'F')}
 
906
        if not self._parent.multipleComponents:
 
907
            return
 
908
        if not event.target.name in distributeSettings.keys():
 
909
            return
 
910
        
 
911
        BX, BY, style = distributeSettings[event.target.name]
 
912
        
 
913
        nameList = [ x[0] for x in self._parent.multipleComponents ]
 
914
        
 
915
        # Calculate First-to-Last.
 
916
        N = len(self._parent.multipleComponents) - 1  # number of gaps
 
917
        if style == "F":
 
918
            if N <= 1: return
 
919
 
 
920
            x1,y1 = self._comp[nameList[0]].position
 
921
            x2,y2 = self._comp[nameList[N]].position
 
922
            
 
923
            if BX*(x2-x1) + BY*(y2-y1) < 0:
 
924
                nameList.reverse()
 
925
                x1,y1 = self._comp[nameList[0]].position
 
926
                x2,y2 = self._comp[nameList[N]].position
 
927
            
 
928
            used = 0
 
929
            for c in nameList:
 
930
                sx,sy = self._comp[c].size
 
931
                used = used + BX*sx + BY*sy
 
932
 
 
933
            gapSpace = BX*(x2-x1-used+sx) + BY*(y2-y1-used+sy)
 
934
        elif style == 'E':
 
935
            gapSpace = 0
 
936
        else:
 
937
            return
 
938
            
 
939
        count = 0
 
940
        for c in nameList:
 
941
            x,y = self._comp[c].position
 
942
            sx,sy = self._comp[c].size
 
943
            if count == 0:
 
944
                basex, basey = (x, y)
 
945
                nextx, nexty = (x+sx, y+sy)
 
946
            else:
 
947
                useup = gapSpace / N
 
948
                N = N-1
 
949
                gapSpace = gapSpace - useup
 
950
                
 
951
                newx = BX * (nextx+useup) + (1-BX) * x
 
952
                newy = BY * (nexty+useup) + (1-BY) * y
 
953
                nextx, nexty = (newx+sx, newy+sy)
 
954
 
 
955
                self._comp[c].position = (newx, newy)
 
956
            count += 1
 
957
        if count > 0:
 
958
            self._parent.showMultiSizingHandles()
 
959
            
 
960