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.lang.reflect.Method;
047: import javax.swing.JButton;
048: import javax.swing.JSplitPane;
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: * Dedicated layout support class for JSplitPane.
058: * @author Tomas Pavek
059: */
060:
061: public class JSplitPaneSupport extends AbstractLayoutSupport {
062:
063: private static Method setLeftComponentMethod;
064: private static Method setRightComponentMethod;
065: private static Method setTopComponentMethod;
066: private static Method setBottomComponentMethod;
067:
068: private static final String LEFT_TOP_BUTTON = "cp_left_top_button"; // NOI18N
069: private static final String RIGHT_BOTTOM_BUTTON = "cp_right_bottom_button"; // NOI18N
070:
071: /** Gets the supported layout manager class - JSplitPane.
072: * @return the class supported by this delegate
073: */
074: public Class getSupportedClass() {
075: return JSplitPane.class;
076: }
077:
078: /** This method calculates layout constraints for a component dragged
079: * over a container (or just for mouse cursor being moved over container,
080: * without any component).
081: * @param container instance of a real container over/in which the
082: * component is dragged
083: * @param containerDelegate effective container delegate of the container
084: * @param component the real component being dragged, not needed here
085: * @param index position (index) of the component in its container;
086: * not needed here
087: * @param posInCont position of mouse in the container
088: * @param posInComp position of mouse in the dragged component; not needed
089: * @return new LayoutConstraints object corresponding to the position of
090: * the component in the container
091: */
092: @Override
093: public LayoutConstraints getNewConstraints(Container container,
094: Container containerDelegate, Component component,
095: int index, Point posInCont, Point posInComp) {
096: if (!(container instanceof JSplitPane))
097: return null;
098:
099: JSplitPane splitPane = (JSplitPane) container;
100: Dimension sz = splitPane.getSize();
101: int orientation = splitPane.getOrientation();
102:
103: JButton left = (JButton) splitPane
104: .getClientProperty(LEFT_TOP_BUTTON);
105: JButton right = (JButton) splitPane
106: .getClientProperty(RIGHT_BOTTOM_BUTTON);
107:
108: if ((left == null && right == null)
109: || (left != null && right != null)) {
110: String freePosition;
111: if (orientation == JSplitPane.HORIZONTAL_SPLIT) {
112: if (posInCont.x <= sz.width / 2)
113: freePosition = JSplitPane.LEFT;
114: else
115: freePosition = JSplitPane.RIGHT;
116: } else {
117: if (posInCont.y <= sz.height / 2)
118: freePosition = JSplitPane.TOP;
119: else
120: freePosition = JSplitPane.BOTTOM;
121: }
122: assistantParams = freePosition;
123: return new SplitConstraints(freePosition);
124: }
125:
126: assistantParams = findFreePosition();
127: return new SplitConstraints(assistantParams);
128: }
129:
130: private String assistantParams;
131:
132: @Override
133: public String getAssistantContext() {
134: return "splitPaneLayout"; // NOI18N
135: }
136:
137: @Override
138: public Object[] getAssistantParams() {
139: return new Object[] { assistantParams };
140: }
141:
142: /** This method paints a dragging feedback for a component dragged over
143: * a container (or just for mouse cursor being moved over container,
144: * without any component).
145: * @param container instance of a real container over/in which the
146: * component is dragged
147: * @param containerDelegate effective container delegate of the container
148: * @param component the real component being dragged; not needed here
149: * @param newConstraints component layout constraints to be presented
150: * @param newIndex component's index position to be presented; not needed
151: * @param g Graphics object for painting (with color and line style set)
152: * @return whether any feedback was painted (true in this case)
153: */
154: @Override
155: public boolean paintDragFeedback(Container container,
156: Container containerDelegate, Component component,
157: LayoutConstraints newConstraints, int newIndex, Graphics g) {
158: if (!(container instanceof JSplitPane))
159: return false;
160:
161: String position = (String) newConstraints
162: .getConstraintsObject();
163: if (position == null)
164: return false;
165:
166: JSplitPane splitPane = (JSplitPane) container;
167: int orientation = splitPane.getOrientation();
168:
169: Dimension sz = splitPane.getSize();
170: Insets insets = container.getInsets();
171: sz.width -= insets.left + insets.right;
172: sz.height -= insets.top + insets.bottom;
173:
174: Rectangle rect = new Rectangle(insets.left, insets.top,
175: sz.width, sz.height);
176:
177: if (orientation == JSplitPane.HORIZONTAL_SPLIT) {
178: Component left = splitPane.getLeftComponent();
179: Component right = splitPane.getRightComponent();
180:
181: if (position == JSplitPane.LEFT) {
182: if ((right == null) || (right == component)) {
183: rect.width = sz.width / 2;
184: } else {
185: rect.width = right.getBounds().x - rect.x;
186: }
187: } else {
188: if ((left == null) || (left == component)) {
189: rect.x = insets.left + sz.width / 2;
190: rect.width = sz.width - rect.x;
191: } else {
192: rect.x = left.getBounds().x
193: + left.getBounds().width;
194: rect.width = sz.width - rect.x;
195: }
196: }
197: } else {
198: Component top = splitPane.getTopComponent();
199: Component bottom = splitPane.getBottomComponent();
200:
201: if (position == JSplitPane.TOP) {
202: if ((bottom == null) || (bottom == component)) {
203: rect.height /= 2;
204: } else {
205: rect.height = bottom.getBounds().y - rect.y;
206: }
207: } else {
208: if ((top == null) || (top == component)) {
209: rect.y = insets.top + sz.height / 2;
210: rect.height = sz.height - rect.y;
211: } else {
212: rect.y = top.getBounds().y + top.getBounds().height;
213: rect.height = sz.height - rect.y;
214: }
215: }
216: }
217: g.drawRect(rect.x, rect.y, rect.width, rect.height);
218: return true;
219: }
220:
221: /** Adds real components to given container (according to layout
222: * constraints stored for the components).
223: * @param container instance of a real container to be added to
224: * @param containerDelegate effective container delegate of the container
225: * @param components components to be added
226: * @param index position at which to add the components to container
227: */
228: @Override
229: public void addComponentsToContainer(Container container,
230: Container containerDelegate, Component[] components,
231: int index) {
232: if (!(container instanceof JSplitPane))
233: return;
234:
235: for (int i = 0; i < components.length; i++) {
236: JSplitPane splitPane = (JSplitPane) container;
237:
238: int descPos = convertPosition(getConstraints(i + index));
239: if (descPos == 0) {
240: if (splitPane.getClientProperty(LEFT_TOP_BUTTON) == null) {
241: // store the defaul swing button, so we can fall back to it
242: // if component[i] will be removed later...
243: splitPane.putClientProperty(LEFT_TOP_BUTTON,
244: splitPane.getLeftComponent());
245: }
246: splitPane.setLeftComponent(components[i]);
247: } else if (descPos == 1) {
248: if (splitPane.getClientProperty(RIGHT_BOTTOM_BUTTON) == null) {
249: // store the defaul swing button, so we can fall back to it
250: // if component[i] will be removed later...
251: splitPane.putClientProperty(RIGHT_BOTTOM_BUTTON,
252: splitPane.getRightComponent());
253: }
254: splitPane.setRightComponent(components[i]);
255: }
256:
257: }
258: }
259:
260: /** Removes a real component from a real container.
261: * @param container instance of a real container
262: * @param containerDelegate effective container delegate of the container
263: * @param component component to be removed
264: * @return whether it was possible to remove the component (some containers
265: * may not support removing individual components reasonably)
266: */
267: @Override
268: public boolean removeComponentFromContainer(Container container,
269: Container containerDelegate, Component component) {
270: if (!(containerDelegate instanceof JSplitPane)) {
271: return false; // should not happen
272: }
273:
274: JSplitPane splitPane = (JSplitPane) containerDelegate;
275:
276: if (component == splitPane.getLeftComponent()) {
277: if (super .removeComponentFromContainer(container,
278: containerDelegate, component)) {
279: JButton left = (JButton) splitPane
280: .getClientProperty(LEFT_TOP_BUTTON);
281: if (left != null) {
282: // fall back to the default swing setting
283: splitPane.setLeftComponent(left);
284: splitPane.putClientProperty(LEFT_TOP_BUTTON, null);
285: }
286: return true;
287: }
288: } else if (component == splitPane.getRightComponent()) {
289: if (super .removeComponentFromContainer(container,
290: containerDelegate, component)) {
291: JButton right = (JButton) splitPane
292: .getClientProperty(RIGHT_BOTTOM_BUTTON);
293: if (right != null) {
294: // fall back to the default swing setting
295: splitPane.setRightComponent(right);
296: splitPane.putClientProperty(RIGHT_BOTTOM_BUTTON,
297: null);
298: }
299: return true;
300: }
301: }
302:
303: return false;
304: }
305:
306: /** Removes all components from given real container.
307: * @param container instance of a real container to be cleared
308: * @param containerDelegate effective container delegate of the container
309: * (e.g. like content pane of JFrame)
310: * @return whether it was possible to clear the container (some containers
311: * may not support this)
312: */
313: @Override
314: public boolean clearContainer(Container container,
315: Container containerDelegate) {
316:
317: // don't remove components which are a default part of JSplitPane
318:
319: JSplitPane splitPane = (JSplitPane) container;
320: JButton left = (JButton) splitPane
321: .getClientProperty(LEFT_TOP_BUTTON);
322: JButton right = (JButton) splitPane
323: .getClientProperty(RIGHT_BOTTOM_BUTTON);
324:
325: if (left != null) {
326: // left/top component has already been set -> remove it
327: removeComponentFromContainer(container, containerDelegate,
328: splitPane.getLeftComponent());
329: }
330: if (right != null) {
331: // right/bottom component has already been set -> remove it
332: removeComponentFromContainer(container, containerDelegate,
333: splitPane.getRightComponent());
334: }
335:
336: return true;
337: }
338:
339: // ------
340:
341: /** This method is used for scanning code structures and recognizing
342: * components added to containers and their constraints. It's called from
343: * initialize method. When a relevant code statement is found, then the
344: * CodeExpression of component is get and added to component, and also the
345: * layout constraints information is read.
346: * @param statement CodeStatement to be tested if it contains relevant code
347: * @param componentCode CodeGroup to be filled with all component code
348: * @return CodeExpression representing found component; null if the
349: * statement is not relevant
350: */
351: @Override
352: protected CodeExpression readComponentCode(CodeStatement statement,
353: CodeGroup componentCode) {
354: CodeExpression[] params = statement.getStatementParameters();
355: if (params.length != 1)
356: return null;
357:
358: String position = null;
359: Object connectingObject = statement.getMetaObject();
360: if (getSimpleAddMethod().equals(connectingObject)) {
361: position = getComponentCount() == 0 ? JSplitPane.LEFT
362: : JSplitPane.RIGHT;
363: } else if (getSetLeftComponentMethod().equals(connectingObject)) {
364: position = JSplitPane.LEFT;
365: } else if (getSetRightComponentMethod()
366: .equals(connectingObject)) {
367: position = JSplitPane.RIGHT;
368: } else if (getSetTopComponentMethod().equals(connectingObject)) {
369: position = JSplitPane.TOP;
370: } else if (getSetBottomComponentMethod().equals(
371: connectingObject)) {
372: position = JSplitPane.BOTTOM;
373: }
374:
375: SplitConstraints constr = new SplitConstraints(position);
376: getConstraintsList().add(constr);
377:
378: componentCode.addStatement(statement);
379:
380: return params[0];
381: }
382:
383: /** Creates code for a component added to the layout (opposite to
384: * readComponentCode method).
385: * @param componentCode CodeGroup to be filled with complete component code
386: * (code for initializing the layout constraints and adding the
387: * component to the layout)
388: * @param componentExpression CodeExpression object representing component
389: * @param index position of the component in the layout
390: */
391: @Override
392: protected void createComponentCode(CodeGroup componentCode,
393: CodeExpression componentExpression, int index) {
394: LayoutConstraints constr = getConstraints(index);
395: if (!(constr instanceof SplitConstraints))
396: return; // should not happen
397:
398: ((SplitConstraints) constr).createComponentCode(componentCode,
399: getLayoutContext().getContainerCodeExpression(),
400: componentExpression);
401: }
402:
403: /** This method is called to get a default component layout constraints
404: * metaobject in case it is not provided (e.g. in addComponents method).
405: * @return the default LayoutConstraints object for the supported layout;
406: * null if no component constraints are used
407: */
408: @Override
409: protected LayoutConstraints createDefaultConstraints() {
410: return new SplitConstraints(findFreePosition());
411: }
412:
413: // ------------
414:
415: private int convertPosition(LayoutConstraints desc) {
416: if (desc != null) {
417: Object position = desc.getConstraintsObject();
418: if (JSplitPane.LEFT.equals(position)
419: || JSplitPane.TOP.equals(position))
420: return 0;
421: if (JSplitPane.RIGHT.equals(position)
422: || JSplitPane.BOTTOM.equals(position))
423: return 1;
424: }
425: return -1;
426: }
427:
428: private String findFreePosition() {
429: int leftTop = 0, rightBottom = 0;
430: int orientation = JSplitPane.HORIZONTAL_SPLIT;
431:
432: for (int i = 0, n = getComponentCount(); i < n; i++) {
433: LayoutConstraints constraints = getConstraints(i);
434: if (!(constraints instanceof SplitConstraints))
435: continue;
436:
437: int constrPos = convertPosition(constraints);
438: if (constrPos == 0)
439: leftTop++;
440: else if (constrPos == 1)
441: rightBottom++;
442: }
443:
444: if (leftTop == 0 || leftTop < rightBottom)
445: return orientation == JSplitPane.HORIZONTAL_SPLIT ? JSplitPane.LEFT
446: : JSplitPane.TOP;
447: else
448: return orientation == JSplitPane.HORIZONTAL_SPLIT ? JSplitPane.RIGHT
449: : JSplitPane.BOTTOM;
450: }
451:
452: // --------
453:
454: private static Method getSetLeftComponentMethod() {
455: if (setLeftComponentMethod == null)
456: setLeftComponentMethod = getAddMethod("setLeftComponent"); // NOI18N
457: return setLeftComponentMethod;
458: }
459:
460: private static Method getSetRightComponentMethod() {
461: if (setRightComponentMethod == null)
462: setRightComponentMethod = getAddMethod("setRightComponent"); // NOI18N
463: return setRightComponentMethod;
464: }
465:
466: private static Method getSetTopComponentMethod() {
467: if (setTopComponentMethod == null)
468: setTopComponentMethod = getAddMethod("setTopComponent"); // NOI18N
469: return setTopComponentMethod;
470: }
471:
472: private static Method getSetBottomComponentMethod() {
473: if (setBottomComponentMethod == null)
474: setBottomComponentMethod = getAddMethod("setBottomComponent"); // NOI18N
475: return setBottomComponentMethod;
476: }
477:
478: private static Method getAddMethod(String name) {
479: try {
480: return JSplitPane.class.getMethod(name,
481: new Class[] { Component.class });
482: } catch (NoSuchMethodException ex) { // should not happen
483: ex.printStackTrace();
484: }
485: return null;
486: }
487:
488: // -----------
489:
490: /** LayoutConstraints implementation holding component position in
491: * JSplitPane.
492: */
493: public static class SplitConstraints implements LayoutConstraints {
494: private String position;
495:
496: private Node.Property[] properties;
497:
498: private CodeExpression containerExpression;
499: private CodeExpression componentExpression;
500: private CodeGroup componentCode;
501:
502: public SplitConstraints(String position) {
503: this .position = position;
504: }
505:
506: public Node.Property[] getProperties() {
507: if (properties == null) {
508: properties = new Node.Property[] { new FormProperty(
509: "SplitConstraints splitPosition", // NOI18N
510: String.class, getBundle().getString(
511: "PROP_splitPos"), // NOI18N
512: getBundle().getString("HINT_splitPos")) // NOI18N
513: {
514: public Object getTargetValue() {
515: return position;
516: }
517:
518: public void setTargetValue(Object value) {
519: position = (String) value;
520: }
521:
522: @Override
523: public PropertyEditor getExpliciteEditor() {
524: return new SplitPositionEditor();
525: }
526:
527: @Override
528: protected void propertyValueChanged(Object old,
529: Object current) {
530: if (isChangeFiring())
531: updateCode();
532: super .propertyValueChanged(old, current);
533: }
534:
535: @Override
536: public void setPropertyContext(
537: org.netbeans.modules.form.FormPropertyContext ctx) { // disabling this method due to limited persistence
538: } // capabilities (compatibility with previous versions)
539: } };
540: properties[0].setValue("NOI18N", Boolean.TRUE); // NOI18N
541: }
542:
543: return properties;
544: }
545:
546: public Object getConstraintsObject() {
547: return position;
548: }
549:
550: public LayoutConstraints cloneConstraints() {
551: return new SplitConstraints(position);
552: }
553:
554: private void createComponentCode(CodeGroup compCode,
555: CodeExpression contExp, CodeExpression compExp) {
556: componentCode = compCode;
557: containerExpression = contExp;
558: componentExpression = compExp;
559: updateCode();
560: }
561:
562: private void updateCode() {
563: if (componentCode == null)
564: return;
565:
566: CodeStructure.removeStatements(componentCode
567: .getStatementsIterator());
568: componentCode.removeAll();
569:
570: Method addMethod;
571: if (JSplitPane.LEFT.equals(position))
572: addMethod = getSetLeftComponentMethod();
573: else if (JSplitPane.RIGHT.equals(position))
574: addMethod = getSetRightComponentMethod();
575: else if (JSplitPane.TOP.equals(position))
576: addMethod = getSetTopComponentMethod();
577: else if (JSplitPane.BOTTOM.equals(position))
578: addMethod = getSetBottomComponentMethod();
579: else
580: return;
581:
582: componentCode.addStatement(CodeStructure.createStatement(
583: containerExpression, addMethod,
584: new CodeExpression[] { componentExpression }));
585: }
586: }
587:
588: static class SplitPositionEditor extends PropertyEditorSupport {
589: private final String[] values = { JSplitPane.LEFT,
590: JSplitPane.RIGHT, JSplitPane.TOP, JSplitPane.BOTTOM };
591:
592: @Override
593: public String[] getTags() {
594: return values;
595: }
596:
597: @Override
598: public String getAsText() {
599: return (String) getValue();
600: }
601:
602: @Override
603: public void setAsText(String str) {
604: for (int i = 0; i < values.length; i++)
605: if (str.equals(values[i])) {
606: setValue(str);
607: break;
608: }
609: }
610: }
611:
612: }
|