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-2007 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.xml.xam.ui.layout;
043:
044: import java.awt.Component;
045: import java.awt.Container;
046: import java.awt.Dimension;
047: import java.awt.Insets;
048: import java.awt.LayoutManager2;
049: import java.awt.Rectangle;
050: import javax.swing.JComponent;
051: import javax.swing.JPanel;
052: import javax.swing.JScrollBar;
053: import javax.swing.JScrollPane;
054:
055: /**
056: * SplitterLayout is a layout manager that will layout a container holding
057: * other components and SplitterBars.
058: *
059: * <p>Each component added to a container to be laid out using SplitterLayout
060: * must provide a String containing a "weight" for the component. This
061: * weight will be used to determine the initial spacing of all components
062: * being laid out. The weight numbers are arbitrary integers. The
063: * amount of space initially allocated for a component is
064: * <pre>
065: * (wc/wt) * (size-insets-splitterSize)
066: * </pre>
067: * <p>where
068: * <dl>
069: * <dt>wc
070: * <dd>the weight number for the component
071: * <dt>wt
072: * <dd>the total weight of all visible components in the container
073: * <dt>size
074: * <dd>the space free to display the components
075: * <dt>insets
076: * <dd>space used by insets in the container
077: * <dt>splitterSize
078: * <dd>amount of space needed to display SplitterBars
079: * </dl>
080: *
081: * <p>If the container being laid out holds no SplitterBars, SplitterLayout
082: * acts like a relational-weight layout manager. All components are always
083: * laid out based on their proportionate weights.
084: *
085: * <p>If the container being laid out holds some SplitterBars, SplitterLayout
086: * will initially size all non JSplitterBar components based on their weights.
087: * Any succesive layouts are computed strictly on the locations of the
088: * SplitterBars.
089: *
090: * <p>SplitterLayout can be oriented Horizontally or Vertically. Any SpliterBars
091: * placed in the container will automatically be oriented.
092: *
093: * <p>If a JSplitterBar has been modified (adding components to it) you will
094: * need to add JSplitterSpace components to it. See JSplitterBar for more
095: * details.
096: *
097: * <p><b>Known Problems</b>:
098: * <ul>
099: * <li>If there are any SplitterBars contained in the container,
100: * it is best to have them between <u>every</u> non-JSplitterBar.
101: * Otherwise, once SplitterBars are moved, some components will
102: * use their proportional size while others will use the
103: * JSplitterBar positions. (Non-Splitterbars will check the next
104: * component to see if it's a JSplitterBar. If it's not, it uses
105: * its proportional size.) This may eventually be changed...
106: * <li>Results of adding new SplitterBars to an existing (and user-
107: * interacted) SplitterLayout-laid container might be a bit
108: * unpredictable. The safest way to ensure the container is laid
109: * out correctly would be to explicitly set all pre-existing
110: * JSplitterBar positions to (0,0). This will cause the relational
111: * layout algorithm to take effect.
112: * </ul>
113: *
114: * <p>Use this code at your own risk! MageLang Institute is not
115: * responsible for any damage caused directly or indirctly through
116: * use of this code.
117: * <p><p>
118: * <b>SOFTWARE RIGHTS</b>
119: * <p>
120: * MageLang support classes, version 1.0, MageLang Institute
121: * <p>
122: * We reserve no legal rights to this code--it is fully in the
123: * public domain. An individual or company may do whatever
124: * they wish with source code distributed with it, including
125: * including the incorporation of it into commerical software.
126: *
127: * <p>However, this code cannot be sold as a standalone product.
128: * <p>
129: * We encourage users to develop software with this code. However,
130: * we do ask that credit is given to us for developing it
131: * By "credit", we mean that if you use these components or
132: * incorporate any source code into one of your programs
133: * (commercial product, research project, or otherwise) that
134: * you acknowledge this fact somewhere in the documentation,
135: * research report, etc... If you like these components and have
136: * developed a nice tool with the output, please mention that
137: * you developed it using these components. In addition, we ask that
138: * the headers remain intact in our source code. As long as these
139: * guidelines are kept, we expect to continue enhancing this
140: * system and expect to make other tools available as they are
141: * completed.
142: * <p>
143: * The MageLang Support Classes Gang:
144: * @version MageLang Support Classes 1.0, MageLang Insitute, 1997
145: * @author <a href="http:www.scruz.net/~thetick">Scott Stanchfield</a>, <a href=http://www.MageLang.com>MageLang Institute</a>
146: * @see JSplitterBar
147: * @see JSplitterSpace
148: *
149: * @author Jeri Lockhart - jeri.lockhart@sun.com
150: * Modified for use in the NbColumnView widget.
151: * When the user moves the splitter bar to the left, the column that is adjacent to the
152: * left maintains its minimum size.
153: * When the user moves the splitter bar to the right, the columns to the right of the
154: * splitter bar, maintain their widths.
155: *
156: * layoutComponent() - can be called when the components in the container
157: * do not yet have their bounds set. In this case, use the component's
158: * preferred size. JSplitterBar can set the bounds of the components
159: * when the user drags a splitter bar. If the bounds for a component
160: * are set, use this size.
161: *
162: * checkLayoutSize() - is called for both preferredLayoutSize() and
163: * minimumLayoutSize(). To calculate the width of the layout, uses the
164: * actual width of the component, if present, or uses the preferred width
165: * of the component.
166: *
167: */
168: public class SplitterLayout implements LayoutManager2,
169: java.io.Serializable {
170: /** Aligns components vertically -- SplitterBars will move up/down */
171: public static final int VERTICAL = 0;
172: /** Aligns components horizontally -- SplitterBars will move left-right */
173: public static final int HORIZONTAL = 1;
174:
175: static JSplitterBar dragee;
176:
177: private int lastW = -1, lastH = -1;
178: private boolean newComponentAdded;
179:
180: private static final long serialVersionUID = -8658291919501921765L;
181: private boolean fill = true; // false - use preferred size,
182: // instead of weights
183: private Dimension originalPreferredSize;
184:
185: public SplitterLayout() {
186: }
187:
188: /** Create a new SplitterLayout
189: * @param orientation -- VERTICAL or HORIZONTAL
190: * @param fill - expand to fill target or use preferred size of components
191: */
192: public SplitterLayout(boolean fill) {
193: setFill(fill);
194: }
195:
196: /** Adds a component w/ constraints to the layout. This should only
197: * be called by java.awt.Container's add method.
198: */
199: public final void addLayoutComponent(Component comp,
200: Object constraints) {
201: // //System.out.println("addLayoutContainer(Component, Object) comp " + comp + ", constraints " + constraints);
202: if (constraints == null)
203: constraints = "1";
204: if (constraints instanceof Integer) {
205: newComponentAdded = true;
206: } else
207: addLayoutComponent((String) constraints, comp);
208: }
209:
210: /** Adds a component w/ a String constraint to the layout. This should
211: * only be called by java.awt.Container's add method.
212: */
213: public final void addLayoutComponent(String name, Component comp) {
214: // //System.out.println("addLayoutComponent(String, Component) name " + name + ", comp "+ comp);
215: newComponentAdded = true;
216:
217: }
218:
219: // preferred and min layout size
220: public final Dimension checkLayoutSize(Container target,
221: boolean getPrefSize) {
222: // //System.out.println("checkLayoutSize getPrefSize: " + getPrefSize);
223: Dimension dim = new Dimension(0, 0);
224: Component c[] = target.getComponents();
225:
226: Dimension d = null;
227: for (int i = 0; i < c.length; i++) {
228: if (c[i].isVisible()) {
229: if (getPrefSize || (c[i] instanceof JSplitterBar)) {
230: d = c[i].getPreferredSize();
231: } else {
232: d = c[i].getMinimumSize();
233: }
234: dim.height = Math.max(d.height, dim.height);
235: dim.width += d.width;
236: // //System.out.println("checkLayoutSize comp #" + i + " d.height: " + d.height + ", dim.height: " + dim.height);
237: // //System.out.println("checkLayoutSize comp #" + i + " d.width: " + d.width + ", dim.width: " + dim.width);
238: }
239: }
240:
241: Insets insets = target.getInsets();
242: // //System.out.println("checkLayoutSize insets " + insets);
243: dim.width += insets.left + insets.right;
244: dim.height += insets.top + insets.bottom;
245: // //System.out.println("checkLayoutSize returning dim " + dim);
246: return dim;
247: }
248:
249: /** Tells the caller that we prefer to be centered */
250: public final float getLayoutAlignmentX(Container parent) {
251: return 0.5f;
252: }
253:
254: /** Tells the caller that we prefer to be centered */
255: public final float getLayoutAlignmentY(Container parent) {
256: return 0.5f;
257: }
258:
259: /** Does not have any effect (overridden to null the effect) */
260: public final void invalidateLayout(Container target) {
261: }
262:
263: /** Lays out the components in the specified container by telling
264: * them what their size will be
265: */
266: public final void layoutContainer(Container target) {
267: // //System.out.println("layoutContainer start");
268: //System.out.println("layoutContainer target start preferred size " + target.getPreferredSize());
269: if (originalPreferredSize == null) { // save it the first time
270: originalPreferredSize = target.getPreferredSize();
271: }
272: // Thread.dumpStack();
273: Component c[] = target.getComponents();
274: Insets insets = target.getInsets();
275: Dimension dim = target.getSize();
276: // //System.out.println("layoutContainer target original size " + dim);
277: int top = insets.top;
278: int bottom = dim.height - insets.bottom;
279: int left = insets.left;
280: int right = dim.width - insets.right;
281:
282: boolean reScaleW = false, reScaleH = false;
283: // float scaleW = 0, scaleH = 0;
284:
285: // if the width/height has changed, scale the splitter bar positions
286: // //System.out.println("layoutContainer lastW "+ lastW + ", dim.width " + dim.width);
287: // //System.out.println("layoutContainer lastH "+ lastH + ", dim.height " + dim.height);
288: if (lastW == -1) { // save it the first time
289: lastW = dim.width;
290: lastH = dim.height;
291: } else {
292: if (lastW != dim.width) {
293: reScaleW = true;
294: // scaleW = (float)dim.width/(float)lastW;
295: // //System.out.println("layoutContainer scaleW "+ scaleW);
296: lastW = dim.width;
297: }
298: if (lastH != dim.height) {
299: reScaleH = true;
300: // scaleH = (float)dim.height/(float)lastH;
301: // //System.out.println("layoutContainer scaleH "+ scaleH);
302: lastH = dim.height;
303: }
304: }
305: // //System.out.println("layoutContainer reScaleW " + reScaleW);
306: // //System.out.println("layoutContainer reScaleH " + reScaleH);
307:
308: dim.width = right - left;
309: dim.height = bottom - top;
310:
311: int x = left;
312: int y = top;
313: for (int i = 0; i < c.length; i++) {
314: // //System.out.println("layoutContainer bounds " + i + " " + c[i].getBounds());
315: // //System.out.println("layoutContainer pref size " + i + " " + c[i].getPreferredSize());
316: // //System.out.println("layoutContainer " + i + " is visible " + c[i].isVisible());
317: if (c[i].isVisible()) {
318: // if (c[i] instanceof JSplitterBar) {
319: // if (reScaleW) {
320: // //System.out.println("layoutContainer reScaleW");
321: // Point p = c[i].getLocation();
322: // c[i].setLocation((int)(((float)p.x)*scaleW),p.y); // dims set later
323: // //System.out.println("layoutContainer setLocation " + i + " " + c[i].getLocation());
324: // }
325: // }
326:
327: // if the component hasn't been sized, use it's preferred size, else use its bounds
328: Dimension prefD = c[i].getPreferredSize();
329: Dimension size = c[i].getSize();
330: if (size.width == 0) {
331: c[i].setBounds(x, y, prefD.width, dim.height);
332: x += prefD.width;
333: } else {
334: // get the ColumnView height
335: Container scrollpane = target.getParent()
336: .getParent();
337: Container cv = scrollpane.getParent();
338: int cvHeight = 0;
339: // int vBarWidth = 0;
340: if (cv instanceof JPanel) {
341: cvHeight = cv.getSize().height;
342:
343: }
344: if (scrollpane instanceof JScrollPane) {
345: JScrollBar hBar = ((JScrollPane) scrollpane)
346: .getHorizontalScrollBar();
347: //System.out.println("layoutContainer hBar isVisible " + hBar.isVisible());
348: // JScrollBar vBar = ((JScrollPane)scrollpane).getVerticalScrollBar();
349: if (hBar.isVisible()) {
350: cvHeight -= hBar.getHeight();
351: }
352: // if (vBar.isVisible()){
353: // //System.out.println("layoutContainer vBar isVisible " + vBar.isVisible());
354: // vBarWidth = vBar.getWidth();
355: // }
356: }
357: c[i].setBounds(x, y, size.width, cvHeight - 2);
358: // c[i].setBounds(x, y, size.width, dim.height);
359: x += size.width;
360: }
361:
362: // //System.out.println("layoutContainer new bounds " + i + " " + c[i].getBounds());
363: }
364: }
365:
366: // set new width for container if it's too small
367: // or if the total width of the components is smaller
368: // than the container (because columns have been removed)
369: Rectangle lastComp = c[c.length - 1].getBounds();
370: int totalCompsWidth = lastComp.x + lastComp.width;
371: Dimension targetSize = target.getSize();
372: //System.out.println("layoutContainer target size " + target.getSize());
373: //System.out.println("layoutContainer target pref size " + target.getPreferredSize());
374: //System.out.println("layoutContainer lastComp height "+ lastComp.height);
375: //System.out.println("layoutContainer target height "+ targetSize.height);
376: if (!(targetSize.width == totalCompsWidth && targetSize.height == lastComp.height)) {
377: target.setPreferredSize(new Dimension(totalCompsWidth,
378: lastComp.height));
379: if (target instanceof JComponent) {
380: ((JComponent) target).revalidate();
381: }
382: }
383: //System.out.println("layoutContainer end preferred size " + target.getPreferredSize());
384: newComponentAdded = false;
385: }
386:
387: /** Determines the maximum amount of space that could be used
388: * when laying out the components in the specified container.
389: * @param -- the container being laid out
390: */
391: public final Dimension maximumLayoutSize(Container target) {
392: // //System.out.println("maximumLayoutSize ");
393: return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
394: }
395:
396: /** Determines the minimum amount of room requested for the layout
397: * of components contained in the specified container.
398: * @param target -- the Container being laid out
399: */
400: // public final Dimension minimumLayoutSize(Container target) {return checkLayoutSize(target, false);}
401: public final Dimension minimumLayoutSize(Container target) {
402: // //System.out.println("minimumLayoutSize");
403: return checkLayoutSize(target, true);
404: }
405:
406: // TEMP -- CHECK TO SEE HOW minsize==prefsize seems
407:
408: /** Determines the preferred amount of room requested for the layout
409: * of components contained in the specified container.
410: * @param target -- the Container being laid out
411: */
412: public final Dimension preferredLayoutSize(Container target) {
413: // //System.out.println("preferredLayoutSize");
414: return checkLayoutSize(target, true);
415: }
416:
417: /** Removes a component from the layout. This should
418: * only be called by java.awt.Container's remove method.
419: */
420: public final void removeLayoutComponent(Component comp) {
421: newComponentAdded = true; // so layout gets re-adjusted
422: }
423:
424: /**
425: *
426: *
427: */
428: public void setFill(boolean fill) {
429: this .fill = fill;
430: }
431:
432: /**
433: *
434: *
435: */
436: public boolean getFill() {
437: return this .fill;
438: }
439:
440: /** Returns a String representation of the Layout */
441: public final String toString() {
442: return getClass().getName();
443: }
444: }
|