PageRenderTime 90ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/bundles/plugins-trunk/CommonControls/ise/java/awt/KappaLayout.java

#
Java | 1356 lines | 773 code | 112 blank | 471 comment | 200 complexity | 3a53cc3465fb2dfea648159d6e03aef1 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. package ise.java.awt;
  2. import java.awt.LayoutManager2;
  3. import java.awt.Component;
  4. import java.awt.Container;
  5. import java.awt.Dimension;
  6. import java.awt.Insets;
  7. import java.awt.Point;
  8. import java.io.Serializable;
  9. import java.util.BitSet;
  10. import java.util.Enumeration;
  11. import java.util.Hashtable;
  12. import java.util.Vector;
  13. /**
  14. * KappaLayout, a Java layout manager.<br>
  15. * Copyright (C) 2000, Dale Anson<br>
  16. *<br>
  17. * This library is free software; you can redistribute it and/or<br>
  18. * modify it under the terms of the GNU Lesser General Public<br>
  19. * License as published by the Free Software Foundation; either<br>
  20. * version 2.1 of the License, or (at your option) any later version.<br>
  21. *<br>
  22. * This library is distributed in the hope that it will be useful,<br>
  23. * but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
  24. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU<br>
  25. * Lesser General Public License for more details.<br>
  26. *<br>
  27. * You should have received a copy of the GNU Lesser General Public<br>
  28. * License along with this library; if not, write to the Free Software<br>
  29. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA<br>
  30. * <p>
  31. * KappaLayout -- similar to others, but this one's simpler and easier to use.<br>
  32. * Example use:<br>
  33. * This will put a button on a panel in the top of its cell, stretched to
  34. * fill the cell width, with a 3 pixel pad:<br>
  35. * <code>
  36. * Panel p = new Panel(new KappaLayout());
  37. * Button b = new Button("OK");
  38. * p.add(b, "0, 0, 1, 2, 2, w, 3");
  39. * </code>
  40. * <br>
  41. * The constraints string has this layout:<br>
  42. * "x, y, w, h, a, s, p"<br>
  43. * defined as follows:<br>
  44. * <ul>
  45. * <li>'x' is the column to put the component, default is 0<br>
  46. * <li>'y' is the row to put the component, default is 0<br>
  47. * <li>'w' is the width of the component in columns (column span), default is 1.
  48. * Can also be R or r, which means the component will span the remaining cells
  49. * in the row.<br>
  50. * <li>'h' is the height of the component in rows (row span), default is 1.
  51. * Can also be R or r, which means the component will span the remaining cells
  52. * in the column.<br>
  53. * <li>'a' is the alignment within the cell. 'a' can be a value between 0 and 8,
  54. * inclusive, (default is 0) and causes the alignment of the component within the cell to follow
  55. * this pattern:<br>
  56. * 8 1 2<br>
  57. * 7 0 3<br>
  58. * 6 5 4<br>, or<br>
  59. * 0 horizontal center, vertical center,<br>
  60. * 1 horizontal center, vertical top,<br>
  61. * 2 horizontal right, vertical top,<br>
  62. * 3 horizontal right, vertical center,<br>
  63. * 4 horizontal right, vertical bottom,<br>
  64. * 5 horizontal center, vertical bottom,<br>
  65. * 6 horizontal left, vertical bottom,<br>
  66. * 7 horizontal left, vertical center,<br>
  67. * 8 horizontal left, vertical top.<br>
  68. * <p>
  69. * By popular request, the alignment constraint can also be represented as:<br>
  70. * NW N NE<br>
  71. * &nbsp;W 0 E<br>
  72. * SW S SE<br>
  73. * which are compass directions for alignment within the cell.
  74. * <li>'s' is the stretch value. 's' can have these values:<br>
  75. * 'w' stretch to fill cell width<br>
  76. * 'h' stretch to fill cell height<br>
  77. * 'wh' or 'hw' stretch to fill both cell width and cell height<br>
  78. * '0' (character 'zero') no stretch (default)
  79. * <li>'p' is the amount of padding to put around the component. This much blank
  80. * space will be applied on all sides of the component, default is 0.
  81. * </ul>
  82. * Parameters may be omitted (default values will be used), e.g.,
  83. * <code> p.add(new Button("OK), "1,4,,,w,");</code><br>
  84. * which means put the button at column 1, row 4, default 1 column wide, default
  85. * 1 row tall, stretch to fit width of column, no padding. <br>
  86. * Spaces in the parameter string are ignored, so these are identical:<br>
  87. * <code> p.add(new Button("OK), "1,4,,,w,");</code><br>
  88. * <code> p.add(new Button("OK), " 1, 4, , , w");</code><p>
  89. * Rather than use a constraints string, a Constraints object may be used
  90. * directly, similar to how GridBag uses a GridBagConstraint. E.g,<br>
  91. * <code>
  92. * Panel p = new Panel();<br>
  93. * KappaLayout tl = new KappaLayout();<br>
  94. * p.setLayout(tl);<br>
  95. * KappaLayout.Constraints con = tl.getConstraint();<br>
  96. * con.x = 1;<br>
  97. * con.y = 2;<br>
  98. * con.w = 2;<br>
  99. * con.h = 2;<br>
  100. * con.s = "wh";<br>
  101. * panel.add(new Button("OK"), con);<br>
  102. * con.x = 3;<br>
  103. * panel.add(new Button("Cancel"), con);<br>
  104. * </code><br>
  105. * Note that the same Constraints can be reused, thereby reducing the number of
  106. * objects created.<p>
  107. * @author Dale Anson
  108. */
  109. public class KappaLayout implements LayoutManager2, Serializable {
  110. // overall preferred width of components in container
  111. protected int _preferred_width = 0;
  112. // overall preferred height of components in container
  113. protected int _preferred_height = 0;
  114. protected boolean _size_unknown = true;
  115. protected int _col_count = 0; // number of columns in the layout
  116. protected int _row_count = 0; // number of rows in the layout
  117. // storage for component constraints
  118. // key is the component, value is a Constraints
  119. protected Hashtable _constraints = new Hashtable();
  120. // model of the table -- key is a Point with Point.x
  121. // representing the column, Point.y representing the row. As most layouts
  122. // are sparse, this is an efficient way to represent the table without
  123. // creating excess objects. The value is the dimension of the component.
  124. protected Hashtable _table = null;
  125. // a Dimension with no width and no height, used repeatedly to represent
  126. // empty cells.
  127. protected Dimension _0dim = new Dimension( 0, 0 );
  128. // model of the component layout in the table -- key is a Point with Point.x
  129. // representing the column, Point.y representing the row. The value is a
  130. // reference to the component.
  131. protected Hashtable _components = null;
  132. // in both _col_widths and _row_heights, if the number is negative, the column or
  133. // row will be treated as having a fixed width or height. During layout, the negative
  134. // numbers will be treated as positive numbers -- negative width and negative height
  135. // being meaningless concepts.
  136. protected int[] _col_widths; // stores a width per column which is the widest preferred width of components in each column
  137. protected int[] _row_heights; // stores a height per row which is the tallest preferred height of components in each row
  138. // storage for columns and rows that want to be the same size. Each element
  139. // in these vectors is an int[], with each item in the array being a column
  140. // or row number.
  141. protected Vector _same_width_cols;
  142. protected Vector _same_height_rows;
  143. // if true, spread extra space equally between components in both directions,
  144. // doesn't stretch the components, just the space between them
  145. protected boolean _stretch = false;
  146. // by user request, the following are alternate compass directions for
  147. // the alignment constraint. Center is still 0.
  148. /**
  149. * For alignment constraint (a), align component to North in cell.
  150. */
  151. public static final int N = 1; // north
  152. /**
  153. * For alignment constraint (a), align component to NorthEast in cell.
  154. */
  155. public static final int NE = 2; // northeast
  156. /**
  157. * For alignment constraint (a), align component to East in cell.
  158. */
  159. public static final int E = 3; // east
  160. /**
  161. * For alignment constraint (a), align component to SouthEast in cell.
  162. */
  163. public static final int SE = 4; // southeast
  164. /**
  165. * For alignment constraint (a), align component to South in cell.
  166. */
  167. public static final int S = 5; // south
  168. /**
  169. * For alignment constraint (a), align component to SouthWest in cell.
  170. */
  171. public static final int SW = 6; // southwest
  172. /**
  173. * For alignment constraint (a), align component to West in cell.
  174. */
  175. public static final int W = 7; // west
  176. /**
  177. * For alignment constraint (a), align component to NorthWest in cell.
  178. */
  179. public static final int NW = 8; // northwest
  180. /**
  181. * Convenience setting for width (w) or height (h), causes component to use
  182. * Remaining cells.
  183. */
  184. public static final int R = Integer.MAX_VALUE;
  185. /**
  186. * Default constructor, no stretching.
  187. */
  188. public KappaLayout() {
  189. this( false );
  190. }
  191. /**
  192. * Constructor, allows stretching.
  193. * @param s if true, stretches layout to fill container by adding extra space
  194. * between components, does not stretch the components, just the space between them.
  195. */
  196. public KappaLayout( boolean s ) {
  197. _stretch = s;
  198. }
  199. /**
  200. * Useful for debugging a layout. Call after components are showing to make
  201. * sure all data is available.
  202. * @return a String with lots of useful info about the layout.
  203. */
  204. public String toString() {
  205. StringBuffer sb = new StringBuffer();
  206. sb.append( "-------------------------------\n" );
  207. sb.append( getClass().getName() + ":\n" );
  208. sb.append( "columns=" + _col_count );
  209. sb.append( ", rows=" + _row_count );
  210. sb.append( ", cells=" + ( _col_count * _row_count ) + "\n" );
  211. sb.append( "preferred width=" + _preferred_width );
  212. sb.append( ", preferred height=" + _preferred_height + "\n" );
  213. if ( _col_widths != null ) {
  214. sb.append( "column widths (left to right):" );
  215. for ( int i = 0; i < _col_widths.length; i++ )
  216. sb.append( _col_widths[ i ] + "," );
  217. }
  218. if ( _row_heights != null ) {
  219. sb.append( "\nrow heights (top to bottom):" );
  220. for ( int i = 0; i < _row_heights.length; i++ )
  221. sb.append( _row_heights[ i ] + "," );
  222. }
  223. if ( _constraints != null ) {
  224. sb.append( "\ncomponent count=" + _constraints.size() );
  225. sb.append( "\ncomponents (no order):\n" );
  226. Enumeration en = _constraints.keys();
  227. while ( en.hasMoreElements() )
  228. sb.append( en.nextElement() + "\n" );
  229. }
  230. sb.append( "-------------------------------\n" );
  231. return sb.toString();
  232. }
  233. /**
  234. * Required by LayoutManager, simply calls <code>addLayoutComponent(Component, Object)</code>.
  235. */
  236. public void addLayoutComponent( String n, Component c ) {
  237. addLayoutComponent( c, n );
  238. }
  239. /**
  240. * Required by LayoutManager.
  241. */
  242. public void removeLayoutComponent( Component c ) {
  243. if (c == null)
  244. return;
  245. synchronized ( c.getTreeLock() ) {
  246. if ( _constraints != null ) {
  247. _constraints.remove( c );
  248. if (_components == null)
  249. return;
  250. Enumeration keys = _components.keys();
  251. while(keys.hasMoreElements()) {
  252. Object key = keys.nextElement();
  253. Object value = _constraints.get(key);
  254. if (value == null)
  255. continue;
  256. if (value.equals(c)) {
  257. _components.remove(key);
  258. break;
  259. }
  260. }
  261. keys = _table.keys();
  262. while(keys.hasMoreElements()) {
  263. Object key = keys.nextElement();
  264. Object value = _constraints.get(key);
  265. if (value == null)
  266. continue;
  267. if (value.equals(c)) {
  268. _table.remove(key);
  269. break;
  270. }
  271. }
  272. _size_unknown = true;
  273. calculateDimensions();
  274. }
  275. }
  276. }
  277. /**
  278. * Required by LayoutManager.
  279. */
  280. public Dimension preferredLayoutSize( Container parent ) {
  281. synchronized ( parent.getTreeLock() ) {
  282. Dimension dim = new Dimension( 0, 0 );
  283. _size_unknown = true;
  284. calculateDimensions();
  285. Insets insets = parent.getInsets();
  286. dim.width = _preferred_width + insets.left + insets.right;
  287. dim.height = _preferred_height + insets.top + insets.bottom;
  288. return dim;
  289. }
  290. }
  291. /**
  292. * Required by LayoutManager.
  293. * @return <code>preferredLayoutSize(parent)</code>
  294. */
  295. public Dimension minimumLayoutSize( Container parent ) {
  296. synchronized ( parent.getTreeLock() ) {
  297. return preferredLayoutSize( parent );
  298. }
  299. }
  300. /**
  301. * Required by LayoutManager, does all the real layout work.
  302. */
  303. public void layoutContainer( Container parent ) {
  304. synchronized ( parent.getTreeLock() ) {
  305. Insets insets = parent.getInsets();
  306. int max_width = parent.getSize().width - ( insets.left + insets.right );
  307. int max_height = parent.getSize().height - ( insets.top + insets.bottom );
  308. int x = insets.left; // x and y location to put component in pixels
  309. int y = insets.top;
  310. int xfill = 0; // how much extra space to put between components
  311. int yfill = 0; // when stretching to fill entire container
  312. // make sure preferred size is known, a side effect is that countColumns
  313. // and countRows are automatically called.
  314. calculateDimensions();
  315. // if necessary, calculate the amount of padding to add between the
  316. // components to fill the container
  317. if ( _stretch ) {
  318. if ( max_width > _preferred_width && _col_count > 1 ) {
  319. xfill = ( max_width - _preferred_width ) / ( _col_count - 1 );
  320. }
  321. if ( max_height > _preferred_height && _row_count > 1 ) {
  322. yfill = ( max_height - _preferred_height ) / ( _row_count - 1 );
  323. }
  324. }
  325. // do the layout. Components are handled by columns, top to bottom,
  326. // left to right. i is current column, j is current row.
  327. Point cell = new Point();
  328. for ( int i = 0; i < _col_count; i++ ) {
  329. cell.x = i;
  330. y = insets.top;
  331. if ( i > 0 ) {
  332. x += Math.abs( _col_widths[ i - 1 ] );
  333. if ( _stretch && i < _col_count ) {
  334. x += xfill;
  335. }
  336. }
  337. for ( int j = 0; j < _row_count; j++ ) {
  338. cell.y = j;
  339. if ( j > 0 ) {
  340. y += Math.abs( _row_heights[ j - 1 ] );
  341. if ( _stretch && j < _row_count ) {
  342. y += yfill;
  343. }
  344. }
  345. Component c = ( Component ) _components.get( cell );
  346. if ( c != null && c.isVisible() ) {
  347. Dimension d = c.getPreferredSize();
  348. Constraints q = ( Constraints ) _constraints.get( c );
  349. // calculate width of spanned columns
  350. int sum_cols = 0;
  351. if ( q.w == R ) {
  352. for ( int n = i; n < _col_count; n++ ) {
  353. sum_cols += Math.abs( _col_widths[ n ] );
  354. }
  355. }
  356. else {
  357. for ( int n = i; n < i + q.w; n++ ) {
  358. sum_cols += Math.abs( _col_widths[ n ] );
  359. }
  360. }
  361. // calculate height of spanned rows
  362. int sum_rows = 0;
  363. if ( q.h == R ) {
  364. for ( int n = i; n < _row_count; n++ ) {
  365. sum_rows += Math.abs( _row_heights[ n ] );
  366. }
  367. }
  368. else {
  369. for ( int n = j; n < j + q.h; n++ ) {
  370. sum_rows += Math.abs( _row_heights[ n ] );
  371. }
  372. }
  373. // stretch and pad if required
  374. if ( q.s.indexOf( "w" ) != -1 ) {
  375. d.width = sum_cols - q.p * 2;
  376. }
  377. if ( q.s.indexOf( "h" ) != -1 ) {
  378. d.height = sum_rows - q.p * 2;
  379. }
  380. // calculate adjustment
  381. int x_adj = sum_cols - d.width; // max amount to put component at right edge of spanned cell(s)
  382. int y_adj = sum_rows - d.height; // max amount to put component at bottom edge of spanned cell(s)
  383. // in each case, add the correction for the cell, then subtract
  384. // the correction after applying it. This prevents the corrections
  385. // from improperly accumulating across cells. Padding must be handled
  386. // explicitly for each case.
  387. // Alignment follows this pattern within the spanned cells:
  388. // 8 1 2 new pattern: NW N NE
  389. // 7 0 3 W 0 E
  390. // 6 5 4 SW S SE
  391. switch ( q.a ) {
  392. case N: // top center
  393. x += x_adj / 2 ;
  394. y += q.p;
  395. c.setBounds( x, y, d.width, d.height );
  396. x -= x_adj / 2;
  397. y -= q.p;
  398. break;
  399. case NE: // top right
  400. x += x_adj - q.p;
  401. y += q.p;
  402. c.setBounds( x, y, d.width, d.height );
  403. x -= x_adj - q.p;
  404. y -= q.p;
  405. break;
  406. case E: // center right
  407. x += x_adj - q.p;
  408. y += y_adj / 2;
  409. c.setBounds( x, y, d.width, d.height );
  410. x -= x_adj - q.p;
  411. y -= y_adj / 2;
  412. break;
  413. case SE: // bottom right
  414. x += x_adj - q.p;
  415. y += y_adj - q.p;
  416. c.setBounds( x, y, d.width, d.height );
  417. x -= x_adj - q.p;
  418. y -= y_adj - q.p;
  419. break;
  420. case S: // bottom center
  421. x += x_adj / 2;
  422. y += y_adj - q.p;
  423. c.setBounds( x, y, d.width, d.height );
  424. x -= x_adj / 2;
  425. y -= y_adj - q.p;
  426. break;
  427. case SW: // bottom left
  428. x += q.p;
  429. y += y_adj - q.p;
  430. c.setBounds( x, y, d.width, d.height );
  431. x -= q.p;
  432. y -= y_adj - q.p;
  433. break;
  434. case W: // center left
  435. x += q.p;
  436. y += y_adj / 2;
  437. c.setBounds( x, y, d.width, d.height );
  438. x -= q.p;
  439. y -= y_adj / 2;
  440. break;
  441. case NW: // top left
  442. x += q.p;
  443. y += q.p;
  444. c.setBounds( x, y, d.width, d.height );
  445. x -= q.p;
  446. y -= q.p;
  447. break;
  448. case 0: // dead center
  449. default:
  450. x += x_adj / 2;
  451. y += y_adj / 2;
  452. c.setBounds( x, y, d.width, d.height );
  453. x -= x_adj / 2;
  454. y -= y_adj / 2;
  455. break;
  456. }
  457. }
  458. }
  459. }
  460. }
  461. }
  462. /**
  463. * Required by LayoutManager2. Will NOT add component if either component or
  464. * constraints are null, or if constraints is not a String or
  465. * Kappa/LambdaLayout.Constraint.
  466. */
  467. public void addLayoutComponent( Component comp, Object constraint ) {
  468. synchronized ( comp.getTreeLock() ) {
  469. if ( comp == null ) {
  470. throw new IllegalArgumentException( "No component." );
  471. }
  472. if ( constraint == null ) {
  473. throw new IllegalArgumentException( "No constraint." );
  474. }
  475. if ( constraint instanceof Constraints ) {
  476. // clone and store a Constraint so user can reuse his original
  477. Constraints q = ( Constraints ) constraint;
  478. _constraints.put( comp, q.clone() );
  479. // if component is a rigid strut, set the column and/or row size
  480. if ( comp instanceof Strut ) {
  481. Strut strut = ( Strut ) comp;
  482. if ( strut.isRigid() ) {
  483. Dimension d = strut.getSize();
  484. if ( d.width > 0 ) {
  485. setColumnWidth( q.x, d.width );
  486. }
  487. if ( d.height > 0 ) {
  488. setRowHeight( q.y, d.height );
  489. }
  490. }
  491. }
  492. _size_unknown = true;
  493. // that's all that needs to be done for this case (Constraint and
  494. // Component), so return now
  495. return ;
  496. }
  497. // already dealt with Constraint, so check for constraint
  498. // String, if not a String, bail out
  499. if ( !( constraint instanceof String ) )
  500. throw new IllegalArgumentException( "Illegal constraint object." );
  501. // parse constraint string into tokens. There may be as few as 0 tokens,
  502. // or as many as 7.
  503. Vector tokens = new Vector();
  504. String token;
  505. String c = constraint.toString();
  506. while ( c.length() > 0 ) {
  507. int comma = c.indexOf( ',' );
  508. // find a token
  509. if ( comma != -1 ) {
  510. token = c.substring( 0, comma );
  511. c = c.substring( comma + 1 );
  512. }
  513. else {
  514. token = c;
  515. c = "";
  516. }
  517. // clean it up
  518. if ( token != null )
  519. token = token.trim();
  520. // if there's something left, store it, otherwise, mark missing
  521. // token with -1 (example of constraint string with missing tokens:
  522. // "1,1,,,,w", width, height, alignment are missing)
  523. if ( token != null && token.length() > 0 )
  524. tokens.addElement( token );
  525. else
  526. tokens.addElement( "-1" );
  527. }
  528. // turn tokens into a Constraints. Default Constraints values are used
  529. // for missing or non-specified values
  530. Constraints q = new Constraints();
  531. Enumeration en = tokens.elements();
  532. // get column
  533. if ( en.hasMoreElements() ) {
  534. token = en.nextElement().toString();
  535. try {
  536. q.x = Integer.parseInt( token );
  537. if ( q.x < 0 )
  538. q.x = 0;
  539. }
  540. catch ( Exception e ) {
  541. q.x = 0;
  542. }
  543. }
  544. // get row
  545. if ( en.hasMoreElements() ) {
  546. token = en.nextElement().toString();
  547. try {
  548. q.y = Integer.parseInt( token );
  549. if ( q.y < 0 )
  550. q.y = 0;
  551. }
  552. catch ( Exception e ) {
  553. q.y = 0;
  554. }
  555. }
  556. // get column span (width)
  557. if ( en.hasMoreElements() ) {
  558. token = en.nextElement().toString();
  559. if ( token.equalsIgnoreCase( "R" ) ) {
  560. q.w = R;
  561. }
  562. else {
  563. try {
  564. q.w = Integer.parseInt( token );
  565. if ( q.w < 1 )
  566. q.w = 1;
  567. }
  568. catch ( Exception e ) {
  569. q.w = 1;
  570. }
  571. }
  572. }
  573. // get row span (height)
  574. if ( en.hasMoreElements() ) {
  575. token = en.nextElement().toString();
  576. if ( token.equalsIgnoreCase( "R" ) ) {
  577. q.h = R;
  578. }
  579. else {
  580. try {
  581. q.h = Integer.parseInt( token );
  582. if ( q.h < 1 )
  583. q.h = 1;
  584. }
  585. catch ( Exception e ) {
  586. q.h = 1;
  587. }
  588. }
  589. }
  590. // get alignment
  591. if ( en.hasMoreElements() ) {
  592. token = en.nextElement().toString().trim();
  593. if ( token.equalsIgnoreCase( "N" ) || token.equals( "1" ) )
  594. q.a = N;
  595. else if ( token.equalsIgnoreCase( "NE" ) || token.equals( "2" ) )
  596. q.a = NE;
  597. else if ( token.equalsIgnoreCase( "E" ) || token.equals( "3" ) )
  598. q.a = E;
  599. else if ( token.equalsIgnoreCase( "SE" ) || token.equals( "4" ) )
  600. q.a = SE;
  601. else if ( token.equalsIgnoreCase( "S" ) || token.equals( "5" ) )
  602. q.a = S;
  603. else if ( token.equalsIgnoreCase( "SW" ) || token.equals( "6" ) )
  604. q.a = SW;
  605. else if ( token.equalsIgnoreCase( "W" ) || token.equals( "7" ) )
  606. q.a = W;
  607. else if ( token.equalsIgnoreCase( "NW" ) || token.equals( "8" ) )
  608. q.a = NW;
  609. else
  610. q.a = 0;
  611. }
  612. // get component stretch
  613. if ( en.hasMoreElements() ) {
  614. token = en.nextElement().toString().trim().toLowerCase();
  615. if ( token.equals( "w" ) || token.equals( "h" ) || token.equals( "wh" ) || token.equals( "hw" ) ) {
  616. q.s = token;
  617. }
  618. else
  619. q.s = "0";
  620. }
  621. // get component padding
  622. if ( en.hasMoreElements() ) {
  623. token = en.nextElement().toString();
  624. try {
  625. q.p = Integer.parseInt( token );
  626. if ( q.p < 0 )
  627. q.p = 0;
  628. }
  629. catch ( Exception e ) {
  630. q.p = 0;
  631. }
  632. }
  633. // save the component and its constraints for later use
  634. _constraints.put( comp, q );
  635. // if component is a rigid strut, set the column and/or row size
  636. if ( comp instanceof Strut ) {
  637. Strut strut = ( Strut ) comp;
  638. if ( strut.isRigid() ) {
  639. Dimension d = strut.getSize();
  640. if ( d.width > 0 ) {
  641. setColumnWidth( q.x, d.width );
  642. }
  643. if ( d.height > 0 ) {
  644. setRowHeight( q.y, d.height );
  645. }
  646. }
  647. }
  648. _size_unknown = true;
  649. }
  650. }
  651. /**
  652. * Required by LayoutManager2.
  653. * @return <code>preferredLayoutSize(parent)</code>
  654. */
  655. public Dimension maximumLayoutSize( Container c ) {
  656. synchronized ( c.getTreeLock() ) {
  657. return preferredLayoutSize( c );
  658. }
  659. }
  660. /**
  661. * Required by LayoutManager2.
  662. * @return 0.5f
  663. */
  664. public float getLayoutAlignmentX( Container c ) {
  665. return 0.5f; // default to centered.
  666. }
  667. /**
  668. * Required by LayoutManager2.
  669. * @return 0.5f
  670. */
  671. public float getLayoutAlignmentY( Container c ) {
  672. return 0.5f; // default to centered.
  673. }
  674. /**
  675. * Required by LayoutManager2.
  676. */
  677. public void invalidateLayout( Container c ) {
  678. /* I would think this is the right thing to do, but doing this causes
  679. the layout to be wrong every time. Also causes the stretch option to
  680. fail.
  681. _size_unknown = true;
  682. */
  683. }
  684. /**
  685. * Calculate preferred size and other dimensions.
  686. */
  687. protected void calculateDimensions() {
  688. if ( !_size_unknown )
  689. return ;
  690. _preferred_width = 0;
  691. _preferred_height = 0;
  692. Dimension dim = null;
  693. // count columns and rows
  694. countColumns();
  695. countRows();
  696. // set up table and component maps
  697. if ( _table == null )
  698. _table = new Hashtable( 23, 0.75f );
  699. else
  700. _table.clear();
  701. if ( _components == null )
  702. _components = new Hashtable( 23, 0.75f );
  703. else
  704. _components.clear();
  705. // set up storage for max col width and max row height. These arrays
  706. // have an entry per column and row and will hold the largest width or
  707. // height of components in each column or row respectively.
  708. int[] temp;
  709. if ( _col_widths != null ) {
  710. // if column count has changed, need to preserve existing column widths
  711. // in case one or more remaining columns has been set to a fixed width.
  712. temp = new int[ _col_widths.length ];
  713. System.arraycopy( _col_widths, 0, temp, 0, _col_widths.length );
  714. _col_widths = new int[ _col_count ];
  715. System.arraycopy( temp, 0, _col_widths, 0, Math.min( temp.length, _col_widths.length ) );
  716. }
  717. else {
  718. _col_widths = new int[ _col_count ];
  719. }
  720. if ( _row_heights != null ) {
  721. // if row count has changed, need to preserve existing row heights
  722. // in case one or more remaining rows has been set to a fixed height.
  723. temp = new int[ _row_heights.length ];
  724. System.arraycopy( _row_heights, 0, temp, 0, _row_heights.length );
  725. _row_heights = new int[ _row_count ];
  726. System.arraycopy( temp, 0, _row_heights, 0, Math.min( temp.length, _row_heights.length ) );
  727. }
  728. else {
  729. _row_heights = new int[ _row_count ];
  730. }
  731. // get the constraints
  732. Enumeration en = _constraints.keys();
  733. while ( en.hasMoreElements() ) {
  734. Component c = ( Component ) en.nextElement();
  735. Constraints q = ( Constraints ) _constraints.get( c );
  736. if ( q.w == R )
  737. q.w = _col_count - q.x;
  738. if ( q.h == R )
  739. q.h = _row_count - q.y;
  740. // store the component in its (x, y) location
  741. _components.put( new Point( q.x, q.y ), c );
  742. // as components can span columns and rows, store the maximum dimension
  743. // of the component that could be in the spanned cells. Note that it
  744. // may happen that none of the component is actually in the cell.
  745. dim = new Dimension(c.getPreferredSize().width, c.getPreferredSize().height);
  746. dim.width += q.p * 2; // adjust for padding if necessary
  747. dim.height += q.p * 2;
  748. dim.width /= q.w;
  749. dim.height /= q.h;
  750. for ( int i = q.x; i < q.x + q.w; i++ ) {
  751. for ( int j = q.y; j < q.y + q.h; j++ ) {
  752. _table.put( new Point( i, j ), dim );
  753. }
  754. }
  755. }
  756. // calculate preferred width
  757. int col_width = 0;
  758. for ( int i = 0; i < _col_count; i++ ) {
  759. for ( int j = 0; j < _row_count; j++ ) {
  760. Dimension p = ( Dimension ) _table.get( new Point( i, j ) );
  761. if ( p == null )
  762. p = _0dim;
  763. col_width = Math.max( p.width, col_width );
  764. }
  765. // store max width of each column
  766. if ( _col_widths[ i ] >= 0 ) {
  767. _col_widths[ i ] = col_width;
  768. _preferred_width += col_width;
  769. }
  770. else {
  771. _preferred_width += Math.abs( _col_widths[ i ] );
  772. }
  773. col_width = 0;
  774. }
  775. // adjust for same width columns
  776. if ( _same_width_cols != null ) {
  777. en = _same_width_cols.elements();
  778. while ( en.hasMoreElements() ) {
  779. int[] same = ( int[] ) en.nextElement();
  780. // find widest column of this group
  781. int widest = same[ 0 ];
  782. for ( int i = 0; i < same.length; i++ ) {
  783. if ( same[ i ] < _col_widths.length )
  784. widest = Math.max( widest, _col_widths[ same[ i ] ] );
  785. }
  786. // now set all columns to this widest width
  787. for ( int i = 0; i < same.length; i++ ) {
  788. if ( same[ i ] < _col_widths.length ) {
  789. _preferred_width += widest - _col_widths[ same[ i ] ];
  790. _col_widths[ same[ i ] ] = widest;
  791. }
  792. }
  793. }
  794. }
  795. // calculate preferred height
  796. int row_height = 0;
  797. for ( int j = 0; j < _row_count; j++ ) {
  798. for ( int i = 0; i < _col_count; i++ ) {
  799. Dimension p = ( Dimension ) _table.get( new Point( i, j ) );
  800. if ( p == null )
  801. p = _0dim;
  802. row_height = Math.max( p.height, row_height );
  803. }
  804. // store max height of each row
  805. if ( _row_heights[ j ] >= 0 ) {
  806. _row_heights[ j ] = row_height;
  807. _preferred_height += row_height;
  808. }
  809. else {
  810. _preferred_height += Math.abs( _row_heights[ j ] );
  811. }
  812. row_height = 0;
  813. }
  814. // adjust for same height rows
  815. if ( _same_height_rows != null ) {
  816. en = _same_height_rows.elements();
  817. while ( en.hasMoreElements() ) {
  818. int[] same = ( int[] ) en.nextElement();
  819. // find tallest row of this group
  820. int tallest = same[ 0 ];
  821. for ( int i = 0; i < same.length; i++ ) {
  822. if ( same[ i ] < _row_heights.length )
  823. tallest = Math.max( tallest, _row_heights[ same[ i ] ] );
  824. }
  825. // now set all rows to this tallest height
  826. for ( int i = 0; i < same.length; i++ ) {
  827. if ( same[ i ] < _row_heights.length ) {
  828. _preferred_height += tallest - _row_heights[ same[ i ] ];
  829. _row_heights[ same[ i ] ] = tallest;
  830. }
  831. }
  832. }
  833. }
  834. _size_unknown = false;
  835. }
  836. /**
  837. * Calculate number of columns in table.
  838. */
  839. private void countColumns() {
  840. _col_count = 0;
  841. Hashtable rows = new Hashtable();
  842. // get the constraints
  843. Enumeration en = _constraints.elements();
  844. while ( en.hasMoreElements() ) {
  845. Constraints q = ( Constraints ) en.nextElement();
  846. // figure out which columns this component spans. The BitSet represents
  847. // a row, and for each non-empty column in the row, a bit is set. The
  848. // BitSets representing each row are stored in the 'rows' Hashtable.
  849. BitSet row = null;
  850. String y = String.valueOf( q.y );
  851. if ( !rows.containsKey( y ) ) {
  852. row = new BitSet();
  853. rows.put( y, row );
  854. }
  855. row = ( BitSet ) rows.get( y );
  856. int last_col = ( q.w == R ? q.x + 1 : q.x + q.w );
  857. for ( int i = q.x; i < last_col; i++ )
  858. row.set( i );
  859. }
  860. // calculate the number of columns by going through each BitSet and
  861. // counting the number of set bits. The highest bit is the number of
  862. // columns.
  863. en = rows.elements();
  864. while ( en.hasMoreElements() ) {
  865. BitSet row = ( BitSet ) en.nextElement();
  866. for ( int i = 0; i < row.size(); i++ ) {
  867. if ( row.get( i ) )
  868. _col_count = Math.max( _col_count, i + 1 ); // add 1 as column index is 0-based
  869. }
  870. }
  871. }
  872. /**
  873. * Calculate number of rows in table.
  874. */
  875. private void countRows() {
  876. // this is done exactly in the same manner as countColumns, see the comments
  877. // there for details on the counting algorithm
  878. _row_count = 0;
  879. Hashtable cols = new Hashtable();
  880. Enumeration en = _constraints.elements();
  881. while ( en.hasMoreElements() ) {
  882. Constraints q = ( Constraints ) en.nextElement();
  883. BitSet col = null;
  884. String x = String.valueOf( q.x );
  885. if ( !cols.containsKey( x ) ) {
  886. col = new BitSet();
  887. cols.put( x, col );
  888. }
  889. col = ( BitSet ) cols.get( x );
  890. int last_row = ( q.h == R ? q.y + 1 : q.y + q.h );
  891. for ( int i = q.y; i < last_row; i++ ) {
  892. col.set( i );
  893. }
  894. }
  895. en = cols.elements();
  896. while ( en.hasMoreElements() ) {
  897. BitSet col = ( BitSet ) en.nextElement();
  898. for ( int i = 0; i < col.size(); i++ ) {
  899. if ( col.get( i ) ) {
  900. _row_count = Math.max( _row_count, i + 1 );
  901. }
  902. }
  903. }
  904. }
  905. /**
  906. * Makes two columns be the same width. The actual width will be the larger
  907. * of the preferred widths of these columns.
  908. * @param column1 column number
  909. * @param column2 column number
  910. */
  911. public void makeColumnsSameWidth( int column1, int column2 ) {
  912. makeColumnsSameWidth( new int[] {column1, column2} );
  913. }
  914. /**
  915. * Makes several columns be the same width. The actual width will be the largest
  916. * preferred width of these columns.
  917. * @param columns array of column numbers to make the same width.
  918. */
  919. public void makeColumnsSameWidth( int[] columns ) {
  920. if ( columns.length <= 1 )
  921. return ;
  922. for ( int i = 0; i < columns.length; i++ ) {
  923. if ( columns[ i ] < 0 )
  924. throw new IllegalArgumentException( "Column parameter must be greater than 0." );
  925. }
  926. if ( _same_width_cols == null )
  927. _same_width_cols = new Vector();
  928. _same_width_cols.addElement( columns );
  929. _size_unknown = true;
  930. }
  931. /**
  932. * Makes all columns be the same width. The actual width will be the largest
  933. * preferred width of these columns.
  934. */
  935. public void makeColumnsSameWidth() {
  936. countColumns();
  937. int[] columns = new int[ _col_count ];
  938. for ( int i = 0; i < _col_count; i++ ) {
  939. columns[ i ] = i;
  940. }
  941. makeColumnsSameWidth( columns );
  942. }
  943. /**
  944. * Makes two rows be the same height. The actual height will be the larger
  945. * of the preferred heights of these rows.
  946. * @param row1 row number
  947. * @param row2 row number
  948. */
  949. public void makeRowsSameHeight( int row1, int row2 ) {
  950. makeRowsSameHeight( new int[] {row1, row2} );
  951. }
  952. /**
  953. * Makes several rows be the same height. The actual height will be the largest
  954. * preferred height of these rows.
  955. * @param rows array of row numbers to make the same height.
  956. */
  957. public void makeRowsSameHeight( int[] rows ) {
  958. if ( rows.length <= 1 )
  959. return ;
  960. for ( int i = 0; i < rows.length; i++ ) {
  961. if ( rows[ i ] < 0 )
  962. throw new IllegalArgumentException( "Row parameter must be greater than 0." );
  963. }
  964. if ( _same_height_rows == null ) // laziness pays off now
  965. _same_height_rows = new Vector();
  966. _same_height_rows.addElement( rows );
  967. _size_unknown = true;
  968. }
  969. /**
  970. * Makes all rows be the same height. The actual height will be the largest
  971. * preferred height of these rows.
  972. */
  973. public void makeRowsSameHeight() {
  974. countRows();
  975. int[] rows = new int[ _row_count ];
  976. for ( int i = 0; i < rows.length; i++ ) {
  977. rows[ i ] = i;
  978. }
  979. makeRowsSameHeight( rows );
  980. }
  981. /**
  982. * Sets a column to a specific width. Use care with this method, components
  983. * wider than the set width will be truncated.
  984. * @param column column number
  985. * @param width width in pixels
  986. */
  987. public void setColumnWidth( int column, int width ) {
  988. if ( column < 0 )
  989. throw new IllegalArgumentException( "Column must be >= 0." );
  990. if ( _col_widths == null )
  991. _col_widths = new int[ column + 1 ];
  992. if ( _col_widths.length <= column ) {
  993. int[] tmp = new int[ _col_widths.length ];
  994. System.arraycopy( _col_widths, 0, tmp, 0, _col_widths.length );
  995. _col_widths = new int[ column + 1 ];
  996. System.arraycopy( tmp, 0, _col_widths, 0, tmp.length );
  997. }
  998. // store fixed width columns as a negative number
  999. _col_widths[ column ] = -1 * width;
  1000. _size_unknown = true;
  1001. }
  1002. /**
  1003. * Sets a row to a specific height. Use care with this method, components
  1004. * taller than the set height will be truncated.
  1005. * @param row row number
  1006. * @param height height in pixels
  1007. */
  1008. public void setRowHeight( int row, int height ) {
  1009. if ( row < 0 )
  1010. throw new IllegalArgumentException( "Row must be >= 0." );
  1011. if ( _row_heights == null )
  1012. _row_heights = new int[ row + 1 ];
  1013. if ( _row_heights.length <= row ) {
  1014. int[] tmp = new int[ _row_heights.length ];
  1015. System.arraycopy( _row_heights, 0, tmp, 0, _row_heights.length );
  1016. _row_heights = new int[ row + 1 ];
  1017. System.arraycopy( tmp, 0, _row_heights, 0, tmp.length );
  1018. }
  1019. // store fixed height rows as a negative number
  1020. _row_heights[ row ] = -1 * height;
  1021. _size_unknown = true;
  1022. }
  1023. /**
  1024. * Creates a Constraints for direct manipulation.
  1025. * @return a Constraints object for direct manipulation.
  1026. */
  1027. public static Constraints createConstraint() {
  1028. return new Constraints();
  1029. }
  1030. /**
  1031. * Useful for holding an otherwise empty column to a minimum width.
  1032. * @param width desired width of component
  1033. * @return a component with some width but no height
  1034. */
  1035. public static Component createHorizontalStrut( int width ) {
  1036. return new Strut( width, 0 );
  1037. }
  1038. /**
  1039. * Useful for holding a column to a fixed width.
  1040. * @param width desired width of component
  1041. * @param rigid if true, will not stretch
  1042. * @return a component with some width but no height
  1043. */
  1044. public static Component createHorizontalStrut( int width, boolean rigid ) {
  1045. return new Strut( width, 0, rigid );
  1046. }
  1047. /**
  1048. * Useful for holding an otherwise empty row to a minimum height.
  1049. * @param height desired height of component
  1050. * @return a component with some height but no width
  1051. */
  1052. public static Component createVerticalStrut( int height ) {
  1053. return new Strut( 0, height );
  1054. }
  1055. /**
  1056. * Useful for holding a row to a fixed height.
  1057. * @param height desired height of component
  1058. * @param rigid if true, will not stretch
  1059. * @return a component with some height but no width
  1060. */
  1061. public static Component createVerticalStrut( int height, boolean rigid ) {
  1062. return new Strut( 0, height, rigid );
  1063. }
  1064. /**
  1065. * Useful for setting an otherwise blank cell to a minimum width and height.
  1066. * @param width desired width of component
  1067. * @param height desired height of component
  1068. * @return a component with some height and width
  1069. */
  1070. public static Component createStrut( int width, int height ) {
  1071. return new Strut( width, height );
  1072. }
  1073. /**
  1074. * Useful for setting a row and column to a fixed width and height.
  1075. * @param width desired width of component
  1076. * @param height desired height of component
  1077. * @return a component with some height and width
  1078. */
  1079. public static Component createStrut( int width, int height, boolean rigid ) {
  1080. return new Strut( width, height, rigid );
  1081. }
  1082. /**
  1083. * Simple component that is invisible. Struts can be either rigid or non-
  1084. * rigid. While this component could be used with other layout managers,
  1085. * it special properties are intended for use with KappaLayout. In particular,
  1086. * when the strut is set to rigid, it will lock the column or row (depends on
  1087. * the orientation of the strut) to the width or height of the strut. A non-
  1088. * rigid strut sets a minimum width or height on a column or row, but does
  1089. * not set a maximum like a rigid strut does.
  1090. */
  1091. public static class Strut extends Component implements Serializable {
  1092. private Dimension _dim;
  1093. private boolean _rigid;
  1094. /**
  1095. * @param w width
  1096. * @param h height
  1097. */
  1098. public Strut( int w, int h ) {
  1099. this( w, h, false );
  1100. }
  1101. /**
  1102. * @param w width
  1103. * @param h height
  1104. * @param rigid rigid
  1105. */
  1106. public Strut( int w, int h, boolean rigid ) {
  1107. _dim = new Dimension( w, h );
  1108. _rigid = rigid;
  1109. }
  1110. /**
  1111. * Overrides <code>getPreferredSize</code> from Component.
  1112. */
  1113. public Dimension getPreferredSize() {
  1114. return _dim;
  1115. }
  1116. /**
  1117. * Overrides <code>getSize</code> from Component.
  1118. */
  1119. public Dimension getSize() {
  1120. return _dim;
  1121. }
  1122. /**
  1123. * @return true if this strut is rigid.
  1124. */
  1125. public boolean isRigid() {
  1126. return _rigid;
  1127. }
  1128. /**
  1129. * @param rigid if true, this strut will act as a rigid strut
  1130. */
  1131. public void setRigid( boolean rigid ) {
  1132. _rigid = rigid;
  1133. }
  1134. }
  1135. /**
  1136. * This class is cloneable so that users may create and reuse a Constraints object
  1137. * similar to how one would use a GridBagConstraints rather than the string parameters.
  1138. */
  1139. public static class Constraints extends Object implements Cloneable, Serializable {
  1140. /**
  1141. * start column
  1142. */
  1143. public int x = 0;
  1144. /**
  1145. * start row
  1146. */
  1147. public int y = 0;
  1148. /**
  1149. * # columns wide
  1150. */
  1151. public int w = 1;
  1152. /**
  1153. * # rows high
  1154. */
  1155. public int h = 1;
  1156. /**
  1157. * alignment within cell, see comments in KappaLayout
  1158. */
  1159. public int a = 0;
  1160. /**
  1161. * stretch: default is 0 (character zero, no stretch), w = width of cell, h = height of cell, wh = both width and height
  1162. */
  1163. public String s = "0";
  1164. /**
  1165. * padding, same amount of blank space on all four sides of component
  1166. */
  1167. public int p = 0;
  1168. /**
  1169. * Plain String representation of this Constraints, suitable for using as a
  1170. * constraints string if needed.
  1171. */
  1172. public String toString() {
  1173. StringBuffer sb = new StringBuffer();
  1174. sb.append( String.valueOf( x ) + "," )
  1175. .append( String.valueOf( y ) + "," )
  1176. .append( String.valueOf( w ) + "," );
  1177. if ( w == R )
  1178. sb.append( "R," );
  1179. else
  1180. sb.append( String.valueOf( w ) + "," );
  1181. if ( h == R )
  1182. sb.append( "R," );
  1183. else
  1184. sb.append( String.valueOf( h ) + "," );
  1185. sb.append( String.valueOf( a ) + "," )
  1186. .append( String.valueOf( s ) + "," )
  1187. .append( String.valueOf( p ) )
  1188. .toString();
  1189. return sb.toString();
  1190. }
  1191. /**
  1192. * @return a clone of this object.
  1193. */
  1194. public Object clone() {
  1195. try {
  1196. return super.clone();
  1197. }
  1198. catch ( Exception e ) {
  1199. return null;
  1200. }
  1201. }
  1202. }
  1203. /*
  1204. public Component getDebugPanel() {
  1205. java.awt.Panel p = new java.awt.Panel() {
  1206. public Dimension getPreferredSize() {
  1207. int width = 0;
  1208. for ( int i = 0; i < _col_widths.length; i++ ) {
  1209. width += Math.abs(_col_widths[i]);
  1210. }
  1211. int height = 0;
  1212. for ( int i = 0; i < _row_heights.length; i++ ) {
  1213. height += Math.abs(_row_heights[i]);
  1214. }
  1215. return new Dimension(width, height);
  1216. }
  1217. public void paint(Graphics g) {
  1218. g.setColor(Color.black);
  1219. Dimension d = getPreferredSize();
  1220. int x = 0;
  1221. g.drawLine(x, 0, x, d.height);
  1222. for ( int i = 0; i < _col_widths.length; i++ ) {
  1223. x = Math.abs(_col_widths[i]);
  1224. g.drawLine(x, 0, x, d.height);
  1225. }
  1226. g.drawLine(d.width, 0, d.width, d.height);
  1227. int y = 0;
  1228. g.drawLine(0, y, d.width, y);
  1229. for ( int i = 0; i < _row_heights.length; i++ ) {
  1230. y = Math.abs(_row_heights[i]);
  1231. g.drawLine(0, y, d.width, y);
  1232. }
  1233. g.drawLine(0, d.height, d.width, d.height);
  1234. }
  1235. };
  1236. return p;
  1237. }
  1238. */
  1239. }