0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.form;
0043:
0044: import java.util.*;
0045: import javax.swing.event.UndoableEditEvent;
0046: import javax.swing.undo.*;
0047:
0048: import org.openide.awt.UndoRedo;
0049: import org.openide.util.Mutex;
0050: import org.openide.util.MutexException;
0051:
0052: import org.netbeans.modules.form.layoutsupport.*;
0053: import org.netbeans.modules.form.codestructure.CodeStructure;
0054: import org.netbeans.modules.form.layoutdesign.*;
0055:
0056: /**
0057: * Holds all data of a form.
0058: *
0059: * @author Tran Duc Trung, Tomas Pavek
0060: */
0061:
0062: public class FormModel {
0063: // name of the form (name of the DataObject)
0064: private String formName;
0065:
0066: private boolean readOnly = false;
0067:
0068: public enum FormVersion {
0069: BASIC, // form file version up to 1.2
0070: NB50, // form file verson 1.3
0071: NB60_PRE, // until NB 6.0 beta 1 (incl. 5.5 with 6.0 update), form file version 1.4
0072: NB60, // since NB 6.0 beta1, form file version 1.5
0073: NB61
0074: // since NB 6.1 milestone 2, for file version 1.6
0075: }
0076:
0077: final static FormVersion LATEST_VERSION = FormVersion.NB61;
0078:
0079: private FormVersion currentVersionLevel;
0080: private FormVersion lastVersionLevel;
0081: private FormVersion maxVersionLevel; // max version to upgrade to without user confirmation
0082:
0083: // the class on which the form is based (which is extended in the java file)
0084: private Class<?> formBaseClass;
0085:
0086: // the top metacomponent of the form (null if form is based on Object)
0087: private RADComponent topRADComponent;
0088:
0089: // other components - out of the main hierarchy under topRADComponent
0090: private List<RADComponent> otherComponents = new ArrayList<RADComponent>(
0091: 10);
0092:
0093: // holds both topRADComponent and otherComponents
0094: private ComponentContainer modelContainer;
0095:
0096: private LayoutModel layoutModel;
0097:
0098: private Map<String, RADComponent> idToComponents = new HashMap<String, RADComponent>();
0099:
0100: private boolean formLoaded = false;
0101:
0102: private UndoRedo.Manager undoRedoManager;
0103: private boolean undoRedoRecording = false;
0104: private CompoundEdit compoundEdit;
0105: private boolean undoCompoundEdit = false;
0106:
0107: private FormEvents formEvents;
0108:
0109: // list of listeners registered on FormModel
0110: private ArrayList<FormModelListener> listeners;
0111: private List<FormModelEvent> eventList;
0112: private boolean firing;
0113:
0114: private MetaComponentCreator metaCreator;
0115:
0116: private CodeStructure codeStructure = new CodeStructure(false);
0117:
0118: private FormSettings settings = new FormSettings(this );
0119:
0120: private boolean freeDesignDefaultLayout = false;
0121:
0122: // -------------
0123: // initialization
0124:
0125: FormModel() {
0126: }
0127:
0128: /** This methods sets the form base class (which is in fact the superclass
0129: * of the form class in source java file). It is used for initializing
0130: * the top meta component, and is also presented as the top component
0131: * in designer and inspector.
0132: *
0133: * @param formClass form base class.
0134: * @throws java.lang.Exception if anything goes wrong.
0135: */
0136: public void setFormBaseClass(Class<?> formClass) throws Exception {
0137: if (formBaseClass != null)
0138: throw new IllegalStateException(
0139: "Form type already initialized."); // NOI18N
0140:
0141: RADComponent topComp;
0142: if (FormUtils.isVisualizableClass(formClass)) {
0143: if (FormUtils.isContainer(formClass)) {
0144: topComp = new RADVisualFormContainer();
0145: } else {
0146: topComp = new RADVisualComponent() {
0147: // top-level component does not have a variable
0148: @Override
0149: public String getName() {
0150: return FormUtils
0151: .getBundleString("CTL_FormTopContainerName"); // NOI18N
0152: }
0153:
0154: @Override
0155: public void setName(String value) {
0156: }
0157: };
0158: }
0159: } else if (java.lang.Object.class != formClass)
0160: topComp = new RADFormContainer();
0161: else
0162: topComp = null;
0163:
0164: if (topComp != null) {
0165: topRADComponent = topComp;
0166: topComp.initialize(this );
0167: topComp.initInstance(formClass);
0168: topComp.setInModel(true);
0169: }
0170:
0171: formBaseClass = formClass;
0172: // topRADComponent = topComp;
0173: layoutModel = new LayoutModel();
0174: layoutModel.setChangeRecording(false);
0175: }
0176:
0177: public Class<?> getFormBaseClass() {
0178: return formBaseClass;
0179: }
0180:
0181: void setName(String name) {
0182: formName = name;
0183: }
0184:
0185: /**
0186: * Requires the form version to be at least 'minVersion'. If the actual
0187: * version is lower, it is upgraded to 'upgradeTo'. If the upgrad exceeds
0188: * the maximum version level set for this form (roughly corresponding
0189: * to the NB version in which the form was created) a confirmation message
0190: * is shown to the user later (see FormEditor.checkFormVersionUpgrade).
0191: * @param minVersion the minimum version level required
0192: * @param upgradeTo version level to upgrade to if the minimum version is not met
0193: */
0194: public void raiseVersionLevel(FormVersion minVersion,
0195: FormVersion upgradeTo) {
0196: if (minVersion.ordinal() > currentVersionLevel.ordinal()
0197: && (undoRedoRecording || !formLoaded)) {
0198: assert upgradeTo.ordinal() >= minVersion.ordinal();
0199: setCurrentVersionLevel(upgradeTo);
0200: }
0201: }
0202:
0203: void setCurrentVersionLevel(FormVersion version) {
0204: lastVersionLevel = currentVersionLevel;
0205: currentVersionLevel = version;
0206: }
0207:
0208: FormVersion getCurrentVersionLevel() {
0209: return currentVersionLevel;
0210: }
0211:
0212: void revertVersionLevel() {
0213: currentVersionLevel = lastVersionLevel;
0214: }
0215:
0216: void setMaxVersionLevel(FormVersion version) {
0217: maxVersionLevel = version;
0218: }
0219:
0220: FormVersion getMaxVersionLevel() {
0221: return maxVersionLevel;
0222: }
0223:
0224: void setReadOnly(boolean readOnly) {
0225: this .readOnly = readOnly;
0226: }
0227:
0228: // -----------
0229: // getters
0230:
0231: public final String getName() {
0232: return formName;
0233: }
0234:
0235: public final boolean isReadOnly() {
0236: return readOnly;
0237: }
0238:
0239: public final boolean isFormLoaded() {
0240: return formLoaded;
0241: }
0242:
0243: public final boolean wasCorrected() {
0244: return formLoaded && layoutModel != null
0245: && layoutModel.wasCorrected();
0246: }
0247:
0248: // public final FormDesigner getFormDesigner() {
0249: // return FormEditorSupport.getFormDesigner(this);
0250: // }
0251: //
0252: // // for compatibility with previous version
0253: // public final FormDataObject getFormDataObject() {
0254: // return FormEditorSupport.getFormDataObject(this);
0255: // }
0256:
0257: public final RADComponent getTopRADComponent() {
0258: return topRADComponent;
0259: }
0260:
0261: public ComponentContainer getModelContainer() {
0262: if (modelContainer == null)
0263: modelContainer = new ModelContainer();
0264: return modelContainer;
0265: }
0266:
0267: public Collection<RADComponent> getOtherComponents() {
0268: return Collections.unmodifiableCollection(otherComponents);
0269: }
0270:
0271: public final LayoutModel getLayoutModel() {
0272: return layoutModel;
0273: }
0274:
0275: public final RADComponent getMetaComponent(String id) {
0276: return idToComponents.get(id);
0277: }
0278:
0279: public RADComponent findRADComponent(String name) {
0280: Iterator allComps = idToComponents.values().iterator(); // getMetaComponents().iterator();
0281: while (allComps.hasNext()) {
0282: RADComponent comp = (RADComponent) allComps.next();
0283: if (name.equals(comp.getName()))
0284: return comp;
0285: }
0286: return null;
0287: }
0288:
0289: /**
0290: * Returns list of all components in the model. A new List instance is
0291: * created. The order of the components is random.
0292: *
0293: * @return list of components in the model.
0294: */
0295: public java.util.List<RADComponent> getComponentList() {
0296: return new ArrayList<RADComponent>(idToComponents.values());
0297: }
0298:
0299: /**
0300: * Returns list of all components in the model. A new instance of list is
0301: * created and the components are added to the list in the traversal order
0302: * (used e.g. by code generator or persistence manager).
0303: *
0304: * @return list of components in the model.
0305: */
0306: public java.util.List<RADComponent> getOrderedComponentList() {
0307: java.util.List<RADComponent> list = new ArrayList<RADComponent>(
0308: idToComponents.size());
0309: collectMetaComponents(getModelContainer(), list);
0310: return list;
0311: }
0312:
0313: /**
0314: * Returns an unmodifiable collection of all components in the model
0315: * in random order.
0316: *
0317: * @return list of components in the model.
0318: */
0319: public Collection<RADComponent> getAllComponents() {
0320: return Collections.unmodifiableCollection(idToComponents
0321: .values());
0322: }
0323:
0324: public List<RADComponent> getNonVisualComponents() {
0325: List<RADComponent> list = new ArrayList<RADComponent>(
0326: otherComponents.size());
0327: for (RADComponent metacomp : otherComponents) {
0328: if (!(metacomp instanceof RADVisualComponent)) {
0329: list.add(metacomp);
0330: }
0331: }
0332: return list;
0333: }
0334:
0335: public List<RADComponent> getVisualComponents() {
0336: List<RADComponent> list = new ArrayList<RADComponent>(
0337: idToComponents.size());
0338: for (Map.Entry<String, RADComponent> e : idToComponents
0339: .entrySet()) {
0340: RADComponent metacomp = e.getValue();
0341: if (metacomp instanceof RADVisualComponent) {
0342: list.add(metacomp);
0343: }
0344: }
0345: return list;
0346: }
0347:
0348: public FormEvents getFormEvents() {
0349: if (formEvents == null)
0350: formEvents = new FormEvents(this );
0351: return formEvents;
0352: }
0353:
0354: private static void collectMetaComponents(ComponentContainer cont,
0355: java.util.List<RADComponent> list) {
0356: RADComponent[] comps = cont.getSubBeans();
0357: for (int i = 0; i < comps.length; i++) {
0358: RADComponent comp = comps[i];
0359: list.add(comp);
0360: if (comp instanceof ComponentContainer)
0361: collectMetaComponents((ComponentContainer) comp, list);
0362: }
0363: }
0364:
0365: private static void collectVisualMetaComponents(
0366: RADVisualContainer cont, java.util.List<RADComponent> list) {
0367: RADVisualComponent[] comps = cont.getSubComponents();
0368: for (int i = 0; i < comps.length; i++) {
0369: RADComponent comp = comps[i];
0370: list.add(comp);
0371: if (comp instanceof RADVisualContainer)
0372: collectVisualMetaComponents((RADVisualContainer) comp,
0373: list);
0374: }
0375: }
0376:
0377: public FormSettings getSettings() {
0378: return settings;
0379: }
0380:
0381: // -----------
0382: // adding/deleting components, setting layout, etc
0383:
0384: /**
0385: * @return MetaComponentCreator responsible for creating new components and
0386: * adding them to the model.
0387: */
0388: public MetaComponentCreator getComponentCreator() {
0389: if (metaCreator == null)
0390: metaCreator = new MetaComponentCreator(this );
0391: return metaCreator;
0392: }
0393:
0394: /** Adds a new component to given (non-visual) container in the model. If
0395: * the container is not specified, the component is added to the
0396: * "other components".
0397: *
0398: * @param metacomp component to add.
0399: * @param parentContainer parent of the added component.
0400: * @param newlyAdded is newly added?
0401: */
0402: public void addComponent(RADComponent metacomp,
0403: ComponentContainer parentContainer, boolean newlyAdded) {
0404: if (newlyAdded || !metacomp.isInModel()) {
0405: setInModelRecursively(metacomp, true);
0406: newlyAdded = true;
0407: }
0408:
0409: if (parentContainer != null) {
0410: parentContainer.add(metacomp);
0411: } else {
0412: metacomp.setParentComponent(null);
0413: otherComponents.add(metacomp);
0414: }
0415: if (!newlyAdded && (metacomp instanceof RADVisualComponent)) {
0416: ((RADVisualComponent) metacomp)
0417: .resetConstraintsProperties();
0418: }
0419:
0420: fireComponentAdded(metacomp, newlyAdded);
0421: }
0422:
0423: /** Adds a new visual component to given container managed by the old
0424: * layout support.
0425: *
0426: * @param metacomp component to add.
0427: * @param parentContainer parent of the added component.
0428: * @param constraints layout constraints.
0429: * @param newlyAdded is newly added?
0430: */
0431: public void addVisualComponent(RADVisualComponent metacomp,
0432: RADVisualContainer parentContainer, Object constraints,
0433: boolean newlyAdded) {
0434: LayoutSupportManager layoutSupport = parentContainer
0435: .getLayoutSupport();
0436: if (layoutSupport != null) {
0437: RADVisualComponent[] compArray = new RADVisualComponent[] { metacomp };
0438: LayoutConstraints c = constraints instanceof LayoutConstraints ? (LayoutConstraints) constraints
0439: : null;
0440: LayoutConstraints[] constrArray = new LayoutConstraints[] { c };
0441: int index = constraints instanceof Integer ? ((Integer) constraints)
0442: .intValue()
0443: : -1;
0444:
0445: // component needs to be "in model" (have code expression) before added to layout
0446: if (newlyAdded || !metacomp.isInModel()) {
0447: setInModelRecursively(metacomp, true);
0448: newlyAdded = true;
0449: }
0450:
0451: try {
0452: layoutSupport.acceptNewComponents(compArray,
0453: constrArray, index);
0454: } catch (RuntimeException ex) {
0455: // LayoutSupportDelegate may not accept the component
0456: if (newlyAdded)
0457: setInModelRecursively(metacomp, false);
0458: throw ex;
0459: }
0460:
0461: parentContainer.add(metacomp, index);
0462:
0463: layoutSupport.addComponents(compArray, constrArray, index);
0464:
0465: fireComponentAdded(metacomp, newlyAdded);
0466: } else {
0467: addComponent(metacomp, parentContainer, newlyAdded);
0468: }
0469: }
0470:
0471: void setContainerLayoutImpl(RADVisualContainer metacont,
0472: LayoutSupportDelegate layoutDelegate) throws Exception {
0473: LayoutSupportManager currentLS = metacont.getLayoutSupport();
0474: LayoutSupportDelegate currentDel = currentLS != null ? currentLS
0475: .getLayoutDelegate()
0476: : null;
0477:
0478: if (currentLS == null) { // switching to old layout support
0479: metacont.setOldLayoutSupport(true);
0480: }
0481: try {
0482: metacont.setLayoutSupportDelegate(layoutDelegate);
0483: } catch (Exception ex) {
0484: if (currentLS == null) {
0485: // failure might leave the layout delegate null (#115431)
0486: metacont.setOldLayoutSupport(false);
0487: }
0488: throw ex;
0489: }
0490:
0491: fireContainerLayoutExchanged(metacont, currentDel,
0492: layoutDelegate);
0493: }
0494:
0495: public void setContainerLayout(RADVisualContainer metacont,
0496: LayoutSupportDelegate layoutDelegate) throws Exception {
0497: LayoutSupportManager currentLS = metacont.getLayoutSupport();
0498: setContainerLayoutImpl(metacont, layoutDelegate);
0499: if (currentLS == null) { // switching to old layout support
0500: Object layoutStartMark = layoutModel.getChangeMark();
0501: UndoableEdit ue = layoutModel.getUndoableEdit();
0502: boolean autoUndo = true;
0503: try {
0504: layoutModel
0505: .changeContainerToComponent(metacont.getId());
0506: autoUndo = false;
0507: } finally {
0508: if (layoutStartMark != null
0509: && !layoutStartMark.equals(layoutModel
0510: .getChangeMark())) {
0511: addUndoableEdit(ue);
0512: }
0513: if (autoUndo) {
0514: forceUndoOfCompoundEdit();
0515: }
0516: }
0517: }
0518: }
0519:
0520: void setNaturalContainerLayoutImpl(RADVisualContainer metacont) {
0521: LayoutSupportDelegate currentDel = metacont.getLayoutSupport()
0522: .getLayoutDelegate();
0523: metacont.setOldLayoutSupport(false);
0524: fireContainerLayoutExchanged(metacont, currentDel, null);
0525: for (RADVisualComponent metacomp : metacont.getSubComponents()) {
0526: metacomp.resetConstraintsProperties();
0527: }
0528: }
0529:
0530: public void setNaturalContainerLayout(RADVisualContainer metacont) {
0531: LayoutSupportManager currentLS = metacont.getLayoutSupport();
0532: if (currentLS == null)
0533: return; // already set (no old layout support)
0534:
0535: setNaturalContainerLayoutImpl(metacont);
0536: Object layoutStartMark = layoutModel.getChangeMark();
0537: UndoableEdit ue = layoutModel.getUndoableEdit();
0538: boolean autoUndo = true;
0539: try {
0540: if (!layoutModel.changeComponentToContainer(metacont
0541: .getId())) {
0542: layoutModel.addRootComponent(new LayoutComponent(
0543: metacont.getId(), true));
0544: }
0545: autoUndo = false;
0546: } finally {
0547: if (layoutStartMark != null
0548: && !layoutStartMark.equals(layoutModel
0549: .getChangeMark())) {
0550: addUndoableEdit(ue);
0551: }
0552: if (autoUndo) {
0553: forceUndoOfCompoundEdit();
0554: }
0555: }
0556: }
0557:
0558: public void removeComponent(RADComponent metacomp, boolean fromModel) {
0559: Object layoutStartMark = null;
0560: UndoableEdit ue = null;
0561: boolean autoUndo = true;
0562: try {
0563: if (fromModel && (layoutModel != null)) {
0564: layoutStartMark = layoutModel.getChangeMark();
0565: ue = layoutModel.getUndoableEdit();
0566: layoutModel.removeComponent(metacomp.getId(), true);
0567: removeLayoutComponentsRecursively(metacomp);
0568: }
0569:
0570: // [TODO need effective multi-component remove from LayoutModel (start in ComponentInspector.DeleteActionPerformer)]
0571: autoUndo = false;
0572: } finally {
0573: removeComponentImpl(metacomp, fromModel);
0574: if (layoutStartMark != null
0575: && !layoutStartMark.equals(layoutModel
0576: .getChangeMark())) {
0577: addUndoableEdit(ue); // is added to a compound edit
0578: }
0579: if (autoUndo) {
0580: forceUndoOfCompoundEdit();
0581: }
0582: }
0583: }
0584:
0585: void removeComponentImpl(RADComponent metacomp, boolean fromModel) {
0586: if (fromModel && formEvents != null) {
0587: removeEventHandlersRecursively(metacomp);
0588: }
0589:
0590: RADComponent parent = metacomp.getParentComponent();
0591: ComponentContainer parentContainer = parent instanceof ComponentContainer ? (ComponentContainer) parent
0592: : getModelContainer();
0593:
0594: int index = parentContainer.getIndexOf(metacomp);
0595: parentContainer.remove(metacomp);
0596:
0597: if (fromModel) {
0598: setInModelRecursively(metacomp, false);
0599: }
0600:
0601: FormModelEvent ev = fireComponentRemoved(metacomp,
0602: parentContainer, index, fromModel);
0603: }
0604:
0605: // needed for the case of mixed hierarchy of new/old layout support
0606: private void removeLayoutComponentsRecursively(RADComponent metacomp) {
0607: if (metacomp instanceof ComponentContainer) {
0608: RADComponent[] comps = ((ComponentContainer) metacomp)
0609: .getSubBeans();
0610: for (int i = 0; i < comps.length; i++) {
0611: removeLayoutComponentsRecursively(comps[i]);
0612: }
0613: }
0614: LayoutComponent layoutComp = layoutModel == null ? null
0615: : layoutModel.getLayoutComponent(metacomp.getId());
0616: if (layoutComp != null && layoutComp.getParent() == null) {
0617: // remove only root components
0618: layoutModel.removeComponent(layoutComp.getId(), true);
0619: }
0620: }
0621:
0622: void updateMapping(RADComponent metacomp, boolean register) {
0623: if (register)
0624: idToComponents.put(metacomp.getId(), metacomp);
0625: else
0626: idToComponents.remove(metacomp.getId());
0627: }
0628:
0629: // removes all event handlers attached to given component and all
0630: // its subcomponents
0631: private void removeEventHandlersRecursively(RADComponent comp) {
0632: if (comp instanceof ComponentContainer) {
0633: RADComponent[] subcomps = ((ComponentContainer) comp)
0634: .getSubBeans();
0635: for (int i = 0; i < subcomps.length; i++)
0636: removeEventHandlersRecursively(subcomps[i]);
0637: }
0638:
0639: Event[] events = comp.getKnownEvents();
0640: for (int i = 0; i < events.length; i++)
0641: if (events[i].hasEventHandlers())
0642: getFormEvents().detachEvent(events[i]);
0643: }
0644:
0645: static void setInModelRecursively(RADComponent metacomp,
0646: boolean inModel) {
0647: if (metacomp instanceof ComponentContainer) {
0648: RADComponent[] comps = ((ComponentContainer) metacomp)
0649: .getSubBeans();
0650: for (int i = 0; i < comps.length; i++)
0651: setInModelRecursively(comps[i], inModel);
0652: }
0653: metacomp.setInModel(inModel);
0654: }
0655:
0656: // ----------
0657: // undo and redo
0658:
0659: public void setUndoRedoRecording(boolean record) {
0660: t("turning undo/redo recording " + (record ? "on" : "off")); // NOI18N
0661: undoRedoRecording = record;
0662: }
0663:
0664: public boolean isUndoRedoRecording() {
0665: return undoRedoRecording;
0666: }
0667:
0668: private void startCompoundEdit() {
0669: if (compoundEdit == null) {
0670: t("starting compound edit"); // NOI18N
0671: compoundEdit = new CompoundEdit();
0672: }
0673: }
0674:
0675: public CompoundEdit endCompoundEdit(boolean commit) {
0676: if (compoundEdit != null) {
0677: t("ending compound edit: " + commit); // NOI18N
0678: compoundEdit.end();
0679: if (commit && undoRedoRecording
0680: && compoundEdit.isSignificant()) {
0681: getUndoRedoManager().undoableEditHappened(
0682: new UndoableEditEvent(this , compoundEdit));
0683: }
0684: CompoundEdit edit = compoundEdit;
0685: compoundEdit = null;
0686: return edit;
0687: }
0688: return null;
0689: }
0690:
0691: public void forceUndoOfCompoundEdit() {
0692: if (compoundEdit != null) {
0693: undoCompoundEdit = true;
0694: }
0695: }
0696:
0697: public boolean isCompoundEditInProgress() {
0698: return compoundEdit != null; // && compoundEdit.isInProgress();
0699: }
0700:
0701: public void addUndoableEdit(UndoableEdit edit) {
0702: t("adding undoable edit"); // NOI18N
0703: if (!isCompoundEditInProgress()) {
0704: startCompoundEdit();
0705: }
0706: compoundEdit.addEdit(edit);
0707: }
0708:
0709: UndoRedo.Manager getUndoRedoManager() {
0710: // if (undoRedoManager == null) {
0711: // undoRedoManager = new UndoRedoManager();
0712: // undoRedoManager.setLimit(50);
0713: // }
0714: return undoRedoManager;
0715: }
0716:
0717: // [Undo manager performing undo/redo in AWT event thread should not be
0718: // probably implemented here - in FormModel - but seperately.]
0719: class UndoRedoManager extends UndoRedo.Manager {
0720: private Mutex.ExceptionAction<Object> runUndo = new Mutex.ExceptionAction<Object>() {
0721: public Object run() throws Exception {
0722: super Undo();
0723: return null;
0724: }
0725: };
0726: private Mutex.ExceptionAction<Object> runRedo = new Mutex.ExceptionAction<Object>() {
0727: public Object run() throws Exception {
0728: super Redo();
0729: return null;
0730: }
0731: };
0732:
0733: public void super Undo() throws CannotUndoException {
0734: super .undo();
0735: }
0736:
0737: public void super Redo() throws CannotRedoException {
0738: super .redo();
0739: }
0740:
0741: @Override
0742: public void undo() throws CannotUndoException {
0743: if (java.awt.EventQueue.isDispatchThread()) {
0744: super Undo();
0745: } else {
0746: try {
0747: Mutex.EVENT.readAccess(runUndo);
0748: } catch (MutexException ex) {
0749: Exception e = ex.getException();
0750: if (e instanceof CannotUndoException)
0751: throw (CannotUndoException) e;
0752: else
0753: // should not happen, ignore
0754: e.printStackTrace();
0755: }
0756: }
0757: }
0758:
0759: @Override
0760: public void redo() throws CannotRedoException {
0761: if (java.awt.EventQueue.isDispatchThread()) {
0762: super Redo();
0763: } else {
0764: try {
0765: Mutex.EVENT.readAccess(runRedo);
0766: } catch (MutexException ex) {
0767: Exception e = ex.getException();
0768: if (e instanceof CannotRedoException)
0769: throw (CannotRedoException) e;
0770: else
0771: // should not happen, ignore
0772: e.printStackTrace();
0773: }
0774: }
0775: }
0776: }
0777:
0778: // ----------
0779: // listeners registration, firing methods
0780:
0781: public synchronized void addFormModelListener(FormModelListener l) {
0782: if (listeners == null)
0783: listeners = new ArrayList<FormModelListener>();
0784: listeners.add(l);
0785: }
0786:
0787: public synchronized void removeFormModelListener(FormModelListener l) {
0788: if (listeners != null)
0789: listeners.remove(l);
0790: }
0791:
0792: /** Fires an event informing about that the form has been just loaded. */
0793: public void fireFormLoaded() {
0794: t("firing form loaded"); // NOI18N
0795:
0796: formLoaded = true;
0797: // if (undoRedoManager != null)
0798: // undoRedoManager.discardAllEdits();
0799: if (!readOnly && !Boolean.getBoolean("netbeans.form.no_undo")) { // NOI18N
0800: undoRedoManager = new UndoRedoManager();
0801: undoRedoManager.setLimit(50);
0802: setUndoRedoRecording(true);
0803: if (layoutModel != null)
0804: layoutModel.setChangeRecording(true);
0805: }
0806: // initializeCodeGenerator(); // [should go away]
0807:
0808: sendEventLater(new FormModelEvent(this ,
0809: FormModelEvent.FORM_LOADED));
0810: }
0811:
0812: /** Fires an event informing about that the form is just about to be saved. */
0813: public void fireFormToBeSaved() {
0814: t("firing form to be saved"); // NOI18N
0815:
0816: sendEventImmediately(new FormModelEvent(this ,
0817: FormModelEvent.FORM_TO_BE_SAVED));
0818: }
0819:
0820: /** Fires an event informing about that the form is just about to be closed. */
0821: public void fireFormToBeClosed() {
0822: t("firing form to be closed"); // NOI18N
0823:
0824: if (undoRedoManager != null)
0825: undoRedoManager.discardAllEdits();
0826:
0827: sendEventImmediately(new FormModelEvent(this ,
0828: FormModelEvent.FORM_TO_BE_CLOSED));
0829: }
0830:
0831: /** Fires an event informing about changing layout manager of a container.
0832: * An undoable edit is created and registered automatically.
0833: *
0834: * @param metacont container whose layout has been changed.
0835: * @param oldLayout old layout.
0836: * @param newLayout new layout.
0837: * @return event that has been fired.
0838: */
0839: public FormModelEvent fireContainerLayoutExchanged(
0840: RADVisualContainer metacont,
0841: LayoutSupportDelegate oldLayout,
0842: LayoutSupportDelegate newLayout) {
0843: t("firing container layout exchange, container: " // NOI18N
0844: + (metacont != null ? metacont.getName() : "null")); // NOI18N
0845:
0846: FormModelEvent ev = new FormModelEvent(this ,
0847: FormModelEvent.CONTAINER_LAYOUT_EXCHANGED);
0848: ev.setLayout(metacont, oldLayout, newLayout);
0849: sendEvent(ev);
0850:
0851: if (undoRedoRecording && metacont != null
0852: && oldLayout != newLayout)
0853: addUndoableEdit(ev.getUndoableEdit());
0854:
0855: return ev;
0856: }
0857:
0858: /** Fires an event informing about changing a property of container layout.
0859: * An undoable edit is created and registered automatically.
0860: *
0861: * @param metacont container whose layout has been changed.
0862: * @param propName name of the layout property.
0863: * @param oldValue old value of the property.
0864: * @param newValue new value of the property.
0865: * @return event that has been fired.
0866: */
0867: public FormModelEvent fireContainerLayoutChanged(
0868: RADVisualContainer metacont, String propName,
0869: Object oldValue, Object newValue) {
0870: t("firing container layout change, container: " // NOI18N
0871: + (metacont != null ? metacont.getName() : "null") // NOI18N
0872: + ", property: " + propName); // NOI18N
0873:
0874: FormModelEvent ev = new FormModelEvent(this ,
0875: FormModelEvent.CONTAINER_LAYOUT_CHANGED);
0876: ev.setComponentAndContainer(metacont, metacont);
0877: ev.setProperty(propName, oldValue, newValue);
0878: sendEvent(ev);
0879:
0880: if (undoRedoRecording && metacont != null
0881: && (propName == null || oldValue != newValue)) {
0882: addUndoableEdit(ev.getUndoableEdit());
0883: }
0884:
0885: return ev;
0886: }
0887:
0888: /** Fires an event informing about changing a property of component layout
0889: * constraints. An undoable edit is created and registered automatically.
0890: *
0891: * @param metacomp component whose layout property has been changed.
0892: * @param propName name of the layout property.
0893: * @param oldValue old value of the property.
0894: * @param newValue new value of the property.
0895: * @return event that has been fired.
0896: */
0897: public FormModelEvent fireComponentLayoutChanged(
0898: RADVisualComponent metacomp, String propName,
0899: Object oldValue, Object newValue) {
0900: t("firing component layout change: " // NOI18N
0901: + (metacomp != null ? metacomp.getName() : "null")); // NOI18N
0902:
0903: FormModelEvent ev = new FormModelEvent(this ,
0904: FormModelEvent.COMPONENT_LAYOUT_CHANGED);
0905: ev.setComponentAndContainer(metacomp, null);
0906: ev.setProperty(propName, oldValue, newValue);
0907: sendEvent(ev);
0908:
0909: if (undoRedoRecording && metacomp != null && propName != null
0910: && oldValue != newValue) {
0911: addUndoableEdit(ev.getUndoableEdit());
0912: }
0913:
0914: return ev;
0915: }
0916:
0917: /** Fires an event informing about adding a component to the form.
0918: * An undoable edit is created and registered automatically.
0919: *
0920: * @param metacomp component that has been added.
0921: * @param addedNew is newly added?
0922: * @return event that has been fired.
0923: */
0924: public FormModelEvent fireComponentAdded(RADComponent metacomp,
0925: boolean addedNew) {
0926: t("firing component added: " // NOI18N
0927: + (metacomp != null ? metacomp.getName() : "null")); // NOI18N
0928:
0929: FormModelEvent ev = new FormModelEvent(this ,
0930: FormModelEvent.COMPONENT_ADDED);
0931: ev.setAddData(metacomp, null, addedNew);
0932: sendEvent(ev);
0933:
0934: if (undoRedoRecording && metacomp != null)
0935: addUndoableEdit(ev.getUndoableEdit());
0936:
0937: return ev;
0938: }
0939:
0940: /** Fires an event informing about removing a component from the form.
0941: * An undoable edit is created and registered automatically.
0942: *
0943: * @param metacomp component that has been removed.
0944: * @param metacont container from which the component was removed.
0945: * @param index index of the component in the container.
0946: * @param removedFromModel determines whether the component has been
0947: * removed from the model.
0948: * @return event that has been fired.
0949: */
0950: public FormModelEvent fireComponentRemoved(RADComponent metacomp,
0951: ComponentContainer metacont, int index,
0952: boolean removedFromModel) {
0953: t("firing component removed: " // NOI18N
0954: + (metacomp != null ? metacomp.getName() : "null")); // NOI18N
0955:
0956: FormModelEvent ev = new FormModelEvent(this ,
0957: FormModelEvent.COMPONENT_REMOVED);
0958: ev.setRemoveData(metacomp, metacont, index, removedFromModel);
0959: sendEvent(ev);
0960:
0961: if (undoRedoRecording && metacomp != null && metacont != null)
0962: addUndoableEdit(ev.getUndoableEdit());
0963:
0964: return ev;
0965: }
0966:
0967: /** Fires an event informing about reordering components in a container.
0968: * An undoable edit is created and registered automatically.
0969: *
0970: * @param metacont container whose subcomponents has been reordered.
0971: * @param perm permutation describing the change in order.
0972: * @return event that has been fired.
0973: */
0974: public FormModelEvent fireComponentsReordered(
0975: ComponentContainer metacont, int[] perm) {
0976: t("firing components reorder in container: " // NOI18N
0977: + (metacont instanceof RADComponent ? ((RADComponent) metacont)
0978: .getName()
0979: : "<top>")); // NOI18N
0980:
0981: FormModelEvent ev = new FormModelEvent(this ,
0982: FormModelEvent.COMPONENTS_REORDERED);
0983: ev.setComponentAndContainer(null, metacont);
0984: ev.setReordering(perm);
0985: sendEvent(ev);
0986:
0987: if (undoRedoRecording && metacont != null)
0988: addUndoableEdit(ev.getUndoableEdit());
0989:
0990: return ev;
0991: }
0992:
0993: /** Fires an event informing about changing a property of a component.
0994: * An undoable edit is created and registered automatically.
0995: * @param metacomp component whose property has been changed.
0996: * @param propName name of the changed property.
0997: * @param oldValue old value of the property.
0998: * @param newValue new value of the property.
0999: * @return event that has been fired.
1000: */
1001: public FormModelEvent fireComponentPropertyChanged(
1002: RADComponent metacomp, String propName, Object oldValue,
1003: Object newValue) {
1004: t("firing component property change, component: " // NOI18N
1005: + (metacomp != null ? metacomp.getName()
1006: : "<null component>") // NOI18N
1007: + ", property: " + propName); // NOI18N
1008:
1009: FormModelEvent ev = new FormModelEvent(this ,
1010: FormModelEvent.COMPONENT_PROPERTY_CHANGED);
1011: ev.setComponentAndContainer(metacomp, null);
1012: ev.setProperty(propName, oldValue, newValue);
1013: sendEvent(ev);
1014:
1015: if (undoRedoRecording && metacomp != null && propName != null
1016: && oldValue != newValue) {
1017: addUndoableEdit(ev.getUndoableEdit());
1018: }
1019:
1020: return ev;
1021: }
1022:
1023: public FormModelEvent fireBindingChanged(RADComponent metacomp,
1024: String path, String subProperty, Object oldValue,
1025: Object newValue) {
1026: FormModelEvent ev = new FormModelEvent(this ,
1027: FormModelEvent.BINDING_PROPERTY_CHANGED);
1028: ev.setComponentAndContainer(metacomp, null);
1029: ev.setProperty(path, oldValue, newValue);
1030: ev.setSubProperty(subProperty);
1031: sendEvent(ev);
1032:
1033: if (undoRedoRecording && oldValue != newValue) {
1034: addUndoableEdit(ev.getUndoableEdit());
1035: }
1036:
1037: return ev;
1038: }
1039:
1040: /** Fires an event informing about changing a synthetic property of
1041: * a component. An undoable edit is created and registered automatically.
1042: *
1043: * @param metacomp component whose synthetic property has been changed.
1044: * @param propName name of the synthetic property that has been changed.
1045: * @param oldValue old value of the property.
1046: * @param newValue new value of the property.
1047: * @return event that has been fired.
1048: */
1049: public FormModelEvent fireSyntheticPropertyChanged(
1050: RADComponent metacomp, String propName, Object oldValue,
1051: Object newValue) {
1052: t("firing synthetic property change, component: " // NOI18N
1053: + (metacomp != null ? metacomp.getName() : "null") // NOI18N
1054: + ", property: " + propName); // NOI18N
1055:
1056: FormModelEvent ev = new FormModelEvent(this ,
1057: FormModelEvent.SYNTHETIC_PROPERTY_CHANGED);
1058: ev.setComponentAndContainer(metacomp, null);
1059: ev.setProperty(propName, oldValue, newValue);
1060: sendEvent(ev);
1061:
1062: if (undoRedoRecording && propName != null
1063: && oldValue != newValue) {
1064: addUndoableEdit(ev.getUndoableEdit());
1065: }
1066:
1067: return ev;
1068: }
1069:
1070: /** Fires an event informing about attaching a new event to an event handler
1071: * (createdNew parameter indicates whether the event handler was created
1072: * first). An undoable edit is created and registered automatically.
1073: *
1074: * @param event event for which the handler was created.
1075: * @param handler name of the event handler.
1076: * @param bodyText body of the event handler.
1077: * @param createdNew newly created event handler?
1078: * @return event that has been fired.
1079: */
1080: public FormModelEvent fireEventHandlerAdded(Event event,
1081: String handler, String bodyText, String annotationText,
1082: boolean createdNew) {
1083: t("event handler added: " + handler); // NOI18N
1084:
1085: FormModelEvent ev = new FormModelEvent(this ,
1086: FormModelEvent.EVENT_HANDLER_ADDED);
1087: ev.setEvent(event, handler, bodyText, annotationText,
1088: createdNew);
1089: sendEvent(ev);
1090:
1091: if (undoRedoRecording && event != null && handler != null)
1092: addUndoableEdit(ev.getUndoableEdit());
1093:
1094: return ev;
1095: }
1096:
1097: /** Fires an event informing about detaching an event from event handler
1098: * (handlerDeleted parameter indicates whether the handler was deleted as
1099: * the last event was detached). An undoable edit is created and registered
1100: * automatically.
1101: *
1102: * @param event event for which the handler was removed.
1103: * @param handler removed event handler.
1104: * @param handlerDeleted was deleted?
1105: * @return event that has been fired.
1106: */
1107: public FormModelEvent fireEventHandlerRemoved(Event event,
1108: String handler, boolean handlerDeleted) {
1109: t("firing event handler removed: " + handler); // NOI18N
1110:
1111: FormModelEvent ev = new FormModelEvent(this ,
1112: FormModelEvent.EVENT_HANDLER_REMOVED);
1113: ev.setEvent(event, handler, null, null, handlerDeleted);
1114: sendEvent(ev);
1115:
1116: if (undoRedoRecording && event != null && handler != null)
1117: addUndoableEdit(ev.getUndoableEdit());
1118:
1119: return ev;
1120: }
1121:
1122: /** Fires an event informing about renaming an event handler. An undoable
1123: * edit is created and registered automatically.
1124: *
1125: * @param oldHandlerName old name of the event handler.
1126: * @param newHandlerName new name of the event handler.
1127: * @return event that has been fired.
1128: */
1129: public FormModelEvent fireEventHandlerRenamed(
1130: String oldHandlerName, String newHandlerName) {
1131: t("event handler renamed: " + oldHandlerName + " to "
1132: + newHandlerName); // NOI18N
1133:
1134: FormModelEvent ev = new FormModelEvent(this ,
1135: FormModelEvent.EVENT_HANDLER_RENAMED);
1136: ev.setEvent(oldHandlerName, newHandlerName);
1137: sendEvent(ev);
1138:
1139: if (undoRedoRecording && oldHandlerName != null
1140: && newHandlerName != null)
1141: addUndoableEdit(ev.getUndoableEdit());
1142:
1143: return ev;
1144: }
1145:
1146: /** Fires an event informing about general form change.
1147: *
1148: * @param immediately determines whether the change should be fire immediately.
1149: * @return event that has been fired.
1150: */
1151: public FormModelEvent fireFormChanged(boolean immediately) {
1152: t("firing form change"); // NOI18N
1153:
1154: FormModelEvent ev = new FormModelEvent(this ,
1155: FormModelEvent.OTHER_CHANGE);
1156: if (immediately)
1157: sendEventImmediately(ev);
1158: else
1159: sendEvent(ev);
1160:
1161: return ev;
1162: }
1163:
1164: // ---------
1165: // firing methods for batch event processing
1166:
1167: private void sendEvent(FormModelEvent ev) {
1168: if (formLoaded) {
1169: if (eventList != null || ev.isModifying()) {
1170: sendEventLater(ev);
1171: } else {
1172: sendEventImmediately(ev);
1173: }
1174: } else {
1175: fireEvents(ev);
1176: }
1177: }
1178:
1179: private synchronized void sendEventLater(FormModelEvent ev) {
1180: // works properly only if called from AWT event dispatch thread
1181: if (!java.awt.EventQueue.isDispatchThread()) {
1182: sendEventImmediately(ev);
1183: return;
1184: }
1185:
1186: if (eventList == null) {
1187: eventList = new ArrayList<FormModelEvent>();
1188: java.awt.EventQueue.invokeLater(new Runnable() {
1189: public void run() {
1190: firePendingEvents();
1191: }
1192: });
1193: }
1194: eventList.add(ev);
1195: }
1196:
1197: private synchronized void sendEventImmediately(FormModelEvent ev) {
1198: if (eventList == null) {
1199: eventList = new ArrayList<FormModelEvent>();
1200: }
1201: eventList.add(ev);
1202: firePendingEvents();
1203: }
1204:
1205: private void firePendingEvents() {
1206: List<FormModelEvent> list = pickUpEvents();
1207: if (list != null && !list.isEmpty()) {
1208: FormModelEvent[] events = new FormModelEvent[list.size()];
1209: list.toArray(events);
1210: fireEventBatch(events);
1211: }
1212: }
1213:
1214: private synchronized List<FormModelEvent> pickUpEvents() {
1215: List<FormModelEvent> list = eventList;
1216: eventList = null;
1217: return list;
1218: }
1219:
1220: boolean hasPendingEvents() {
1221: return eventList != null;
1222: }
1223:
1224: /**
1225: * This method fires events collected from all changes done during the last
1226: * round of AWT event queue. After all fired successfully (no error occurred),
1227: * all the changes are placed as one UndoableEdit into undo/redo queue. When
1228: * the fired events are being processed, some more changes may happen (they
1229: * are included in the same UndoableEdit). These changes are typically fired
1230: * immediately causing this method is re-entered while previous firing is not
1231: * finished yet.
1232: * Additionally - for robustness, if some unhandled error happens before or
1233: * during firing the events, all the changes done so far are undone:
1234: * If an operation failed before firing, the undoCompoundEdit field is set
1235: * and then no events are fired at all (the changes were defective), and the
1236: * changes done before the failure are undone. All the changes are undone
1237: * also if the failure happens during processing the events (e.g. the layout
1238: * can't be built).
1239: */
1240: private void fireEventBatch(FormModelEvent... events) {
1241: if (!firing) {
1242: boolean firingFailed = false;
1243: try {
1244: firing = true;
1245: if (!undoCompoundEdit) {
1246: firingFailed = true;
1247: fireEvents(events);
1248: firingFailed = false;
1249: }
1250: } finally {
1251: firing = false;
1252: boolean revert = undoCompoundEdit || firingFailed;
1253: undoCompoundEdit = false;
1254: CompoundEdit edit = endCompoundEdit(!revert);
1255: if (edit != null && revert) {
1256: edit.undo();
1257: }
1258: }
1259: } else { // re-entrant call
1260: fireEvents(events);
1261: }
1262: }
1263:
1264: void fireEvents(FormModelEvent... events) {
1265: java.util.List targets;
1266: synchronized (this ) {
1267: if (listeners == null || listeners.size() == 0) {
1268: return;
1269: }
1270: targets = (ArrayList) listeners.clone();
1271: }
1272: for (int i = 0; i < targets.size(); i++) {
1273: FormModelListener l = (FormModelListener) targets.get(i);
1274: l.formChanged(events);
1275: }
1276: }
1277:
1278: // -------------
1279:
1280: public CodeStructure getCodeStructure() {
1281: return codeStructure;
1282: }
1283:
1284: public boolean isFreeDesignDefaultLayout() {
1285: return freeDesignDefaultLayout;
1286: }
1287:
1288: void setFreeDesignDefaultLayout(boolean freeDesignDefaultLayout) {
1289: this .freeDesignDefaultLayout = freeDesignDefaultLayout;
1290: }
1291:
1292: // CodeGenerator getCodeGenerator() {
1293: //// return FormEditorSupport.getCodeGenerator(this);
1294: // if (codeGenerator == null)
1295: // codeGenerator = new JavaCodeGenerator();
1296: // return codeGenerator;
1297: // }
1298: //
1299: // void initializeCodeGenerator() {
1300: // getCodeGenerator().initialize(this);
1301: // }
1302:
1303: // ---------------
1304: // ModelContainer innerclass
1305:
1306: final class ModelContainer implements ComponentContainer {
1307: public RADComponent[] getSubBeans() {
1308: int n = otherComponents.size();
1309: if (topRADComponent != null)
1310: n++;
1311: RADComponent[] comps = new RADComponent[n];
1312: otherComponents.toArray(comps);
1313: if (topRADComponent != null)
1314: comps[n - 1] = topRADComponent;
1315: return comps;
1316: }
1317:
1318: public void initSubComponents(RADComponent[] initComponents) {
1319: otherComponents.clear();
1320: for (int i = 0; i < initComponents.length; i++)
1321: if (initComponents[i] != topRADComponent)
1322: otherComponents.add(initComponents[i]);
1323: }
1324:
1325: public void reorderSubComponents(int[] perm) {
1326: RADComponent[] components = new RADComponent[otherComponents
1327: .size()];
1328: for (int i = 0; i < perm.length; i++)
1329: components[perm[i]] = otherComponents.get(i);
1330:
1331: otherComponents.clear();
1332: otherComponents.addAll(Arrays.asList(components));
1333: }
1334:
1335: public void add(RADComponent comp) {
1336: comp.setParentComponent(null);
1337: otherComponents.add(comp);
1338: }
1339:
1340: public void remove(RADComponent comp) {
1341: if (otherComponents.remove(comp))
1342: comp.setParentComponent(null);
1343: }
1344:
1345: public int getIndexOf(RADComponent comp) {
1346: int index = otherComponents.indexOf(comp);
1347: if (index < 0 && comp == topRADComponent)
1348: index = otherComponents.size();
1349: return index;
1350: }
1351: }
1352:
1353: // ---------------
1354:
1355: /** For debugging purposes only. */
1356: static private int traceCount = 0;
1357: /** For debugging purposes only. */
1358: static private final boolean TRACE = false;
1359:
1360: /** For debugging purposes only. */
1361: static void t(String str) {
1362: if (TRACE)
1363: if (str != null)
1364: System.out.println("FormModel " + (++traceCount) + ": "
1365: + str); // NOI18N
1366: else
1367: System.out.println(""); // NOI18N
1368: }
1369: }
|