4
__version__ = "$Revision: 1.5 $"
5
__date__ = "$Date: 2005/10/27 22:54:41 $"
8
from PythonCard import dialog, font, model, registry, util
9
from PythonCard.event import ChangeListener
10
import multiresourceOutput
17
DO_TRICKY_NAME_DERIVATIONS = True
19
# KEA this is a load of dingos' kidneys and needs to be rewritten
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):
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)
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']
39
self.standardProps = ['name', 'allowNameLabelVariation', \
40
'enabled', 'visible', 'checked', 'backgroundColor', 'foregroundColor',
41
'font', 'position', 'size', 'label', 'text']
43
self.standardPrefixes = ["txt", "fld", "btn", "chk", "pop", "clr", "fnt"]
45
self.compOrderControls = ['SendToBack', 'MoveBack',
46
'reLayer', 'MoveTogether',
47
'MoveForward', 'SendToFront']
49
self.multiCompControls = ['stbAlign',
50
'alignRight', 'alignLeft', 'alignTop', 'alignBottom',
51
'alignVerticalCentres','alignHorizontalCentres',
53
'equalWidth', 'equalHeight', 'equalBoth',
55
'distHorizFirstLast', 'distHorizEdge',
56
'distVertFirstLast', 'distVertEdge', ]
57
## 'stbNudge', 'nudgeDistance',
58
## 'nudgeLeft', 'nudgeRight',
59
## 'nudgeUp', 'nudgeDown'
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() == []:
69
wName, wClass = self.components.wComponentList.stringSelection.split(" : ")
70
#multicol wName, wClass = self.components.wComponentList.getStringSelection()[0].split(" : ")
71
self.setValidProps(wClass)
73
#self.displayComponents(self.components)
74
self.displayComponents(self._comp)
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,
88
def add_chk(self, name, pos, siz):
89
# NB - set the command option to trigger the event
90
self.components[name] = {'type':"CheckBox",
93
'command':'checkedProperty',
96
def add_pop(self, name, pos, siz):
97
#rint "add_pop", name, pos, siz
98
self.components[name] = {'type':"Choice",
103
def add_btnFile(self, name, pos, siz):
104
# NB - set the command option to trigger the event
105
self.components[name] = {'type':"Button",
113
def on_componentSendBack_command(self, event):
114
self._parent.on_componentSendBack_command(event)
116
def on_componentMoveBack_command(self, event):
117
self._parent.on_componentMoveBack_command(event)
119
def on_componentMoveTogether_command(self, event):
120
self._parent.on_componentMoveTogether_command(event)
122
def on_componentRelayer_command(self, event):
123
self._parent.on_componentRelayer_command(event)
125
def on_componentMoveForward_command(self, event):
126
self._parent.on_componentMoveForward_command(event)
128
def on_componentBringFront_command(self, event):
129
self._parent.on_componentBringFront_command(event)
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)
139
def on_checkedProperty_command(self, event):
140
which = event.target.name.replace("chk", "")
141
self.updateComponent(which)
143
def on_select(self, event):
144
which = event.target.name.replace("pop", "")
145
self.updateComponent(which)
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))
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)
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]
162
desc = font.fontDescription(widget.GetFont())
164
result = dialog.fontDialog(self, f)
167
self.components["fld"+which].text = "%s" % f
168
self.updateComponent(which)
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]
176
self.components["fld"+which].text = util.relativePath(self._parent.filename, result.paths[0])
177
self.updateComponent(which)
180
def addWidgetToComponentList(self, widget):
182
wClass = widget.__class__.__name__
183
self.components.wComponentList.Append(wName + " : " + wClass)
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:
193
self.components.wComponentList.Delete(j)
196
self.components.wComponentList.SetSelection(j - 1)
198
self.components.wComponentList.Delete(j)
199
if self.components.wComponentList.GetSelection() == -1:
200
self.setValidProps("")
202
wName, wClass = self.components.wComponentList.stringSelection.split(" : ")
203
# deselect the name from properties list
204
self.setValidProps(wClass)
205
self.displayProperties(wName, wClass)
207
def updateComponentList(self):
208
if not self._parent.multipleSelected:
210
self.components.alphabetizeComponents.enabled = False
212
###### lastList = self.components.wComponentList.getStringSelection()
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])
227
def selectComponentList(self, wName, wClass):
228
if self._parent.multipleSelected:
229
for c in self.multiCompControls:
230
self.components[c].visible = False
232
self.components.wComponentList.stringSelection = wName + " : " + wClass
234
###### lastList = self.components.wComponentList.getStringSelection()
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)
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)
248
def changed(self, event):
249
##comp = self.components
250
if self._updatingComponent:
252
# hack to speed up updates in place
256
wName, wClass = event.getOldValue().split(",")
259
if not self._parent.isSizingHandle(wName):
260
self.addWidgetToComponentList(comp[wName])
263
self.deleteWidgetFromComponentList(wName, wClass)
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])
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:
290
deriveName = self._parent.convertToValidName(self._comp[wName].label)
293
deriveName = self._parent.convertToValidName(self._comp[wName].text)
294
# do they already match ?
295
if wName == deriveName:
296
self.components.chkallowNameLabelVariation.checked = False
299
result = dialog.messageDialog(self,
300
'Do you want '+propName+' to revert to reflect the name: '+wName,
302
wx.ICON_QUESTION | wx.YES_NO | wx.NO_DEFAULT)
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
310
self.components.chkallowNameLabelVariation.checked = True
312
def updateComponent(self, which):
313
wName, wClass = self.components.wComponentList.stringSelection.split(" : ")
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
323
value = self.components["fld"+propName].text
325
if propName == "textArea": propName = 'text'
326
if propName not in ['name', 'label', 'stringSelection', 'text', 'toolTip', 'userdata']:
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':
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:
346
alphanumeric = string.ascii_letters + string.digits
348
if c not in alphanumeric:
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)
358
# check for duplicate names is done below
360
##widget = self.components[wName]
361
widget = self._comp[wName]
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()
373
widget.size = (width, height)
375
if (propName in self.cantModify) or \
376
(propName == 'items' and wClass == 'RadioGroup') or \
377
(propName in ['label', 'text']):
378
if (propName == 'layout'):
382
order = self._comp.order.index(wName)
383
desc = multiresourceOutput.widgetAttributes(self._parent, widget)
384
if desc.endswith(',\n'):
385
desc = eval(desc[:-2])
389
if propName == 'name':
391
# user didn't actually change the name
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)
401
if propName in ['label', 'text']:
402
if not self.components.chkallowNameLabelVariation.checked:
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)
409
self.components.chkallowNameLabelVariation.checked = True
410
desc[propName] = value
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)
419
oldval = desc[propName]
420
desc[propName] = value
421
self._parent.deriveNameFromLabel(desc)
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')
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)
436
desc[propName] = 'none'
437
elif propName in ['min', 'max']:
438
desc[propName] = int(value)
440
desc[propName] = value
443
# need to experiment with freeze and thaw to avoid
444
# a lot of update events
445
startTime = time.time()
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)
454
elif propName in ['label', 'text']:
455
if not self.components.chkallowNameLabelVariation: self._parent.deriveNameFromLabel(desc)
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)
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)
473
self._parent.fixComponentOrder(wName)
475
self._updatingComponent = False
477
endTime = time.time()
478
#print "attribute change took:", endTime - startTime
480
if wClass in ['Image', 'ImageButton'] and propName == 'file':
483
os.chdir(self._parent.filename)
486
setattr(widget, propName, value)
489
setattr(widget, propName, value)
490
#print propName, value
492
self._parent.showSizingHandles(wName)
494
# and check if we now have matching name/label, and if so, assume they now maintain derivation
495
self.determineNameLabelState(wName)
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)
506
if wName <> deriveName or not DO_TRICKY_NAME_DERIVATIONS:
507
self.components["chkallowNameLabelVariation"].checked = True
510
def setValidProps(self, wClass):
512
self.PropertyListClear()
514
# get the property (attribute) list from the spec
515
klass = registry.Registry.getInstance().getComponentClass(wClass)
516
props = klass._spec.getAttributes().keys()
519
# only show the 'id' attribute for Button
520
# and only when displaying dialog properties
521
if not (self._parent.editingDialog and wClass == 'Button'):
524
##print "spec props", specProps
526
self.PropertyListClear()
527
self.PropertyListInsertItems(props)
529
def PropertyListClear(self):
530
self.propertyList = []
531
for name, c in self.components.iteritems():
533
if prefix in self.standardPrefixes:
534
self.components[name].visible = False
535
self.components[name].enabled = False
537
def PropertyListInsertItems(self, props):
538
self.propertyList = props
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)
546
newValue = str(newValue) #str(getattr(widget, propName))
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)
556
for v in widget._spec.getAttributes()[propName].values:
557
self.components["pop"+propName].Append(v)
559
self.components["pop"+propName].stringSelection = newValue
561
# if value is empty or doesn't already exist
563
elif propName in ('items', 'userdata') or (wClass == 'TextArea' and propName == 'text'):
564
self.components["fld"+propName].text = newValue
566
self.components["fld"+propName].text = newValue
569
def displayProperties(self, wName, wClass):
571
for theprop in self.standardProps:
573
if prop == "text" and wClass == "TextArea": prop = "textArea"
574
if prop in self.propertyList:
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()
585
x,y = self.components.Properties.position
587
# get values from one of the standard fields - use backgroundCcolor
588
# for text, field and button sizes
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
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
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
609
#rint bx, by, bsx, bsy
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)
618
self.components["chkallowNameLabelVariation"].visible = False
619
self.components["chkallowNameLabelVariation"].enabled = True
620
self.determineNameLabelState(wName)
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)
627
value = str(getattr(widget, propName))
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))
634
self.components["chk"+propName].position = (fx, y)
635
self.components["chk"+propName].size = (fsx, fsy)
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
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")
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))
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)
666
for v in widget._spec.getAttributes()[propName].values:
667
self.components["pop"+propName].Append(v)
669
self.components["pop"+propName].stringSelection = value
671
# if value is empty or doesn't already exist
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")
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")
683
self.components["fld"+propName].position = (fx, y)
684
self.components["fld"+propName].size = (fsx, fsy*3)
686
if wClass == 'TextArea' and propName == 'text':
687
propName = 'textArea'
688
self.components["txt"+propName].visible = True
689
self.components["txt"+propName].enabled = True
691
self.components["fld"+propName].visible = True
692
self.components["fld"+propName].enabled = True
693
self.components["fld"+propName].text = value
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")
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")
704
self.components["fld"+propName].position = (fx, y)
705
self.components["fld"+propName].size = (fsx, fsy)
707
if propName == 'file':
708
if not "btn"+propName in self.components.iterkeys():
709
self.add_btnFile("btn"+propName, (bx, y), (bsx,bsy))
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)
719
y += max(tsy, fsy) + 5
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)
727
self.components["txt"+propName].visible = True
728
self.components["txt"+propName].enabled = True
730
if propName == "font":
731
self.components["fld"+propName].visible = False
733
self.components["fld"+propName].visible = True
734
self.components["fld"+propName].enabled = True
735
self.components["fld"+propName].text = value
737
## self.components.wName.text = propName + ":"
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:
747
if height == bestHeight:
749
size = (width, height)
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
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(" : ")
763
if self._parent.multipleSelected: return # cannot select from list while in multi-mode
765
self.setValidProps(wClass)
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)
774
def clearComponentList(self):
775
self.components.wComponentList.Clear()
776
self.statusBar.text = ''
778
def clearPropertyList(self):
779
self.PropertyListClear()
781
def on_alphabetizeComponents_mouseClick(self, event):
782
self.displayComponents(self._comp)
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])
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
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])
811
self.addWidgetToComponentList(c)
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()
823
def on_close(self, event):
825
parent = self.GetParent()
826
parent.menuBar.setChecked('menuViewPropertyEditor', 0)
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:
842
if not event.target.name in alignOffsets.keys():
844
BX, BY, SX, SY = alignOffsets[event.target.name]
846
for c, pre in self._parent.multipleComponents:
847
x,y = self._comp[c].position
848
sx,sy = self._comp[c].size
853
newx = x + BX * (finalx - SX*sx - x)
854
newy = y + BY * (finaly - SY*sy - y)
855
self._comp[c].position = (newx, newy)
858
self._parent.showMultiSizingHandles()
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),
867
if not self._parent.multipleComponents:
869
if not event.target.name in equalSettings.keys():
871
# set up Base fraction and Own fraction
872
BX, BY = equalSettings[event.target.name]
873
OX, OY = (1-BX, 1-BY)
875
for c, pre in self._parent.multipleComponents:
876
w,h = self._comp[c].size
881
neww,newh = (BX*basew + OX*w, BY*baseh+OY*h)
882
self._comp[c].size = (neww, newh)
885
self._parent.showMultiSizingHandles()
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)}
893
# can be applied in single component mode
894
## if not self._parent.multipleComponents:
897
if not event.target.name in nudgeOffsets.keys():
899
self._parent.nudge( nudgeOffsets[event.target.name], self.components.nudgeDistance.value)
901
def on_distribute_command(self, event):
902
# setting is x,y for which is to change, plus style
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:
908
if not event.target.name in distributeSettings.keys():
911
BX, BY, style = distributeSettings[event.target.name]
913
nameList = [ x[0] for x in self._parent.multipleComponents ]
915
# Calculate First-to-Last.
916
N = len(self._parent.multipleComponents) - 1 # number of gaps
920
x1,y1 = self._comp[nameList[0]].position
921
x2,y2 = self._comp[nameList[N]].position
923
if BX*(x2-x1) + BY*(y2-y1) < 0:
925
x1,y1 = self._comp[nameList[0]].position
926
x2,y2 = self._comp[nameList[N]].position
930
sx,sy = self._comp[c].size
931
used = used + BX*sx + BY*sy
933
gapSpace = BX*(x2-x1-used+sx) + BY*(y2-y1-used+sy)
941
x,y = self._comp[c].position
942
sx,sy = self._comp[c].size
944
basex, basey = (x, y)
945
nextx, nexty = (x+sx, y+sy)
949
gapSpace = gapSpace - useup
951
newx = BX * (nextx+useup) + (1-BX) * x
952
newy = BY * (nexty+useup) + (1-BY) * y
953
nextx, nexty = (newx+sx, newy+sy)
955
self._comp[c].position = (newx, newy)
958
self._parent.showMultiSizingHandles()