/jEdit/branches/4.3.x-merge-request-2980833/org/gjt/sp/jedit/gui/ExtendedGridLayout.java

# · Java · 1224 lines · 796 code · 56 blank · 372 comment · 153 complexity · a98d83e16b1fcb103ab7788e4b025a28 MD5 · raw file

  1. /*
  2. * ExtendedGridLayout.java - a grid layout manager with variable cell sizes
  3. * that supports colspans and rowspans
  4. * :tabSize=8:indentSize=8:noTabs=false:
  5. * :folding=explicit:collapseFolds=1:
  6. *
  7. * Originally written by Bj??rn Kautler for the jEdit project. This work has been
  8. * placed into the public domain. You may use this work in any way and for any
  9. * purpose you wish.
  10. *
  11. * THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, NOT EVEN THE
  12. * IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, ASSUMES
  13. * _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE RESULTING FROM THE USE, MODIFICATION,
  14. * OR REDISTRIBUTION OF THIS SOFTWARE.
  15. */
  16. package org.gjt.sp.jedit.gui;
  17. import java.awt.Component;
  18. import java.awt.Container;
  19. import java.awt.Dimension;
  20. import java.awt.Insets;
  21. import java.awt.LayoutManager2;
  22. import java.util.ArrayList;
  23. import java.util.Arrays;
  24. import java.util.HashSet;
  25. import java.util.Hashtable;
  26. import java.util.Iterator;
  27. import java.util.List;
  28. import java.util.ListIterator;
  29. import java.util.Set;
  30. import static java.awt.Component.CENTER_ALIGNMENT;
  31. import static org.gjt.sp.jedit.gui.ExtendedGridLayoutConstraints.REMAINDER;
  32. /**
  33. * A layout manager that places components in a rectangular grid
  34. * with variable cell sizes that supports colspans and rowspans.
  35. * <p>
  36. * The container is divided into rectangles, and each component is placed
  37. * in a rectangular space defined by its colspan and rowspan.
  38. * Each row is as large as the largest component in
  39. * that row, and each column is as wide as the widest component in
  40. * that column. </p>
  41. * <p>
  42. * This behavior is similar to
  43. * <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/awt/GridLayout.html">{@code java.awt.GridLayout}</a>
  44. * but it supports different row heights and
  45. * column widths for each row/column. </p>
  46. * <p>
  47. * For example, the following is a Dialog that lays out ten buttons
  48. * exactly the same as in the example of the JavaDoc of
  49. * <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/awt/GridBagLayout.html">{@code java.awt.GridBagLayout}</a>
  50. * with the difference of vertical and horizontal gaps that can be configured:
  51. * <hr>
  52. * <blockquote><pre><font color="#000000">
  53. * <font color="#000000"> 1:</font><font color="#009966"><strong>import</strong></font> java.awt.Button;
  54. * <font color="#000000"> 2:</font><font color="#009966"><strong>import</strong></font> java.awt.Dimension;
  55. * <font color="#000000"> 3:</font>
  56. * <font color="#000000"> 4:</font><font color="#009966"><strong>import</strong></font> javax.swing.JDialog;
  57. * <font color="#990066"> 5:</font>
  58. * <font color="#000000"> 6:</font><font color="#009966"><strong>import</strong></font> org.gjt.sp.jedit.gui.ExtendedGridLayout;
  59. * <font color="#000000"> 7:</font><font color="#009966"><strong>import</strong></font> org.gjt.sp.jedit.gui.ExtendedGridLayoutConstraints;
  60. * <font color="#000000"> 8:</font>
  61. * <font color="#000000"> 9:</font><font color="#009966"><strong>import</strong></font> <font color="#006699"><strong>static</strong></font> org.gjt.sp.jedit.gui.ExtendedGridLayoutConstraints.REMAINDER;
  62. * <font color="#990066"> 10:</font>
  63. * <font color="#000000"> 11:</font><font color="#006699"><strong>public</strong></font> <font color="#0099ff"><strong>class</strong></font> ExampleDialog <font color="#006699"><strong>extends</strong></font> JDialog <font color="#000000"><strong>{</strong></font>
  64. * <font color="#000000"> 12:</font> <font color="#006699"><strong>public</strong></font> <font color="#9966ff">ExampleDialog</font>() <font color="#000000"><strong>{</strong></font>
  65. * <font color="#000000"> 13:</font> <font color="#cc00cc">super</font>(<font color="#cc00cc">null</font>,<font color="#ff00cc">&quot;</font><font color="#ff00cc">Example</font><font color="#ff00cc"> </font><font color="#ff00cc">Dialog</font><font color="#ff00cc">&quot;</font>,<font color="#cc00cc">true</font>);
  66. * <font color="#000000"> 14:</font> <font color="#9966ff">setLayout</font>(<font color="#006699"><strong>new</strong></font> <font color="#9966ff">ExtendedGridLayout</font>(<font color="#ff0000">5</font>,<font color="#ff0000">5</font>,<font color="#006699"><strong>new</strong></font> <font color="#9966ff">Insets</font>(<font color="#ff0000">5</font>,<font color="#ff0000">5</font>,<font color="#ff0000">5</font>,<font color="#ff0000">5</font>)));
  67. * <font color="#990066"> 15:</font>
  68. * <font color="#000000"> 16:</font> <font color="#9966ff">add</font>(<font color="#9966ff">makeButton</font>(<font color="#ff00cc">&quot;</font><font color="#ff00cc">Button1</font><font color="#ff00cc">&quot;</font>));
  69. * <font color="#000000"> 17:</font> <font color="#9966ff">add</font>(<font color="#9966ff">makeButton</font>(<font color="#ff00cc">&quot;</font><font color="#ff00cc">Button2</font><font color="#ff00cc">&quot;</font>));
  70. * <font color="#000000"> 18:</font> <font color="#9966ff">add</font>(<font color="#9966ff">makeButton</font>(<font color="#ff00cc">&quot;</font><font color="#ff00cc">Button3</font><font color="#ff00cc">&quot;</font>));
  71. * <font color="#000000"> 19:</font> <font color="#9966ff">add</font>(<font color="#9966ff">makeButton</font>(<font color="#ff00cc">&quot;</font><font color="#ff00cc">Button4</font><font color="#ff00cc">&quot;</font>));
  72. * <font color="#990066"> 20:</font> Button button <font color="#000000"><strong>=</strong></font> <font color="#9966ff">makeButton</font>(<font color="#ff00cc">&quot;</font><font color="#ff00cc">Button5</font><font color="#ff00cc">&quot;</font>);
  73. * <font color="#000000"> 21:</font> <font color="#9966ff">add</font>(button,<font color="#006699"><strong>new</strong></font> <font color="#9966ff">ExtendedGridLayoutConstraints</font>(<font color="#ff0000">1</font>,REMAINDER,<font color="#ff0000">1</font>,button));
  74. * <font color="#000000"> 22:</font> button <font color="#000000"><strong>=</strong></font> <font color="#9966ff">makeButton</font>(<font color="#ff00cc">&quot;</font><font color="#ff00cc">Button6</font><font color="#ff00cc">&quot;</font>);
  75. * <font color="#000000"> 23:</font> <font color="#9966ff">add</font>(button,<font color="#006699"><strong>new</strong></font> <font color="#9966ff">ExtendedGridLayoutConstraints</font>(<font color="#ff0000">2</font>,<font color="#ff0000">3</font>,<font color="#ff0000">1</font>,button));
  76. * <font color="#000000"> 24:</font> button <font color="#000000"><strong>=</strong></font> <font color="#9966ff">makeButton</font>(<font color="#ff00cc">&quot;</font><font color="#ff00cc">Button7</font><font color="#ff00cc">&quot;</font>);
  77. * <font color="#990066"> 25:</font> <font color="#9966ff">add</font>(button,<font color="#006699"><strong>new</strong></font> <font color="#9966ff">ExtendedGridLayoutConstraints</font>(<font color="#ff0000">2</font>,button));
  78. * <font color="#000000"> 26:</font> button <font color="#000000"><strong>=</strong></font> <font color="#9966ff">makeButton</font>(<font color="#ff00cc">&quot;</font><font color="#ff00cc">Button8</font><font color="#ff00cc">&quot;</font>);
  79. * <font color="#000000"> 27:</font> <font color="#9966ff">add</font>(button,<font color="#006699"><strong>new</strong></font> <font color="#9966ff">ExtendedGridLayoutConstraints</font>(<font color="#ff0000">3</font>,<font color="#ff0000">1</font>,<font color="#ff0000">2</font>,button));
  80. * <font color="#000000"> 28:</font> button <font color="#000000"><strong>=</strong></font> <font color="#9966ff">makeButton</font>(<font color="#ff00cc">&quot;</font><font color="#ff00cc">Button9</font><font color="#ff00cc">&quot;</font>);
  81. * <font color="#000000"> 29:</font> <font color="#9966ff">add</font>(button,<font color="#006699"><strong>new</strong></font> <font color="#9966ff">ExtendedGridLayoutConstraints</font>(<font color="#ff0000">3</font>,<font color="#ff0000">3</font>,<font color="#ff0000">1</font>,button));
  82. * <font color="#990066"> 30:</font> button <font color="#000000"><strong>=</strong></font> <font color="#9966ff">makeButton</font>(<font color="#ff00cc">&quot;</font><font color="#ff00cc">Button10</font><font color="#ff00cc">&quot;</font>);
  83. * <font color="#000000"> 31:</font> <font color="#9966ff">add</font>(button,<font color="#006699"><strong>new</strong></font> <font color="#9966ff">ExtendedGridLayoutConstraints</font>(<font color="#ff0000">4</font>,REMAINDER,<font color="#ff0000">1</font>,button));
  84. * <font color="#000000"> 32:</font>
  85. * <font color="#000000"> 33:</font> <font color="#9966ff">pack</font>();
  86. * <font color="#000000"> 34:</font> <font color="#9966ff">setLocationRelativeTo</font>(<font color="#cc00cc">null</font>);
  87. * <font color="#990066"> 35:</font> <font color="#9966ff">setVisible</font>(<font color="#cc00cc">true</font>);
  88. * <font color="#000000"> 36:</font> <font color="#000000"><strong>}</strong></font>
  89. * <font color="#000000"> 37:</font>
  90. * <font color="#000000"> 38:</font> <font color="#006699"><strong>private</strong></font> Button <font color="#9966ff">makeButton</font>(String name) <font color="#000000"><strong>{</strong></font>
  91. * <font color="#000000"> 39:</font> Button button <font color="#000000"><strong>=</strong></font> <font color="#006699"><strong>new</strong></font> <font color="#9966ff">Button</font>(name);
  92. * <font color="#990066"> 40:</font> button.<font color="#9966ff">setMaximumSize</font>(<font color="#006699"><strong>new</strong></font> <font color="#9966ff">Dimension</font>(Integer.MAX_VALUE,Integer.MAX_VALUE));
  93. * <font color="#000000"> 41:</font> <font color="#006699"><strong>return</strong></font> button;
  94. * <font color="#000000"> 42:</font> <font color="#000000"><strong>}</strong></font>
  95. * <font color="#000000"> 43:</font><font color="#000000"><strong>}</strong></font>
  96. * </font></pre></blockquote>
  97. * <hr>
  98. * If you use {@code REMAINDER} as colspan or rowspan then a component takes
  99. * up the remaining space in that column or row. Any additional components in
  100. * a row are ignored and not displayed. Additional components in a column are
  101. * moved rightside. If a rowspan hits a colspan, the colspan ends and the
  102. * rowspan takes precedence.
  103. * <p>
  104. * Components for which {@code isVisible() == false} are ignored. Because
  105. * of this, components can be replaced "in-place" by adding two components next to
  106. * each other, with different {@code isVisible()} values, and toggling the
  107. * {@code setVisible()} values of both when we wish to swap the currently
  108. * visible component with the one that is hidden. </p>
  109. *
  110. * <p>
  111. * If you want to reserve free space in a row inbetween components,
  112. * add a <a href="http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/Box.Filler.html">{@code javax.swing.Box.Filler}</a>
  113. * to the layout if the free space is in the middle of a row,
  114. * or just don't add components if the free space
  115. * should be at the end of a row.</p>
  116. * <p>
  117. * If a row is taller, or a column is wider than the {@code maximumSize} of a component,
  118. * the component is resized to its maximum size and aligned according to its
  119. * {@code alignmentX} and {@code alignmentY} values. </p>
  120. * <p>
  121. * One instance of this class can be used to layout multiple
  122. * containers at the same time. </p>
  123. *
  124. * @author Bj??rn "Vampire" Kautler
  125. * @version 1.0
  126. * @see ExtendedGridLayoutConstraints
  127. * @see <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/awt/Component.html"><code>java.awt.Component</code></a>
  128. * @see <a href="http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/Box.Filler.html"><code>javax.swing.Box.Filler</code></a>
  129. */
  130. public class ExtendedGridLayout implements LayoutManager2
  131. {
  132. /**
  133. * This hashtable maintains the association between
  134. * a component and its ExtendedGridLayoutConstraints.
  135. * The Keys in {@code comptable} are the components and the
  136. * values are the instances of {@code ExtendedGridLayoutConstraints}.
  137. *
  138. * @see ExtendedGridLayoutConstraints
  139. */
  140. private final Hashtable<Component,ExtendedGridLayoutConstraints> comptable;
  141. /**
  142. * Specifies the horizontal space between two columns.
  143. * The default value is 0.
  144. *
  145. * @see #distanceToBorders
  146. * @see #vgap
  147. */
  148. private final int hgap;
  149. /**
  150. * Specifies the vertical space between two rows.
  151. * The default value is 0.
  152. *
  153. * @see #distanceToBorders
  154. * @see #hgap
  155. */
  156. private final int vgap;
  157. /**
  158. * Specifies the gap between the grid and the borders of the parent container.
  159. * The default value is 0 for all four borders.
  160. *
  161. * @see #hgap
  162. * @see #vgap
  163. */
  164. private final Insets distanceToBorders;
  165. /**
  166. * An enum to tell the {@code getSize()} method which size is requested.
  167. *
  168. * @see #getSize()
  169. */
  170. private static enum LayoutSize { MINIMUM, PREFERRED, MAXIMUM }
  171. /**
  172. * Creates an extended grid layout manager with the specified horizontal
  173. * and vertical gap, and the specified distance to the borders
  174. * of the parent container.
  175. *
  176. * @param hgap The horizontal space between two columns ({@literal >=0})
  177. * @param vgap The vertical space between two rows ({@literal >=0})
  178. * @param distanceToBorders The distances to the borders of the parent container
  179. * @throws IllegalArgumentException if hgap {@literal < 0}
  180. * @throws IllegalArgumentException if vgap {@literal < 0}
  181. */
  182. public ExtendedGridLayout(int hgap, int vgap, Insets distanceToBorders)
  183. {
  184. if (hgap < 0)
  185. {
  186. throw new IllegalArgumentException("hgap must be non-negative (" + hgap + ')');
  187. }
  188. if (vgap < 0)
  189. {
  190. throw new IllegalArgumentException("vgap must be non-negative (" + vgap + ')');
  191. }
  192. this.hgap = hgap;
  193. this.vgap = vgap;
  194. this.distanceToBorders = (Insets)distanceToBorders.clone();
  195. comptable = new Hashtable<Component,ExtendedGridLayoutConstraints>();
  196. }
  197. /**
  198. * Creates an extended grid layout manager with zero horizontal
  199. * and vertical gap, and zero distance to the borders
  200. * of the parent container.
  201. */
  202. public ExtendedGridLayout()
  203. {
  204. this(0,0,new Insets(0,0,0,0));
  205. }
  206. /**
  207. * If the layout manager uses a per-component string,
  208. * adds the component <code>component</code> to the layout,
  209. * associating it with the string specified by <code>name</code>.
  210. *
  211. * @param name The string to be associated with the component.
  212. * Has to be {@code null}, so that default constraints are used.
  213. * @param component The component to be added
  214. * @throws IllegalArgumentException if {@code name} is not {@code null}
  215. * @see #addLayoutComponent(java.awt.Component, java.lang.Object)
  216. */
  217. public void addLayoutComponent(String name, Component component)
  218. {
  219. addLayoutComponent(component,name);
  220. }
  221. /**
  222. * Adds the specified component to the layout, using the specified
  223. * constraints object.
  224. *
  225. * @param component The component to be added
  226. * @param constraints Where/how the component is added to the layout.
  227. * @throws IllegalArgumentException if {@code constraints} is not an ExtendedGridLayoutConstraints object
  228. * @throws IllegalArgumentException if {@code constraints} is a placeholder
  229. * @throws IllegalArgumentException if {@code constraints} is not the right one for the component
  230. * @see ExtendedGridLayoutConstraints
  231. */
  232. public void addLayoutComponent(Component component, Object constraints)
  233. {
  234. if (null == constraints)
  235. {
  236. constraints = new ExtendedGridLayoutConstraints(component);
  237. }
  238. if (constraints instanceof ExtendedGridLayoutConstraints)
  239. {
  240. ExtendedGridLayoutConstraints eglConstraints = (ExtendedGridLayoutConstraints)constraints;
  241. if (eglConstraints.isPlaceholder())
  242. {
  243. throw new IllegalArgumentException("constraints must not be a placeholder");
  244. }
  245. else if (component != eglConstraints.getComponent())
  246. {
  247. throw new IllegalArgumentException("constraints is not the right one for this component");
  248. }
  249. comptable.put(component,eglConstraints);
  250. }
  251. else
  252. {
  253. throw new IllegalArgumentException("constraints must not be an ExtendedGridLayoutConstraints object");
  254. }
  255. }
  256. /**
  257. * Retrieves the constraints for the specified {@code component}.
  258. * If {@code component} is not in the {@code ExtendedGridLayout},
  259. * a set of default {@code ExtendedGridLayoutConstraints} are returned.
  260. *
  261. * @param component the {@code component} to be queried
  262. * @return the contraints for the specified {@code component}
  263. * @throws NullPointerException if {@code component} is {@code null}
  264. * @see ExtendedGridLayoutConstraints
  265. */
  266. private ExtendedGridLayoutConstraints lookupConstraints(Component component)
  267. {
  268. if (null == component)
  269. {
  270. throw new NullPointerException("component must not be null");
  271. }
  272. ExtendedGridLayoutConstraints constraints = comptable.get(component);
  273. if (null == constraints)
  274. {
  275. constraints = new ExtendedGridLayoutConstraints(component);
  276. comptable.put(component,constraints);
  277. }
  278. return constraints;
  279. }
  280. /**
  281. * Removes the specified component from the layout.
  282. *
  283. * @param component The component to be removed
  284. */
  285. public void removeLayoutComponent(Component component)
  286. {
  287. comptable.remove(component);
  288. }
  289. /**
  290. * Returns the alignment along the X axis. This specifies how
  291. * the component would like to be aligned relative to other
  292. * components. The value should be a number between 0 and 1
  293. * where 0 represents alignment along the origin, 1 is aligned
  294. * the furthest away from the origin, 0.5 is centered, etc.
  295. *
  296. * @param container The container for which the alignment should be returned
  297. * @return {@code java.awt.Component.CENTER_ALIGNMENT}
  298. */
  299. public float getLayoutAlignmentX(Container container)
  300. {
  301. return CENTER_ALIGNMENT;
  302. }
  303. /**
  304. * Returns the alignment along the Y axis. This specifies how
  305. * the component would like to be aligned relative to other
  306. * components. The value should be a number between 0 and 1
  307. * where 0 represents alignment along the origin, 1 is aligned
  308. * the furthest away from the origin, 0.5 is centered, etc.
  309. *
  310. * @param container The container for which the alignment should be returned
  311. * @return {@code java.awt.Component.CENTER_ALIGNMENT}
  312. */
  313. public float getLayoutAlignmentY(Container container)
  314. {
  315. return CENTER_ALIGNMENT;
  316. }
  317. /**
  318. * Calculates the minimum size dimensions for the specified
  319. * container, given the components it contains.
  320. *
  321. * @param parent The component to be laid out
  322. * @return The minimum size for the container
  323. * @see #maximumLayoutSize
  324. * @see #preferredLayoutSize
  325. */
  326. public Dimension minimumLayoutSize(Container parent)
  327. {
  328. synchronized (parent.getTreeLock())
  329. {
  330. List<List<ExtendedGridLayoutConstraints>> gridRows = new ArrayList<List<ExtendedGridLayoutConstraints>>();
  331. Set<ExtendedGridLayoutConstraints> colspans = new HashSet<ExtendedGridLayoutConstraints>();
  332. Set<ExtendedGridLayoutConstraints> rowspans = new HashSet<ExtendedGridLayoutConstraints>();
  333. Dimension gridSize = buildGrid(parent,gridRows,colspans,rowspans);
  334. return getSize(parent,LayoutSize.MINIMUM,false,gridSize,gridRows,colspans,rowspans,new int[0][0]);
  335. }
  336. }
  337. /**
  338. * Calculates the preferred size dimensions for the specified
  339. * container, given the components it contains.
  340. *
  341. * @param parent The container to be laid out
  342. * @return The preferred size for the container
  343. * @see #maximumLayoutSize
  344. * @see #minimumLayoutSize
  345. */
  346. public Dimension preferredLayoutSize(Container parent)
  347. {
  348. synchronized (parent.getTreeLock())
  349. {
  350. List<List<ExtendedGridLayoutConstraints>> gridRows = new ArrayList<List<ExtendedGridLayoutConstraints>>();
  351. Set<ExtendedGridLayoutConstraints> colspans = new HashSet<ExtendedGridLayoutConstraints>();
  352. Set<ExtendedGridLayoutConstraints> rowspans = new HashSet<ExtendedGridLayoutConstraints>();
  353. Dimension gridSize = buildGrid(parent,gridRows,colspans,rowspans);
  354. return getSize(parent,LayoutSize.PREFERRED,false,gridSize,gridRows,colspans,rowspans,new int[0][0]);
  355. }
  356. }
  357. /**
  358. * Calculates the maximum size dimensions for the specified
  359. * container, given the components it contains.
  360. *
  361. * @param parent The container to be laid out
  362. * @return The maximum size for the container
  363. * @see #minimumLayoutSize
  364. * @see #preferredLayoutSize
  365. */
  366. public Dimension maximumLayoutSize(Container parent)
  367. {
  368. synchronized (parent.getTreeLock())
  369. {
  370. List<List<ExtendedGridLayoutConstraints>> gridRows = new ArrayList<List<ExtendedGridLayoutConstraints>>();
  371. Set<ExtendedGridLayoutConstraints> colspans = new HashSet<ExtendedGridLayoutConstraints>();
  372. Set<ExtendedGridLayoutConstraints> rowspans = new HashSet<ExtendedGridLayoutConstraints>();
  373. Dimension gridSize = buildGrid(parent,gridRows,colspans,rowspans);
  374. return getSize(parent,LayoutSize.MAXIMUM,false,gridSize,gridRows,colspans,rowspans,new int[0][0]);
  375. }
  376. }
  377. /**
  378. * Invalidates the layout, indicating that if the layout manager
  379. * has cached information it should be discarded.
  380. *
  381. * @param container The container for which the cached information should be discarded
  382. */
  383. public void invalidateLayout(Container container)
  384. {
  385. }
  386. /**
  387. * Lays out the specified container.
  388. *
  389. * @param parent The container to be laid out
  390. */
  391. public void layoutContainer(Container parent)
  392. {
  393. synchronized (parent.getTreeLock())
  394. {
  395. // Pass 1: build the grid
  396. List<List<ExtendedGridLayoutConstraints>> gridRows = new ArrayList<List<ExtendedGridLayoutConstraints>>();
  397. Set<ExtendedGridLayoutConstraints> colspans = new HashSet<ExtendedGridLayoutConstraints>();
  398. Set<ExtendedGridLayoutConstraints> rowspans = new HashSet<ExtendedGridLayoutConstraints>();
  399. Dimension gridSize = buildGrid(parent,gridRows,colspans,rowspans);
  400. // Pass 2: compute minimum, preferred and maximum column widths / row heights
  401. int[][] layoutSizes = new int[6][];
  402. Dimension preferredSize = getSize(parent,LayoutSize.PREFERRED,true,gridSize,gridRows,colspans,rowspans,layoutSizes);
  403. int[] minimumColWidths = layoutSizes[0];
  404. int[] minimumRowHeights = layoutSizes[1];
  405. int[] preferredColWidths = layoutSizes[2];
  406. int[] preferredRowHeights = layoutSizes[3];
  407. int[] maximumColWidths = layoutSizes[4];
  408. int[] maximumRowHeights = layoutSizes[5];
  409. // Pass 3: redistribute free space
  410. Dimension parentSize = parent.getSize();
  411. Insets insets = parent.getInsets();
  412. int freeWidth = parentSize.width
  413. - insets.left - insets.right
  414. - (gridSize.width - 1) * hgap
  415. - distanceToBorders.left - distanceToBorders.right;
  416. int freeHeight = parentSize.height
  417. - insets.top - insets.bottom
  418. - (gridSize.height - 1) * vgap
  419. - distanceToBorders.top - distanceToBorders.bottom;
  420. redistributeSpace(preferredSize.width,
  421. freeWidth,
  422. 0,gridSize.width,
  423. preferredColWidths,
  424. minimumColWidths,
  425. maximumColWidths);
  426. redistributeSpace(preferredSize.height,
  427. freeHeight,
  428. 0,gridSize.height,
  429. preferredRowHeights,
  430. minimumRowHeights,
  431. maximumRowHeights);
  432. // Pass 4: layout components
  433. for (int row=0, y=insets.top+distanceToBorders.top ; row<gridSize.height ; y+=preferredRowHeights[row]+vgap, row++)
  434. {
  435. List<ExtendedGridLayoutConstraints> gridRow = gridRows.get(row);
  436. for (int col=0, x=insets.left+distanceToBorders.left ; col<gridSize.width; x+=preferredColWidths[col]+hgap, col++)
  437. {
  438. ExtendedGridLayoutConstraints cell = gridRow.get(col);
  439. if ((null != cell) && (null != cell.getComponent()) && !cell.isPlaceholder())
  440. {
  441. Component component = cell.getComponent();
  442. Dimension maxSize = component.getMaximumSize();
  443. int fromCol = cell.getCol();
  444. int colspan = cell.getEffectiveColspan();
  445. int toCol = fromCol + colspan;
  446. int width = 0;
  447. for (int col2=fromCol ; col2<toCol ; col2++)
  448. {
  449. width += preferredColWidths[col2];
  450. }
  451. width += (colspan - 1) * hgap;
  452. int fromRow = cell.getRow();
  453. int rowspan = cell.getEffectiveRowspan();
  454. int toRow = fromRow + rowspan;
  455. int height = 0;
  456. for (int row2=fromRow ; row2<toRow ; row2++)
  457. {
  458. height += preferredRowHeights[row2];
  459. }
  460. height += (rowspan - 1) * vgap;
  461. int xCorrection = 0;
  462. int yCorrection = 0;
  463. if (width > maxSize.width)
  464. {
  465. xCorrection = (int)((width - maxSize.width) * component.getAlignmentX());
  466. width = maxSize.width;
  467. }
  468. if (height > maxSize.height)
  469. {
  470. yCorrection = (int)((height-maxSize.height) * component.getAlignmentY());
  471. height = maxSize.height;
  472. }
  473. component.setBounds(x + xCorrection, y + yCorrection, width, height);
  474. }
  475. }
  476. }
  477. }
  478. }
  479. /**
  480. * Redistributs free space (positive or negative) to all available
  481. * columns or rows while taking elements maximum and minimum sizes into
  482. * account if possible.
  483. *
  484. * @param totalSize The cumulated preferred sizes of the components
  485. * @param freeSize The available space for displaying components
  486. * without any gaps between components or between
  487. * the grid and the borders of the parent container
  488. * @param start The start in the arrays of rows or columns inclusive
  489. * @param stop The stop in the arrays of rows or columns exclusive
  490. * @param preferredElementSizes The preferredSizes of the rows or columns.
  491. * After invocation of this method, this array
  492. * holds the sizes that should be used
  493. * @param minimumElementSizes The minimumSizes of the rows or columns
  494. * @param maximumElementSizes The maximumSizes of the rows or columns
  495. */
  496. private void redistributeSpace(int totalSize, int freeSize,
  497. int start, int stop,
  498. int[] preferredElementSizes,
  499. int[] minimumElementSizes,
  500. int[] maximumElementSizes)
  501. {
  502. if (totalSize != freeSize)
  503. {
  504. boolean grow = totalSize < freeSize;
  505. // calculate the size that is available for redistribution
  506. freeSize = (freeSize - totalSize) * (grow ? 1 : -1);
  507. while (freeSize > 0)
  508. {
  509. // calculate the amount of elements that can be resized without violating
  510. // the minimum and maximum sizes and their current cumulated size
  511. int modifyableAmount = 0;
  512. long modifySize = 0;
  513. for (int i=start ; i<stop ; i++)
  514. {
  515. if ((grow && (preferredElementSizes[i] < maximumElementSizes[i])) ||
  516. (!grow && (preferredElementSizes[i] > minimumElementSizes[i])))
  517. {
  518. modifyableAmount++;
  519. modifySize += preferredElementSizes[i];
  520. }
  521. }
  522. boolean checkBounds = true;
  523. // if all elements are at their minimum or maximum size, resize all elements
  524. if (0 == modifyableAmount)
  525. {
  526. for (int i=start ; i<stop ; i++)
  527. {
  528. modifySize += preferredElementSizes[i];
  529. }
  530. checkBounds = false;
  531. modifyableAmount = stop - start;
  532. }
  533. // to prevent an endless loop if the container gets resized to a very small amount
  534. if (modifySize == 0)
  535. {
  536. break;
  537. }
  538. // resize the elements
  539. if (freeSize < modifyableAmount)
  540. {
  541. for (int i=start ; i<stop ; i++)
  542. {
  543. if ((freeSize != 0) &&
  544. (!checkBounds ||
  545. (checkBounds &&
  546. (grow && (preferredElementSizes[i] < maximumElementSizes[i])) ||
  547. (!grow && (preferredElementSizes[i] > minimumElementSizes[i])))))
  548. {
  549. preferredElementSizes[i] += (grow ? 1 : -1);
  550. if (0 > preferredElementSizes[i])
  551. {
  552. preferredElementSizes[i] = 0;
  553. }
  554. freeSize--;
  555. }
  556. }
  557. }
  558. else
  559. {
  560. long modifySizeAddition = 0;
  561. double factor = (double)(freeSize + modifySize) / (double)modifySize;
  562. for (int i=start ; i<stop ; i++)
  563. {
  564. long modifyableSize = (checkBounds ? (grow ? maximumElementSizes[i] - preferredElementSizes[i] : preferredElementSizes[i] - minimumElementSizes[i]) : Integer.MAX_VALUE - preferredElementSizes[i]);
  565. long elementModifySize = Math.abs(Math.round((factor * preferredElementSizes[i]) - preferredElementSizes[i]));
  566. if (elementModifySize <= modifyableSize)
  567. {
  568. preferredElementSizes[i] += (grow ? elementModifySize : -elementModifySize);
  569. modifySizeAddition += (grow ? elementModifySize : -elementModifySize);
  570. freeSize -= elementModifySize;
  571. }
  572. else
  573. {
  574. preferredElementSizes[i] += (grow ? modifyableSize : -modifyableSize);
  575. modifySizeAddition += (grow ? modifyableSize : -modifyableSize);
  576. freeSize -= modifyableSize;
  577. }
  578. if (0 > preferredElementSizes[i])
  579. {
  580. preferredElementSizes[i] = 0;
  581. }
  582. }
  583. modifySize += modifySizeAddition;
  584. }
  585. }
  586. }
  587. }
  588. /**
  589. * Calculates the minimum, preferred or maximum size dimensions
  590. * for the specified container, given the components it contains.
  591. *
  592. * @param parent The container to be laid out
  593. * @param layoutSize if {@code LayoutSize.MINIMUM} compute minimum layout size,
  594. * if {@code LayoutSize.PREFERRED} compute preferred layout size,
  595. * if {@code LayoutSize.MAXIMUM} compute maximum layout size,
  596. * if {@code fillRawSizes} is {@code true}, the layout size is computed
  597. * without applying gaps between components or between
  598. * the grid and the borders of the parent container
  599. * @param fillRawSizes Whether to fill the resultArrays with the raw
  600. * row heights and column widths and whether to apply
  601. * gaps between components or between
  602. * the grid and the borders of the parent container
  603. * when computing the layout size
  604. * @param gridSize The amount of rows and columns in the grid
  605. * @param gridRows The grid holding the constraints for the components
  606. * @param colspans In this {@code Set} the constraints which are part
  607. * of a colspan are stored
  608. * @param rowspans In this {@code Set} the constraints which are part
  609. * of a rowspan are stored
  610. * @param resultArrays If {@code fillRawSizes} is {@code true}, the first six arrays
  611. * get filled with the raw row heights and column widths.
  612. * resultArrays[0] = resultMinimumColWidths;
  613. * resultArrays[1] = resultMinimumRowHeights;
  614. * resultArrays[2] = resultPreferredColWidths;
  615. * resultArrays[3] = resultPreferredRowHeights;
  616. * resultArrays[4] = resultMaximumColWidths;
  617. * resultArrays[5] = resultMaximumRowHeights;
  618. * @return The minimum, preferred or maximum size dimensions for the specified container
  619. * @throws IllegalArgumentException If {@code fillRawSizes == true} and {@code resultArrays.length < 6}
  620. */
  621. private Dimension getSize(Container parent, LayoutSize layoutSize, boolean fillRawSizes,
  622. Dimension gridSize, List<List<ExtendedGridLayoutConstraints>> gridRows,
  623. Set<ExtendedGridLayoutConstraints> colspans,
  624. Set<ExtendedGridLayoutConstraints> rowspans,
  625. int[][] resultArrays)
  626. {
  627. if (fillRawSizes && (resultArrays.length < 6))
  628. {
  629. throw new IllegalArgumentException("If fillRawSizes is true, resultArrays.length must be >= 6 (" + resultArrays.length + ')');
  630. }
  631. int[] minimumColWidths = new int[gridSize.width];
  632. int[] minimumRowHeights = new int[gridSize.height];
  633. int[] preferredColWidths = new int[gridSize.width];
  634. int[] preferredRowHeights = new int[gridSize.height];
  635. int[] maximumColWidths = new int[gridSize.width];
  636. int[] maximumRowHeights = new int[gridSize.height];
  637. Arrays.fill(minimumColWidths,0);
  638. Arrays.fill(minimumRowHeights,0);
  639. Arrays.fill(preferredColWidths,0);
  640. Arrays.fill(preferredRowHeights,0);
  641. Arrays.fill(maximumColWidths,0);
  642. Arrays.fill(maximumRowHeights,0);
  643. // get the maximum of the minimum sizes,
  644. // the maximum of the preferred sizes and
  645. // the minimum of the maximum sizes
  646. // of all rows and columns, not taking
  647. // rowspans and colspans into account
  648. for (int row=0 ; row<gridSize.height ; row++)
  649. {
  650. List<ExtendedGridLayoutConstraints> gridRow = gridRows.get(row);
  651. for (int col=0 ; col<gridSize.width ; col++)
  652. {
  653. ExtendedGridLayoutConstraints cell = gridRow.get(col);
  654. if ((null != cell) && (null != cell.getComponent()))
  655. {
  656. Component component = cell.getComponent();
  657. Dimension minimumSize = component.getMinimumSize();
  658. Dimension preferredSize = component.getPreferredSize();
  659. Dimension maximumSize = component.getMaximumSize();
  660. if (!colspans.contains(cell))
  661. {
  662. minimumColWidths[col] = Math.max(minimumColWidths[col],minimumSize.width);
  663. preferredColWidths[col] = Math.max(preferredColWidths[col],preferredSize.width);
  664. maximumColWidths[col] = Math.max(maximumColWidths[col],maximumSize.width);
  665. }
  666. if (!rowspans.contains(cell))
  667. {
  668. minimumRowHeights[row] = Math.max(minimumRowHeights[row],minimumSize.height);
  669. preferredRowHeights[row] = Math.max(preferredRowHeights[row],preferredSize.height);
  670. maximumRowHeights[row] = Math.max(maximumRowHeights[row],maximumSize.height);
  671. }
  672. }
  673. }
  674. }
  675. // correct cases where
  676. // minimumColWidths[col] <= preferredColWidths[col] <= maximumColWidths[col]
  677. // is not true by clipping to the minimumColWidths and maximumColWidths
  678. for (int col=0 ; col<gridSize.width ; col++)
  679. {
  680. if (minimumColWidths[col] >= maximumColWidths[col])
  681. {
  682. maximumColWidths[col] = minimumColWidths[col];
  683. preferredColWidths[col] = minimumColWidths[col];
  684. }
  685. else if (preferredColWidths[col] < minimumColWidths[col])
  686. {
  687. preferredColWidths[col] = minimumColWidths[col];
  688. }
  689. else if (preferredColWidths[col] > maximumColWidths[col])
  690. {
  691. preferredColWidths[col] = maximumColWidths[col];
  692. }
  693. }
  694. // plug in the colspans and correct the minimum, preferred and
  695. // maximum column widths the colspans are part of
  696. for (ExtendedGridLayoutConstraints cell : colspans)
  697. {
  698. int fromCol = cell.getCol();
  699. int colspan = cell.getEffectiveColspan();
  700. int toCol = fromCol + colspan;
  701. int currentMinimumColWidth = 0;
  702. int currentPreferredColWidth = 0;
  703. int currentMaximumColWidth = 0;
  704. for (int col=fromCol ; col<toCol ; col++)
  705. {
  706. int minimumColWidth = minimumColWidths[col];
  707. if ((Integer.MAX_VALUE-minimumColWidth) < currentMinimumColWidth)
  708. {
  709. currentMinimumColWidth = Integer.MAX_VALUE;
  710. }
  711. else
  712. {
  713. currentMinimumColWidth += minimumColWidth;
  714. }
  715. int preferredColWidth = preferredColWidths[col];
  716. if ((Integer.MAX_VALUE-preferredColWidth) < currentPreferredColWidth)
  717. {
  718. currentPreferredColWidth = Integer.MAX_VALUE;
  719. }
  720. else
  721. {
  722. currentPreferredColWidth += preferredColWidth;
  723. }
  724. int maximumColWidth = maximumColWidths[col];
  725. if ((Integer.MAX_VALUE-maximumColWidth) < currentMaximumColWidth)
  726. {
  727. currentMaximumColWidth = Integer.MAX_VALUE;
  728. }
  729. else
  730. {
  731. currentMaximumColWidth += maximumColWidth;
  732. }
  733. }
  734. Component component = cell.getComponent();
  735. int wantedMaximumColWidth = component.getMaximumSize().width - ((colspan - 1) * hgap);
  736. if (currentMaximumColWidth < wantedMaximumColWidth)
  737. {
  738. redistributeSpace(currentMaximumColWidth,
  739. wantedMaximumColWidth,
  740. fromCol,toCol,
  741. maximumColWidths,
  742. maximumColWidths,
  743. maximumColWidths);
  744. }
  745. int wantedMinimumColWidth = component.getMinimumSize().width - ((colspan - 1) * hgap);
  746. if (currentMinimumColWidth < wantedMinimumColWidth)
  747. {
  748. redistributeSpace(currentMinimumColWidth,
  749. wantedMinimumColWidth,
  750. fromCol,toCol,
  751. minimumColWidths,
  752. minimumColWidths,
  753. maximumColWidths);
  754. }
  755. int wantedPreferredColWidth = component.getPreferredSize().width - ((colspan - 1) * hgap);
  756. if (currentPreferredColWidth < wantedPreferredColWidth)
  757. {
  758. redistributeSpace(currentPreferredColWidth,
  759. wantedPreferredColWidth,
  760. fromCol,toCol,
  761. preferredColWidths,
  762. minimumColWidths,
  763. maximumColWidths);
  764. }
  765. }
  766. // correct cases where
  767. // minimumColWidths[col] <= preferredColWidths[col] <= maximumColWidths[col]
  768. // is not true by clipping to the minimumColWidths and maximumColWidths
  769. for (int col=0 ; col<gridSize.width ; col++)
  770. {
  771. if (minimumColWidths[col] >= maximumColWidths[col])
  772. {
  773. maximumColWidths[col] = minimumColWidths[col];
  774. preferredColWidths[col] = minimumColWidths[col];
  775. }
  776. else if (preferredColWidths[col] < minimumColWidths[col])
  777. {
  778. preferredColWidths[col] = minimumColWidths[col];
  779. }
  780. else if (preferredColWidths[col] > maximumColWidths[col])
  781. {
  782. preferredColWidths[col] = maximumColWidths[col];
  783. }
  784. }
  785. // correct cases where
  786. // minimumRowHeights[row] <= preferredRowHeights[row] <= maximumRowHeights[row]
  787. // is not true by clipping to the minimumRowHeights and maximumRowHeights
  788. for (int row=0 ; row<gridSize.height ; row++)
  789. {
  790. if (minimumRowHeights[row] >= maximumRowHeights[row])
  791. {
  792. maximumRowHeights[row] = minimumRowHeights[row];
  793. preferredRowHeights[row] = minimumRowHeights[row];
  794. }
  795. else if (preferredRowHeights[row] < minimumRowHeights[row])
  796. {
  797. preferredRowHeights[row] = minimumRowHeights[row];
  798. }
  799. else if (preferredRowHeights[row] > maximumRowHeights[row])
  800. {
  801. preferredRowHeights[row] = maximumRowHeights[row];
  802. }
  803. }
  804. // plug in the rowspans and correct the minimum, preferred and
  805. // maximum row heights the rowspans are part of
  806. for (ExtendedGridLayoutConstraints cell : rowspans)
  807. {
  808. int fromRow = cell.getRow();
  809. int rowspan = cell.getEffectiveRowspan();
  810. int toRow = fromRow + rowspan;
  811. int currentMinimumRowHeight = 0;
  812. int currentPreferredRowHeight = 0;
  813. int currentMaximumRowHeight = 0;
  814. for (int row=fromRow ; row<toRow ; row++)
  815. {
  816. int minimumRowHeight = minimumRowHeights[row];
  817. if ((Integer.MAX_VALUE-minimumRowHeight) < currentMinimumRowHeight)
  818. {
  819. currentMinimumRowHeight = Integer.MAX_VALUE;
  820. }
  821. else
  822. {
  823. currentMinimumRowHeight += minimumRowHeight;
  824. }
  825. int preferredRowHeight = preferredRowHeights[row];
  826. if ((Integer.MAX_VALUE-preferredRowHeight) < currentPreferredRowHeight)
  827. {
  828. currentPreferredRowHeight = Integer.MAX_VALUE;
  829. }
  830. else
  831. {
  832. currentPreferredRowHeight += preferredRowHeight;
  833. }
  834. int maximumRowHeight = maximumRowHeights[row];
  835. if ((Integer.MAX_VALUE-maximumRowHeight) < currentMaximumRowHeight)
  836. {
  837. currentMaximumRowHeight = Integer.MAX_VALUE;
  838. }
  839. else
  840. {
  841. currentMaximumRowHeight += maximumRowHeight;
  842. }
  843. }
  844. Component component = cell.getComponent();
  845. int wantedMaximumRowHeight = component.getMaximumSize().height - ((rowspan - 1) * vgap);
  846. if (currentMaximumRowHeight < wantedMaximumRowHeight)
  847. {
  848. redistributeSpace(currentMaximumRowHeight,
  849. wantedMaximumRowHeight,
  850. fromRow,toRow,
  851. maximumRowHeights,
  852. maximumRowHeights,
  853. maximumRowHeights);
  854. }
  855. int wantedMinimumRowHeight = component.getMinimumSize().height - ((rowspan - 1) * vgap);
  856. if (currentMinimumRowHeight < wantedMinimumRowHeight)
  857. {
  858. redistributeSpace(currentMinimumRowHeight,
  859. wantedMinimumRowHeight,
  860. fromRow,toRow,
  861. minimumRowHeights,
  862. minimumRowHeights,
  863. maximumRowHeights);
  864. }
  865. int wantedPreferredRowHeight = component.getPreferredSize().height - ((rowspan - 1) * vgap);
  866. if (currentPreferredRowHeight < wantedPreferredRowHeight)
  867. {
  868. redistributeSpace(currentPreferredRowHeight,
  869. wantedPreferredRowHeight,
  870. fromRow,toRow,
  871. preferredRowHeights,
  872. minimumRowHeights,
  873. maximumRowHeights);
  874. }
  875. }
  876. // correct cases where
  877. // minimumRowHeights[row] <= preferredRowHeights[row] <= maximumRowHeights[row]
  878. // is not true by clipping to the minimumRowHeights and maximumRowHeights
  879. for (int row=0 ; row<gridSize.height ; row++)
  880. {
  881. if (minimumRowHeights[row] >= maximumRowHeights[row])
  882. {
  883. maximumRowHeights[row] = minimumRowHeights[row];
  884. preferredRowHeights[row] = minimumRowHeights[row];
  885. }
  886. else if (preferredRowHeights[row] < minimumRowHeights[row])
  887. {
  888. preferredRowHeights[row] = minimumRowHeights[row];
  889. }
  890. else if (preferredRowHeights[row] > maximumRowHeights[row])
  891. {
  892. preferredRowHeights[row] = maximumRowHeights[row];
  893. }
  894. }
  895. // copies the computed sizes to the result arrays
  896. if (fillRawSizes)
  897. {
  898. resultArrays[0] = minimumColWidths;
  899. resultArrays[1] = minimumRowHeights;
  900. resultArrays[2] = preferredColWidths;
  901. resultArrays[3] = preferredRowHeights;
  902. resultArrays[4] = maximumColWidths;
  903. resultArrays[5] = maximumRowHeights;
  904. }
  905. // sums up the sizes for return value
  906. int[] colWidths;
  907. int[] rowHeights;
  908. switch (layoutSize)
  909. {
  910. case MINIMUM:
  911. colWidths = minimumColWidths;
  912. rowHeights = minimumRowHeights;
  913. break;
  914. case PREFERRED:
  915. colWidths = preferredColWidths;
  916. rowHeights = preferredRowHeights;
  917. break;
  918. case MAXIMUM:
  919. colWidths = maximumColWidths;
  920. rowHeights = maximumRowHeights;
  921. break;
  922. default:
  923. throw new InternalError("Missing case branch for LayoutSize: " + layoutSize);
  924. }
  925. long totalWidth = 0;
  926. long totalHeight = 0;
  927. for (int width : colWidths)
  928. {
  929. totalWidth += width;
  930. }
  931. for (int height : rowHeights)
  932. {
  933. totalHeight += height;
  934. }
  935. // add space between components or between
  936. // componetns and the borders of the parent container
  937. if (!fillRawSizes)
  938. {
  939. Insets insets = parent.getInsets();
  940. totalWidth += insets.left + insets.right + ((gridSize.width - 1) * hgap) + distanceToBorders.left + distanceToBorders.right;
  941. totalHeight += insets.top + insets.bottom + ((gridSize.height - 1) * vgap) + distanceToBorders.top + distanceToBorders.bottom;
  942. }
  943. // clip the size to Integer.MAX_VALUE if too big
  944. if (totalWidth > Integer.MAX_VALUE)
  945. {
  946. totalWidth = Integer.MAX_VALUE;
  947. }
  948. if (totalHeight > Integer.MAX_VALUE)
  949. {
  950. totalHeight = Integer.MAX_VALUE;
  951. }
  952. return new Dimension((int)totalWidth,(int)totalHeight);
  953. }
  954. /**
  955. * Builds up the grid for the specified container,
  956. * given the components it contains.
  957. *
  958. * @param parent The container to be laid out
  959. * @param gridRows In this {@code List<List>} the grid gets stored
  960. * @param colspans In this {@code Set} the constraints which are part
  961. * of a colspan get stored
  962. * @param rowspans In this {@code Set} the constraints which are part
  963. * of a rowspan get stored
  964. * @return The amount of rows and columns in the grid
  965. */
  966. private Dimension buildGrid(Container parent, List<List<ExtendedGridLayoutConstraints>> gridRows,
  967. Set<ExtendedGridLayoutConstraints> colspans, Set<ExtendedGridLayoutConstraints> rowspans)
  968. {
  969. // put the parent's components in source rows
  970. List<List<ExtendedGridLayoutConstraints>> rows = new ArrayList<List<ExtendedGridLayoutConstraints>>();
  971. Component[] components = parent.getComponents();
  972. for (Component component : components)
  973. {
  974. if (component.isVisible())
  975. {
  976. ExtendedGridLayoutConstraints constraints = lookupConstraints(component).getWorkCopy();
  977. int rowNumber = constraints.getRow();
  978. for (int i=rowNumber, c=rows.size() ; i>=c ; i--)
  979. {
  980. rows.add(new ArrayList<ExtendedGridLayoutConstraints>());
  981. }
  982. List<ExtendedGridLayoutConstraints> row = rows.get(rowNumber);
  983. row.add(constraints);
  984. }
  985. }
  986. // initialize the rowIterators, gridRowIterators and gridRows
  987. List<Iterator<ExtendedGridLayoutConstraints>> rowIterators = new ArrayList<Iterator<ExtendedGridLayoutConstraints>>();
  988. List<ListIterator<ExtendedGridLayoutConstraints>> gridRowIterators = new ArrayList<ListIterator<ExtendedGridLayoutConstraints>>();
  989. boolean haveNext = false;
  990. for (List<ExtendedGridLayoutConstraints> row : rows)
  991. {
  992. Iterator<ExtendedGridLayoutConstraints> rowIterator = row.iterator();
  993. rowIterators.add(rowIterator);
  994. if (rowIterator.hasNext())
  995. {
  996. haveNext = true;
  997. }
  998. List<ExtendedGridLayoutConstraints> gridRow = new ArrayList<ExtendedGridLayoutConstraints>();
  999. gridRows.add(gridRow);
  1000. gridRowIterators.add(gridRow.listIterator());
  1001. }
  1002. // build the grid
  1003. int col = -1;
  1004. while (haveNext)
  1005. {
  1006. col++;
  1007. haveNext = false;
  1008. for (int row=0, c=gridRows.size() ; row<c ; row++)
  1009. {
  1010. Iterator<ExtendedGridLayoutConstraints> rowIterator = rowIterators.get(row);
  1011. ListIterator<ExtendedGridLayoutConstraints> gridRowIterator = gridRowIterators.get(row);
  1012. // look for a rowspan in the previous row
  1013. if (row > 0)
  1014. {
  1015. ExtendedGridLayoutConstraints rowspanSource = gridRows.get(row-1).get(col);
  1016. if (null != rowspanSource)
  1017. {
  1018. ExtendedGridLayoutConstraints rowspanPlaceholder = rowspanSource.getRowspanPlaceholder(true);
  1019. if (null != rowspanPlaceholder)
  1020. {
  1021. rowspans.add(rowspanSource);
  1022. gridRowIterator.add(rowspanPlaceholder);
  1023. if (null != rowspanPlaceholder.getColspanPlaceholder(false))
  1024. {
  1025. switch (rowspanPlaceholder.getColspan())
  1026. {
  1027. case REMAINDER:
  1028. break;
  1029. default:
  1030. haveNext = true;
  1031. }
  1032. }
  1033. else if (rowIterator.hasNext())
  1034. {
  1035. haveNext = true;
  1036. }
  1037. continue;
  1038. }
  1039. }
  1040. }
  1041. // look for a colspan in the previous column
  1042. if (gridRowIterator.hasPrevious())
  1043. {
  1044. ExtendedGridLayoutConstraints colspanSource = gridRowIterator.previous();
  1045. gridRowIterator.next();
  1046. if (null != colspanSource)
  1047. {
  1048. ExtendedGridLayoutConstraints colspanPlaceholder = colspanSource.getColspanPlaceholder(true);
  1049. if (null != colspanPlaceholder)
  1050. {
  1051. colspans.add(colspanSource);
  1052. gridRowIterator.add(colspanPlaceholder);
  1053. if (null != colspanPlaceholder.getColspanPlaceholder(false))
  1054. {
  1055. switch (colspanPlaceholder.getColspan())
  1056. {
  1057. case REMAINDER:
  1058. break;
  1059. default:
  1060. haveNext = true;
  1061. }
  1062. }
  1063. else if (rowIterator.hasNext())
  1064. {
  1065. haveNext = true;
  1066. }
  1067. continue;
  1068. }
  1069. }
  1070. }
  1071. // add a new element or null
  1072. if (rowIterator.hasNext())
  1073. {
  1074. ExtendedGridLayoutConstraints newConstraints = rowIterator.next();
  1075. newConstraints.setCol(col);
  1076. gridRowIterator.add(newConstraints);
  1077. if (null != newConstraints.getColspanPlaceholder(false))
  1078. {
  1079. switch (newConstraints.getColspan())
  1080. {
  1081. case REMAINDER:
  1082. break;
  1083. default:
  1084. haveNext = true;
  1085. }
  1086. }
  1087. else if (rowIterator.hasNext())
  1088. {
  1089. haveNext = true;
  1090. }
  1091. }
  1092. else
  1093. {
  1094. gridRowIterator.add(null);
  1095. }
  1096. }
  1097. }
  1098. // check the last gridRow for rowspans and probably add rows for these
  1099. haveNext = false;
  1100. int gridRowsSize = gridRows.size();
  1101. if (gridRowsSize > 0)
  1102. {
  1103. ListIterator<ExtendedGridLayoutConstraints> gridRowIterator = gridRows.get(gridRows.size()-1).listIterator();
  1104. while (gridRowIterator.hasNext())
  1105. {
  1106. ExtendedGridLayoutConstraints cell = gridRowIterator.next();
  1107. if ((null != cell) &&
  1108. ((REMAINDER != cell.getRowspan()) &&
  1109. (null != cell.getRowspanPlaceholder(false))))
  1110. {
  1111. haveNext = true;
  1112. break;
  1113. }
  1114. }
  1115. while (haveNext)
  1116. {
  1117. haveNext = false;
  1118. gridRowIterator = gridRows.get(gridRows.size()-1).listIterator();
  1119. List<ExtendedGridLayoutConstraints> gridRow = new ArrayList<ExtendedGridLayoutConstraints>();
  1120. gridRows.add(gridRow);
  1121. ListIterator<ExtendedGridLayoutConstraints> newGridRowIterator = gridRow.listIterator();
  1122. while (gridRowIterator.hasNext())
  1123. {
  1124. ExtendedGridLayoutConstraints cell = gridRowIterator.next();
  1125. if ((null != cell) &&
  1126. (null != cell.getRowspanPlaceholder(false)))
  1127. {
  1128. rowspans.add(cell);
  1129. ExtendedGridLayoutConstraints rowspanPlaceholder = cell.getRowspanPlaceholder(true);
  1130. newGridRowIterator.add(rowspanPlaceholder);
  1131. }
  1132. else
  1133. {
  1134. newGridRowIterator.add(null);
  1135. }
  1136. }
  1137. gridRowIterator = gridRow.listIterator();
  1138. while (gridRowIterator.hasNext())
  1139. {
  1140. ExtendedGridLayoutConstraints cell = gridRowIterator.next();
  1141. if ((null != cell) &&
  1142. ((REMAINDER != cell.getRowspan()) &&
  1143. (null != cell.getRowspanPlaceholder(false))))
  1144. {
  1145. haveNext = true;
  1146. break;
  1147. }
  1148. }
  1149. }
  1150. }
  1151. return new Dimension(col+1,gridRows.size());
  1152. }
  1153. /**
  1154. * Returns a string representation of the object. In general, the
  1155. * {@code toString} method returns a string that
  1156. * "textually represents" this object. The result should
  1157. * be a concise but informative representation that is easy for a
  1158. * person to read.
  1159. *
  1160. * @return a string representation of the object.
  1161. */
  1162. public String toString()
  1163. {
  1164. return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap
  1165. + ",distanceToBorders=" + distanceToBorders
  1166. + ",comptable=" + comptable + "]";
  1167. }
  1168. }