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.layoutdesign;
043:
044: import java.awt.Rectangle;
045: import java.beans.PropertyChangeListener;
046: import java.beans.PropertyChangeSupport;
047: import java.util.*;
048:
049: /**
050: * This class manages layout information about a component in the layout.
051: * It refers to corresponding layout intervals (horizontal and vertical) in the
052: * layout structure.
053: *
054: * A layout component can be found according to its Id in the layout model.
055: *
056: * The component may serve the role of layout container - then it also defines
057: * top (root) intervals (horizontal and vertical) for its internal layouts.
058: *
059: * @see LayoutInterval
060: *
061: * @author Tomas Pavek
062: */
063:
064: public final class LayoutComponent implements LayoutConstants {
065:
066: // Identification of the component in the model
067: private String componentId;
068:
069: // The parent component of this component
070: private LayoutComponent parentComponent;
071:
072: // Layout intervals representing the component in the layout hierarchy.
073: // There is one interval for each dimension.
074: private LayoutInterval[] layoutIntervals;
075:
076: // Potential resizability of the component in the design area.
077: private boolean[] resizability;
078:
079: // Root layout intervals of a container layout. There is one interval for
080: // each dimension. Defined by components that are layout containers, i.e.
081: // managing layout of their subcomponents. Otherwise the array is null.
082: private List<LayoutInterval[]> layoutRoots;
083:
084: // Subcomponents of this component.
085: private List<LayoutComponent> subComponents;
086:
087: private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
088: this );
089:
090: // horizontal size-link
091: private int horizontalLinkId = NOT_EXPLICITLY_DEFINED;
092:
093: // vertical size-link
094: private int verticalLinkId = NOT_EXPLICITLY_DEFINED;
095:
096: // -----
097: // setup
098:
099: public LayoutComponent(String id, boolean isContainer) {
100: if (id == null)
101: throw new NullPointerException();
102: componentId = id;
103: layoutIntervals = new LayoutInterval[DIM_COUNT];
104: for (int i = 0; i < DIM_COUNT; i++) {
105: layoutIntervals[i] = new LayoutInterval(SINGLE);
106: layoutIntervals[i].setComponent(this );
107: layoutIntervals[i].setSizes(USE_PREFERRED_SIZE,
108: NOT_EXPLICITLY_DEFINED, USE_PREFERRED_SIZE);
109: }
110: if (isContainer) {
111: createRoots();
112: }
113: }
114:
115: public LayoutComponent(String id, boolean isContainer,
116: int initialWidth, int initialHeight) {
117: this (id, isContainer);
118: if (isContainer) {
119: LayoutInterval[] defaultRoots = layoutRoots.get(0);
120: for (int i = 0; i < DIM_COUNT; i++) {
121: LayoutInterval gap = new LayoutInterval(SINGLE);
122: gap.setSizes(0, i == HORIZONTAL ? initialWidth
123: : initialHeight, Short.MAX_VALUE);
124: defaultRoots[i].add(gap, 0);
125: }
126: } else {
127: layoutIntervals[HORIZONTAL].setPreferredSize(initialWidth);
128: layoutIntervals[VERTICAL].setPreferredSize(initialHeight);
129: }
130: }
131:
132: private void createRoots() {
133: layoutRoots = new LinkedList<LayoutInterval[]>();
134: addNewLayoutRoots();
135: }
136:
137: // -----
138:
139: void setId(String id) {
140: componentId = id;
141: }
142:
143: public String getId() {
144: return componentId;
145: }
146:
147: public LayoutComponent getParent() {
148: return parentComponent;
149: }
150:
151: public boolean isParentOf(LayoutComponent comp) {
152: do {
153: comp = comp.getParent();
154: if (comp == this )
155: return true;
156: } while (comp != null);
157: return false;
158: }
159:
160: static LayoutComponent getCommonParent(LayoutComponent comp1,
161: LayoutComponent comp2) {
162: // Find all parents of given components
163: Iterator<LayoutComponent> parents1 = parentsOfComponent(comp1)
164: .iterator();
165: Iterator<LayoutComponent> parents2 = parentsOfComponent(comp2)
166: .iterator();
167: LayoutComponent parent1 = parents1.next();
168: LayoutComponent parent2 = parents2.next();
169:
170: // Candidate for the common parent
171: LayoutComponent parent = null;
172: while (parent1 == parent2) {
173: parent = parent1;
174: if (parents1.hasNext()) {
175: parent1 = parents1.next();
176: } else {
177: break;
178: }
179: if (parents2.hasNext()) {
180: parent2 = parents2.next();
181: } else {
182: break;
183: }
184: }
185: return parent;
186: }
187:
188: private static List<LayoutComponent> parentsOfComponent(
189: LayoutComponent comp) {
190: List<LayoutComponent> parents = new LinkedList<LayoutComponent>();
191: while (comp != null) {
192: parents.add(0, comp);
193: comp = comp.getParent();
194: }
195: return parents;
196: }
197:
198: public LayoutInterval getLayoutInterval(int dimension) {
199: return layoutIntervals[dimension];
200: }
201:
202: void setLayoutInterval(LayoutInterval interval, int dimension) {
203: layoutIntervals[dimension] = interval;
204: }
205:
206: public boolean isLayoutContainer() {
207: return layoutRoots != null;
208: }
209:
210: void setResizability(boolean[] resizability) {
211: this .resizability = resizability;
212: }
213:
214: boolean[] getResizability() {
215: return resizability;
216: }
217:
218: LayoutInterval[] getParentRoots() {
219: return parentComponent != null ? parentComponent
220: .getLayoutRoots(layoutIntervals[0]) : null;
221: }
222:
223: // -----
224: // subcomponents
225:
226: public List<LayoutComponent> getSubcomponents() {
227: return subComponents != null && subComponents.size() > 0 ? Collections
228: .unmodifiableList(subComponents)
229: : Collections.EMPTY_LIST;
230: }
231:
232: int getSubComponentCount() {
233: return (subComponents == null) ? 0 : subComponents.size();
234: }
235:
236: LayoutComponent getSubComponent(int index) {
237: return subComponents.get(index);
238: }
239:
240: int indexOf(LayoutComponent comp) {
241: return subComponents != null ? subComponents.indexOf(comp) : -1;
242: }
243:
244: // int add(LayoutComponent comp) {
245: // return add(comp, -1);
246: // }
247:
248: int addComponent(LayoutComponent comp, int index) {
249: assert isLayoutContainer();
250:
251: if (subComponents == null) {
252: subComponents = new LinkedList<LayoutComponent>();
253: }
254: if (index < 0) {
255: index = subComponents.size();
256: }
257: subComponents.add(index, comp);
258: comp.parentComponent = this ;
259:
260: return index;
261: }
262:
263: int removeComponent(LayoutComponent comp) {
264: if (subComponents != null) {
265: Iterator it = subComponents.iterator();
266: int index = -1;
267: while (it.hasNext()) {
268: index++;
269: if (comp == it.next()) {
270: it.remove();
271: comp.parentComponent = null;
272: return index;
273: }
274: }
275: }
276: return -1;
277: }
278:
279: // -----
280: // container's layout roots
281:
282: public int getLayoutRootCount() {
283: return layoutRoots != null ? layoutRoots.size() : 0;
284: }
285:
286: public LayoutInterval getLayoutRoot(int rootIndex, int dimension) {
287: return layoutRoots.get(rootIndex)[dimension];
288: }
289:
290: LayoutInterval getDefaultLayoutRoot(int dimension) {
291: return layoutRoots.get(0)[dimension];
292: }
293:
294: List<LayoutInterval[]> getLayoutRoots() {
295: return layoutRoots;
296: }
297:
298: void setLayoutRoots(List<LayoutInterval[]> roots) {
299: if (roots == null && layoutRoots != null) {
300: // instead of no roots create default empty roots (to keep this a container)
301: // for no roots use setLayoutContainer(false, null)
302: createRoots();
303: } else {
304: layoutRoots = roots;
305: }
306: }
307:
308: LayoutInterval[] getLayoutRoots(LayoutInterval interval) {
309: interval = LayoutInterval.getRoot(interval);
310: for (LayoutInterval[] roots : layoutRoots) {
311: for (int dim = 0; dim < DIM_COUNT; dim++) {
312: if (interval == roots[dim]) {
313: return roots;
314: }
315: }
316: }
317: return null;
318: }
319:
320: int getLayoutRootsIndex(LayoutInterval interval) {
321: interval = LayoutInterval.getRoot(interval);
322: int index = -1;
323: for (LayoutInterval[] roots : layoutRoots) {
324: index++;
325: for (int dim = 0; dim < DIM_COUNT; dim++) {
326: if (interval == roots[dim]) {
327: return index;
328: }
329: }
330: }
331: return -1;
332: }
333:
334: void addLayoutRoots(LayoutInterval[] roots, int index) {
335: if (index < 0) {
336: index = layoutRoots.size();
337: }
338: layoutRoots.add(index, roots);
339: }
340:
341: int removeLayoutRoots(LayoutInterval[] roots) {
342: Iterator it = layoutRoots.iterator();
343: int index = -1;
344: while (it.hasNext()) {
345: index++;
346: if (roots == it.next()) {
347: it.remove();
348: return index;
349: }
350: }
351: return -1;
352: }
353:
354: LayoutInterval[] addNewLayoutRoots() {
355: LayoutInterval[] roots = new LayoutInterval[DIM_COUNT];
356: for (int i = 0; i < DIM_COUNT; i++) {
357: roots[i] = new LayoutInterval(PARALLEL);
358: }
359: layoutRoots.add(roots);
360: return roots;
361: }
362:
363: void setLayoutContainer(boolean isContainer,
364: List<LayoutInterval[]> roots) {
365: if (isContainer != isLayoutContainer()) {
366: if (isContainer) {
367: if (roots == null) {
368: createRoots();
369: } else {
370: layoutRoots = roots;
371: }
372: } else {
373: layoutRoots = null;
374: subComponents = null;
375: }
376: }
377: }
378:
379: // -----
380: // current state of the layout - current position and size of component
381: // kept to be available quickly for the layout designer
382:
383: void setCurrentBounds(Rectangle bounds, int baseline) {
384: LayoutRegion space = layoutIntervals[0].getCurrentSpace();
385: space.set(bounds, baseline > 0 ? bounds.y + baseline
386: : LayoutRegion.UNKNOWN);
387: for (int i = 1; i < layoutIntervals.length; i++) {
388: layoutIntervals[i].setCurrentSpace(space);
389: }
390: }
391:
392: void setCurrentInterior(Rectangle bounds) {
393: LayoutRegion space = null;
394: for (LayoutInterval[] roots : layoutRoots) {
395: for (int i = 0; i < roots.length; i++) {
396: if (space == null) {
397: space = roots[i].getCurrentSpace();
398: space.set(bounds, LayoutRegion.UNKNOWN);
399: } else {
400: roots[i].setCurrentSpace(space);
401: }
402: }
403: }
404: }
405:
406: // -----
407:
408: /**
409: * @return whether this intervals size is linked with some other component in a direction horizontal or vertical
410: */
411: public boolean isLinkSized(int dimension) {
412: if (dimension == HORIZONTAL) {
413: return NOT_EXPLICITLY_DEFINED != horizontalLinkId;
414: }
415: return NOT_EXPLICITLY_DEFINED != verticalLinkId;
416: }
417:
418: /**
419: * @return whether this intervals size is linked with some other component in a direction horizontal or vertical
420: */
421: public int getLinkSizeId(int dimension) {
422: if (dimension == HORIZONTAL) {
423: return horizontalLinkId;
424: }
425: return verticalLinkId;
426: }
427:
428: /**
429: * @return whether this intervals size is linked with some other component in a direction horizontal or vertical
430: */
431: public void setLinkSizeId(int id, int dimension) {
432: if (dimension == HORIZONTAL) {
433: horizontalLinkId = id;
434: } else {
435: verticalLinkId = id;
436: }
437:
438: }
439:
440: // -----
441: // listener support
442:
443: public void addPropertyChangeListener(
444: PropertyChangeListener listener) {
445: propertyChangeSupport.addPropertyChangeListener(listener);
446: }
447:
448: public void removePropertyChangeListener(
449: PropertyChangeListener listener) {
450: propertyChangeSupport.removePropertyChangeListener(listener);
451: }
452:
453: void firePropertyChange(String propertyName, Object oldValue,
454: Object newValue) {
455: propertyChangeSupport.firePropertyChange(propertyName,
456: oldValue, newValue);
457: }
458:
459: }
|