001: /*
002: * Copyright (C) 2004 TiongHiang Lee
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2.1 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: * Email: thlee@onemindsoft.org
019: */
020:
021: package org.onemind.swingweb.templaterender.layout;
022:
023: import java.awt.*;
024: import java.util.List;
025: import java.util.*;
026: import java.util.logging.Level;
027: import java.util.logging.Logger;
028:
029: /**
030: * ComponentGridLayout calculates the components the ordering of some components according their x, y position and the cells that they will
031: * fill in a 2 dimensional grid
032: * @author TiongHiang Lee (thlee@onemindsoft.org)
033: *
034: */
035: public class DefaultComponentGridLayout implements ComponentGridLayout {
036:
037: /** the logger */
038: private static final Logger _logger = Logger
039: .getLogger(DefaultComponentGridLayout.class.getName());
040:
041: /** the sorted cells */
042: private SortedSet _cells = new TreeSet(
043: ComponentPositionSorter.INSTANCE);
044:
045: /** the y axis splits */
046: private SortedSet _yAxisSplits = new TreeSet();
047:
048: /** the x axis splits */
049: private SortedSet _xAxisSplits = new TreeSet();
050:
051: /** the container */
052: private Container _con;
053:
054: /** the results **/
055: private List _results;
056:
057: /** whether the layout has been done **/
058: private boolean _layoutDone = false;
059:
060: /**
061: * Constructor.
062: * IMPORTANT: the helper must be constructed after layout is done since it uses the bounds information
063: * of the children components.
064: * @param con the container
065: */
066: public DefaultComponentGridLayout(Container con) //, Object trStrt, Object trEnd, Object tdStrt, Object tdEnd)
067: {
068: this (con.getComponents());
069: _con = con;
070: }
071:
072: public DefaultComponentGridLayout(Component[] coms) {
073: Rectangle lastBound = null;
074: for (int i = 0; i < coms.length; i++) {
075: Component com = coms[i];
076: if (com.isVisible()) {
077: _cells.add(new ComponentCell(com));
078: }
079: }
080: //second pass filter out the one that cannot be seen
081: Component lastCom = null;
082: Iterator it = _cells.iterator();
083: List toBeRemoved = new ArrayList();
084: while (it.hasNext()) {
085: ComponentCell cell = (ComponentCell) it.next();
086: if (lastCom != null) {
087: if (lastCom.getBounds().contains(
088: cell.getComponent().getBounds())) {
089: toBeRemoved.add(cell);
090: } else {
091: lastCom = cell.getComponent();
092: addComponentForCalculation(cell.getComponent());
093: }
094: } else {
095: lastCom = cell.getComponent();
096: addComponentForCalculation(cell.getComponent());
097: }
098: }
099: _cells.removeAll(toBeRemoved);
100: doLayout();
101: }
102:
103: /**
104: * Add the component for calculation
105: * @param com the component
106: */
107: private void addComponentForCalculation(Component com) {
108: _xAxisSplits.add(new Integer(com.getX()));
109: _xAxisSplits.add(new Integer(com.getX() + com.getWidth()));
110: _yAxisSplits.add(new Integer(com.getY()));
111: _yAxisSplits.add(new Integer(com.getY() + com.getHeight()));
112: }
113:
114: /**
115: * Get the split count of targetSplit as in splits[] array. The currentSplit allow optimization to start detecting from
116: * splits[currentSplit]
117: * @param currentSplit the current split
118: * @param targetSplit the target split #
119: * @param splits splits arrays containing the split
120: * @return the position of targetSplit in splits[] array
121: */
122: private static int getIndex(int targetSplit, Object[] splits) {
123: //binary search
124: return seekIndex(0, splits.length - 1, targetSplit, splits);
125: }
126:
127: /**
128: * Recursive binary search on index in splits
129: */
130: private static int seekIndex(int left, int right, int target,
131: Object[] splits) {
132: if (left > right) {
133: return -1;
134: } else {
135: int inc = (right - left) / 2;
136: int current = left + inc;
137: int value = ((Integer) splits[current]).intValue();
138:
139: if (target == value) {
140: return current;
141: } else if (target > value) {
142: return seekIndex(current + 1, right, target, splits);
143: } else {
144: return seekIndex(left, current - 1, target, splits);
145: }
146: }
147: }
148:
149: private static int getNullContinuityCountX(int startX,
150: int currentY, Object[][] objs) {
151: for (int i = startX; i < objs.length; i++) {
152: if (objs[i][currentY] != null) {
153: return i - startX;
154: }
155: }
156: return objs.length - startX;
157: }
158:
159: private static int getNullContinuityCountY(int startY,
160: int currentX, int colspan, Object[][] objs) {
161: for (int y = startY; y < objs[currentX].length; y++) {
162: for (int x = currentX; x < currentX + colspan; x++) {
163: if (objs[x][y] != null) {
164: return y - startY;
165: }
166: }
167: }
168: return objs[currentX].length - startY;
169: }
170:
171: /**
172: * Always return true
173: * TODO: find a way to make this dynamic
174: * @return true if position changed
175: */
176: private boolean positionsChanged() {
177: return true;
178: }
179:
180: /**
181: * Do the layout calculation
182: */
183: public void doLayout() {
184: if (!_layoutDone) {
185: _results = new ArrayList();
186: if (_xAxisSplits.size() == 0) {
187: return;
188: }
189: //first of all, sort the splits
190: Object[] xsplits = _xAxisSplits.toArray();
191: Object[] ysplits = _yAxisSplits.toArray();
192: if (_logger.isLoggable(Level.FINEST)) {
193: _logger.finest("x splits = " + _xAxisSplits
194: + " y splits = " + _yAxisSplits);
195: }
196: //secondly, go throught the components one by one and print out the tag.
197: Object objs[][] = new Object[xsplits.length - 1][ysplits.length - 1];
198: //fill the array with cell references
199: Iterator it = _cells.iterator();
200: while (it.hasNext()) {
201: ComponentCell cell = (ComponentCell) it.next();
202: Component com = cell.getComponent();
203: int xstart = getIndex(com.getX(), xsplits);
204: int ystart = getIndex(com.getY(), ysplits);
205: int xend = getIndex(com.getX() + com.getWidth(),
206: xsplits);
207: int yend = getIndex(com.getY() + com.getHeight(),
208: ysplits);
209: cell.setRow(ystart);
210: cell.setCol(xstart);
211: cell.setColspan(xend - xstart);
212: cell.setRowspan(yend - ystart);
213: cell.setEndRow(xend == xsplits.length - 1);
214: cell.setStartRow(xstart == 0);
215: fillReference(objs, cell, xstart, ystart);
216: }
217:
218: HashSet seen = new HashSet();
219: for (int y = 0; y < ysplits.length - 1; y++) {
220: for (int x = 0; x < xsplits.length - 1; x++) {
221: if (objs[x][y] == null) {
222: ComponentCell cell = new ComponentCell(null);
223: _results.add(cell);
224: seen.add(cell);
225: cell.setCol(x);
226: cell.setRow(y);
227: cell.setColspan(getNullContinuityCountX(x, y,
228: objs));
229: cell.setRowspan(1);
230: cell
231: .setEndRow(x + cell.getColspan() == xsplits.length - 1);
232: cell.setStartRow(x == 0);
233: fillReference(objs, cell, x, y);
234: } else if (!seen.contains(objs[x][y])) {
235: _results.add(objs[x][y]);
236: seen.add(objs[x][y]);
237: }
238: }
239: }
240: _layoutDone = true;
241: }
242: }
243:
244: private void fillReference(Object[][] objs, ComponentCell cell,
245: int xstart, int ystart) {
246: for (int x = xstart; x < xstart + cell.getColspan(); x++) {
247: for (int y = ystart; y < ystart + cell.getRowspan(); y++) {
248: if (objs[x][y] != null) {
249: // throw new IllegalArgumentException("Component " + cell.getComponent() + " overlap with component "
250: // + ((ComponentCell) objs[x][y]).getComponent() + " with " + _con.getLayout());
251: }
252: objs[x][y] = cell;
253: }
254: }
255: }
256:
257: /**
258: * The main program
259: * @param args the arguments
260: */
261: public static void main(String[] args) {
262: /*
263: * ContainerLayoutHelper util = new ContainerLayoutHelper(); //second TextField f = new TextField(); f.setBounds(10, 0, 10,
264: * 10); util.addComponent(f); //third f = new TextField(); f.setBounds(10, 10, 10, 10); util.addComponent(f); //first f =
265: * new TextField(); f.setBounds(0, 0, 10, 20); util.addComponent(f); _logger.finest(util.getHtml());
266: */
267: }
268:
269: /**
270: * Returnn the cells
271: * @return the cells
272: */
273: public final List getCells() {
274: return Collections.unmodifiableList(_results);
275: }
276:
277: /**
278: * Get the container
279: * @return the container
280: */
281: public Container getContainer() {
282: return _con;
283: }
284:
285: public int getCols() {
286: return _xAxisSplits.size() - 1;
287: }
288:
289: public int getRows() {
290: return _yAxisSplits.size() - 1;
291: }
292: }
|