1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """For implementing buffered property editors."""
17
18 from warnings import warn
19
20 from muntjac.event.shortcut_listener import ShortcutListener
21 from muntjac.event.action_manager import ActionManager
22
23 from muntjac.ui.abstract_component import AbstractComponent
24 from muntjac.ui.component import Event as ComponentEvent
25
26 from muntjac.event import action
27 from muntjac.ui import field
28 from muntjac.data import property as prop
29
30 from muntjac.data.validator import EmptyValueException
31 from muntjac.data.buffered import SourceException
32 from muntjac.data.validator import InvalidValueException
33 from muntjac.data.validatable import IValidatable
34
35 from muntjac.terminal.composite_error_message import CompositeErrorMessage
36
37
38 _VALUE_CHANGE_METHOD = getattr(prop.IValueChangeListener, 'valueChange')
39
40 _READ_ONLY_STATUS_CHANGE_METHOD = getattr(prop.IReadOnlyStatusChangeListener,
41 'readOnlyStatusChange')
42
43
44 -class AbstractField(AbstractComponent, field.IField,
45 action.IShortcutNotifier, prop.IReadOnlyStatusChangeNotifier,
46 prop.IReadOnlyStatusChangeListener):
47 """Abstract field component for implementing buffered property editors.
48 The field may hold an internal value, or it may be connected to any data
49 source that implements the L{IProperty}interface. C{AbstractField}
50 implements that interface itself, too, so accessing the IProperty value
51 represented by it is straightforward.
52
53 AbstractField also provides the L{IBuffered} interface for buffering the
54 data source value. By default the IField is in write through-mode and
55 L{setWriteThrough}should be called to enable buffering.
56
57 The class also supports L{validators<IValidator>} to
58 make sure the value contained in the field is valid.
59
60 @author: Vaadin Ltd.
61 @author: Richard Lincoln
62 @version: 1.1.2
63 """
64
66 super(AbstractField, self).__init__()
67
68
69 self._value = None
70
71
72 self._dataSource = None
73
74
75 self._validators = None
76
77
78 self._writeThroughMode = True
79
80
81 self._readThroughMode = True
82
83
84 self._modified = False
85
86
87
88 self._committingValueToDataSource = False
89
90
91 self._currentBufferedSourceException = None
92
93
94 self._invalidAllowed = True
95
96
97 self._invalidCommitted = False
98
99
100 self._tabIndex = 0
101
102
103 self._required = False
104
105
106
107 self._requiredError = ''
108
109
110 self._validationVisible = True
111
112
113
114 self._actionManager = None
115
116 self._valueWasModifiedByDataSourceDuringCommit = False
117
118
119 - def paintContent(self, target):
120
121
122 if self.getTabIndex() != 0:
123 target.addAttribute('tabindex', self.getTabIndex())
124
125
126 if self.isModified():
127 target.addAttribute('modified', True)
128
129
130 if not self.isReadOnly() and self.isRequired():
131 target.addAttribute('required', True)
132
133
134 if self.shouldHideErrors():
135 target.addAttribute('hideErrors', True)
136
137
138
140 """Returns true if the error indicator be hidden when painting the
141 component even when there are errors.
142
143 This is a mostly internal method, but can be overridden in subclasses
144 e.g. if the error indicator should also be shown for empty fields in
145 some cases.
146
147 @return: true to hide the error indicator, false to use the normal
148 logic to show it when there are errors
149 """
150 return (self.isRequired() and self.isEmpty()
151 and self.getComponentError() == None
152 and self.getErrorMessage() != None)
153
154
156
157 raise NotImplementedError
158
159
161 """The abstract field is read only also if the data source is in
162 read only mode.
163 """
164 return (super(AbstractField, self).isReadOnly()
165 or (self._dataSource is not None
166 and self._dataSource.isReadOnly()))
167
168
177
178
180 """Tests if the invalid data is committed to datasource.
181
182 @see: L{BufferedValidatable.isInvalidCommitted}
183 """
184 return self._invalidCommitted
185
186
188 """Sets if the invalid data should be committed to datasource.
189
190 @see: L{BufferedValidatable.setInvalidCommitted}
191 """
192 self._invalidCommitted = isCommitted
193
194
196
197 if (self._dataSource is not None
198 and not self._dataSource.isReadOnly()):
199
200 if self.isInvalidCommitted() or self.isValid():
201 newValue = self.getValue()
202
203
204
205 self._valueWasModifiedByDataSourceDuringCommit = False
206 self._committingValueToDataSource = True
207 self._dataSource.setValue(newValue)
208
209
210
211
212
213
214
215
216
217
218 self._committingValueToDataSource = False
219
220 else:
221
222
223 self.validate()
224
225 repaintNeeded = False
226
227
228 if self._modified:
229 self._modified = False
230 repaintNeeded = True
231
232
233 if self._currentBufferedSourceException is not None:
234 self._currentBufferedSourceException = None
235 repaintNeeded = True
236
237 if self._valueWasModifiedByDataSourceDuringCommit:
238 self._valueWasModifiedByDataSourceDuringCommit = False
239 self.fireValueChange(False)
240 elif repaintNeeded:
241 self.requestRepaint()
242
243
245
246 if self._dataSource is not None:
247
248
249 newValue = None
250 try:
251
252 if self.getType() == str:
253 newValue = str(self._dataSource)
254 else:
255 newValue = self._dataSource.getValue()
256
257
258 if self._currentBufferedSourceException is not None:
259 self._currentBufferedSourceException = None
260 self.requestRepaint()
261
262 except Exception, e:
263
264 exception = SourceException(self, e)
265 self._currentBufferedSourceException = exception
266 self.requestRepaint()
267
268
269 raise self._currentBufferedSourceException
270
271 wasModified = self.isModified()
272 self._modified = False
273
274
275 if ((newValue is None and self._value is not None)
276 or (newValue is not None
277 and (newValue != self._value))):
278 self.setInternalValue(newValue)
279 self.fireValueChange(False)
280
281 elif wasModified:
282
283
284 self.requestRepaint()
285
286
288
289 return self._modified
290
291
293
294 return self._writeThroughMode
295
296
298
299 if self._writeThroughMode == writeThrough:
300 return
301
302 self._writeThroughMode = writeThrough
303
304 if self._writeThroughMode:
305 self.commit()
306
307
309
310 return self._readThroughMode
311
312
314
315 if self._readThroughMode == readThrough:
316 return
317
318 self._readThroughMode = readThrough
319
320 if (not self.isModified() and self._readThroughMode
321 and (self._dataSource is not None)):
322 if self.getType() == str:
323 self.setInternalValue( str(self._dataSource) )
324 else:
325 self.setInternalValue(self._dataSource.getValue())
326
327 self.fireValueChange(False)
328
329
331 """Returns the value of the IProperty in human readable textual
332 format.
333 """
334 value = self.getValue()
335 if value is None:
336 return ''
337 return str( self.getValue() )
338
339
341 """Gets the current value of the field.
342
343 This is the visible, modified and possible invalid value the user
344 have entered to the field. In the read-through mode, the abstract
345 buffer is also updated and validation is performed.
346
347 Note that the object returned is compatible with getType(). For
348 example, if the type is String, this returns Strings even when the
349 underlying datasource is of some other type. In order to access the
350 datasources native type, use getPropertyDatasource().getValue()
351 instead.
352
353 Note that when you extend AbstractField, you must reimplement this
354 method if datasource.getValue() is not assignable to class returned
355 by getType() AND getType() is not String. In case of Strings,
356 getValue() calls datasource.toString() instead of
357 datasource.getValue().
358
359 @return: the current value of the field.
360 """
361
362 if (self._dataSource is None or (not self.isReadThrough())
363 or self.isModified()):
364 return self._value
365
366 if self.getType() == str:
367 newValue = str(self._dataSource)
368 else:
369 newValue = self._dataSource.getValue()
370
371 return newValue
372
373
374 - def setValue(self, newValue, repaintIsNotNeeded=False):
375 """Sets the value of the field.
376
377 @param newValue:
378 the new value of the field.
379 @param repaintIsNotNeeded:
380 True iff caller is sure that repaint is not needed.
381 @raise ReadOnlyException:
382 @raise ConversionException:
383 """
384 if ((newValue is None and self._value is not None)
385 or (newValue is not None and newValue != self._value)):
386
387
388 if self.isReadOnly():
389 raise prop.ReadOnlyException()
390
391
392
393 if (repaintIsNotNeeded and (self.isRequired()
394 or (self.getValidators() is not None))):
395 repaintIsNotNeeded = False
396
397
398 if not self.isInvalidAllowed():
399 for v in self.getValidators():
400 v.validate(newValue)
401
402
403 self.setInternalValue(newValue)
404 self._modified = self._dataSource is not None
405
406 self._valueWasModifiedByDataSourceDuringCommit = False
407
408 if (self.isWriteThrough() and (self._dataSource is not None)
409 and (self.isInvalidCommitted() or self.isValid())):
410 try:
411
412
413 self._committingValueToDataSource = True
414 self._dataSource.setValue(newValue)
415
416
417 self._modified = False
418
419 except Exception, e:
420
421 exception = SourceException(self, e)
422 self._currentBufferedSourceException = exception
423 self.requestRepaint()
424
425
426 raise self._currentBufferedSourceException
427 finally:
428 self._committingValueToDataSource = False
429
430
431 if self._currentBufferedSourceException is not None:
432 self._currentBufferedSourceException = None
433 self.requestRepaint()
434
435 if self._valueWasModifiedByDataSourceDuringCommit:
436
437
438 self._valueWasModifiedByDataSourceDuringCommit = \
439 repaintIsNotNeeded = False
440
441
442 self.fireValueChange(repaintIsNotNeeded)
443
444
446 """Gets the current data source of the field, if any.
447
448 @return: the current data source as a IProperty, or C{None}
449 if none defined.
450 """
451 return self._dataSource
452
453
455 """Sets the specified IProperty as the data source for the field.
456 All uncommitted changes are replaced with a value from the new data
457 source.
458
459 If the datasource has any validators, the same validators are added
460 to the field. Because the default behavior of the field is to allow
461 invalid values, but not to allow committing them, this only adds
462 visual error messages to fields and do not allow committing them as
463 long as the value is invalid. After the value is valid, the error
464 message is not shown and the commit can be done normally.
465
466 Note: before 6.5 we actually called discard() method in the beginning
467 of the method. This was removed to simplify implementation, avoid
468 excess calls to backing property and to avoid odd value change events
469 that were previously fired (developer expects 0-1 value change events
470 if this method is called). Some complex field implementations might
471 now need to override this method to do housekeeping similar to
472 discard().
473
474 @param newDataSource:
475 the new data source property.
476 """
477
478 oldValue = self._value
479
480
481 if (self._dataSource is not None
482 and issubclass(self._dataSource.__class__,
483 prop.IValueChangeNotifier)):
484
485 self._dataSource.removeListener(self, prop.IValueChangeListener)
486
487
488 if (self._dataSource is not None
489 and issubclass(self._dataSource.__class__,
490 prop.IReadOnlyStatusChangeNotifier)):
491
492 self._dataSource.removeListener(self,
493 prop.IReadOnlyStatusChangeListener)
494
495
496
497 self._dataSource = newDataSource
498
499
500 try:
501 if self._dataSource is not None:
502 if self.getType() == str:
503 self.setInternalValue( str(self._dataSource) )
504 else:
505 self.setInternalValue(self._dataSource.getValue())
506 self._modified = False
507 except Exception, e:
508 exception = SourceException(self, e)
509 self._currentBufferedSourceException = exception
510 self._modified = True
511
512
513 if isinstance(self._dataSource, prop.IValueChangeNotifier):
514 self._dataSource.addListener(self, prop.IValueChangeListener)
515
516 if isinstance(self._dataSource,
517 prop.IReadOnlyStatusChangeNotifier):
518 self._dataSource.addListener(self,
519 prop.IReadOnlyStatusChangeListener)
520
521
522 if isinstance(self._dataSource, IValidatable):
523 validators = self._dataSource.getValidators()
524 if validators is not None:
525 for v in validators:
526 self.addValidator(v)
527
528
529 if ((self._value != oldValue)
530 and (self._value is not None and self._value != oldValue)
531 or (self._value is None)):
532
533 self.fireValueChange(False)
534
535
537 """Adds a new validator for the field's value. All validators added
538 to a field are checked each time the its value changes.
539
540 @param validator:
541 the new validator to be added.
542 """
543 if self._validators is None:
544 self._validators = list()
545 self._validators.append(validator)
546 self.requestRepaint()
547
548
550 """Gets the validators of the field.
551
552 @return: the Unmodifiable collection that holds all validators for
553 the field.
554 """
555 if self._validators is None or len(self._validators) == 0:
556 return None
557
558 return self._validators
559
560
562 """Removes the validator from the field.
563
564 @param validator:
565 the validator to remove.
566 """
567 if self._validators is not None:
568 self._validators.remove(validator)
569
570 self.requestRepaint()
571
572
574 """Tests the current value against registered validators if the
575 field is not empty. If the field is empty it is considered valid
576 if it is not required and invalid otherwise. Validators are never
577 checked for empty fields.
578
579 @return: C{True} if all registered validators claim that
580 the current value is valid or if the field is empty and
581 not required, C{False} otherwise.
582 """
583 if self.isEmpty():
584 if self.isRequired():
585 return False
586 else:
587 return True
588
589 if self._validators is None:
590 return True
591
592 value = self.getValue()
593 for v in self._validators:
594 if not v.isValid(value):
595 return False
596
597 return True
598
599
601 """Checks the validity of the IValidatable by validating the field
602 with all attached validators except when the field is empty. An
603 empty field is invalid if it is required and valid otherwise.
604
605 The "required" validation is a built-in validation feature. If
606 the field is required, but empty, validation will throw an
607 EmptyValueException with the error message set with
608 setRequiredError().
609
610 @see: L{IValidatable.validate}
611 """
612 if self.isEmpty():
613 if self.isRequired():
614 raise EmptyValueException(self._requiredError)
615 else:
616 return
617
618
619 if self._validators is None:
620 return
621
622
623 firstError = None
624 errors = None
625 value = self.getValue()
626
627
628 for v in self._validators:
629 try:
630 v.validate(value)
631 except InvalidValueException, e:
632 if firstError is None:
633 firstError = e
634 else:
635 if errors is None:
636 errors = list()
637 errors.append(firstError)
638 errors.append(e)
639
640
641 if firstError is None:
642 return
643
644
645 if errors is None:
646 raise firstError
647
648
649 exceptions = [None] * len(errors)
650 index = 0
651 for e in errors:
652 exceptions[index] = e
653 index += 1
654
655 raise InvalidValueException(None, exceptions)
656
657
659 """Fields allow invalid values by default. In most cases this is
660 wanted, because the field otherwise visually forget the user input
661 immediately.
662
663 @return: true iff the invalid values are allowed.
664 @see: L{IValidatable.isInvalidAllowed}
665 """
666 return self._invalidAllowed
667
668
670 """Fields allow invalid values by default. In most cases this is
671 wanted, because the field otherwise visually forget the user input
672 immediately.
673
674 In common setting where the user wants to assure the correctness of
675 the datasource, but allow temporarily invalid contents in the field,
676 the user should add the validators to datasource, that should not
677 allow invalid values. The validators are automatically copied to the
678 field when the datasource is set.
679
680 @see: L{IValidatable.setInvalidAllowed}
681 """
682 self._invalidAllowed = invalidAllowed
683
684
686 """Error messages shown by the fields are composites of the error
687 message thrown by the superclasses (that is the component error
688 message), validation errors and buffered source errors.
689
690 @see: L{AbstractComponent.getErrorMessage}
691 """
692
693
694
695
696 validationError = None
697 if self.isValidationVisible():
698 try:
699 self.validate()
700 except InvalidValueException, e:
701 if not e.isInvisible():
702 validationError = e
703
704
705 superError = super(AbstractField, self).getErrorMessage()
706
707
708 if (superError is None and validationError is None
709 and self._currentBufferedSourceException is None):
710 return None
711
712
713 return CompositeErrorMessage([superError, validationError,
714 self._currentBufferedSourceException])
715
716
718
719 if (isinstance(listener, prop.IReadOnlyStatusChangeListener) and
720 (iface is None or
721 issubclass(iface, prop.IReadOnlyStatusChangeListener))):
722 self.registerListener(prop.IReadOnlyStatusChangeEvent,
723 listener, _READ_ONLY_STATUS_CHANGE_METHOD)
724
725 if (isinstance(listener, prop.IValueChangeListener) and
726 (iface is None or
727 issubclass(iface, prop.IValueChangeListener))):
728 self.registerListener(field.ValueChangeEvent,
729 listener, _VALUE_CHANGE_METHOD)
730
731 super(AbstractField, self).addListener(listener, iface)
732
733
734 - def addCallback(self, callback, eventType=None, *args):
735 if eventType is None:
736 eventType = callback._eventType
737
738 if issubclass(eventType, prop.IReadOnlyStatusChangeEvent):
739 self.registerCallback(prop.IReadOnlyStatusChangeEvent, callback,
740 None, *args)
741
742 elif issubclass(eventType, prop.ValueChangeEvent):
743 self.registerCallback(prop.ValueChangeEvent, callback, None, *args)
744
745 else:
746 super(AbstractField, self).addCallback(callback, eventType, *args)
747
748
750
751 if (isinstance(listener, prop.IReadOnlyStatusChangeListener) and
752 (iface is None or
753 issubclass(iface, prop.IReadOnlyStatusChangeListener))):
754 self.withdrawListener(prop.IReadOnlyStatusChangeEvent, listener,
755 _READ_ONLY_STATUS_CHANGE_METHOD)
756
757 if (isinstance(listener, prop.IValueChangeListener) and
758 (iface is None or
759 issubclass(iface, prop.IValueChangeListener))):
760 self.withdrawListener(field.ValueChangeEvent, listener,
761 _VALUE_CHANGE_METHOD)
762
763 super(AbstractField, self).removeListener(listener, iface)
764
765
778
779
789
790
792 """React to read only status changes of the property by
793 requesting a repaint.
794
795 @see: L{IReadOnlyStatusChangeListener}
796 """
797 self.requestRepaint()
798
799
806
807
809 """This method listens to data source value changes and passes
810 the changes forwards.
811
812 Changes are not forwarded to the listeners of the field during
813 internal operations of the field to avoid duplicate notifications.
814
815 @param event:
816 the value change event telling the data source
817 contents have changed.
818 """
819 if self.isReadThrough():
820 if self._committingValueToDataSource:
821
822 propertyNotifiesOfTheBufferedValue = \
823 (event.getProperty().getValue() == self._value
824 or (self._value is not None
825 and self._value == event.getProperty().getValue()))
826
827 if not propertyNotifiesOfTheBufferedValue:
828
829
830
831
832
833
834
835
836 self.readValueFromProperty(event)
837 self._valueWasModifiedByDataSourceDuringCommit = True
838
839 elif not self.isModified():
840 self.readValueFromProperty(event)
841 self.fireValueChange(False)
842
843
846
847
850
851
854
855
856 @classmethod
858 """Creates abstract field by the type of the property.
859
860 This returns most suitable field type for editing property of
861 given type.
862
863 @param propertyType:
864 the Type of the property, that needs to be edited.
865 @deprecated: use e.g.
866 L{DefaultFieldFactory.createFieldByPropertyType} instead
867 """
868 warn('use createFieldByPropertyType() instead', DeprecationWarning)
869
870
871 from muntjac.ui.default_field_factory import DefaultFieldFactory
872
873 return DefaultFieldFactory.createFieldByPropertyType(propertyType)
874
875
877 return self._tabIndex
878
879
883
884
886 """Sets the internal field value. This is purely used by AbstractField
887 to change the internal IField value. It does not trigger valuechange
888 events. It can be overridden by the inheriting classes to update all
889 dependent variables.
890
891 @param newValue:
892 the new value to be set.
893 """
894 self._value = newValue
895 if self._validators is not None and len(self._validators) > 0:
896 self.requestRepaint()
897
898
900 """Notifies the component that it is connected to an application.
901
902 @see: L{IComponent.attach}
903 """
904 super(AbstractField, self).attach()
905 if self._actionManager is not None:
906 self._actionManager.setViewer(self.getWindow())
907
908
913
914
916 """Is this field required. Required fields must filled by the user.
917
918 If the field is required, it is visually indicated in the user
919 interface. Furthermore, setting field to be required implicitly
920 adds "non-empty" validator and thus isValid() == false or any
921 isEmpty() fields. In those cases validation errors are not painted
922 as it is obvious that the user must fill in the required fields.
923
924 On the other hand, for the non-required fields isValid() == true
925 if the field isEmpty() regardless of any attached validators.
926
927 @return: C{True} if the field is required, otherwise C{False}.
928 """
929 return self._required
930
931
933 """Sets the field required. Required fields must filled by the user.
934
935 If the field is required, it is visually indicated in the user
936 interface. Furthermore, setting field to be required implicitly adds
937 "non-empty" validator and thus isValid() == false or any isEmpty()
938 fields. In those cases validation errors are not painted as it is
939 obvious that the user must fill in the required fields.
940
941 On the other hand, for the non-required fields isValid() == true if
942 the field isEmpty() regardless of any attached validators.
943
944 @param required:
945 Is the field required.
946 """
947 self._required = required
948 self.requestRepaint()
949
950
952 """Set the error that is show if this field is required, but empty.
953 When setting requiredMessage to be "" or null, no error pop-up or
954 exclamation mark is shown for a empty required field. This faults
955 to "". Even in those cases isValid() returns false for empty
956 required fields.
957
958 @param requiredMessage:
959 Message to be shown when this field is required, but empty.
960 """
961 self._requiredError = requiredMessage
962 self.requestRepaint()
963
964
966 return self._requiredError
967
968
970 """Is the field empty?
971
972 In general, "empty" state is same as null. As an exception,
973 TextField also treats empty string as "empty".
974 """
975 return self.getValue() is None
976
977
979 """Is automatic, visible validation enabled?
980
981 If automatic validation is enabled, any validators connected to
982 this component are evaluated while painting the component and
983 potential error messages are sent to client. If the automatic
984 validation is turned off, isValid() and validate() methods still
985 work, but one must show the validation in their own code.
986
987 @return: True, if automatic validation is enabled.
988 """
989 return self._validationVisible
990
991
993 """Enable or disable automatic, visible validation.
994
995 If automatic validation is enabled, any validators connected to
996 this component are evaluated while painting the component and
997 potential error messages are sent to client. If the automatic
998 validation is turned off, isValid() and validate() methods still
999 work, but one must show the validation in their own code.
1000
1001 @param validateAutomatically:
1002 True, if automatic validation is enabled.
1003 """
1004 if self._validationVisible != validateAutomatically:
1005 self.requestRepaint()
1006 self._validationVisible = validateAutomatically
1007
1008
1011 """Sets the current buffered source exception.
1012 """
1013 self._currentBufferedSourceException = currentBufferedSourceException
1014 self.requestRepaint()
1015
1016
1018 """Gets the L{ActionManager} used to manage the
1019 L{ShortcutListener}s added to this L{IField}.
1020
1021 @return: the ActionManager in use
1022 """
1023 if self._actionManager is None:
1024 self._actionManager = ActionManager()
1025 if self.getWindow() is not None:
1026 self._actionManager.setViewer(self.getWindow())
1027 return self._actionManager
1028
1029
1032
1033
1035 if self._actionManager is not None:
1036 self._actionManager.removeAction(shortcut)
1037
1040 """A ready-made L{ShortcutListener} that focuses the given
1041 L{Focusable} (usually a L{IField}) when the keyboard
1042 shortcut is invoked.
1043 """
1044
1046 """Creates a keyboard shortcut for focusing the given
1047 L{IFocusable} using either the shorthand notation defined in
1048 L{ShortcutAction}, or the given key code.
1049
1050 @param args: tuple of the form
1051 - (focusable, shorthandCaption)
1052 1. to b efocused when the shortcut is invoked
1053 2. caption with keycode and modifiers indicated
1054 - (focusable, keyCode, modifiers)
1055 1. to be focused when the shortcut is invoked
1056 2. keycode that invokes the shortcut
1057 3. modifiers required to invoke the shortcut
1058 - (focusable, keyCode)
1059 1. to focused when the shortcut is invoked
1060 2. keycode that invokes the shortcut
1061 """
1062 self.focusable = None
1063
1064 nargs = len(args)
1065 if nargs == 2:
1066 if isinstance(args[1], int):
1067 focusable, keyCode = args
1068 FocusShortcut.__init__(self, focusable, keyCode, None)
1069 else:
1070 focusable, shorthandCaption = args
1071 super(FocusShortcut, self).__init__(shorthandCaption)
1072 self.focusable = focusable
1073 else:
1074 focusable, keyCode = args[:2]
1075 modifiers = args[2:]
1076 super(FocusShortcut, self).__init__(None, keyCode, modifiers)
1077 self.focusable = focusable
1078
1079
1081 self.focusable.focus()
1082
1086 """An C{Event} object specifying the IProperty whose
1087 read-only status has changed.
1088
1089 @author: Vaadin Ltd.
1090 @author: Richard Lincoln
1091 @version: 1.1.2
1092 """
1093
1101
1102
1104 """IProperty where the event occurred.
1105
1106 @return: the Source of the event.
1107 """
1108 return self.getSource()
1109