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:
046: /**
047: * Defines a rectangular area in the layout. For each dimension it holds the
048: * starting, ending, and center positions. For vertical dimension also the
049: * "baseline" position.
050: */
051:
052: class LayoutRegion implements LayoutConstants {
053:
054: // number of tracked points for each dimension
055: static final int[] POINT_COUNT = new int[] { 3, 4 };
056:
057: // all points - used as param where no particular but all points should be used
058: static final int ALL_POINTS = Integer.MAX_VALUE;
059:
060: // no point - used as param where no point should be processed
061: static final int NO_POINT = Integer.MIN_VALUE;
062:
063: // unknown point position value
064: static final int UNKNOWN = Integer.MIN_VALUE;
065:
066: // array of tracked positions - for each tracked point within each dimension
067: // - for HORIZONTAL dimension there are LEADING, TRAILING, CENTER points
068: // - for VERTICAL dimension there are LEADING, TRAILING, CENTER, BASELINE points
069: // (the constants can be used as indexes to the array)
070: int positions[][];
071:
072: LayoutRegion() {
073: positions = new int[DIM_COUNT][];
074: positions[HORIZONTAL] = new int[] { UNKNOWN, UNKNOWN, UNKNOWN };
075: positions[VERTICAL] = new int[] { UNKNOWN, UNKNOWN, UNKNOWN,
076: UNKNOWN }; // including BASELINE
077: }
078:
079: LayoutRegion(LayoutRegion reg) {
080: this ();
081: set(reg);
082: }
083:
084: LayoutRegion(Rectangle bounds, int baselinePos) {
085: this ();
086: set(bounds, baselinePos);
087: }
088:
089: boolean isSet() {
090: return isSet(HORIZONTAL) && isSet(VERTICAL);
091: }
092:
093: boolean isSet(int dimension) {
094: return positions[dimension][LEADING] != UNKNOWN
095: && positions[dimension][TRAILING] != UNKNOWN;
096: }
097:
098: int size(int dimension) {
099: int trail = positions[dimension][TRAILING];
100: int lead = positions[dimension][LEADING];
101: return trail != UNKNOWN && lead != UNKNOWN ? trail - lead
102: : UNKNOWN;
103: }
104:
105: /**
106: * Sets up the region based on given bounds rectangle and baseline position.
107: */
108: void set(Rectangle bounds, int baselinePos) {
109: int[] horiz = positions[HORIZONTAL];
110: horiz[LEADING] = bounds.x;
111: horiz[TRAILING] = bounds.x + bounds.width;
112: horiz[CENTER] = bounds.x + bounds.width / 2;
113:
114: int[] vert = positions[VERTICAL];
115: vert[LEADING] = bounds.y;
116: vert[TRAILING] = bounds.y + bounds.height;
117: vert[CENTER] = bounds.y + bounds.height / 2;
118: vert[BASELINE] = baselinePos;
119: }
120:
121: /**
122: * Converts the region to a rectangle.
123: * @param bounds rectangle to be set (output)
124: * @return Rectangle updated with actual position values (same instance as
125: * passed in as parameter)
126: */
127: Rectangle toRectangle(Rectangle bounds) {
128: int[] horiz = positions[HORIZONTAL];
129: bounds.x = horiz[LEADING];
130: bounds.width = horiz[TRAILING] - bounds.x;
131: int[] vert = positions[VERTICAL];
132: bounds.y = vert[LEADING];
133: bounds.height = vert[TRAILING] - bounds.y;
134: return bounds;
135: }
136:
137: /**
138: * Copies all position values from another region.
139: */
140: void set(LayoutRegion reg) {
141: for (int i = 0; i < DIM_COUNT; i++) {
142: set(i, reg);
143: }
144: }
145:
146: /**
147: * Copies position values of given dimension from another region.
148: */
149: void set(int dimension, LayoutRegion reg) {
150: int[] pos = positions[dimension];
151: int[] setPos = reg.positions[dimension];
152: for (int j = 0; j < pos.length; j++) {
153: pos[j] = setPos[j];
154: }
155: }
156:
157: void set(int dimension, int leading, int trailing) {
158: int[] pos = positions[dimension];
159: if (pos[LEADING] != leading || pos[TRAILING] != trailing) {
160: pos[LEADING] = leading;
161: pos[TRAILING] = trailing;
162: pos[CENTER] = leading != UNKNOWN && trailing != UNKNOWN ? (leading + trailing) / 2
163: : UNKNOWN;
164: if (dimension == VERTICAL) {
165: pos[BASELINE] = UNKNOWN; // undefined after change
166: }
167: }
168: }
169:
170: /**
171: * Reverts the region to unset state - like it was just after the creation.
172: */
173: void reset() {
174: for (int i = 0; i < DIM_COUNT; i++) {
175: int[] pos = positions[i];
176: for (int j = 0; j < pos.length; j++)
177: pos[j] = UNKNOWN;
178: }
179: }
180:
181: /**
182: * @param points array of alignment constants (LEADING or TRAILING) defining
183: * for each dimension which point should be moved (can be null to
184: * move everything)
185: */
186: void reshape(int[] points, int[] moves) {
187: for (int i = 0; i < DIM_COUNT; i++) {
188: reshape(i, (points != null ? points[i] : ALL_POINTS),
189: moves[i]);
190: }
191: }
192:
193: void reshape(int dimension, int align, int move) {
194: int[] pos = positions[dimension];
195: if (align == ALL_POINTS) { // move everything
196: for (int j = 0; j < pos.length; j++) {
197: if (pos[j] != UNKNOWN)
198: pos[j] += move;
199: }
200: } else if (align != NO_POINT) { // move only the desired point
201: assert align == LEADING || align == TRAILING;
202: if (pos[align] != UNKNOWN) {
203: pos[align] += move;
204: if (pos[LEADING] != UNKNOWN && pos[TRAILING] != UNKNOWN) {
205: if (pos[LEADING] > pos[TRAILING]) { // don't allow negative size
206: pos[align] = pos[align ^ 1];
207: }
208: pos[CENTER] = (pos[LEADING] + pos[TRAILING]) / 2;
209: }
210: if (dimension == VERTICAL && move != 0) {
211: pos[BASELINE] = UNKNOWN; // undefined after resizing
212: }
213: }
214: }
215: }
216:
217: /**
218: * Grows to bounds of given region.
219: */
220: void expand(LayoutRegion reg) {
221: for (int i = 0; i < DIM_COUNT; i++) {
222: expand(reg, i);
223: }
224: }
225:
226: void expand(LayoutRegion reg, int dimension) {
227: int[] pos = positions[dimension];
228: int[] exPos = reg.positions[dimension];
229: if (exPos[LEADING] != UNKNOWN
230: && (pos[LEADING] == UNKNOWN || exPos[LEADING] < pos[LEADING])) {
231: pos[LEADING] = exPos[LEADING];
232: }
233: if (exPos[TRAILING] != UNKNOWN
234: && (pos[TRAILING] == UNKNOWN || exPos[TRAILING] > pos[TRAILING])) {
235: pos[TRAILING] = exPos[TRAILING];
236: }
237: if (pos[LEADING] != UNKNOWN && pos[TRAILING] != UNKNOWN) {
238: pos[CENTER] = (pos[LEADING] + pos[TRAILING]) / 2;
239: }
240: }
241:
242: /**
243: * @param sp1 base LayoutSpace
244: * @param sp2 compared LayoutSpace
245: * @param points array of alignment constants defining what points should
246: * be compared in each dimension (can be null if it does not matter)
247: * @param diffs output array with the position difference for each dimension
248: */
249: /* static void positionDiff(LayoutSpace sp1, LayoutSpace sp2,
250: int[] points, int[] diffs)
251: {
252: for (int i=0; i < DIM_COUNT; i++) {
253: int[] pos1 = sp1.positions[i];
254: int[] pos2 = sp2.positions[i];
255: int align = points != null ? points[i] : LEADING;
256: if (align != NO_POINT) {
257: if (align == ALL_POINTS) {
258: align = LEADING;
259: }
260: diffs[i] = pos1[align] != UNKNOWN && pos2[align] != UNKNOWN ?
261: pos2[align] - pos1[align] : UNKNOWN;
262: }
263: }
264: } */
265:
266: static boolean isValidCoordinate(int pos) {
267: return pos > Short.MIN_VALUE && pos < Short.MAX_VALUE;
268: }
269:
270: /**
271: * @param r1 base LayoutRegion
272: * @param r2 compared LayoutRegion
273: * @param dimension HORIZONTAL or VERTICAL (dimension index)
274: * @param align1 alignment constant of a point in base LayoutRegion
275: * @param align2 alignment constant of a point in compared LayoutRegion
276: * @return distance sp2 - sp1 in given dimension between given points
277: */
278: static int distance(LayoutRegion r1, LayoutRegion r2,
279: int dimension, int align1, int align2) {
280: int pos1 = r1.positions[dimension][align1];
281: int pos2 = r2.positions[dimension][align2];
282: return pos1 != UNKNOWN && pos2 != UNKNOWN ? pos2 - pos1
283: : UNKNOWN;
284: }
285:
286: /**
287: * Goes through all points of given two regions in given dimension and finds
288: * the smallest distance (abs) between the regions. Positive value is
289: * returned if r2 has higher position.
290: * @return the smallest distance between the corresponding points of given
291: * regions in given dimension
292: */
293: static int minDistance(LayoutRegion r1, LayoutRegion r2,
294: int dimension) {
295: int[] pos1 = r1.positions[dimension];
296: int[] pos2 = r2.positions[dimension];
297: int min = UNKNOWN;
298: int sign = 1;
299: for (int i = 0; i < pos1.length; i++) {
300: if (pos1[i] != UNKNOWN && pos2[i] != UNKNOWN) {
301: int dst = pos2[i] - pos1[i];
302: int s;
303: if (dst < 0) {
304: dst = -dst;
305: s = -1;
306: } else
307: s = 1;
308: if (min == UNKNOWN || dst < min) {
309: min = dst;
310: sign = s;
311: }
312: }
313: }
314: return min * sign;
315: }
316:
317: /**
318: * Computes distance between two regions supposing they do not overlap.
319: * The distance is between LEADING point of one region and TRAILING of the
320: * other (or vice versa, depending on their relative position). Positive
321: * value is returned if r2 has higher position.
322: * @return the distance between two regions if they don't overlap, or 0 if
323: * they overlap
324: */
325: static int nonOverlapDistance(LayoutRegion r1, LayoutRegion r2,
326: int dimension) {
327: int[] pos1 = r1.positions[dimension];
328: int[] pos2 = r2.positions[dimension];
329: int dst = pos2[LEADING] - pos1[TRAILING];
330: if (dst >= 0) {
331: return dst;
332: }
333: dst = pos2[TRAILING] - pos1[LEADING];
334: if (dst <= 0) {
335: return dst;
336: }
337: return 0;
338: }
339:
340: /**
341: * Checks whether a point of 'contained' region (described by 'alignment')
342: * is inside the 'container' region's area in given 'dimension'.
343: * @return whether a point of a region is inside the other region
344: */
345: static boolean pointInside(LayoutRegion contained, int alignment,
346: LayoutRegion container, int dimension) {
347: int[] pos = container.positions[dimension];
348: int point = contained.positions[dimension][alignment];
349: assert point != UNKNOWN && pos[LEADING] != UNKNOWN
350: && pos[TRAILING] != UNKNOWN;
351: if (alignment == LEADING) {
352: return point >= pos[LEADING] && point < pos[TRAILING];
353: }
354: // if (alignment == TRAILING) {
355: return point > pos[LEADING] && point <= pos[TRAILING];
356: // }
357: }
358:
359: /**
360: * @return whether the given regions overlap in given dimension
361: */
362: static boolean overlap(LayoutRegion r1, LayoutRegion r2,
363: int dimension, int margin) {
364: int[] pos1 = r1.positions[dimension];
365: int[] pos2 = r2.positions[dimension];
366: assert pos1[LEADING] != UNKNOWN && pos1[TRAILING] != UNKNOWN
367: && pos2[LEADING] != UNKNOWN
368: && pos2[TRAILING] != UNKNOWN;
369: return pos1[TRAILING] + margin > pos2[LEADING]
370: && pos1[LEADING] - margin < pos2[TRAILING];
371: }
372:
373: /**
374: * @return whether given regions occupy the same space
375: */
376: static boolean sameSpace(LayoutRegion r1, LayoutRegion r2) {
377: return sameSpace(r1, r2, HORIZONTAL)
378: && sameSpace(r1, r2, VERTICAL);
379: }
380:
381: /**
382: * @return whether given regions occupy the same space in given dimension
383: */
384: static boolean sameSpace(LayoutRegion r1, LayoutRegion r2,
385: int dimension) {
386: return r1.positions[dimension][LEADING] == r2.positions[dimension][LEADING]
387: && r1.positions[dimension][TRAILING] == r2.positions[dimension][TRAILING];
388: }
389: }
|