001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.form.layoutsupport.delegates;
043:
044: import java.awt.*;
045: import java.beans.*;
046: import java.util.*;
047: import javax.swing.*;
048: import java.lang.reflect.Constructor;
049:
050: import org.openide.nodes.Node;
051:
052: import org.netbeans.modules.form.layoutsupport.*;
053: import org.netbeans.modules.form.codestructure.*;
054: import org.netbeans.modules.form.FormProperty;
055:
056: /**
057: * Support class for BoxLayout. This is an example of support for layout
058: * manager which is not a JavaBean - some general functionality from
059: * AbstractLayoutSupport must be overridden and handled differently.
060: *
061: * @author Tran Duc Trung, Tomas Pavek
062: */
063: // Expects ltr orientation of the designer
064: public class BoxLayoutSupport extends AbstractLayoutSupport {
065: private int axis = BoxLayout.LINE_AXIS;
066:
067: private FormProperty[] properties;
068:
069: private static Constructor boxLayoutConstructor;
070:
071: /** Gets the supported layout manager class - BoxLayout.
072: * @return the class supported by this delegate
073: */
074: public Class getSupportedClass() {
075: return BoxLayout.class;
076: }
077:
078: /** This method is called after a property of the layout is changed by
079: * the user. The delagate implementation may check whether the layout is
080: * valid after the change and throw PropertyVetoException if the change
081: * should be reverted.
082: * @param ev PropertyChangeEvent object describing the change
083: */
084: @Override
085: public void acceptContainerLayoutChange(PropertyChangeEvent ev)
086: throws PropertyVetoException { // accept any change, just need to update the BoxLayout instance;
087: // since it has no properties, it must be create again
088: updateLayoutInstance();
089: super .acceptContainerLayoutChange(ev);
090: }
091:
092: /** This method calculates position (index) for a component dragged
093: * over a container (or just for mouse cursor being moved over container,
094: * without any component).
095: * @param container instance of a real container over/in which the
096: * component is dragged
097: * @param containerDelegate effective container delegate of the container
098: * (for layout managers we always use container delegate instead of
099: * the container)
100: * @param component the real component being dragged; not needed here
101: * @param index position (index) of the component in its current container;
102: * not needed here
103: * @param posInCont position of mouse in the container delegate
104: * @param posInComp position of mouse in the dragged component;
105: * not needed here
106: * @return index corresponding to the position of the component in the
107: * container
108: */
109: @Override
110: public int getNewIndex(Container container,
111: Container containerDelegate, Component component,
112: int index, Point posInCont, Point posInComp) {
113: if (!(containerDelegate.getLayout() instanceof BoxLayout))
114: return -1;
115:
116: assistantParams = 0;
117: Component[] components = containerDelegate.getComponents();
118: for (int i = 0; i < components.length; i++) {
119: if (components[i] == component) {
120: assistantParams--;
121: continue;
122: }
123: Rectangle b = components[i].getBounds();
124: if ((axis == BoxLayout.X_AXIS)
125: || (axis == BoxLayout.LINE_AXIS)) {
126: if (posInCont.x < b.x + b.width / 2) {
127: assistantParams += i;
128: return i;
129: }
130: } else {
131: if (posInCont.y < b.y + b.height / 2) {
132: assistantParams += i;
133: return i;
134: }
135: }
136: }
137:
138: assistantParams += components.length;
139: return components.length;
140: }
141:
142: private int assistantParams;
143:
144: @Override
145: public String getAssistantContext() {
146: return "boxLayout"; // NOI18N
147: }
148:
149: @Override
150: public Object[] getAssistantParams() {
151: return new Object[] { Integer.valueOf(assistantParams + 1) };
152: }
153:
154: /** This method paints a dragging feedback for a component dragged over
155: * a container (or just for mouse cursor being moved over container,
156: * without any component).
157: * @param container instance of a real container over/in which the
158: * component is dragged
159: * @param containerDelegate effective container delegate of the container
160: * (for layout managers we always use container delegate instead of
161: * the container)
162: * @param component the real component being dragged, not needed here
163: * @param newConstraints component layout constraints to be presented;
164: * not used for BoxLayout
165: * @param newIndex component's index position to be presented
166: * @param g Graphics object for painting (with color and line style set)
167: * @return whether any feedback was painted (true in this case)
168: */
169: @Override
170: public boolean paintDragFeedback(Container container,
171: Container containerDelegate, Component component,
172: LayoutConstraints newConstraints, int newIndex, Graphics g) {
173: if (!(containerDelegate.getLayout() instanceof BoxLayout))
174: return false;
175:
176: Component[] components = containerDelegate.getComponents();
177: Rectangle rect;
178:
179: if ((components.length == 0)
180: || ((components.length == 1) && (components[0] == component))) {
181: Insets ins = containerDelegate.getInsets();
182: rect = (axis == BoxLayout.X_AXIS || axis == BoxLayout.LINE_AXIS) ? new Rectangle(
183: ins.left, ins.top
184: + (containerDelegate.getHeight() - ins.top
185: - ins.bottom - 20) / 2, 30, 20)
186: : new Rectangle(ins.left
187: + (containerDelegate.getWidth() - ins.left
188: - ins.right - 30) / 2, ins.top, 30,
189: 20);
190: } else if (newIndex < 0 || newIndex >= components.length) {
191: Component comp = components[components.length - 1];
192: if (comp == component) {
193: comp = components[components.length - 2];
194: }
195: Rectangle b = comp.getBounds();
196: rect = (axis == BoxLayout.X_AXIS || axis == BoxLayout.LINE_AXIS) ? new Rectangle(
197: b.x + b.width - 10, b.y, 20, b.height)
198: : new Rectangle(b.x, b.y + b.height - 10, b.width,
199: 20);
200: } else {
201: Rectangle b = components[newIndex].getBounds();
202: rect = (axis == BoxLayout.X_AXIS || axis == BoxLayout.LINE_AXIS) ? new Rectangle(
203: b.x - 10, b.y, 20, b.height)
204: : new Rectangle(b.x, b.y - 10, b.width, 20);
205: }
206:
207: g.drawRect(rect.x, rect.y, rect.width, rect.height);
208:
209: return true;
210: }
211:
212: /** Sets up the layout (without adding components) on a real container,
213: * according to the internal metadata representation. This method must
214: * override AbstractLayoutSupport because BoxLayout instance cannot
215: * be used universally - new instance must be created for each container.
216: * @param container instance of a real container to be set
217: * @param containerDelegate effective container delegate of the container;
218: * for layout managers we always use container delegate instead of
219: * the container
220: */
221: @Override
222: public void setLayoutToContainer(Container container,
223: Container containerDelegate) {
224: containerDelegate.setLayout(cloneLayoutInstance(container,
225: containerDelegate));
226: }
227:
228: @Override
229: public void addComponentsToContainer(Container container,
230: Container containerDelegate, Component[] components,
231: int index) {
232: // Issue 63955 and JDK bug 4294758
233: ((LayoutManager2) containerDelegate.getLayout())
234: .invalidateLayout(containerDelegate);
235: super .addComponentsToContainer(container, containerDelegate,
236: components, index);
237: }
238:
239: // ------------
240:
241: /** Creates a default instance of LayoutManager (for internal use).
242: * This method must override AbstractLayoutSupport because BoxLayout is not
243: * a bean (so it cannot be created automatically).
244: * @return new instance of BoxLayout
245: */
246: @Override
247: protected LayoutManager createDefaultLayoutInstance() {
248: return new BoxLayout(new JPanel(), BoxLayout.LINE_AXIS);
249: }
250:
251: /** Cloning method - creates a clone of the reference LayoutManager
252: * instance (for external use). This method must override
253: * AbstractLayoutSupport because BoxLayout is not a bean (so it cannot be
254: * cloned automatically).
255: * @param container instance of a real container in whose container
256: * delegate the layout manager will be probably used
257: * @param containerDelegate effective container delegate of the container
258: * @return cloned instance of BoxLayout
259: */
260: @Override
261: protected LayoutManager cloneLayoutInstance(Container container,
262: Container containerDelegate) {
263: return new BoxLayout(containerDelegate, axis);
264: }
265:
266: /** This method is to read the layout manager bean code (i.e. code for
267: * constructor and properties). As the BoxLayout is not a bean, this
268: * method must override AbstractLayoutSupport.
269: * @param layoutExp CodeExpressin of the layout manager
270: * @param layoutCode CodeGroup to be filled with relevant initialization
271: * code; not needed here because BoxLayout is represented only by
272: * a single constructor code expression and no statements
273: */
274: @Override
275: protected void readInitLayoutCode(CodeExpression layoutExp,
276: CodeGroup layoutCode) {
277: CodeExpression[] params = layoutExp.getOrigin()
278: .getCreationParameters();
279: if (params.length == 2) {
280: FormCodeSupport.readPropertyExpression(params[1],
281: getProperties()[0], false);
282: updateLayoutInstance();
283: }
284: }
285:
286: /** Creates code structures for a new layout manager (opposite to
287: * readInitLayoutCode). As the BoxLayout is not a bean, this method must
288: * override from AbstractLayoutSupport.
289: * @param layoutCode CodeGroup to be filled with relevant
290: * initialization code; not needed here because BoxLayout is
291: * represented only by a single constructor code expression and
292: * no statements
293: * @return new CodeExpression representing the BoxLayout
294: */
295: @Override
296: protected CodeExpression createInitLayoutCode(CodeGroup layoutCode) {
297: CodeStructure codeStructure = getCodeStructure();
298:
299: CodeExpression[] params = new CodeExpression[2];
300: params[0] = getLayoutContext()
301: .getContainerDelegateCodeExpression();
302: params[1] = codeStructure.createExpression(FormCodeSupport
303: .createOrigin(getProperties()[0]));
304:
305: return codeStructure.createExpression(
306: getBoxLayoutConstructor(), params);
307: }
308:
309: /** Since BoxLayout is not a bean, we must specify its properties
310: * explicitly. This method is called from getPropertySets() implementation
311: * to obtain the default property set for the layout (assuming there's only
312: * one property set). So it woul be also possible to override (more
313: * generally) getPropertySets() instead.
314: * @return array of properties of the layout manager
315: */
316: @Override
317: protected FormProperty[] getProperties() {
318: if (properties == null) {
319: // we cannot use RADProperty because "axis" is not a real
320: // bean property - we must create a special FormProperty
321: properties = new FormProperty[1];
322:
323: properties[0] = new FormProperty("axis", // NOI18N
324: Integer.TYPE, getBundle().getString("PROP_axis"), // NOI18N
325: getBundle().getString("HINT_axis")) // NOI18N
326: {
327: public Object getTargetValue() {
328: return new Integer(axis);
329: }
330:
331: public void setTargetValue(Object value) {
332: int ax = ((Integer) value).intValue();
333: if (ax == BoxLayout.X_AXIS
334: || ax == BoxLayout.Y_AXIS
335: || ax == BoxLayout.LINE_AXIS
336: || ax == BoxLayout.PAGE_AXIS) {
337: axis = ax;
338: }
339: }
340:
341: @Override
342: public boolean supportsDefaultValue() {
343: return true;
344: }
345:
346: @Override
347: public Object getDefaultValue() {
348: return new Integer(BoxLayout.LINE_AXIS);
349: }
350:
351: @Override
352: public PropertyEditor getExpliciteEditor() {
353: return new BoxAxisEditor();
354: }
355: };
356: // [!!]
357: // properties[0].setPropertyContext(
358: // new FormPropertyContext.DefaultImpl(getContainer().getFormModel()));
359: }
360:
361: return properties;
362: }
363:
364: /** Method to obtain just one propetry of given name. Must be override
365: * AbstractLayoutSupport because alternative properties are used for
366: * BoxLayout (see getProperties method)
367: * @return layout property of given name
368: */
369: @Override
370: protected Node.Property getProperty(String propName) {
371: return "axis".equals(propName) ? getProperties()[0] : null; // NOI18N
372: }
373:
374: // --------
375:
376: private static Constructor getBoxLayoutConstructor() {
377: if (boxLayoutConstructor == null) {
378: try {
379: boxLayoutConstructor = BoxLayout.class
380: .getConstructor(new Class[] { Container.class,
381: Integer.TYPE });
382: } catch (NoSuchMethodException ex) { // should not happen
383: ex.printStackTrace();
384: }
385: }
386: return boxLayoutConstructor;
387: }
388:
389: // --------------
390:
391: /** PropertyEditor for axis property of BoxLayoutSupport.
392: */
393: public static final class BoxAxisEditor extends
394: PropertyEditorSupport {
395: private final String[] tags = {
396: getBundle().getString("VALUE_axis_line"), // NOI18N
397: getBundle().getString("VALUE_axis_page"), // NOI18N
398: getBundle().getString("VALUE_axis_x"), // NOI18N
399: getBundle().getString("VALUE_axis_y") // NOI18N
400: };
401: private final Integer[] values = {
402: new Integer(BoxLayout.LINE_AXIS),
403: new Integer(BoxLayout.PAGE_AXIS),
404: new Integer(BoxLayout.X_AXIS),
405: new Integer(BoxLayout.Y_AXIS) };
406: private final String[] javaInitStrings = {
407: "javax.swing.BoxLayout.LINE_AXIS", // NOI18N
408: "javax.swing.BoxLayout.PAGE_AXIS", // NOI18N
409: "javax.swing.BoxLayout.X_AXIS", // NOI18N
410: "javax.swing.BoxLayout.Y_AXIS" // NOI18N
411: };
412:
413: @Override
414: public String[] getTags() {
415: return tags;
416: }
417:
418: @Override
419: public String getAsText() {
420: Object value = getValue();
421: for (int i = 0; i < values.length; i++) {
422: if (values[i].equals(value))
423: return tags[i];
424: }
425: return null;
426: }
427:
428: @Override
429: public void setAsText(String str) {
430: for (int i = 0; i < values.length; i++) {
431: if (tags[i].equals(str)) {
432: setValue(values[i]);
433: break;
434: }
435: }
436: }
437:
438: @Override
439: public String getJavaInitializationString() {
440: Object value = getValue();
441: for (int i = 0; i < values.length; i++)
442: if (values[i].equals(value))
443: return javaInitStrings[i];
444: return null;
445: }
446: }
447: }
|