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.delegates;
0043:
0044: import java.awt.*;
0045: import java.beans.*;
0046: import java.util.*;
0047: import java.util.List;
0048: import java.lang.ref.*;
0049: import java.lang.reflect.*;
0050:
0051: import org.openide.nodes.Node;
0052: import org.openide.util.NbBundle;
0053:
0054: import org.netbeans.modules.form.layoutsupport.*;
0055: import org.netbeans.modules.form.codestructure.*;
0056: import org.netbeans.modules.form.FormProperty;
0057:
0058: /**
0059: * Support class for GridBagLayout. This is an example of support for layout
0060: * managers with complex layout constraints for which rather special code
0061: * structure must be managed - GridBagConstraints require to be set up
0062: * field by field.
0063: *
0064: * @author Tran Duc Trung, Tomas Pavek
0065: */
0066:
0067: public class GridBagLayoutSupport extends AbstractLayoutSupport {
0068: private static Reference<GridBagCustomizer.Window> customizerRef;
0069:
0070: /** Gets the supported layout manager class - GridBagLayout.
0071: * @return the class supported by this delegate
0072: */
0073: public Class getSupportedClass() {
0074: return GridBagLayout.class;
0075: }
0076:
0077: /** Returns a class of customizer for GridBagLayout.
0078: * @return layout customizer class
0079: */
0080: @Override
0081: public Class getCustomizerClass() {
0082: return GridBagCustomizer.Window.class;
0083: }
0084:
0085: /** Creates an instance of customizer for GridBagLayout.
0086: * @return layout customizer class
0087: */
0088: @Override
0089: public Component getSupportCustomizer() {
0090: GridBagCustomizer.Window customizer = null;
0091: if (customizerRef != null)
0092: customizer = customizerRef.get();
0093: if (customizer == null) {
0094: customizer = new GridBagCustomizer.Window();
0095: customizerRef = new WeakReference<GridBagCustomizer.Window>(
0096: customizer);
0097: }
0098: customizer.setObject(this );
0099: return customizer;
0100: }
0101:
0102: /** This method is called when switching layout - giving an opportunity to
0103: * convert the previous constrainst of components to constraints of the new
0104: * layout (this layout). Conversion from AbsoluteConstraints to
0105: * GridBagConstraints is implemented here.
0106: * @param previousConstraints [input] layout constraints of components in
0107: * the previous layout
0108: * @param currentConstraints [output] array of converted constraints for
0109: * the new layout - to be filled
0110: * @param components [input] real components in a real container having the
0111: * previous layout
0112: */
0113: @Override
0114: public void convertConstraints(
0115: LayoutConstraints[] previousConstraints,
0116: LayoutConstraints[] currentConstraints,
0117: Component[] components) {
0118: if (currentConstraints == null
0119: || components == null
0120: || components.length > currentConstraints.length
0121: || components.length == 0
0122: || !(previousConstraints[0] instanceof AbsoluteLayoutSupport.AbsoluteLayoutConstraints))
0123: return;
0124:
0125: List<Integer> xlines = new ArrayList<Integer>();
0126: List<Integer> ylines = new ArrayList<Integer>();
0127:
0128: Rectangle parentbound;
0129: Container con = components[0].getParent();
0130: if (con == null) {
0131: parentbound = components[0].getBounds();
0132: } else {
0133: parentbound = con.getBounds();
0134: }
0135:
0136: // Determine the size of the grid
0137: insertLines(0, xlines);
0138: insertLines(0, ylines);
0139:
0140: for (int i = 0; i < components.length; i++) {
0141: Rectangle ibounds = components[i].getBounds();
0142:
0143: if (ibounds.width > 0) {
0144: insertLines(ibounds.x + ibounds.width, xlines);
0145: } else {
0146: insertLines(ibounds.x + 1, xlines);
0147: }
0148:
0149: if (ibounds.height > 0) {
0150: insertLines(ibounds.y + ibounds.height, ylines);
0151: } else {
0152: insertLines(ibounds.y + 1, ylines);
0153: }
0154: }
0155:
0156: // Determine grid width of components.
0157: LayoutInfo[] layouts = new LayoutInfo[components.length];
0158: for (int i = 0; i < layouts.length; i++)
0159: layouts[i] = new LayoutInfo();
0160:
0161: for (int i = 0; i < xlines.size() - 1; i++) {
0162: int x1 = xlines.get(i);
0163: int x2 = xlines.get(i + 1);
0164:
0165: for (int j = 0; j < components.length; j++) {
0166: Rectangle jbounds = components[j].getBounds();
0167: if (jbounds.width <= 0) {
0168: jbounds.width = 1;
0169: }
0170: if (isOverlapped(x1, x2, jbounds.x, jbounds.x
0171: + jbounds.width - 1))
0172: layouts[j].incGridWidth(i);
0173: }
0174: }
0175:
0176: // Determine grid height of components.
0177: for (int i = 0; i < ylines.size() - 1; i++) {
0178: int y1 = ylines.get(i);
0179: int y2 = ylines.get(i + 1);
0180:
0181: for (int j = 0; j < components.length; j++) {
0182: Rectangle jbounds = components[j].getBounds();
0183: if (jbounds.height <= 0) {
0184: jbounds.height = 1;
0185: }
0186: if (isOverlapped(y1, y2, jbounds.y, jbounds.y
0187: + jbounds.height - 1))
0188: layouts[j].incGridHeight(i);
0189: }
0190: }
0191:
0192: // Calculate insets of the components.
0193: for (int i = 0; i < components.length; i++) {
0194: Rectangle curbounds = components[i].getBounds();
0195: int lastleft = 0;
0196: int lasttop = 0;
0197:
0198: for (int j = 0; j < components.length; j++) {
0199: Rectangle jbounds = components[j].getBounds();
0200: int width = jbounds.width;
0201: if (width < 0)
0202: width = 0;
0203: if (jbounds.x + width - 1 < curbounds.x) {
0204: if (jbounds.x + width > lastleft) {
0205: lastleft = jbounds.x + width;
0206: }
0207: }
0208: int height = jbounds.height;
0209: if (height < 0)
0210: height = 0;
0211: if (jbounds.y + height - 1 < curbounds.y) {
0212: if (jbounds.y + height > lasttop) {
0213: lasttop = jbounds.y + height;
0214: }
0215: }
0216: }
0217:
0218: layouts[i].setLeft(curbounds.x - lastleft);
0219: layouts[i].setTop(curbounds.y - lasttop);
0220:
0221: int width = (curbounds.width < 0) ? 0 : curbounds.width;
0222: int height = (curbounds.height < 0) ? 0 : curbounds.height;
0223:
0224: if (layouts[i].getLastGridX() == xlines.size() - 2) {
0225: layouts[i].setRight(parentbound.width - curbounds.x
0226: - width);
0227: }
0228: if (layouts[i].getLastGridY() == ylines.size() - 2) {
0229: layouts[i].setBottom(parentbound.height - curbounds.y
0230: - height);
0231: }
0232: }
0233:
0234: // GridBagLayout puts the remaining width of the component into the last
0235: // grid column/row (if the component has weight 0). This would not be
0236: // a problem for us if it would take components sorted according to
0237: // their increasing x/y grid coordinate. Unfortunately it takes
0238: // components sorted according to their increasing grid width/height.
0239: // This can result in a layout that is much wider/higher then the previous
0240: // absolute layout. The following code forces the right order by
0241: // introduction of new (otherwise redundant) grid lines.
0242:
0243: LayoutInfoComparator comp = new LayoutInfoComparator(
0244: LayoutInfoComparator.XAXIS);
0245: LayoutInfo[] layoutsX = layouts.clone();
0246: LayoutInfo[] layoutsY = layouts.clone();
0247: Arrays.sort(layoutsX, comp);
0248: comp.cord = LayoutInfoComparator.YAXIS;
0249: Arrays.sort(layoutsY, comp);
0250:
0251: for (int i = 0; i < components.length; i++) {
0252: int expand = 0;
0253: int lastgrid = layoutsX[i].getLastGridX();
0254: for (int j = i + 1; j < components.length; j++) {
0255: if (layoutsX[j].containsGridX(lastgrid)
0256: && (layoutsX[j].getLastGridX() > lastgrid)
0257: && (layoutsX[i].gridwidth >= layoutsX[j].gridwidth)
0258: && (expand < layoutsX[i].gridwidth
0259: - layoutsX[j].gridwidth + 1)) {
0260: expand = layoutsX[i].gridwidth
0261: - layoutsX[j].gridwidth + 1;
0262: }
0263: }
0264: if (expand > 0) {
0265: for (int j = i + 1; j < components.length; j++) {
0266: if (layoutsX[j].containsGridX(lastgrid)
0267: && layoutsX[j].getLastGridX() > lastgrid) {
0268: layoutsX[j].expandGridWidth(expand);
0269: } else if (layoutsX[j].gridx > lastgrid) {
0270: layoutsX[j].moveGridX(expand);
0271: }
0272: }
0273: }
0274:
0275: expand = 0;
0276: lastgrid = layoutsY[i].getLastGridY();
0277: for (int j = i + 1; j < components.length; j++) {
0278: if (layoutsY[j].containsGridY(lastgrid)
0279: && (layoutsY[j].getLastGridY() > lastgrid)
0280: && (layoutsY[i].gridheight >= layoutsY[j].gridheight)
0281: && (expand < layoutsY[i].gridheight
0282: - layoutsY[j].gridheight + 1)) {
0283: expand = layoutsY[i].gridheight
0284: - layoutsY[j].gridheight + 1;
0285: }
0286: }
0287: if (expand > 0) {
0288: for (int j = i + 1; j < components.length; j++) {
0289: if (layoutsY[j].containsGridY(lastgrid)
0290: && layoutsY[j].getLastGridY() > lastgrid) {
0291: layoutsY[j].expandGridHeight(expand);
0292: } else if (layoutsY[j].gridy > lastgrid) {
0293: layoutsY[j].moveGridY(expand);
0294: }
0295: }
0296: }
0297: }
0298:
0299: // Generate constraints
0300: for (int i = 0; i < components.length; i++) {
0301: if (Math.max(layouts[i].gridx + layouts[i].gridwidth - 1,
0302: layouts[i].gridy + layouts[i].gridheight - 1) >= 512) {
0303: for (int j = 0; j < i; j++) {
0304: currentConstraints[j] = null; // Reset partially converted constraints
0305: }
0306: org.openide.DialogDisplayer.getDefault().notify(
0307: new org.openide.NotifyDescriptor.Message(
0308: NbBundle.getMessage(
0309: AbstractLayoutSupport.class,
0310: "MSG_ERR_MoreThan512"))); // NOI18N
0311: return;
0312: }
0313: GridBagConstraints gbc = new GridBagConstraints();
0314: gbc.gridx = layouts[i].gridx;
0315: gbc.gridy = layouts[i].gridy;
0316: gbc.gridwidth = layouts[i].gridwidth;
0317: gbc.gridheight = layouts[i].gridheight;
0318: gbc.anchor = GridBagConstraints.NORTHWEST;
0319:
0320: gbc.insets = new java.awt.Insets(layouts[i].top,
0321: layouts[i].left, layouts[i].bottom,
0322: layouts[i].right);
0323:
0324: if (components[i].getClass().getName().equals(
0325: "javax.swing.JScrollPane")) { // NOI18N
0326: gbc.weightx = 1.0;
0327: gbc.weighty = 1.0;
0328: gbc.fill = java.awt.GridBagConstraints.BOTH;
0329: }
0330:
0331: Rectangle bounds = components[i].getBounds();
0332: Dimension minsize = components[i].getMinimumSize();
0333: Dimension prefsize = components[i].getPreferredSize();
0334:
0335: if (bounds.width > minsize.width)
0336: gbc.ipadx = bounds.width - minsize.width;
0337: else if (bounds.width < prefsize.width)
0338: gbc.ipadx = bounds.width - prefsize.width;
0339: if (bounds.height > minsize.height)
0340: gbc.ipady = bounds.height - minsize.height;
0341: else if (bounds.height < prefsize.height)
0342: gbc.ipady = bounds.height - prefsize.height;
0343:
0344: currentConstraints[i] = new GridBagLayoutConstraints(gbc);
0345: }
0346: }
0347:
0348: private static boolean isOverlapped(int border1, int border2,
0349: int compPos1, int compPos2) {
0350: return compPos2 >= border1 && compPos1 < border2;
0351: }
0352:
0353: private static void insertLines(int line,
0354: java.util.List<Integer> lines) {
0355: if (line < 0)
0356: line = 0;
0357: for (int i = 0; i < lines.size(); i++) {
0358: int ival = lines.get(i);
0359: if (line < ival) {
0360: lines.add(i, new Integer(line));
0361: return;
0362: } else if (line == ival)
0363: return;
0364: }
0365: lines.add(new Integer(line));
0366: }
0367:
0368: /**
0369: * Comparator of <code>LayoutInfo</code> objects.
0370: */
0371: private static class LayoutInfoComparator implements
0372: java.util.Comparator<LayoutInfo> {
0373: final static int XAXIS = 0;
0374: final static int YAXIS = 1;
0375: int cord;
0376:
0377: public LayoutInfoComparator(int cord) {
0378: this .cord = cord;
0379: }
0380:
0381: public int compare(LayoutInfo layoutleft, LayoutInfo layoutright) {
0382: if ((layoutleft == null) || (layoutright == null))
0383: return 0;
0384: if (cord == XAXIS) {
0385: return layoutleft.getLastGridX()
0386: - layoutright.getLastGridX();
0387: } else {
0388: return layoutleft.getLastGridY()
0389: - layoutright.getLastGridY();
0390: }
0391: }
0392:
0393: }
0394:
0395: /**
0396: * Layout information for one component.
0397: */
0398: private static class LayoutInfo {
0399: /** Grid coordinates. */
0400: int gridx, gridy;
0401: /** Grid width. */
0402: int gridwidth;
0403: /** Grid height. */
0404: int gridheight;
0405: /** Insets. */
0406: int top = 0, left = 0, bottom = 0, right = 0;
0407:
0408: void setLeft(int left) {
0409: if (left < 0)
0410: left = 0;
0411: this .left = left;
0412: }
0413:
0414: void setTop(int top) {
0415: if (top < 0)
0416: top = 0;
0417: this .top = top;
0418: }
0419:
0420: void setBottom(int bottom) {
0421: if (bottom < 0)
0422: bottom = 0;
0423: this .bottom = bottom;
0424: }
0425:
0426: void setRight(int right) {
0427: if (right < 0)
0428: right = 0;
0429: this .right = right;
0430: }
0431:
0432: void moveGridX(int diff) {
0433: gridx += diff;
0434: }
0435:
0436: void moveGridY(int diff) {
0437: gridy += diff;
0438: }
0439:
0440: void expandGridWidth(int diff) {
0441: gridwidth += diff;
0442: }
0443:
0444: void expandGridHeight(int diff) {
0445: gridheight += diff;
0446: }
0447:
0448: void incGridWidth(int gridx) {
0449: if (gridwidth == 0)
0450: this .gridx = gridx;
0451: gridwidth++;
0452: }
0453:
0454: void incGridHeight(int gridy) {
0455: if (gridheight == 0)
0456: this .gridy = gridy;
0457: gridheight++;
0458: }
0459:
0460: boolean containsGridX(int grid) {
0461: return ((grid >= gridx) && (grid < gridx + gridwidth));
0462: }
0463:
0464: boolean containsGridY(int grid) {
0465: return ((grid >= gridy) && (grid < gridy + gridheight));
0466: }
0467:
0468: int getLastGridX() {
0469: return gridx + gridwidth - 1;
0470: }
0471:
0472: int getLastGridY() {
0473: return gridy + gridheight - 1;
0474: }
0475:
0476: }
0477:
0478: // --------
0479:
0480: /** This method is called from readComponentCode method to read layout
0481: * constraints of a component from code (GridBagConstraints in this case).
0482: * @param constrExp CodeExpression object of the constraints (taken from
0483: * add method in the code)
0484: * @param constrCode CodeGroup to be filled with the relevant constraints
0485: * initialization code
0486: * @param compExp CodeExpression of the component for which the constraints
0487: * are read (not needed here)
0488: * @return LayoutConstraints based on information read form code
0489: */
0490: @Override
0491: protected LayoutConstraints readConstraintsCode(
0492: CodeExpression constrExp, CodeGroup constrCode,
0493: CodeExpression compExp) {
0494: GridBagLayoutConstraints constr = new GridBagLayoutConstraints();
0495: // reading is done in GridBagLayoutConstraints
0496: constr.readCodeExpression(constrExp, constrCode);
0497: return constr;
0498: }
0499:
0500: /** Called from createComponentCode method, creates code for a component
0501: * layout constraints (opposite to readConstraintsCode).
0502: * @param constrCode CodeGroup to be filled with constraints code
0503: * @param constr layout constraints metaobject representing the constraints
0504: * @param compExp CodeExpression object representing the component; not
0505: * needed here
0506: * @return created CodeExpression representing the layout constraints
0507: */
0508: @Override
0509: protected CodeExpression createConstraintsCode(
0510: CodeGroup constrCode, LayoutConstraints constr,
0511: CodeExpression compExp, int index) {
0512: if (!(constr instanceof GridBagLayoutConstraints))
0513: return null;
0514:
0515: // the code creation is done in GridBagLayoutConstraints
0516: return ((GridBagLayoutConstraints) constr)
0517: .createCodeExpression(getCodeStructure(), constrCode);
0518: }
0519:
0520: /** This method is called to get a default component layout constraints
0521: * metaobject in case it is not provided (e.g. in addComponents method).
0522: * @return the default LayoutConstraints object for the supported layout;
0523: * null if no component constraints are used
0524: */
0525: @Override
0526: protected LayoutConstraints createDefaultConstraints() {
0527: return new GridBagLayoutConstraints();
0528: }
0529:
0530: // -----------------
0531:
0532: /** LayoutConstraints implementation class for GridBagConstraints.
0533: * GridBagConstraints class is special in that it requires more code
0534: * statements for initialization (setting up the individual fields).
0535: *
0536: * There are two possible code variants: simple and complex.
0537: * In the simple situation, no parameter of GridBagConstraints is set, so
0538: * the code looks like:
0539: * container.add(component, new GridBagConstraints());
0540: *
0541: * In the complex situation, there are some parameters set - this requires
0542: * additional code statement for each parameter, and also a variable to
0543: * be used for the constraints object. Then the code looks like:
0544: * GridBagConstraints gridBagConstraints;
0545: * ...
0546: * gridBagConstraints = new GridBagConstraints();
0547: * gridBagConstraints.gridx = 1;
0548: * gridBagConstraints.gridy = 2;
0549: * container.add(component, gridBagConstraints);
0550: */
0551: public static class GridBagLayoutConstraints implements
0552: LayoutConstraints {
0553: private GridBagConstraints constraints;
0554:
0555: private GridBagConstraints defaultConstraints = new GridBagConstraints();
0556:
0557: private Property[] properties;
0558:
0559: private CodeExpression constraintsExpression;
0560: private CodeGroup constraintsCode; // set of all relevant statements
0561: private CodeStatement[] propertyStatements; // statements for properties
0562:
0563: private static Constructor constrConstructor;
0564:
0565: private static final int variableType = CodeVariable.LOCAL
0566: | CodeVariable.EXPLICIT_DECLARATION;
0567: private static final int variableMask = CodeVariable.SCOPE_MASK
0568: | CodeVariable.DECLARATION_MASK;
0569: private static final String defaultVariableName = "gridBagConstraints"; // NOI18N
0570:
0571: public GridBagLayoutConstraints() {
0572: constraints = new GridBagConstraints();
0573: }
0574:
0575: public GridBagLayoutConstraints(GridBagConstraints constraints) {
0576: this .constraints = constraints;
0577: }
0578:
0579: public Node.Property[] getProperties() {
0580: if (properties == null) {
0581: createProperties();
0582: reinstateProperties();
0583: }
0584: return properties;
0585: }
0586:
0587: public Object getConstraintsObject() {
0588: return constraints;
0589: }
0590:
0591: public LayoutConstraints cloneConstraints() {
0592: return new GridBagLayoutConstraints(
0593: (GridBagConstraints) constraints.clone());
0594: }
0595:
0596: // -------
0597:
0598: /** This method creates code expression for the constraints. It's
0599: * called from the delegate's createConstraintsCode method.
0600: * @param codeStructure CodeStructure in which the expression will be
0601: * created
0602: * @param constrCode CodeGroup to be filled with all the initialization
0603: * statements
0604: * @return CodeExpression representing the constraints
0605: */
0606: private CodeExpression createCodeExpression(
0607: CodeStructure codeStructure, CodeGroup constrCode) {
0608: this .constraintsCode = constrCode;
0609: propertyStatements = null;
0610:
0611: // GridBagConstraints is created by a simple constructor...
0612: constraintsExpression = codeStructure.createExpression(
0613: getConstraintsConstructor(),
0614: CodeStructure.EMPTY_PARAMS);
0615: // ...but the additionlly it requires to create the initialization
0616: // code statements
0617: updateCodeExpression();
0618:
0619: return constraintsExpression;
0620: }
0621:
0622: /** This method reads CodeExpression object representing the
0623: * constraints and also all its initialization statements which are
0624: * mapped to the constraints properties. It's called from the
0625: * delegate's readConstraintsCode method.
0626: * @param constrExp CodeExpression of the constraints
0627: * @param constrCode CodeGroup to be filled with recognize
0628: * initialization statements
0629: */
0630: private void readCodeExpression(CodeExpression constrExp,
0631: CodeGroup constrCode) {
0632: constraintsExpression = constrExp;
0633: constraintsCode = constrCode;
0634: propertyStatements = null;
0635:
0636: // constrExp.setOrigin(CodeStructure.createOrigin(
0637: // getConstraintsConstructor(),
0638: // CodeStructure.EMPTY_PARAMS));
0639:
0640: getProperties(); // ensure properties are created
0641:
0642: boolean isAnyChanged = false;
0643:
0644: Iterator it = CodeStructure
0645: .getDefinedStatementsIterator(constrExp);
0646: List<CodeStatement> redundantStatements = new ArrayList<CodeStatement>(
0647: 15);
0648: while (it.hasNext()) {
0649: // go through all the statements of constraints code expression
0650: CodeStatement statement = (CodeStatement) it.next();
0651: for (int j = 0; j < properties.length; j++) {
0652: Property prop = properties[j];
0653: if (prop.field.equals(statement.getMetaObject())) {
0654: // this statement represents a GridBagConstraints field
0655: // assignment, we map the corresponding property to it
0656: FormCodeSupport.readPropertyStatement(
0657: statement, prop, false);
0658: setPropertyStatement(j, statement);
0659: if (prop.isChanged()) { // this is a non-default value
0660: constrCode.addStatement(statement);
0661: isAnyChanged = true;
0662: } else { // remove statement for default value
0663: redundantStatements.add(statement);
0664: }
0665: break;
0666: }
0667: }
0668: }
0669: for (CodeStatement statement : redundantStatements) {
0670: CodeStructure.removeStatement(statement);
0671: }
0672:
0673: setupVariable(isAnyChanged);
0674: }
0675:
0676: /** This method updates the constraints code according to the
0677: * properties. This is called at the beginning - when the constraints
0678: * code expression is created - and then after each change of the
0679: * constraints properties. This keeps the code consistent with the
0680: * properties.
0681: */
0682: private void updateCodeExpression() {
0683: if (constraintsCode == null
0684: || constraintsExpression == null)
0685: return;
0686:
0687: constraintsCode.removeAll();
0688:
0689: getProperties(); // ensure properties are created
0690:
0691: boolean isAnyChanged = false;
0692: for (int i = 0; i < properties.length; i++)
0693: // for each changed property, add the corresponding statement
0694: // to the code (constraintsCode - instance of CodeGroup)
0695: if (properties[i].isChanged()) {
0696: constraintsCode
0697: .addStatement(getPropertyStatement(i));
0698: isAnyChanged = true;
0699: }
0700:
0701: setupVariable(isAnyChanged);
0702: }
0703:
0704: /** This method returns the code statement corresponding to property
0705: * of given index. The statement is created if it does not exist yet.
0706: * @param index index of required statement
0707: */
0708: private CodeStatement getPropertyStatement(int index) {
0709: if (propertyStatements == null)
0710: propertyStatements = new CodeStatement[properties.length];
0711:
0712: CodeStatement propStatement = propertyStatements[index];
0713: if (propStatement == null) {
0714: CodeExpression propExp = constraintsExpression
0715: .getCodeStructure()
0716: .createExpression(
0717: FormCodeSupport
0718: .createOrigin(properties[index]));
0719:
0720: // statement is field assignment; the property code expression
0721: // represents the assigned value
0722: propStatement = CodeStructure.createStatement(
0723: constraintsExpression, properties[index].field,
0724: propExp);
0725:
0726: propertyStatements[index] = propStatement;
0727: }
0728: return propStatement;
0729: }
0730:
0731: /** Sets the code statement read form code for given property index.
0732: * @param index index of the corresponding property
0733: * @param propStatement CodeStatement to be set
0734: */
0735: private void setPropertyStatement(int index,
0736: CodeStatement propStatement) {
0737: if (propertyStatements == null)
0738: propertyStatements = new CodeStatement[properties.length];
0739: propertyStatements[index] = propStatement;
0740: }
0741:
0742: /** This method sets up the variable for constraints code expression.
0743: * The variable is needed only if there's some property changed (i.e.
0744: * there's some statement in which the variable is used). One variable
0745: * is used for all GridBagConstraints in the form.
0746: */
0747: private void setupVariable(boolean anyChangedProperty) {
0748: CodeStructure codeStructure = constraintsExpression
0749: .getCodeStructure();
0750: CodeVariable var = constraintsExpression.getVariable();
0751:
0752: if (anyChangedProperty) { // there should be a variable
0753: if (var == null) { // no variable currently used
0754: var = findVariable(); // find and reuse variable
0755: if (var == null) { // create a new variable
0756: var = codeStructure
0757: .createVariableForExpression(
0758: constraintsExpression,
0759: variableType,
0760: defaultVariableName);
0761: } else { // attach the constraints expression to the variable
0762: codeStructure.attachExpressionToVariable(
0763: constraintsExpression, var);
0764: }
0765: }
0766: // add variable assignment code
0767: constraintsCode.addStatement(0, var
0768: .getAssignment(constraintsExpression));
0769: } else { // no variable needed
0770: codeStructure
0771: .removeExpressionFromVariable(constraintsExpression);
0772: }
0773: }
0774:
0775: private CodeVariable findVariable() {
0776: CodeStructure codeStructure = constraintsExpression
0777: .getCodeStructure();
0778:
0779: // first try "gridBagConstraints" name - this succeeds in most
0780: // cases (unless the name is used elsewhere or not created yet)
0781: CodeVariable var = codeStructure
0782: .getVariable(defaultVariableName);
0783: if (var != null
0784: && (var.getType() & variableMask) == variableType
0785: && GridBagConstraints.class.equals(var
0786: .getDeclaredType()))
0787: return var;
0788:
0789: // try to find variable of corresponding type (time expensive)
0790: Iterator it = codeStructure.getVariablesIterator(
0791: variableType, variableMask,
0792: GridBagConstraints.class);
0793: while (it.hasNext()) {
0794: var = (CodeVariable) it.next();
0795: if (var.getName().startsWith(defaultVariableName))
0796: return var;
0797: }
0798:
0799: return null;
0800: }
0801:
0802: private void createProperties() {
0803: properties = new Property[] {
0804: new Property("gridx", // NOI18N
0805: Integer.TYPE, getBundle().getString(
0806: "PROP_gridx"), // NOI18N
0807: getBundle().getString("HINT_gridx"), // NOI18N
0808: GridPosEditor.class),
0809:
0810: new Property("gridy", // NOI18N
0811: Integer.TYPE, getBundle().getString(
0812: "PROP_gridy"), // NOI18N
0813: getBundle().getString("HINT_gridy"), // NOI18N
0814: GridPosEditor.class),
0815:
0816: new Property("gridwidth", // NOI18N
0817: Integer.TYPE, getBundle().getString(
0818: "PROP_gridwidth"), // NOI18N
0819: getBundle().getString("HINT_gridwidth"), // NOI18N
0820: GridSizeEditor.class),
0821:
0822: new Property("gridheight", // NOI18N
0823: Integer.TYPE, getBundle().getString(
0824: "PROP_gridheight"), // NOI18N
0825: getBundle().getString("HINT_gridheight"), // NOI18N
0826: GridSizeEditor.class),
0827:
0828: new Property("fill", // NOI18N
0829: Integer.TYPE, getBundle().getString(
0830: "PROP_fill"), // NOI18N
0831: getBundle().getString("HINT_fill"), // NOI18N
0832: FillEditor.class),
0833:
0834: new Property("ipadx", // NOI18N
0835: Integer.TYPE, getBundle().getString(
0836: "PROP_ipadx"), // NOI18N
0837: getBundle().getString("HINT_ipadx"), // NOI18N
0838: null),
0839:
0840: new Property("ipady", // NOI18N
0841: Integer.TYPE, getBundle().getString(
0842: "PROP_ipady"), // NOI18N
0843: getBundle().getString("HINT_ipady"), // NOI18N
0844: null),
0845:
0846: new Property("anchor", // NOI18N
0847: Integer.TYPE, getBundle().getString(
0848: "PROP_anchor"), // NOI18N
0849: getBundle().getString("HINT_anchor"), // NOI18N
0850: AnchorEditor.class),
0851:
0852: new Property("weightx", // NOI18N
0853: Double.TYPE, getBundle().getString(
0854: "PROP_weightx"), // NOI18N
0855: getBundle().getString("HINT_weightx"), // NOI18N
0856: null),
0857:
0858: new Property("weighty", // NOI18N
0859: Double.TYPE, getBundle().getString(
0860: "PROP_weighty"), // NOI18N
0861: getBundle().getString("HINT_weighty"), // NOI18N
0862: null),
0863:
0864: new Property("insets", // NOI18N
0865: Insets.class, getBundle().getString(
0866: "PROP_insets"), // NOI18N
0867: getBundle().getString("HINT_insets"), // NOI18N
0868: null) };
0869:
0870: // properties with editable combo box
0871: properties[0].setValue("canEditAsText", Boolean.TRUE); // NOI18N
0872: properties[1].setValue("canEditAsText", Boolean.TRUE); // NOI18N
0873: properties[2].setValue("canEditAsText", Boolean.TRUE); // NOI18N
0874: properties[3].setValue("canEditAsText", Boolean.TRUE); // NOI18N
0875: }
0876:
0877: private void reinstateProperties() {
0878: try {
0879: for (int i = 0; i < properties.length; i++) {
0880: FormProperty prop = (FormProperty) properties[i];
0881: prop.reinstateProperty();
0882: }
0883: } catch (IllegalAccessException e1) {
0884: } // should not happen
0885: catch (InvocationTargetException e2) {
0886: } // should not happen
0887: }
0888:
0889: private static Constructor getConstraintsConstructor() {
0890: if (constrConstructor == null) {
0891: try {
0892: constrConstructor = GridBagConstraints.class
0893: .getConstructor(new Class[0]);
0894: } catch (NoSuchMethodException ex) { // should not happen
0895: ex.printStackTrace();
0896: }
0897: }
0898: return constrConstructor;
0899: }
0900:
0901: // ---------
0902:
0903: /** Property implementation for GridBagLayoutConstraints. Each property
0904: * is tied to one field of GridBagConstraints. After a change in
0905: * property, updateCodeExpression is called to reflect the change in
0906: * the code.
0907: */
0908: private final class Property extends FormProperty {
0909: private Field field;
0910: private Class<? extends PropertyEditor> propertyEditorClass;
0911:
0912: Property(String name, Class type, String displayName,
0913: String shortDescription,
0914: Class<? extends PropertyEditor> propertyEditorClass) {
0915: super ("GridBagLayoutConstraints " + name, type, // NOI18N
0916: displayName, shortDescription);
0917: this .propertyEditorClass = propertyEditorClass;
0918: try {
0919: field = GridBagConstraints.class.getField(name);
0920: } catch (NoSuchFieldException ex) { // should not happen
0921: ex.printStackTrace();
0922: }
0923: }
0924:
0925: public Object getTargetValue() {
0926: try {
0927: return field.get(constraints);
0928: } catch (Exception ex) { // should not happen
0929: ex.printStackTrace();
0930: return null;
0931: }
0932: }
0933:
0934: public void setTargetValue(Object value) {
0935: try {
0936: field.set(constraints, value);
0937: } catch (Exception ex) { // should not happen
0938: ex.printStackTrace();
0939: }
0940: }
0941:
0942: @Override
0943: public boolean supportsDefaultValue() {
0944: return true;
0945: }
0946:
0947: @Override
0948: public Object getDefaultValue() {
0949: try {
0950: return field.get(defaultConstraints);
0951: } catch (Exception ex) { // should not happen
0952: ex.printStackTrace();
0953: return null;
0954: }
0955: }
0956:
0957: @Override
0958: public PropertyEditor getExpliciteEditor() {
0959: if (propertyEditorClass == null)
0960: return null;
0961: try {
0962: return propertyEditorClass.newInstance();
0963: } catch (Exception ex) { //should not happen
0964: ex.printStackTrace();
0965: return null;
0966: }
0967: }
0968:
0969: @Override
0970: protected void propertyValueChanged(Object old,
0971: Object current) {
0972: // #36932 - GridBagLayout allows max. 512 grid size
0973: if (current instanceof Integer) {
0974: String name = getName();
0975: if (((name.endsWith("gridx") || name
0976: .endsWith("gridwidth")) // NOI18N
0977: && constraints.gridx + constraints.gridwidth > 512)
0978: || ((name.endsWith("gridy") || name
0979: .endsWith("gridheight")) // NOI18N
0980: && constraints.gridy
0981: + constraints.gridheight > 512)) {
0982: boolean fire = isChangeFiring();
0983: setChangeFiring(false);
0984: try {
0985: setValue(old);
0986: } catch (Exception ex) {
0987: } // should not happen
0988: setChangeFiring(fire);
0989: return;
0990: }
0991: }
0992:
0993: if (isChangeFiring())
0994: updateCodeExpression();
0995: super .propertyValueChanged(old, current);
0996: }
0997:
0998: @Override
0999: public void setPropertyContext(
1000: org.netbeans.modules.form.FormPropertyContext ctx) { // disabling this method due to limited persistence
1001: } // capabilities (compatibility with previous versions)
1002: }
1003: }
1004:
1005: // ------------
1006: // property editors for properties of GridBagLayoutConstraints
1007:
1008: private abstract static class GridBagConstrEditor extends
1009: PropertyEditorSupport {
1010: String[] tags;
1011: Integer[] values;
1012: String[] javaInitStrings;
1013: boolean otherValuesAllowed;
1014:
1015: @Override
1016: public String[] getTags() {
1017: return tags;
1018: }
1019:
1020: @Override
1021: public String getAsText() {
1022: Object value = getValue();
1023: for (int i = 0; i < values.length; i++)
1024: if (values[i].equals(value))
1025: return tags[i];
1026:
1027: return otherValuesAllowed && value != null ? value
1028: .toString() : null;
1029: }
1030:
1031: @Override
1032: public void setAsText(String str) {
1033: for (int i = 0; i < tags.length; i++)
1034: if (tags[i].equals(str)) {
1035: setValue(values[i]);
1036: return;
1037: }
1038:
1039: if (otherValuesAllowed)
1040: try {
1041: setValue(new Integer(Integer.parseInt(str)));
1042: } catch (NumberFormatException e) {
1043: } // ignore
1044: }
1045:
1046: @Override
1047: public String getJavaInitializationString() {
1048: Object value = getValue();
1049: for (int i = 0; i < values.length; i++)
1050: if (values[i].equals(value))
1051: return javaInitStrings[i];
1052:
1053: if (!otherValuesAllowed)
1054: return javaInitStrings[0];
1055: return value != null ? value.toString() : null;
1056: }
1057: }
1058:
1059: static final class GridPosEditor extends GridBagConstrEditor {
1060:
1061: public GridPosEditor() {
1062: tags = new String[] { getBundle().getString(
1063: "VALUE_relative") // NOI18N
1064: };
1065: values = new Integer[] { new Integer(
1066: GridBagConstraints.RELATIVE) };
1067: javaInitStrings = new String[] { "java.awt.GridBagConstraints.RELATIVE" // NOI18N
1068: };
1069: otherValuesAllowed = true;
1070: }
1071: }
1072:
1073: static final class GridSizeEditor extends GridBagConstrEditor {
1074:
1075: public GridSizeEditor() {
1076: tags = new String[] {
1077: getBundle().getString("VALUE_relative"), // NOI18N
1078: getBundle().getString("VALUE_remainder") // NOI18N
1079: };
1080: values = new Integer[] {
1081: new Integer(GridBagConstraints.RELATIVE),
1082: new Integer(GridBagConstraints.REMAINDER) };
1083: javaInitStrings = new String[] {
1084: "java.awt.GridBagConstraints.RELATIVE", // NOI18N
1085: "java.awt.GridBagConstraints.REMAINDER" // NOI18N
1086: };
1087: otherValuesAllowed = true;
1088: }
1089: }
1090:
1091: static final class FillEditor extends GridBagConstrEditor {
1092: public FillEditor() {
1093: tags = new String[] {
1094: getBundle().getString("VALUE_fill_none"), // NOI18N
1095: getBundle().getString("VALUE_fill_horizontal"), // NOI18N
1096: getBundle().getString("VALUE_fill_vertical"), // NOI18N
1097: getBundle().getString("VALUE_fill_both") // NOI18N
1098: };
1099: values = new Integer[] {
1100: new Integer(GridBagConstraints.NONE),
1101: new Integer(GridBagConstraints.HORIZONTAL),
1102: new Integer(GridBagConstraints.VERTICAL),
1103: new Integer(GridBagConstraints.BOTH) };
1104: javaInitStrings = new String[] {
1105: "java.awt.GridBagConstraints.NONE", // NOI18N
1106: "java.awt.GridBagConstraints.HORIZONTAL", // NOI18N
1107: "java.awt.GridBagConstraints.VERTICAL", // NOI18N
1108: "java.awt.GridBagConstraints.BOTH" // NOI18N
1109: };
1110: otherValuesAllowed = false;
1111: }
1112: }
1113:
1114: static final class AnchorEditor extends GridBagConstrEditor {
1115: public AnchorEditor() {
1116: tags = new String[] {
1117: getBundle().getString("VALUE_anchor_center"), // NOI18N
1118: getBundle().getString("VALUE_anchor_north"), // NOI18N
1119: getBundle().getString("VALUE_anchor_northeast"), // NOI18N
1120: getBundle().getString("VALUE_anchor_east"), // NOI18N
1121: getBundle().getString("VALUE_anchor_southeast"), // NOI18N
1122: getBundle().getString("VALUE_anchor_south"), // NOI18N
1123: getBundle().getString("VALUE_anchor_southwest"), // NOI18N
1124: getBundle().getString("VALUE_anchor_west"), // NOI18N
1125: getBundle().getString("VALUE_anchor_northwest"), // NOI18N
1126: getBundle().getString("VALUE_anchor_pagestart"), // NOI18N
1127: getBundle().getString("VALUE_anchor_pageend"), // NOI18N
1128: getBundle().getString("VALUE_anchor_linestart"), // NOI18N
1129: getBundle().getString("VALUE_anchor_lineend"), // NOI18N
1130: getBundle()
1131: .getString("VALUE_anchor_firstlinestart"), // NOI18N
1132: getBundle().getString("VALUE_anchor_firstlineend"), // NOI18N
1133: getBundle().getString("VALUE_anchor_lastlinestart"), // NOI18N
1134: getBundle().getString("VALUE_anchor_lastlineend") // NOI18N
1135: };
1136: values = new Integer[] {
1137: new Integer(GridBagConstraints.CENTER),
1138: new Integer(GridBagConstraints.NORTH),
1139: new Integer(GridBagConstraints.NORTHEAST),
1140: new Integer(GridBagConstraints.EAST),
1141: new Integer(GridBagConstraints.SOUTHEAST),
1142: new Integer(GridBagConstraints.SOUTH),
1143: new Integer(GridBagConstraints.SOUTHWEST),
1144: new Integer(GridBagConstraints.WEST),
1145: new Integer(GridBagConstraints.NORTHWEST),
1146: new Integer(GridBagConstraints.PAGE_START),
1147: new Integer(GridBagConstraints.PAGE_END),
1148: new Integer(GridBagConstraints.LINE_START),
1149: new Integer(GridBagConstraints.LINE_END),
1150: new Integer(GridBagConstraints.FIRST_LINE_START),
1151: new Integer(GridBagConstraints.FIRST_LINE_END),
1152: new Integer(GridBagConstraints.LAST_LINE_START),
1153: new Integer(GridBagConstraints.LAST_LINE_END) };
1154: javaInitStrings = new String[] {
1155: "java.awt.GridBagConstraints.CENTER", // NOI18N
1156: "java.awt.GridBagConstraints.NORTH", // NOI18N
1157: "java.awt.GridBagConstraints.NORTHEAST", // NOI18N
1158: "java.awt.GridBagConstraints.EAST", // NOI18N
1159: "java.awt.GridBagConstraints.SOUTHEAST", // NOI18N
1160: "java.awt.GridBagConstraints.SOUTH", // NOI18N
1161: "java.awt.GridBagConstraints.SOUTHWEST", // NOI18N
1162: "java.awt.GridBagConstraints.WEST", // NOI18N
1163: "java.awt.GridBagConstraints.NORTHWEST", // NOI18N
1164: "java.awt.GridBagConstraints.PAGE_START", // NOI18N
1165: "java.awt.GridBagConstraints.PAGE_END", // NOI18N
1166: "java.awt.GridBagConstraints.LINE_START", // NOI18N
1167: "java.awt.GridBagConstraints.LINE_END", // NOI18N
1168: "java.awt.GridBagConstraints.FIRST_LINE_START", // NOI18N
1169: "java.awt.GridBagConstraints.FIRST_LINE_END", // NOI18N
1170: "java.awt.GridBagConstraints.LAST_LINE_START", // NOI18N
1171: "java.awt.GridBagConstraints.LAST_LINE_END" // NOI18N
1172: };
1173: otherValuesAllowed = false;
1174: }
1175: }
1176:
1177: // ------
1178: // temporary hacks for GridBagCustomizer and GridBagControlCenter
1179:
1180: static ResourceBundle getBundleHack() {
1181: return getBundle(); // from AbstractLayoutSupport
1182: }
1183:
1184: LayoutSupportContext getLayoutSupportHack() {
1185: return super.getLayoutContext();
1186: }
1187: }
|