001: /* *************************************************************************
002:
003: Millstone(TM)
004: Open Sourced User Interface Library for
005: Internet Development with Java
006:
007: Millstone is a registered trademark of IT Mill Ltd
008: Copyright (C) 2000-2005 IT Mill Ltd
009:
010: *************************************************************************
011:
012: This library is free software; you can redistribute it and/or
013: modify it under the terms of the GNU Lesser General Public
014: license version 2.1 as published by the Free Software Foundation.
015:
016: This library is distributed in the hope that it will be useful,
017: but WITHOUT ANY WARRANTY; without even the implied warranty of
018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: Lesser General Public License for more details.
020:
021: You should have received a copy of the GNU Lesser General Public
022: License along with this library; if not, write to the Free Software
023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: *************************************************************************
026:
027: For more information, contact:
028:
029: IT Mill Ltd phone: +358 2 4802 7180
030: Ruukinkatu 2-4 fax: +358 2 4802 7181
031: 20540, Turku email: info@itmill.com
032: Finland company www: www.itmill.com
033:
034: Primary source for MillStone information and releases: www.millstone.org
035:
036: ********************************************************************** */
037:
038: package org.millstone.base.ui;
039:
040: import java.util.Collection;
041: import java.util.Collections;
042: import java.util.HashMap;
043: import java.util.Iterator;
044: import java.util.LinkedList;
045:
046: import org.millstone.base.data.*;
047: import org.millstone.base.data.Item;
048: import org.millstone.base.data.Buffered;
049: import org.millstone.base.data.Property;
050: import org.millstone.base.data.Validator;
051: import org.millstone.base.data.Validator.InvalidValueException;
052: import org.millstone.base.data.util.BeanItem;
053: import org.millstone.base.terminal.PaintException;
054: import org.millstone.base.terminal.PaintTarget;
055:
056: /** Form component provides easy way of creating and managing sets fields.
057: *
058: * <p>Form is a container for fields implementing {@link Field} interface. It
059: * provides support for any layouts and provides buffering interface for easy
060: * connection of commit- and discard buttons. All the form fields can be
061: * customized by adding validators, setting captions and icons, setting
062: * immediateness, etc. Also direct mechanism for replacing existing fields with
063: * selections is given.
064: * </p>
065: *
066: * <p>Form provides customizable editor for classes implementing
067: * {@link org.millstone.base.data.Item} interface. Also the form itself
068: * implements this interface for easier connectivity to other items.
069: * To use the form as editor for an item, just connect the item to
070: * form with {@link Form#setItemDataSource(Item)}. If only a part of the
071: * item needs to be edited, {@link Form#setItemDataSource(Item,Collection)}
072: * can be used instead. After the item has been connected to the form,
073: * the automatically created fields can be customized and new fields can
074: * be added. If you need to connect a class that does not implement
075: * {@link org.millstone.base.data.Item} interface, most properties of any
076: * class following bean pattern, can be accessed trough
077: * {@link org.millstone.base.data.util.BeanItem}.</p>
078: *
079: * @author IT Mill Ltd.
080: * @version 3.1.1
081: * @since 3.0
082: */
083: public class Form extends AbstractField implements Item.Editor,
084: Buffered, Item, Validatable {
085:
086: private Object propertyValue;
087:
088: /** Layout of the form */
089: private Layout layout;
090:
091: /** Item connected to this form as datasource */
092: private Item itemDatasource;
093:
094: /** Ordered list of property ids in this editor */
095: private LinkedList propertyIds = new LinkedList();
096:
097: /** Current buffered source exception */
098: private Buffered.SourceException currentBufferedSourceException = null;
099:
100: /** Is the form in write trough mode */
101: private boolean writeThrough = true;
102:
103: /** Is the form in read trough mode */
104: private boolean readThrough = true;
105:
106: /** Mapping from propertyName to corresponding field */
107: private HashMap fields = new HashMap();
108:
109: /** Field factory for this form */
110: private FieldFactory fieldFactory;
111:
112: /** Registered Validators */
113: private LinkedList validators;
114:
115: /** Visible item properties */
116: private Collection visibleItemProperties;
117:
118: /** Contruct a new form with default layout.
119: *
120: * <p>By default the form uses <code>OrderedLayout</code>
121: * with <code>form</code>-style.
122: *
123: * @param formLayout The layout of the form.
124: */
125: public Form() {
126: this (null);
127: }
128:
129: /** Contruct a new form with given layout.
130: *
131: * @param formLayout The layout of the form.
132: */
133: public Form(Layout formLayout) {
134: this (formLayout, new BaseFieldFactory());
135: }
136:
137: /** Contruct a new form with given layout and FieldFactory.
138: *
139: * @param formLayout The layout of the form.
140: * @param fieldFactory FieldFactory of the form
141: */
142: public Form(Layout formLayout, FieldFactory fieldFactory) {
143: super ();
144: setLayout(formLayout);
145: setFieldFactory(fieldFactory);
146: }
147:
148: /* Documented in interface */
149: public String getTag() {
150: return "component";
151: }
152:
153: /* Documented in interface */
154: public void paintContent(PaintTarget target) throws PaintException {
155: super .paintContent(target);
156: layout.paint(target);
157:
158: }
159:
160: /* Commit changes to the data source
161: * Don't add a JavaDoc comment here, we use the default one from the
162: * interface.
163: */
164: public void commit() throws Buffered.SourceException {
165:
166: LinkedList problems = null;
167:
168: // Try to commit all
169: for (Iterator i = propertyIds.iterator(); i.hasNext();)
170: try {
171: Field f = ((Field) fields.get(i.next()));
172: //Commit only non-readonly fields.
173: if (!f.isReadOnly()) {
174: f.commit();
175: }
176: } catch (Buffered.SourceException e) {
177: if (problems == null)
178: problems = new LinkedList();
179: problems.add(e);
180: }
181:
182: // No problems occurred
183: if (problems == null) {
184: if (currentBufferedSourceException != null) {
185: currentBufferedSourceException = null;
186: requestRepaint();
187: }
188: return;
189: }
190:
191: // Commit problems
192: Throwable[] causes = new Throwable[problems.size()];
193: int index = 0;
194: for (Iterator i = problems.iterator(); i.hasNext();)
195: causes[index++] = (Throwable) i.next();
196: Buffered.SourceException e = new Buffered.SourceException(this ,
197: causes);
198: currentBufferedSourceException = e;
199: requestRepaint();
200: throw e;
201: }
202:
203: /* Discard local changes and refresh values from the data source
204: * Don't add a JavaDoc comment here, we use the default one from the
205: * interface.
206: */
207: public void discard() throws Buffered.SourceException {
208:
209: LinkedList problems = null;
210:
211: // Try to discard all changes
212: for (Iterator i = propertyIds.iterator(); i.hasNext();)
213: try {
214: ((Field) fields.get(i.next())).discard();
215: } catch (Buffered.SourceException e) {
216: if (problems == null)
217: problems = new LinkedList();
218: problems.add(e);
219: }
220:
221: // No problems occurred
222: if (problems == null) {
223: if (currentBufferedSourceException != null) {
224: currentBufferedSourceException = null;
225: requestRepaint();
226: }
227: return;
228: }
229:
230: // Discard problems occurred
231: Throwable[] causes = new Throwable[problems.size()];
232: int index = 0;
233: for (Iterator i = problems.iterator(); i.hasNext();)
234: causes[index++] = (Throwable) i.next();
235: Buffered.SourceException e = new Buffered.SourceException(this ,
236: causes);
237: currentBufferedSourceException = e;
238: requestRepaint();
239: throw e;
240: }
241:
242: /* Is the object modified but not committed?
243: * Don't add a JavaDoc comment here, we use the default one from the
244: * interface.
245: */
246: public boolean isModified() {
247: for (Iterator i = propertyIds.iterator(); i.hasNext();) {
248: Field f = (Field) fields.get(i.next());
249: if (f != null && f.isModified())
250: return true;
251:
252: }
253: return false;
254: }
255:
256: /* Is the editor in a read-through mode?
257: * Don't add a JavaDoc comment here, we use the default one from the
258: * interface.
259: */
260: public boolean isReadThrough() {
261: return readThrough;
262: }
263:
264: /* Is the editor in a write-through mode?
265: * Don't add a JavaDoc comment here, we use the default one from the
266: * interface.
267: */
268: public boolean isWriteThrough() {
269: return writeThrough;
270: }
271:
272: /* Sets the editor's read-through mode to the specified status.
273: * Don't add a JavaDoc comment here, we use the default one from the
274: * interface.
275: */
276: public void setReadThrough(boolean readThrough) {
277: if (readThrough != this .readThrough) {
278: this .readThrough = readThrough;
279: for (Iterator i = propertyIds.iterator(); i.hasNext();)
280: ((Field) fields.get(i.next()))
281: .setReadThrough(readThrough);
282: }
283: }
284:
285: /* Sets the editor's read-through mode to the specified status.
286: * Don't add a JavaDoc comment here, we use the default one from the
287: * interface.
288: */
289: public void setWriteThrough(boolean writeThrough) {
290: if (writeThrough != this .writeThrough) {
291: this .writeThrough = writeThrough;
292: for (Iterator i = propertyIds.iterator(); i.hasNext();)
293: ((Field) fields.get(i.next()))
294: .setWriteThrough(writeThrough);
295: }
296: }
297:
298: /** Add a new property to form and create corresponding field.
299: *
300: * @see org.millstone.base.data.Item#addItemProperty(Object, Property)
301: */
302: public boolean addItemProperty(Object id, Property property) {
303:
304: // Check inputs
305: if (id == null || property == null)
306: throw new NullPointerException(
307: "Id and property must be non-null");
308:
309: // Check that the property id is not reserved
310: if (propertyIds.contains(id))
311: return false;
312:
313: // Get suitable field
314: Field field = this .fieldFactory.createField(property, this );
315: if (field == null)
316: return false;
317:
318: // Configure the field
319: try {
320: field.setPropertyDataSource(property);
321: String caption = id.toString();
322: if (caption.length() > 50)
323: caption = caption.substring(0, 47) + "...";
324: if (caption.length() > 0)
325: caption = "" + Character.toUpperCase(caption.charAt(0))
326: + caption.substring(1, caption.length());
327: field.setCaption(caption);
328: } catch (Throwable ignored) {
329: return false;
330: }
331:
332: addField(id, field);
333:
334: return true;
335: }
336:
337: /** Add field to form.
338: *
339: * <p>The property id must not be already used in the form.
340: * </p>
341: *
342: * <p>This field is added to the form layout in the default position
343: * (the position used by {@link Layout#addComponent(Component)} method.
344: * In the special case that the underlying layout is a custom layout,
345: * string representation of the property id is used instead of the
346: * default location.</p>
347: *
348: * @param propertyId Property id the the field.
349: * @param field New field added to the form.
350: */
351: public void addField(Object propertyId, Field field) {
352:
353: if (propertyId != null && field != null) {
354: this .dependsOn(field);
355: field.dependsOn(this );
356: fields.put(propertyId, field);
357: propertyIds.addLast(propertyId);
358: field.setReadThrough(readThrough);
359: field.setWriteThrough(writeThrough);
360:
361: if (layout instanceof CustomLayout)
362: ((CustomLayout) layout).addComponent(field, propertyId
363: .toString());
364: else
365: layout.addComponent(field);
366:
367: requestRepaint();
368: }
369: }
370:
371: /** The property identified by the property id.
372: *
373: * <p>The property data source of the field specified with
374: * property id is returned. If there is a (with specified property id)
375: * having no data source,
376: * the field is returned instead of the data source.</p>
377: *
378: * @see org.millstone.base.data.Item#getItemProperty(Object)
379: */
380: public Property getItemProperty(Object id) {
381: Field field = (Field) fields.get(id);
382: if (field == null)
383: return null;
384: Property property = field.getPropertyDataSource();
385:
386: if (property != null)
387: return property;
388: else
389: return field;
390: }
391:
392: /** Get the field identified by the propertyid */
393: public Field getField(Object propertyId) {
394: return (Field) fields.get(propertyId);
395: }
396:
397: /* Documented in interface */
398: public Collection getItemPropertyIds() {
399: return Collections.unmodifiableCollection(propertyIds);
400: }
401:
402: /** Removes the property and corresponding field from the form.
403: *
404: * @see org.millstone.base.data.Item#removeItemProperty(Object)
405: */
406: public boolean removeItemProperty(Object id) {
407:
408: Field field = (Field) fields.get(id);
409:
410: if (field != null) {
411: propertyIds.remove(id);
412: fields.remove(id);
413: this .removeDirectDependency(field);
414: field.removeDirectDependency(this );
415: layout.removeComponent(field);
416: return true;
417: }
418:
419: return false;
420: }
421:
422: /** Removes all properties and fields from the form.
423: *
424: * @return Success of the operation. Removal of all fields succeeded
425: * if (and only if) the return value is true.
426: */
427: public boolean removeAllProperties() {
428: Object[] properties = propertyIds.toArray();
429: boolean success = true;
430:
431: for (int i = 0; i < properties.length; i++)
432: if (!removeItemProperty(properties[i]))
433: success = false;
434:
435: return success;
436: }
437:
438: /* Documented in the interface */
439: public Item getItemDataSource() {
440: return itemDatasource;
441: }
442:
443: /** Set the item datasource for the form.
444: *
445: * <p>Setting item datasource clears any fields, the form might contain
446: * and adds all the properties as fields to the form.</p>
447: *
448: * @see org.millstone.base.data.Item.Viewer#setItemDataSource(Item)
449: */
450: public void setItemDataSource(Item newDataSource) {
451: setItemDataSource(newDataSource,
452: newDataSource != null ? newDataSource
453: .getItemPropertyIds() : null);
454: }
455:
456: /** Set the item datasource for the form, but limit the form contents
457: * to specified properties of the item.
458: *
459: * <p>Setting item datasource clears any fields, the form might contain
460: * and adds the specified the properties as fields to the form, in the
461: * specified order.</p>
462: *
463: * @see org.millstone.base.data.Item.Viewer#setItemDataSource(Item)
464: */
465: public void setItemDataSource(Item newDataSource,
466: Collection propertyIds) {
467:
468: // Remove all fields first from the form
469: removeAllProperties();
470:
471: // Set the datasource
472: itemDatasource = newDataSource;
473:
474: //If the new datasource is null, just set null datasource
475: if (itemDatasource == null)
476: return;
477:
478: // Add all the properties to this form
479: for (Iterator i = propertyIds.iterator(); i.hasNext();) {
480: Object id = i.next();
481: Property property = itemDatasource.getItemProperty(id);
482: if (id != null && property != null) {
483: Field f = this .fieldFactory.createField(itemDatasource,
484: id, this );
485: if (f != null) {
486: f.setPropertyDataSource(property);
487: addField(id, f);
488: }
489: }
490: }
491: }
492:
493: /** Get the layout of the form.
494: *
495: * <p>By default form uses <code>OrderedLayout</code> with <code>form</code>-style.</p>
496: *
497: * @return Layout of the form.
498: */
499: public Layout getLayout() {
500: return layout;
501: }
502:
503: /** Set the layout of the form.
504: *
505: * <p>By default form uses <code>OrderedLayout</code> with <code>form</code>-style.</p>
506: *
507: * @param layout Layout of the form.
508: */
509: public void setLayout(Layout newLayout) {
510:
511: // Use orderedlayout by default
512: if (newLayout == null) {
513: newLayout = new OrderedLayout();
514: newLayout.setStyle("form");
515: }
516:
517: // Move components from previous layout
518: if (this .layout != null) {
519: newLayout.moveComponentsFrom(this .layout);
520: this .layout.setParent(null);
521: }
522:
523: // Replace the previous layout
524: newLayout.setParent(this );
525: this .layout = newLayout;
526: }
527:
528: /** Set a form field to be selectable from static list of changes.
529: *
530: * <p>The list values and descriptions are given as array. The value-array must contain the
531: * current value of the field and the lengths of the arrays must match. Null values are not
532: * supported.</p>
533: *
534: * @return The select property generated
535: */
536: public Select replaceWithSelect(Object propertyId, Object[] values,
537: Object[] descriptions) {
538:
539: // Check the parameters
540: if (propertyId == null || values == null
541: || descriptions == null)
542: throw new NullPointerException(
543: "All parameters must be non-null");
544: if (values.length != descriptions.length)
545: throw new IllegalArgumentException(
546: "Value and description list are of different size");
547:
548: // Get the old field
549: Field oldField = (Field) fields.get(propertyId);
550: if (oldField == null)
551: throw new IllegalArgumentException(
552: "Field with given propertyid '"
553: + propertyId.toString()
554: + "' can not be found.");
555: Object value = oldField.getValue();
556:
557: // Check that the value exists and check if the select should
558: // be forced in multiselect mode
559: boolean found = false;
560: boolean isMultiselect = false;
561: for (int i = 0; i < values.length && !found; i++)
562: if (values[i] == value
563: || (value != null && value.equals(values[i])))
564: found = true;
565: if (value != null && !found) {
566: if (value instanceof Collection) {
567: for (Iterator it = ((Collection) value).iterator(); it
568: .hasNext();) {
569: Object val = it.next();
570: found = false;
571: for (int i = 0; i < values.length && !found; i++)
572: if (values[i] == val
573: || (val != null && val
574: .equals(values[i])))
575: found = true;
576: if (!found)
577: throw new IllegalArgumentException(
578: "Currently selected value '" + val
579: + "' of property '"
580: + propertyId.toString()
581: + "' was not found");
582: }
583: isMultiselect = true;
584: } else
585: throw new IllegalArgumentException("Current value '"
586: + value + "' of property '"
587: + propertyId.toString() + "' was not found");
588: }
589:
590: // Create new field matching to old field parameters
591: Select newField = new Select();
592: if (isMultiselect)
593: newField.setMultiSelect(true);
594: newField.setCaption(oldField.getCaption());
595: newField.setReadOnly(oldField.isReadOnly());
596: newField.setReadThrough(oldField.isReadThrough());
597: newField.setWriteThrough(oldField.isWriteThrough());
598:
599: // Create options list
600: newField.addContainerProperty("desc", String.class, "");
601: newField.setItemCaptionPropertyId("desc");
602: for (int i = 0; i < values.length; i++) {
603: Object id = values[i];
604: if (id == null) {
605: id = new Object();
606: newField.setNullSelectionItemId(id);
607: }
608: Item item = newField.addItem(id);
609: if (item != null)
610: item.getItemProperty("desc").setValue(
611: descriptions[i].toString());
612: }
613:
614: // Set the property data source
615: Property property = oldField.getPropertyDataSource();
616: oldField.setPropertyDataSource(null);
617: newField.setPropertyDataSource(property);
618:
619: // Replace the old field with new one
620: layout.replaceComponent(oldField, newField);
621: fields.put(propertyId, newField);
622: this .removeDirectDependency(oldField);
623: oldField.removeDirectDependency(this );
624: this .dependsOn(newField);
625: newField.dependsOn(this );
626:
627: return newField;
628: }
629:
630: /**
631: * @see org.millstone.base.ui.Component#attach()
632: */
633: public void attach() {
634: super .attach();
635: layout.attach();
636: }
637:
638: /**
639: * @see org.millstone.base.ui.Component#detach()
640: */
641: public void detach() {
642: super .detach();
643: layout.detach();
644: }
645:
646: /**
647: * @see org.millstone.base.data.Validatable#addValidator(org.millstone.base.data.Validator)
648: */
649: public void addValidator(Validator validator) {
650:
651: if (this .validators == null) {
652: this .validators = new LinkedList();
653: }
654: this .validators.add(validator);
655: }
656:
657: /**
658: * @see org.millstone.base.data.Validatable#removeValidator(org.millstone.base.data.Validator)
659: */
660: public void removeValidator(Validator validator) {
661: if (this .validators != null) {
662: this .validators.remove(validator);
663: }
664: }
665:
666: /**
667: * @see org.millstone.base.data.Validatable#getValidators()
668: */
669: public Collection getValidators() {
670: if (this .validators == null) {
671: this .validators = new LinkedList();
672: }
673: return null;
674: }
675:
676: /**
677: * @see org.millstone.base.data.Validatable#isValid()
678: */
679: public boolean isValid() {
680: boolean valid = true;
681: for (Iterator i = propertyIds.iterator(); i.hasNext();)
682: valid &= ((Field) fields.get(i.next())).isValid();
683: return valid;
684: }
685:
686: /**
687: * @see org.millstone.base.data.Validatable#validate()
688: */
689: public void validate() throws InvalidValueException {
690: for (Iterator i = propertyIds.iterator(); i.hasNext();)
691: ((Field) fields.get(i.next())).validate();
692: }
693:
694: /**
695: * @see org.millstone.base.data.Validatable#isInvalidAllowed()
696: */
697: public boolean isInvalidAllowed() {
698: return true;
699: }
700:
701: /**
702: * @see org.millstone.base.data.Validatable#setInvalidAllowed(boolean)
703: */
704: public void setInvalidAllowed(boolean invalidValueAllowed)
705: throws UnsupportedOperationException {
706: throw new UnsupportedOperationException();
707: }
708:
709: /**
710: * @see org.millstone.base.ui.Component#setReadOnly(boolean)
711: */
712: public void setReadOnly(boolean readOnly) {
713: super .setReadOnly(readOnly);
714: for (Iterator i = propertyIds.iterator(); i.hasNext();)
715: ((Field) fields.get(i.next())).setReadOnly(readOnly);
716: }
717:
718: /** Set the field factory of Form.
719: *
720: * FieldFacroty is used to create fields for form properties.
721: * By default the form uses BaseFieldFactory to create Field instances.
722: *
723: * @param fieldFactory New factory used to create the fields
724: * @see Field
725: * @see FieldFactory
726: */
727: public void setFieldFactory(FieldFactory fieldFactory) {
728: this .fieldFactory = fieldFactory;
729: }
730:
731: /** Get the field factory of the form.
732: *
733: * @return FieldFactory Factory used to create the fields
734: */
735: public FieldFactory getFieldFactory() {
736: return this .fieldFactory;
737: }
738:
739: /**
740: * @see org.millstone.base.ui.AbstractField#getType()
741: */
742: public Class getType() {
743: if (getPropertyDataSource() != null)
744: return getPropertyDataSource().getType();
745: return Object.class;
746: }
747:
748: /** Set the internal value.
749: *
750: * This is relevant when the Form is used as Field.
751: * @see org.millstone.base.ui.AbstractField#setInternalValue(java.lang.Object)
752: */
753: protected void setInternalValue(Object newValue) {
754: // Store old value
755: Object oldValue = this .propertyValue;
756:
757: // Set the current Value
758: super .setInternalValue(newValue);
759: this .propertyValue = newValue;
760:
761: // Ignore form updating if data object has not changed.
762: if (oldValue != newValue) {
763: setFormDataSource(newValue, getVisibleItemProperties());
764: }
765: }
766:
767: /**Get first field in form.
768: * @return Field
769: */
770: private Field getFirstField() {
771: Object id = null;
772: if (this .getItemPropertyIds() != null) {
773: id = this .getItemPropertyIds().iterator().next();
774: }
775: if (id != null)
776: return this .getField(id);
777: return null;
778: }
779:
780: /** Update the internal form datasource.
781: *
782: * Method setFormDataSource.
783: * @param value
784: */
785: protected void setFormDataSource(Object data, Collection properties) {
786:
787: // If data is an item use it.
788: Item item = null;
789: if (data instanceof Item) {
790: item = (Item) data;
791: } else if (data != null) {
792: item = new BeanItem(data);
793: }
794:
795: // Set the datasource to form
796: if (item != null && properties != null) {
797: // Show only given properties
798: this .setItemDataSource(item, properties);
799: } else {
800: // Show all properties
801: this .setItemDataSource(item);
802: }
803: }
804:
805: /**
806: * Returns the visibleProperties.
807: * @return Collection
808: */
809: public Collection getVisibleItemProperties() {
810: return visibleItemProperties;
811: }
812:
813: /**
814: * Sets the visibleProperties.
815: * @param visibleProperties The visibleProperties to set
816: */
817: public void setVisibleItemProperties(Collection visibleProperties) {
818: this .visibleItemProperties = visibleProperties;
819: Object value = getValue();
820: setFormDataSource(value, getVisibleItemProperties());
821: }
822:
823: /** Focuses the first field in the form.
824: * @see org.millstone.base.ui.Component.Focusable#focus()
825: */
826: public void focus() {
827: Field f = getFirstField();
828: if (f != null) {
829: f.focus();
830: }
831: }
832:
833: /**
834: * @see org.millstone.base.ui.Component.Focusable#setTabIndex(int)
835: */
836: public void setTabIndex(int tabIndex) {
837: super .setTabIndex(tabIndex);
838: for (Iterator i = this.getItemPropertyIds().iterator(); i
839: .hasNext();)
840: (this.getField(i.next())).setTabIndex(tabIndex);
841: }
842: }
|