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.layoutsupport;
0043:
0044: import java.awt.*;
0045: import java.beans.*;
0046: import java.util.*;
0047: import java.lang.reflect.Method;
0048:
0049: import org.openide.ErrorManager;
0050: import org.openide.nodes.Node;
0051: import org.openide.util.Utilities;
0052:
0053: import org.netbeans.modules.form.*;
0054: import org.netbeans.modules.form.codestructure.*;
0055:
0056: /**
0057: * Default implementation of LayoutSupportDelegate interface. This class
0058: * implements most of general methods of LayoutSupportDelegate and introduces
0059: * other more specific methods which can be easily customized in subclasses.
0060: *
0061: * This class provides basic support for layouts following these rules:
0062: * (1) the supported layout manager is a JavaBean (it means that it has an
0063: * empty public constructor, the parameters are set as properties,
0064: * BeanInfo is used to obtain the properties),
0065: * (2) Container.setLayout(LayoutManager) method is used on the container to
0066: * set the layout,
0067: * (3) Container.add(Component) and Container.add(Component, Object) methods
0068: * are used on the container to add components (the second method for
0069: * adding with layout constraints).
0070: *
0071: * To create basic support for such a layout manager, it's enough to implement
0072: * getSupportedClass method only.
0073: *
0074: * Note that the subclass should have public constructor without parameters,
0075: * otherwise it should override cloneSupportInstance method.
0076: *
0077: * Note that the default implementation does not (and even cannot) provide any
0078: * working support for layout constraints of components in general - this must
0079: * be implemented in the sublasses individually. See BorderLayoutSupport for an
0080: * example of using simple value constraints, AbsoluteLayoutSupport for complex
0081: * object constraints (created by constructor), GridBagLayoutSupport for complex
0082: * constraints with special initialization code.
0083: *
0084: * @author Tomas Pavek
0085: */
0086:
0087: public abstract class AbstractLayoutSupport implements
0088: LayoutSupportDelegate {
0089: /** Default icon URL. */
0090: private static String iconURL = "org/netbeans/modules/form/layoutsupport/resources/AbstractLayout.gif"; // NOI18N
0091: /** Default icon URL. */
0092: private static String icon32URL = "org/netbeans/modules/form/layoutsupport/resources/AbstractLayout32.gif"; // NOI18N
0093:
0094: private static Method simpleAddMethod = null;
0095: private static Method addWithConstraintsMethod = null;
0096: private static Method setLayoutMethod = null;
0097:
0098: // ------
0099:
0100: private LayoutSupportContext layoutContext;
0101:
0102: private java.util.List<CodeExpression> componentCodeExpressions;
0103: private java.util.List<CodeGroup> componentCodeGroups;
0104: private java.util.List<LayoutConstraints> componentConstraints;
0105:
0106: private BeanCodeManager layoutBeanCode;
0107: private CodeGroup setLayoutCode;
0108:
0109: private MetaLayout metaLayout;
0110: private FormProperty[] allProperties;
0111:
0112: // ------------------
0113: // LayoutSupportDelegate interface implementation
0114:
0115: /** Initialization of the layout delegate before the first use.
0116: * There are three types of initialization supported:
0117: * (1) default initialization for an empty (newly created) layout
0118: * (lmInstance == null, fromCode == false),
0119: * (2) initialization from an already existing instance of LayoutManager
0120: * (lmInstance != null, fromCode == false),
0121: * (3) initialization from persistent code structure,
0122: * (lmInstance == null, fromCode == true).
0123: * @param layoutContext provides a necessary context information for the
0124: * layout delegate
0125: * @param lmInstance LayoutManager instance for initialization (may be null)
0126: * @param fromCode indicates whether to initialize from code structure
0127: * @exception any Exception occurred during initialization
0128: */
0129: public void initialize(LayoutSupportContext layoutContext,
0130: LayoutManager lmInstance, boolean fromCode)
0131: throws Exception {
0132: if (this .layoutContext == layoutContext) {
0133: // already initialized - just reuse the delegate
0134: if (setLayoutCode != null)
0135: setLayoutCode.removeAll();
0136: else
0137: setLayoutCode = layoutContext.getCodeStructure()
0138: .createCodeGroup();
0139:
0140: readLayoutCode(setLayoutCode); // reinstate layout code (for sure)
0141: return;
0142: }
0143:
0144: this .layoutContext = layoutContext;
0145: clean();
0146:
0147: Class cls = getSupportedClass();
0148: if (cls != null && LayoutManager.class.isAssignableFrom(cls)) {
0149: // create MetaLayout to manage layout manager as a bean
0150: if (lmInstance == null
0151: || !lmInstance.getClass().equals(cls)) {
0152: // no valid layout manager instance - create a default one
0153: lmInstance = createDefaultLayoutInstance();
0154: }
0155:
0156: if (lmInstance != null) {
0157: metaLayout = new MetaLayout(this , lmInstance);
0158: deriveChangedPropertiesFromInstance(metaLayout);
0159: }
0160: } else
0161: metaLayout = null;
0162:
0163: // read layout code (if there's any)
0164: readLayoutCode(setLayoutCode);
0165:
0166: if (fromCode) {
0167: // read components from code
0168: CodeGroup componentCode = null;
0169: Iterator it = CodeStructure
0170: .getDefinedStatementsIterator(getActiveContainerCodeExpression());
0171: while (it.hasNext()) {
0172: if (componentCode == null)
0173: componentCode = layoutContext.getCodeStructure()
0174: .createCodeGroup();
0175:
0176: CodeStatement statement = (CodeStatement) it.next();
0177: CodeExpression compExp = readComponentCode(statement,
0178: componentCode);
0179: if (compExp != null) {
0180: componentCodeExpressions.add(compExp);
0181: componentCodeGroups.add(componentCode);
0182: componentCode = null;
0183:
0184: if (componentConstraints.size() < componentCodeExpressions
0185: .size())
0186: componentConstraints.add(null);
0187: }
0188: }
0189: }
0190: }
0191:
0192: protected void deriveChangedPropertiesFromInstance(
0193: MetaLayout metaLayout) {
0194: }
0195:
0196: /** States whether this support class is dedicted to some special container.
0197: * @return true if only certain container is supported,
0198: * false if a layout manager for use in any container is supported
0199: */
0200: public boolean isDedicated() {
0201: Class cls = getSupportedClass();
0202: return cls != null
0203: && !LayoutManager.class.isAssignableFrom(cls);
0204: }
0205:
0206: /** For dedicated supports: check whether given default container instance
0207: * is empty. Default implementation returns true - it's up to subcalsses
0208: * to check the special containers.
0209: * @param cont default instance of Container
0210: * @return true if the container can be used as default (empty) instance
0211: * with this layout support
0212: */
0213: public boolean checkEmptyContainer(Container cont) {
0214: return true;
0215: }
0216:
0217: /** Indicates whether the layout should be presented as a node in Component
0218: * Inspector (for setting properties). The node is provided for layout
0219: * managers typically (except null layou), and not for dedicated containers
0220: * support.
0221: * @return whether a node should be created for the layout
0222: */
0223: public boolean shouldHaveNode() {
0224: Class cls = getSupportedClass();
0225: return cls == null || LayoutManager.class.isAssignableFrom(cls);
0226: }
0227:
0228: /** Provides a display name for the layout node - derived from the name
0229: * of supported class here.
0230: * @return display name of supported layout
0231: */
0232: public String getDisplayName() {
0233: Class cls = getSupportedClass();
0234: String name;
0235:
0236: if (cls != null) {
0237: name = cls.getName();
0238: int lastdot = name.lastIndexOf('.');
0239: if (lastdot > 0)
0240: name = name.substring(lastdot + 1);
0241: } else
0242: name = "null"; // NOI18N
0243:
0244: return name;
0245: }
0246:
0247: /** Provides an icon to be used for the layout node in Component
0248: * Inspector. Only 16x16 color icon is required. The default implementation
0249: * tries to obtain the icon from BeanInfo of the layout manager.
0250: * @param type is one of BeanInfo constants: ICON_COLOR_16x16,
0251: * ICON_COLOR_32x32, ICON_MONO_16x16, ICON_MONO_32x32
0252: * @return icon to be used for layout node
0253: */
0254: public Image getIcon(int type) {
0255: if (metaLayout != null) {
0256: Image icon = metaLayout.getBeanInfo().getIcon(type);
0257: if (icon != null)
0258: return icon;
0259: }
0260:
0261: switch (type) {
0262: case BeanInfo.ICON_COLOR_16x16:
0263: case BeanInfo.ICON_MONO_16x16:
0264: return Utilities.loadImage(iconURL);
0265: default:
0266: return Utilities.loadImage(icon32URL);
0267: }
0268: }
0269:
0270: /** This method provides properties of the supported layout - if it is
0271: * a JavaBean class implementing LayoutManager. The properties are obtained
0272: * from the BeanInfo of the layout manager. Note these are not properties
0273: * of individual components constraints.
0274: * @return properties of supported layout
0275: */
0276: public Node.PropertySet[] getPropertySets() {
0277: Node.PropertySet[] propertySets;
0278:
0279: FormProperty[] properties = getProperties();
0280: if (properties == null) {
0281: propertySets = metaLayout != null ? metaLayout
0282: .getProperties() : null;
0283: } else { // a subclass provides special properties
0284: propertySets = new Node.PropertySet[1];
0285: propertySets[0] = new Node.PropertySet("properties", // NOI18N
0286: FormUtils.getBundleString("CTL_PropertiesTab"), // NOI18N
0287: FormUtils.getBundleString("CTL_PropertiesTabHint")) // NOI18N
0288: {
0289: public Node.Property[] getProperties() {
0290: return AbstractLayoutSupport.this .getProperties();
0291: }
0292: };
0293: }
0294:
0295: if (propertySets != null) {
0296: java.util.List<Node.Property> allPropsList = new ArrayList<Node.Property>();
0297: for (int i = 0; i < propertySets.length; i++) {
0298: Node.Property[] props = propertySets[i].getProperties();
0299: for (int j = 0; j < props.length; j++)
0300: if (props[j] instanceof FormProperty)
0301: allPropsList.add(props[j]);
0302: }
0303: allProperties = new FormProperty[allPropsList.size()];
0304: allPropsList.toArray(allProperties);
0305: } else {
0306: allProperties = new FormProperty[0];
0307: propertySets = new Node.PropertySet[0];
0308: }
0309:
0310: return propertySets;
0311: }
0312:
0313: /** Returns a class of a customizer for the layout manager being used as
0314: * a JavaBean. The class should be a java.awt.Component and
0315: * java.beans.Customizer. The default implementation tries to get the
0316: * customizer class from layout manager's BeanInfo.
0317: * @return layout bean customizer class, null if no customizer is provided
0318: */
0319: public Class getCustomizerClass() {
0320: return metaLayout == null ? null : metaLayout.getBeanInfo()
0321: .getBeanDescriptor().getCustomizerClass();
0322: }
0323:
0324: /** Returns an instance of a special customizer provided by the layout
0325: * delegate. This customizer need not implement java.beans.Customizer,
0326: * because its creation is under full control of the layout delegate - and
0327: * vice versa, the customizer can have full control over the layout
0328: * delegate (unlike the bean customizer which operates only with layout
0329: * manager bean instance). The default implementation returns null.
0330: * @return instance of layout support customizer
0331: */
0332: public Component getSupportCustomizer() {
0333: return null;
0334: }
0335:
0336: /** Gets the complete code for setting up the layout (including adding
0337: * components).
0338: * @return whole container's layout code
0339: */
0340: public CodeGroup getLayoutCode() {
0341: return setLayoutCode;
0342: }
0343:
0344: /** Gets code for setting up one component's constraints and adding the
0345: * component to the layout (container).
0346: * @return one component's layout code
0347: */
0348: public CodeGroup getComponentCode(int index) {
0349: return componentCodeGroups.get(index);
0350: }
0351:
0352: /** Gets CodeExpression object representing one component.
0353: * @param index index of the component in the layout
0354: * @return CodeExpression for a component
0355: */
0356: public CodeExpression getComponentCodeExpression(int index) {
0357: return componentCodeExpressions.get(index);
0358: }
0359:
0360: /** Gets number of components in the layout.
0361: * @return number of components in the layout
0362: */
0363: public int getComponentCount() {
0364: return componentCodeExpressions != null ? componentCodeExpressions
0365: .size()
0366: : 0;
0367: }
0368:
0369: /** This method is called to accept new components before they are added
0370: * to the layout (by calling addComponents method). It may adjust the
0371: * constraints, or refuse the components by throwing a RuntimeException
0372: * (e.g. IllegalArgumentException). It's up to the delagate to display an
0373: * error or warning message, the exception is not reported outside.
0374: * The default implementation accepts any components - simply does nothing.
0375: * @param compExpressions array of CodeExpression objects representing the
0376: * components to be accepted
0377: * @param constraints array of layout constraints of the components, may
0378: * contain nulls
0379: * @param index position at which the components are to be added (inserted);
0380: * -1 means that the components will be added at the end
0381: */
0382: public void acceptNewComponents(CodeExpression[] compExpressions,
0383: LayoutConstraints[] constraints, int index) {
0384: }
0385:
0386: /** This method is called after a property of the layout is changed by
0387: * the user. Subclasses may check whether the layout is valid after the
0388: * change and throw PropertyVetoException if the change should be reverted.
0389: * It's up to the delagate to display an error or warning message, the
0390: * exception is not reported outside. The default implementation accepts
0391: * any change.
0392: * @param ev PropertyChangeEvent object describing the change
0393: */
0394: public void acceptContainerLayoutChange(PropertyChangeEvent ev)
0395: throws PropertyVetoException {
0396: // as this method is called for each change, we update the layout
0397: // bean code here too
0398: if (layoutBeanCode != null)
0399: layoutBeanCode.updateCode();
0400: }
0401:
0402: /** This method is called after a constraint property of some component
0403: * is changed by the user. Subclasses may check if the layout is valid
0404: * after the change and throw PropertyVetoException if the change should
0405: * be reverted. It's up to the delagate to display an error or warning
0406: * message, the exception is not reported outside. The default
0407: * implementation accepts any change.
0408: * @param index index of the component in the layout
0409: * @param ev PropertyChangeEvent object describing the change
0410: */
0411: public void acceptComponentLayoutChange(int index,
0412: PropertyChangeEvent ev) throws PropertyVetoException {
0413: }
0414:
0415: /** Adds new components to the layout. This is done just at the metadata
0416: * level, no real components but their CodeExpression representations
0417: * are added.
0418: * The code structures describing the layout is updated immediately.
0419: * @param compExpressions array of CodeExpression objects representing the
0420: * components to be accepted
0421: * @param constraints array of layout constraints of the components, may
0422: * contain nulls
0423: * @param index position at which the components should be added (inserted);
0424: * if -1, the components should be added at the end
0425: */
0426: public void addComponents(CodeExpression[] newCompExps,
0427: LayoutConstraints[] newConstraints, int index) {
0428: if (index < 0 || index > componentCodeExpressions.size())
0429: index = componentCodeExpressions.size();
0430:
0431: CodeStructure codeStructure = layoutContext.getCodeStructure();
0432:
0433: for (int i = 0; i < newCompExps.length; i++) {
0434: int ii = index + i;
0435:
0436: CodeExpression compExp = newCompExps[i];
0437: componentCodeExpressions.add(ii, compExp);
0438:
0439: LayoutConstraints constr = newConstraints != null ? newConstraints[i]
0440: : null;
0441: if (constr == null)
0442: constr = createDefaultConstraints();
0443:
0444: componentConstraints.add(ii, constr);
0445:
0446: CodeGroup componentCode = codeStructure.createCodeGroup();
0447: createComponentCode(componentCode, compExp, ii);
0448: componentCodeGroups.add(ii, componentCode);
0449: }
0450: }
0451:
0452: /** Removes one component from the layout (at metadata level).
0453: * The code structures describing the layout is updated immediately.
0454: * @param index index of the component in the layout
0455: */
0456: public void removeComponent(int index) {
0457: componentCodeExpressions.remove(index);
0458: componentCodeGroups.remove(index);
0459: componentConstraints.remove(index);
0460: }
0461:
0462: /** Removes all components from the layout (at metadata level).
0463: * The code structures describing the layout is updated immediately.
0464: */
0465: public void removeAll() {
0466: if (componentCodeExpressions != null)
0467: componentCodeExpressions.clear();
0468: if (componentCodeGroups != null)
0469: componentCodeGroups.clear();
0470: if (componentConstraints != null)
0471: componentConstraints.clear();
0472: }
0473:
0474: /** Indicates whether there's some change in the layout in comparison
0475: * with the default layout of given container. If there's no change, no
0476: * code needs to be delegate (e.g. default FlowLayout in JPanel).
0477: * Note this is related to the container layout only, not to components.
0478: * @param defaultContainer instance of the default container to compare with
0479: * @param defaultContainerDelegate effective container delegate of the
0480: * default container (e.g. like content pane of JFrame)
0481: * @return whether the current layout is different from the default one
0482: */
0483: public boolean isLayoutChanged(Container defaultContainer,
0484: Container defaultContainerDelegate) {
0485: if (isDedicated())
0486: return false;
0487:
0488: Class<?> layoutClass = getSupportedClass();
0489: LayoutManager lm = defaultContainerDelegate.getLayout();
0490:
0491: if (layoutClass == null)
0492: return lm != null;
0493: if (lm == null)
0494: return true;
0495:
0496: //
0497: if (!layoutClass.isAssignableFrom(lm.getClass()))
0498: return true;
0499:
0500: FormProperty[] props = getAllProperties();
0501: for (int i = 0; i < props.length; i++)
0502: if (props[i].isChanged())
0503: return true;
0504:
0505: return false;
0506: }
0507:
0508: /** Gets layout constraints for a component at the given index.
0509: * @param index index of the component in the layout
0510: * @return layout constraints of given component
0511: */
0512: public LayoutConstraints getConstraints(int index) {
0513: return index < 0 || index >= componentConstraints.size() ? null
0514: : componentConstraints.get(index);
0515: }
0516:
0517: /** This method is called when switching layout - giving an opportunity to
0518: * convert the previous constrainst of components to constraints of the new
0519: * layout (this layout). The default implementation does nothing.
0520: * @param previousConstraints [input] layout constraints of components in
0521: * the previous layout
0522: * @param currentConstraints [output] array of converted constraints for
0523: * the new layout - to be filled
0524: * @param components [input] real components in a real container having the
0525: * previous layout
0526: */
0527: public void convertConstraints(
0528: LayoutConstraints[] previousConstraints,
0529: LayoutConstraints[] currentConstraints,
0530: Component[] components) {
0531: }
0532:
0533: /** Sets up the layout (without adding components) on a real container,
0534: * according to the internal metadata representation.
0535: * @param container instance of a real container to be set
0536: * @param containerDelegate effective container delegate of the container
0537: * (e.g. like content pane of JFrame)
0538: */
0539: public void setLayoutToContainer(Container container,
0540: Container containerDelegate) {
0541: if (isDedicated())
0542: return;
0543:
0544: LayoutManager lm = null;
0545: try {
0546: if (containerDelegate == layoutContext
0547: .getPrimaryContainerDelegate()) {
0548: if (metaLayout != null) // use the instance of MetaLayout
0549: lm = (LayoutManager) metaLayout.getBeanInstance();
0550: } else { // use cloned layout instance
0551: lm = cloneLayoutInstance(container, containerDelegate);
0552: }
0553: } catch (Exception ex) { // should not happen
0554: ex.printStackTrace();
0555: }
0556:
0557: if (lm != null)
0558: containerDelegate.setLayout(lm);
0559: }
0560:
0561: /** Adds real components to given container (according to layout
0562: * constraints stored for the components).
0563: * @param container instance of a real container to be added to
0564: * @param containerDelegate effective container delegate of the container
0565: * (e.g. like content pane of JFrame)
0566: * @param components components to be added
0567: * @param index position at which to add the components to container
0568: */
0569: public void addComponentsToContainer(Container container,
0570: Container containerDelegate, Component[] components,
0571: int index) {
0572: for (int i = 0; i < components.length; i++) {
0573: LayoutConstraints constr = getConstraints(i + index);
0574: if (constr != null)
0575: containerDelegate.add(components[i], constr
0576: .getConstraintsObject(), i + index);
0577: else
0578: containerDelegate.add(components[i], i + index);
0579: }
0580: }
0581:
0582: /** Removes a real component from a real container.
0583: * @param container instance of a real container
0584: * @param containerDelegate effective container delegate of the container
0585: * (e.g. like content pane of JFrame)
0586: * @param component component to be removed
0587: * @return whether it was possible to remove the component (some containers
0588: * may not support removing individual components reasonably)
0589: */
0590: public boolean removeComponentFromContainer(Container container,
0591: Container containerDelegate, Component component) {
0592: containerDelegate.remove(component);
0593: component.setBounds(0, 0, 0, 0);
0594: return true;
0595: }
0596:
0597: /** Removes all components from given real container.
0598: * @param container instance of a real container to be cleared
0599: * @param containerDelegate effective container delegate of the container
0600: * (e.g. like content pane of JFrame)
0601: * @return whether it was possible to clear the container (some containers
0602: * may not support this)
0603: */
0604: public boolean clearContainer(Container container,
0605: Container containerDelegate) {
0606: Component[] components = containerDelegate.getComponents();
0607: containerDelegate.removeAll();
0608: for (int i = 0; i < components.length; i++)
0609: components[i].setBounds(0, 0, 0, 0);
0610: return true;
0611: }
0612:
0613: /** This method is called when user clicks on the container in form
0614: * designer. The layout delegate may do something with the container,
0615: * e.g. for JTabbedPane it might switch the selected TAB. The default
0616: * implementation does nothing.
0617: * @param p Point of click in the container
0618: * @param real instance of the container when the click occurred
0619: * @param containerDelegate effective container delegate of the container
0620: * (e.g. like content pane of JFrame)
0621: */
0622: public void processMouseClick(Point p, Container container,
0623: Container containerDelegate) {
0624: }
0625:
0626: /** This method is called when a component is selected in Component
0627: * Inspector. If the layout delegate is interested in such information,
0628: * it may store it and use it e.g. in arrangeContainer method.
0629: * The default implementation does nothing.
0630: * @param index position (index) of the selected component in container
0631: */
0632: public void selectComponent(int index) {
0633: }
0634:
0635: /** In this method, the layout delegate has a chance to "arrange" real
0636: * container instance additionally - some other way that cannot be
0637: * done through layout properties and added components. For example, the
0638: * selected component index can be applied here (see delegates for
0639: * CardLayout and JTabbedPane). The default implementation does nothing.
0640: * @param container instance of a real container to be arranged
0641: * @param containerDelegate effective container delegate of the container
0642: * (e.g. like content pane of JFrame)
0643: */
0644: public void arrangeContainer(Container container,
0645: Container containerDelegate) {
0646: }
0647:
0648: /** This method should calculate layout constraints for a component dragged
0649: * over a container (or just for mouse cursor being moved over container,
0650: * without any component). This method is useful for "constraints oriented"
0651: * layout managers (like e.g. BorderLayout or GridBagLayout).
0652: * @param container instance of a real container over/in which the
0653: * component is dragged
0654: * @param containerDelegate effective container delegate of the container
0655: * (e.g. like content pane of JFrame)
0656: * @param component the real component being dragged, can be null
0657: * @param index position (index) of the component in its current container;
0658: * -1 if there's no dragged component
0659: * @param posInCont position of mouse in the container delegate
0660: * @param posInComp position of mouse in the dragged component; null if
0661: * there's no dragged component
0662: * @return new LayoutConstraints object corresponding to the position of
0663: * the component in the container; may return null if the layout
0664: * does not use component constraints, or if default constraints
0665: * should be used
0666: */
0667: public LayoutConstraints getNewConstraints(Container container,
0668: Container containerDelegate, Component component,
0669: int index, Point posInCont, Point posInComp) {
0670: return null;
0671: }
0672:
0673: /** This method should calculate position (index) for a component dragged
0674: * over a container (or just for mouse cursor being moved over container,
0675: * without any component). This method is useful for layout managers that
0676: * don't use component constraints (like e.g. FlowLayout or GridLayout)
0677: * @param container instance of a real container over/in which the
0678: * component is dragged
0679: * @param containerDelegate effective container delegate of the container
0680: * (e.g. like content pane of JFrame)
0681: * @param component the real component being dragged, can be null
0682: * @param index position (index) of the component in its current container;
0683: * -1 if there's no dragged component
0684: * @param posInCont position of mouse in the container delegate
0685: * @param posInComp position of mouse in the dragged component; null if
0686: * there's no dragged component
0687: * @return index corresponding to the position of the component in the
0688: * container; may return -1 if the layout rather uses component
0689: * constraints, or if a default index should be used
0690: */
0691: public int getNewIndex(Container container,
0692: Container containerDelegate, Component component,
0693: int index, Point posInCont, Point posInComp) {
0694: return -1;
0695: }
0696:
0697: /**
0698: * Returns context for the assistant (context sensitive help bar).
0699: *
0700: * @rerurn context for the assistant.
0701: */
0702: public String getAssistantContext() {
0703: return null;
0704: }
0705:
0706: /**
0707: * Returns context parameters for the assistant (context sensitive help bar).
0708: *
0709: * @return context parameters for the assistant.
0710: */
0711: public Object[] getAssistantParams() {
0712: return null;
0713: }
0714:
0715: /** This method should paint a feedback for a component dragged over
0716: * a container (or just for mouse cursor being moved over container,
0717: * without any component). In principle, it should present given component
0718: * layout constraints or index graphically.
0719: * @param container instance of a real container over/in which the
0720: * component is dragged
0721: * @param containerDelegate effective container delegate of the container
0722: * (e.g. like content pane of JFrame) - here the feedback is painted
0723: * @param component the real component being dragged, can be null
0724: * @param newConstraints component layout constraints to be presented
0725: * @param newIndex component's index position to be presented
0726: * (if newConstraints == null)
0727: * @param g Graphics object for painting (with color and line style set)
0728: * @return whether any feedback was painted (may return false if the
0729: * constraints or index are invalid, or if the painting is not
0730: * implemented)
0731: */
0732: public boolean paintDragFeedback(Container container,
0733: Container containerDelegate, Component component,
0734: LayoutConstraints newConstraints, int newIndex, Graphics g) {
0735: return false;
0736: }
0737:
0738: /** Provides resizing options for given component. It can combine the
0739: * bit-flag constants RESIZE_UP, RESIZE_DOWN, RESIZE_LEFT, RESIZE_RIGHT.
0740: * @param container instance of a real container in which the
0741: * component is to be resized
0742: * @param containerDelegate effective container delegate of the container
0743: * (e.g. like content pane of JFrame)
0744: * @param component real component to be resized
0745: * @param index position of the component in its container
0746: * @return resizing options for the component; 0 if no resizing is possible
0747: */
0748: public int getResizableDirections(Container container,
0749: Container containerDelegate, Component component, int index) {
0750: return 0;
0751: }
0752:
0753: /** This method should calculate layout constraints for a component being
0754: * resized.
0755: * @param container instance of a real container in which the
0756: * component is resized
0757: * @param containerDelegate effective container delegate of the container
0758: * (e.g. like content pane of JFrame)
0759: * @param component real component being resized
0760: * @param index position of the component in its container
0761: * @param sizeChanges Insets object with size differences
0762: * @param posInCont position of mouse in the container delegate
0763: * @return component layout constraints for resized component; null if
0764: * resizing is not possible or not implemented
0765: */
0766: public LayoutConstraints getResizedConstraints(Container container,
0767: Container containerDelegate, Component component,
0768: int index, Rectangle originalBounds, Insets sizeChanges,
0769: Point posInCont) {
0770: return null;
0771: }
0772:
0773: /** Cloning method - creates a copy of the layout delegate.
0774: * @param targetContext LayoutSupportContext for the new layout delegate
0775: * @param compExpressions array of CodeExpression objects representing the
0776: * components for the new layout delegate (corresponding to the
0777: * current ones)
0778: * @return cloned layout delegate instance
0779: */
0780: public LayoutSupportDelegate cloneLayoutSupport(
0781: LayoutSupportContext targetContext,
0782: CodeExpression[] targetComponents) {
0783: AbstractLayoutSupport clone = createLayoutSupportInstance();
0784: try {
0785: clone.initialize(targetContext, null, false);
0786: } catch (Exception ex) { // should not fail (not reading from code)
0787: ErrorManager.getDefault().notify(
0788: ErrorManager.INFORMATIONAL, ex);
0789: return null;
0790: }
0791:
0792: FormProperty[] sourceProperties = getAllProperties();
0793: FormProperty[] targetProperties = clone.getAllProperties();
0794: FormUtils.copyProperties(sourceProperties, targetProperties,
0795: FormUtils.CHANGED_ONLY
0796: | FormUtils.DISABLE_CHANGE_FIRING);
0797:
0798: int compCount = getComponentCount();
0799: LayoutConstraints[] constraints = new LayoutConstraints[compCount];
0800: for (int i = 0; i < compCount; i++) {
0801: LayoutConstraints constr = getConstraints(i);
0802: constraints[i] = constr != null ? constr.cloneConstraints()
0803: : null;
0804: }
0805:
0806: clone.addComponents(targetComponents, constraints, 0);
0807:
0808: return clone;
0809: }
0810:
0811: // ------------------
0812: // extended API for AbstractLayoutSupport subclasses
0813:
0814: /** Creates a default instance of LayoutManager (for internal use).
0815: * Override this method if the layout manager is not a bean (cannot
0816: * be created from default constructor).
0817: * @return new (default) instance of supported layout manager
0818: */
0819: protected LayoutManager createDefaultLayoutInstance()
0820: throws Exception {
0821: return (LayoutManager) CreationFactory
0822: .createDefaultInstance(getSupportedClass());
0823: }
0824:
0825: /** Cloning method - creates a clone of the reference LayoutManager
0826: * instance (for external use). Override this method if the layout manager
0827: * is not a bean (cannot be created from default constructor, and copied
0828: * using properties).
0829: * @param container instance of a real container in whose container
0830: * delegate the layout manager will be probably used
0831: * @param containerDelegate effective container delegate of the container
0832: * (e.g. like content pane of JFrame)
0833: * @return new instance of layout manager representing the layout (with
0834: * all properties set)
0835: */
0836: protected LayoutManager cloneLayoutInstance(Container container,
0837: Container containerDelegate) throws Exception {
0838: return metaLayout == null ? null : (LayoutManager) metaLayout
0839: .cloneBeanInstance(null);
0840: }
0841:
0842: /** Cloning method - creates a new instance of this layout support, just
0843: * not initialized yet.
0844: * @return new instance of this layout support
0845: */
0846: protected AbstractLayoutSupport createLayoutSupportInstance() {
0847: try {
0848: return (AbstractLayoutSupport) getClass().newInstance();
0849: } catch (Exception ex) { // should not happen for AbstractLayoutSupport subclasses
0850: return null;
0851: }
0852: }
0853:
0854: /** This methods returns the code expression to be used for container on
0855: * which the layout is set and to which components are added. This can be
0856: * either container, or container delegate expression. In fact, it is
0857: * container delegate in most cases (so this method needs to be overridden
0858: * very rarely). But there's e.g. JScrollPane which has its viewport as the
0859: * container delegate, but we work with the JScrollPane (whole container).
0860: * @return code expression representing the effective container
0861: */
0862: protected CodeExpression getActiveContainerCodeExpression() {
0863: return layoutContext.getContainerDelegateCodeExpression();
0864: }
0865:
0866: /** Cleans all data before the delegate is initialized.
0867: */
0868: protected void clean() {
0869: if (componentCodeExpressions != null)
0870: componentCodeExpressions.clear();
0871: else
0872: componentCodeExpressions = new ArrayList<CodeExpression>();
0873:
0874: if (componentCodeGroups != null)
0875: componentCodeGroups.clear();
0876: else
0877: componentCodeGroups = new ArrayList<CodeGroup>();
0878:
0879: if (componentConstraints != null)
0880: componentConstraints.clear();
0881: else
0882: componentConstraints = new ArrayList<LayoutConstraints>();
0883:
0884: if (setLayoutCode != null)
0885: setLayoutCode.removeAll();
0886: else
0887: setLayoutCode = layoutContext.getCodeStructure()
0888: .createCodeGroup();
0889:
0890: layoutBeanCode = null;
0891: metaLayout = null;
0892:
0893: allProperties = null;
0894: }
0895:
0896: /** This method is used for "reading layout from code", called from
0897: * initialize method. It recognizes relevant code which sets the layout
0898: * manager on the container and reads the layout information from the code.
0899: * This includes the code for setting up the layout manager itself and the
0900: * code for setting the layout manger to container. For setting up just the
0901: * layout manager bean, the method readInitLayoutCode is used.
0902: * Reading components code is not done here.
0903: * @param layoutCode CodeGroup to be filled with relevant layout code
0904: * @see readInitLayoutCode method
0905: */
0906: protected void readLayoutCode(CodeGroup layoutCode) {
0907: if (isDedicated())
0908: return;
0909:
0910: CodeGroup initLayoutCode = getCodeStructure().createCodeGroup();
0911: CodeStatement setLayoutStatement = null;
0912:
0913: Iterator it = CodeStructure
0914: .getDefinedStatementsIterator(getActiveContainerCodeExpression());
0915: CodeStatement[] statements = CodeStructure.filterStatements(it,
0916: getSetLayoutMethod());
0917: if (statements.length > 0) { // read from code
0918: setLayoutStatement = statements[0];
0919: readInitLayoutCode(setLayoutStatement
0920: .getStatementParameters()[0], initLayoutCode);
0921: } else { // create new
0922: CodeExpression layoutExp = createInitLayoutCode(initLayoutCode);
0923: if (layoutExp != null)
0924: setLayoutStatement = CodeStructure.createStatement(
0925: getActiveContainerCodeExpression(),
0926: getSetLayoutMethod(),
0927: new CodeExpression[] { layoutExp });
0928: }
0929:
0930: if (setLayoutStatement != null) {
0931: layoutCode.addGroup(initLayoutCode);
0932: layoutCode.addStatement(setLayoutStatement);
0933: }
0934: }
0935:
0936: /** This method is called from readLayoutCode to read the layout manager
0937: * bean code (i.e. code for constructor and properties).
0938: * @param layoutExp CodeExpressin of the layout manager
0939: * @param initLayoutCode CodeGroup to be filled with relevant
0940: * initialization code
0941: */
0942: protected void readInitLayoutCode(CodeExpression layoutExp,
0943: CodeGroup initLayoutCode) {
0944: if (metaLayout == null)
0945: return;
0946:
0947: layoutBeanCode = new BeanCodeManager(getSupportedClass(),
0948: getAllProperties(), CreationDescriptor.PLACE_ALL
0949: | CreationDescriptor.CHANGED_ONLY, false, // don't force empty constructor
0950: false, // disable changes firing when properties are restored
0951: layoutExp, initLayoutCode);
0952: }
0953:
0954: /** Creates code structures for a new layout manager (opposite to
0955: * readInitLayoutCode).
0956: * @param initLayoutCode CodeGroup to be filled with relevant
0957: * initialization code
0958: * @return created CodeExpression representing the layout manager
0959: */
0960: protected CodeExpression createInitLayoutCode(
0961: CodeGroup initLayoutCode) {
0962: if (metaLayout == null)
0963: return null;
0964:
0965: layoutBeanCode = new BeanCodeManager(getSupportedClass(),
0966: getAllProperties(), CreationDescriptor.PLACE_ALL
0967: | CreationDescriptor.CHANGED_ONLY, false,
0968: layoutContext.getCodeStructure(), CodeVariable.LOCAL,
0969: initLayoutCode);
0970:
0971: return layoutBeanCode.getCodeExpression();
0972: }
0973:
0974: /** This method is used for scanning code structures and recognizing
0975: * components added to containers and their constraints. It's called from
0976: * initialize method. When a relevant code statement is found, then the
0977: * CodeExpression of component is get and added to component, and also the
0978: * layout constraints information is read (using separate
0979: * readConstraintsCode method).
0980: * @param statement CodeStatement to be tested if it contains relevant code
0981: * @param componentCode CodeGroup to be filled with all component code
0982: * @return CodeExpression representing found component; null if the
0983: * statement is not relevant
0984: */
0985: protected CodeExpression readComponentCode(CodeStatement statement,
0986: CodeGroup componentCode) {
0987: CodeExpression compExp;
0988: CodeGroup constrCode;
0989: LayoutConstraints constr;
0990:
0991: // look for Container.add(Component) or Container.add(Component, Object)
0992: if (getSimpleAddMethod().equals(statement.getMetaObject())) {
0993: compExp = statement.getStatementParameters()[0];
0994: constrCode = null;
0995: constr = null;
0996: } else if (getAddWithConstraintsMethod().equals(
0997: statement.getMetaObject())) {
0998: CodeExpression[] params = statement
0999: .getStatementParameters();
1000:
1001: compExp = params[0];
1002: constrCode = getCodeStructure().createCodeGroup();
1003: constr = readConstraintsCode(params[1], constrCode, compExp);
1004: } else
1005: return null;
1006:
1007: componentConstraints.add(constr);
1008: if (constrCode != null)
1009: componentCode.addGroup(constrCode);
1010: componentCode.addStatement(statement);
1011:
1012: return compExp;
1013: }
1014:
1015: /** This method is called from readComponentCode method to read layout
1016: * constraints of a component from code.
1017: * @param constrExp CodeExpression object of the constraints (taken from
1018: * add method in the code)
1019: * @param constrCode CodeGroup to be filled with the relevant constraints
1020: * initialization code
1021: * @param compExp CodeExpression of the component for which the constraints
1022: * are read
1023: * @return LayoutConstraints based on information read form code
1024: */
1025: protected LayoutConstraints readConstraintsCode(
1026: CodeExpression constrExp, CodeGroup constrCode,
1027: CodeExpression compExp) {
1028: return null; // no default implementation possible
1029: }
1030:
1031: /** Creates code for a component added to the layout (opposite to
1032: * readComponentCode method).
1033: * @param componentCode CodeGroup to be filled with complete component code
1034: * (code for initializing the layout constraints and adding the
1035: * component to the layout)
1036: * @param compExp CodeExpression object representing component
1037: * @param index position of the component in the layout
1038: */
1039: protected void createComponentCode(CodeGroup componentCode,
1040: CodeExpression compExp, int index) {
1041: CodeGroup constrCode = getCodeStructure().createCodeGroup();
1042: LayoutConstraints constr = getConstraints(index);
1043:
1044: // first create init code for the constraints object
1045: CodeExpression constrExp = createConstraintsCode(constrCode,
1046: constr, compExp, index);
1047:
1048: // create "add" code for the component
1049: CodeStatement compAddStatement;
1050: if (constrExp != null) { // add with constraints
1051: compAddStatement = CodeStructure.createStatement(
1052: getActiveContainerCodeExpression(),
1053: getAddWithConstraintsMethod(),
1054: new CodeExpression[] { compExp, constrExp });
1055: } else { // add without constraints
1056: compAddStatement = CodeStructure.createStatement(
1057: getActiveContainerCodeExpression(),
1058: getSimpleAddMethod(),
1059: new CodeExpression[] { compExp });
1060: }
1061:
1062: componentCode.addGroup(constrCode);
1063: componentCode.addStatement(compAddStatement);
1064: }
1065:
1066: /** Called from createComponentCode method, creates code for a component
1067: * layout constraints (opposite to readConstraintsCode).
1068: * @param constrCode CodeGroup to be filled with constraints code
1069: * @param constr layout constraints metaobject representing the constraints
1070: * @param compExp CodeExpression object representing the component
1071: * @return created CodeExpression representing the layout constraints
1072: */
1073: protected CodeExpression createConstraintsCode(
1074: CodeGroup constrCode, LayoutConstraints constr,
1075: CodeExpression compExp, int index) {
1076: return null; // no default implementation possible
1077: }
1078:
1079: /** This method is called to get a default component layout constraints
1080: * metaobject in case it is not provided (e.g. in addComponents method).
1081: * @return the default LayoutConstraints object for the supported layout;
1082: * null if no component constraints are used
1083: */
1084: protected LayoutConstraints createDefaultConstraints() {
1085: return null; // no default implementation possible
1086: }
1087:
1088: /** Method to obtain just one propetry of given name.
1089: * @return layout property of given name
1090: */
1091: protected Node.Property getProperty(String propName) {
1092: return metaLayout == null ? null : metaLayout
1093: .getPropertyByName(propName);
1094: }
1095:
1096: /** This method can be overridden to provide other layout properties than
1097: * the standard ones of LayoutManager handled automatically as a bean.
1098: * This method is called from getPropertySets() implementation to obtain
1099: * the default property set for the layout (assuming there's only one
1100: * property set). So it is also possible to override (more generally)
1101: * getPropertySets() instead.
1102: * @return array of alternative properties of the layout
1103: */
1104: protected FormProperty[] getProperties() {
1105: return null; // use default "bean" properties
1106: }
1107:
1108: // ---------------
1109: // useful methods for subclasses
1110:
1111: /** Gets the LayoutSupportContext instance set in initialize method.
1112: * @return the attached LayoutSupportContext object providing necessary
1113: * context information
1114: */
1115: protected final LayoutSupportContext getLayoutContext() {
1116: return layoutContext;
1117: }
1118:
1119: /** Gets the main CodeStructure object (holding all code structure data)
1120: * for creating/reading the code. Obtained from the layout context.
1121: * @return main CodeStructure object (for working with code structure data)
1122: */
1123: protected final CodeStructure getCodeStructure() {
1124: return layoutContext.getCodeStructure();
1125: }
1126:
1127: /** Gets the internal list of layout constraints of components in the
1128: * layout. (The list contains instances of LayoutConstraints).
1129: * @return list of internally stored layout constraints of components
1130: */
1131: protected final java.util.List<LayoutConstraints> getConstraintsList() {
1132: return componentConstraints;
1133: }
1134:
1135: /** This methods collects properties from all property sets to one array.
1136: * @return all properties of the layout in an array
1137: */
1138: protected final FormProperty[] getAllProperties() {
1139: if (allProperties == null)
1140: getPropertySets();
1141:
1142: return allProperties;
1143: }
1144:
1145: /** This method should be used by subclasses if they need to re-create
1146: * the reference layout manager instance (see BoxLayoutSupport for example).
1147: */
1148: protected final void updateLayoutInstance() {
1149: Container cont = layoutContext.getPrimaryContainer();
1150: Container contDel = layoutContext.getPrimaryContainerDelegate();
1151:
1152: LayoutManager lm = null;
1153: try {
1154: lm = cloneLayoutInstance(cont, contDel);
1155: } catch (Exception ex) { // should not happen
1156: ex.printStackTrace();
1157: }
1158:
1159: if (lm != null && metaLayout != null)
1160: metaLayout.updateInstance(lm);
1161:
1162: layoutContext.updatePrimaryContainer();
1163: }
1164:
1165: /** This method finds the CodeStatement object representing setLayout
1166: * method call on the container.
1167: * @return CodeStatement of setLayout method call on the container
1168: */
1169: protected final CodeStatement getSetLayoutStatement() {
1170: Iterator it = CodeStructure
1171: .getDefinedStatementsIterator(getActiveContainerCodeExpression());
1172: CodeStatement[] found = CodeStructure.filterStatements(it,
1173: getSetLayoutMethod());
1174: return found != null && found.length > 0 ? found[0] : null;
1175: }
1176:
1177: // ---------
1178: // utility methods
1179:
1180: /** Used only internally.
1181: */
1182: protected static ResourceBundle getBundle() {
1183: return org.openide.util.NbBundle
1184: .getBundle(AbstractLayoutSupport.class);
1185: }
1186:
1187: /** Gets java.lang.reflect.Method object representing the simple
1188: * Container.add(Component) method. This is needed when working with
1189: * code structures.
1190: * @return java.lang.reflect.Method representing Container.add(Component)
1191: */
1192: protected static Method getSimpleAddMethod() {
1193: if (simpleAddMethod == null) {
1194: try {
1195: simpleAddMethod = Container.class.getMethod("add", // NOI18N
1196: new Class[] { Component.class });
1197: } catch (NoSuchMethodException ex) { // should not happen
1198: ex.printStackTrace();
1199: }
1200: }
1201: return simpleAddMethod;
1202: }
1203:
1204: /** Gets java.lang.reflect.Method object representing the method for adding
1205: * component with layout constraints - Container.add(Component, Object).
1206: * This is needed when working with code structures.
1207: * @return java.lang.reflect.Method object representing
1208: * Container.add(Component, Object) method
1209: */
1210: protected static Method getAddWithConstraintsMethod() {
1211: if (addWithConstraintsMethod == null) {
1212: try {
1213: addWithConstraintsMethod = Container.class.getMethod(
1214: "add", // NOI18N
1215: new Class[] { Component.class, Object.class });
1216: } catch (NoSuchMethodException ex) { // should not happen
1217: ex.printStackTrace();
1218: }
1219: }
1220: return addWithConstraintsMethod;
1221: }
1222:
1223: /** Gets java.lang.reflect.Method object representing
1224: * Container.setLayout(LayoutManager) method. This is needed when working
1225: * with code structures.
1226: * @return java.lang.reflect.Method object representing
1227: * Container.setLayout(LayoutManager) method
1228: */
1229: protected static Method getSetLayoutMethod() {
1230: if (setLayoutMethod == null) {
1231: try {
1232: setLayoutMethod = Container.class.getMethod(
1233: "setLayout", // NOI18N
1234: new Class[] { LayoutManager.class });
1235: } catch (NoSuchMethodException ex) { // should not happen
1236: ex.printStackTrace();
1237: }
1238: }
1239: return setLayoutMethod;
1240: }
1241: }
|