1. /*
  2. * @(#)SizeRequirements.java 1.31 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing;
  8. import java.awt.*;
  9. import java.io.Serializable;
  10. /**
  11. * For the convenience of layout managers,
  12. * calculates information about the size and position of components.
  13. * All size and position calculation methods are class methods
  14. * that take arrays of SizeRequirements as arguments.
  15. * The SizeRequirements class supports two types of layout:
  16. *
  17. * <blockquote>
  18. * <dl>
  19. * <dt> tiled
  20. * <dd> The components are placed end-to-end,
  21. * starting either at coordinate 0 (the leftmost or topmost position)
  22. * or at the coordinate representing the end of the allocated span
  23. * (the rightmost or bottommost position).
  24. *
  25. * <dt> aligned
  26. * <dd> The components are aligned as specified
  27. * by each component's X or Y alignment value.
  28. * </dl>
  29. * </blockquote>
  30. *
  31. * <p>
  32. *
  33. * Each SizeRequirements object contains information
  34. * about either the width (and X alignment)
  35. * or height (and Y alignment)
  36. * of a single component or a group of components:
  37. *
  38. * <blockquote>
  39. * <dl>
  40. * <dt> <code>minimum</code>
  41. * <dd> The smallest reasonable width/height of the component
  42. * or component group, in pixels.
  43. *
  44. * <dt> <code>preferred</code>
  45. * <dd> The natural width/height of the component
  46. * or component group, in pixels.
  47. *
  48. * <dt> <code>maximum</code>
  49. * <dd> The largest reasonable width/height of the component
  50. * or component group, in pixels.
  51. *
  52. * <dt> <code>alignment</code>
  53. * <dd> The X/Y alignment of the component
  54. * or component group.
  55. * </dl>
  56. * </blockquote>
  57. * <p>
  58. * <strong>Warning:</strong>
  59. * Serialized objects of this class will not be compatible with
  60. * future Swing releases. The current serialization support is
  61. * appropriate for short term storage or RMI between applications running
  62. * the same version of Swing. As of 1.4, support for long term storage
  63. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  64. * has been added to the <code>java.beans</code> package.
  65. * Please see {@link java.beans.XMLEncoder}.
  66. *
  67. * @see Component#getMinimumSize
  68. * @see Component#getPreferredSize
  69. * @see Component#getMaximumSize
  70. * @see Component#getAlignmentX
  71. * @see Component#getAlignmentY
  72. *
  73. * @version 1.31 01/23/03
  74. * @author Timothy Prinzing
  75. */
  76. public class SizeRequirements implements Serializable {
  77. /**
  78. * The minimum size required.
  79. * For a component <code>comp</code>, this should be equal to either
  80. * <code>comp.getMinimumSize().width</code> or
  81. * <code>comp.getMinimumSize().height</code>.
  82. */
  83. public int minimum;
  84. /**
  85. * The preferred (natural) size.
  86. * For a component <code>comp</code>, this should be equal to either
  87. * <code>comp.getPreferredSize().width</code> or
  88. * <code>comp.getPreferredSize().height</code>.
  89. */
  90. public int preferred;
  91. /**
  92. * The maximum size allowed.
  93. * For a component <code>comp</code>, this should be equal to either
  94. * <code>comp.getMaximumSize().width</code> or
  95. * <code>comp.getMaximumSize().height</code>.
  96. */
  97. public int maximum;
  98. /**
  99. * The alignment, specified as a value between 0.0 and 1.0,
  100. * inclusive.
  101. * To specify centering, the alignment should be 0.5.
  102. */
  103. public float alignment;
  104. /**
  105. * Creates a SizeRequirements object with the minimum, preferred,
  106. * and maximum sizes set to zero and an alignment value of 0.5
  107. * (centered).
  108. */
  109. public SizeRequirements() {
  110. minimum = 0;
  111. preferred = 0;
  112. maximum = 0;
  113. alignment = 0.5f;
  114. }
  115. /**
  116. * Creates a SizeRequirements object with the specified minimum, preferred,
  117. * and maximum sizes and the specified alignment.
  118. *
  119. * @param min the minimum size >= 0
  120. * @param pref the preferred size >= 0
  121. * @param max the maximum size >= 0
  122. * @param a the alignment >= 0.0f && <= 1.0f
  123. */
  124. public SizeRequirements(int min, int pref, int max, float a) {
  125. minimum = min;
  126. preferred = pref;
  127. maximum = max;
  128. alignment = a > 1.0f ? 1.0f : a < 0.0f ? 0.0f : a;
  129. }
  130. /**
  131. * Returns a string describing the minimum, preferred, and maximum
  132. * size requirements, along with the alignment.
  133. *
  134. * @return the string
  135. */
  136. public String toString() {
  137. return "[" + minimum + "," + preferred + "," + maximum + "]@" + alignment;
  138. }
  139. /**
  140. * Determines the total space necessary to
  141. * place a set of components end-to-end. The needs
  142. * of each component in the set are represented by an entry in the
  143. * passed-in SizeRequirements array.
  144. * The returned SizeRequirements object has an alignment of 0.5
  145. * (centered). The space requirement is never more than
  146. * Integer.MAX_VALUE.
  147. *
  148. * @param children the space requirements for a set of components.
  149. * The vector may be of zero length, which will result in a
  150. * default SizeRequirements object instance being passed back.
  151. * @return the total space requirements.
  152. */
  153. public static SizeRequirements getTiledSizeRequirements(SizeRequirements[]
  154. children) {
  155. SizeRequirements total = new SizeRequirements();
  156. for (int i = 0; i < children.length; i++) {
  157. SizeRequirements req = children[i];
  158. total.minimum = (int) Math.min((long) total.minimum + (long) req.minimum, Integer.MAX_VALUE);
  159. total.preferred = (int) Math.min((long) total.preferred + (long) req.preferred, Integer.MAX_VALUE);
  160. total.maximum = (int) Math.min((long) total.maximum + (long) req.maximum, Integer.MAX_VALUE);
  161. }
  162. return total;
  163. }
  164. /**
  165. * Determines the total space necessary to
  166. * align a set of components. The needs
  167. * of each component in the set are represented by an entry in the
  168. * passed-in SizeRequirements array. The total space required will
  169. * never be more than Integer.MAX_VALUE.
  170. *
  171. * @param children the set of child requirements. If of zero length,
  172. * the returns result will be a default instance of SizeRequirements.
  173. * @return the total space requirements.
  174. */
  175. public static SizeRequirements getAlignedSizeRequirements(SizeRequirements[]
  176. children) {
  177. SizeRequirements totalAscent = new SizeRequirements();
  178. SizeRequirements totalDescent = new SizeRequirements();
  179. for (int i = 0; i < children.length; i++) {
  180. SizeRequirements req = children[i];
  181. int ascent = (int) (req.alignment * req.minimum);
  182. int descent = req.minimum - ascent;
  183. totalAscent.minimum = Math.max(ascent, totalAscent.minimum);
  184. totalDescent.minimum = Math.max(descent, totalDescent.minimum);
  185. ascent = (int) (req.alignment * req.preferred);
  186. descent = req.preferred - ascent;
  187. totalAscent.preferred = Math.max(ascent, totalAscent.preferred);
  188. totalDescent.preferred = Math.max(descent, totalDescent.preferred);
  189. ascent = (int) (req.alignment * req.maximum);
  190. descent = req.maximum - ascent;
  191. totalAscent.maximum = Math.max(ascent, totalAscent.maximum);
  192. totalDescent.maximum = Math.max(descent, totalDescent.maximum);
  193. }
  194. int min = (int) Math.min((long) totalAscent.minimum + (long) totalDescent.minimum, Integer.MAX_VALUE);
  195. int pref = (int) Math.min((long) totalAscent.preferred + (long) totalDescent.preferred, Integer.MAX_VALUE);
  196. int max = (int) Math.min((long) totalAscent.maximum + (long) totalDescent.maximum, Integer.MAX_VALUE);
  197. float alignment = 0.0f;
  198. if (min > 0) {
  199. alignment = (float) totalAscent.minimum / min;
  200. alignment = alignment > 1.0f ? 1.0f : alignment < 0.0f ? 0.0f : alignment;
  201. }
  202. return new SizeRequirements(min, pref, max, alignment);
  203. }
  204. /**
  205. * Creates a set of offset/span pairs representing how to
  206. * lay out a set of components end-to-end.
  207. * This method requires that you specify
  208. * the total amount of space to be allocated,
  209. * the size requirements for each component to be placed
  210. * (specified as an array of SizeRequirements), and
  211. * the total size requirement of the set of components.
  212. * You can get the total size requirement
  213. * by invoking the getTiledSizeRequirements method. The components
  214. * will be tiled in the forward direction with offsets increasing from 0.
  215. *
  216. * @param allocated the total span to be allocated >= 0.
  217. * @param total the total of the children requests. This argument
  218. * is optional and may be null.
  219. * @param children the size requirements for each component.
  220. * @param offsets the offset from 0 for each child where
  221. * the spans were allocated (determines placement of the span).
  222. * @param spans the span allocated for each child to make the
  223. * total target span.
  224. */
  225. public static void calculateTiledPositions(int allocated,
  226. SizeRequirements total,
  227. SizeRequirements[] children,
  228. int[] offsets,
  229. int[] spans) {
  230. calculateTiledPositions(allocated, total, children, offsets, spans, true);
  231. }
  232. /**
  233. * Creates a set of offset/span pairs representing how to
  234. * lay out a set of components end-to-end.
  235. * This method requires that you specify
  236. * the total amount of space to be allocated,
  237. * the size requirements for each component to be placed
  238. * (specified as an array of SizeRequirements), and
  239. * the total size requirement of the set of components.
  240. * You can get the total size requirement
  241. * by invoking the getTiledSizeRequirements method.
  242. *
  243. * This method also requires a flag indicating whether components
  244. * should be tiled in the forward direction (offsets increasing
  245. * from 0) or reverse direction (offsets decreasing from the end
  246. * of the allocated space). The forward direction represents
  247. * components tiled from left to right or top to bottom. The
  248. * reverse direction represents components tiled from right to left
  249. * or bottom to top.
  250. *
  251. * @param allocated the total span to be allocated >= 0.
  252. * @param total the total of the children requests. This argument
  253. * is optional and may be null.
  254. * @param children the size requirements for each component.
  255. * @param offsets the offset from 0 for each child where
  256. * the spans were allocated (determines placement of the span).
  257. * @param spans the span allocated for each child to make the
  258. * total target span.
  259. * @param forward tile with offsets increasing from 0 if true
  260. * and with offsets decreasing from the end of the allocated space
  261. * if false.
  262. */
  263. public static void calculateTiledPositions(int allocated,
  264. SizeRequirements total,
  265. SizeRequirements[] children,
  266. int[] offsets,
  267. int[] spans,
  268. boolean forward) {
  269. // The total argument turns out to be a bad idea since the
  270. // total of all the children can overflow the integer used to
  271. // hold the total. The total must therefore be calculated and
  272. // stored in long variables.
  273. long min = 0;
  274. long pref = 0;
  275. long max = 0;
  276. for (int i = 0; i < children.length; i++) {
  277. min += children[i].minimum;
  278. pref += children[i].preferred;
  279. max += children[i].maximum;
  280. }
  281. if (allocated >= pref) {
  282. expandedTile(allocated, min, pref, max, children, offsets, spans, forward);
  283. } else {
  284. compressedTile(allocated, min, pref, max, children, offsets, spans, forward);
  285. }
  286. }
  287. private static void compressedTile(int allocated, long min, long pref, long max,
  288. SizeRequirements[] request,
  289. int[] offsets, int[] spans,
  290. boolean forward) {
  291. // ---- determine what we have to work with ----
  292. float totalPlay = Math.min(pref - allocated, pref - min);
  293. float factor = (pref - min == 0) ? 0.0f : totalPlay / (pref - min);
  294. // ---- make the adjustments ----
  295. int totalOffset;
  296. if( forward ) {
  297. // lay out with offsets increasing from 0
  298. totalOffset = 0;
  299. for (int i = 0; i < spans.length; i++) {
  300. offsets[i] = totalOffset;
  301. SizeRequirements req = request[i];
  302. float play = factor * (req.preferred - req.minimum);
  303. spans[i] = (int)(req.preferred - play);
  304. totalOffset = (int) Math.min((long) totalOffset + (long) spans[i], Integer.MAX_VALUE);
  305. }
  306. } else {
  307. // lay out with offsets decreasing from the end of the allocation
  308. totalOffset = allocated;
  309. for (int i = 0; i < spans.length; i++) {
  310. SizeRequirements req = request[i];
  311. float play = factor * (req.preferred - req.minimum);
  312. spans[i] = (int)(req.preferred - play);
  313. offsets[i] = totalOffset - spans[i];
  314. totalOffset = (int) Math.max((long) totalOffset - (long) spans[i], 0);
  315. }
  316. }
  317. }
  318. private static void expandedTile(int allocated, long min, long pref, long max,
  319. SizeRequirements[] request,
  320. int[] offsets, int[] spans,
  321. boolean forward) {
  322. // ---- determine what we have to work with ----
  323. float totalPlay = Math.min(allocated - pref, max - pref);
  324. float factor = (max - pref == 0) ? 0.0f : totalPlay / (max - pref);
  325. // ---- make the adjustments ----
  326. int totalOffset;
  327. if( forward ) {
  328. // lay out with offsets increasing from 0
  329. totalOffset = 0;
  330. for (int i = 0; i < spans.length; i++) {
  331. offsets[i] = totalOffset;
  332. SizeRequirements req = request[i];
  333. int play = (int)(factor * (req.maximum - req.preferred));
  334. spans[i] = (int) Math.min((long) req.preferred + (long) play, Integer.MAX_VALUE);
  335. totalOffset = (int) Math.min((long) totalOffset + (long) spans[i], Integer.MAX_VALUE);
  336. }
  337. } else {
  338. // lay out with offsets decreasing from the end of the allocation
  339. totalOffset = allocated;
  340. for (int i = 0; i < spans.length; i++) {
  341. SizeRequirements req = request[i];
  342. int play = (int)(factor * (req.maximum - req.preferred));
  343. spans[i] = (int) Math.min((long) req.preferred + (long) play, Integer.MAX_VALUE);
  344. offsets[i] = totalOffset - spans[i];
  345. totalOffset = (int) Math.max((long) totalOffset - (long) spans[i], 0);
  346. }
  347. }
  348. }
  349. /**
  350. * Creates a bunch of offset/span pairs specifying how to
  351. * lay out a set of components with the specified alignments.
  352. * The resulting span allocations will overlap, with each one
  353. * fitting as well as possible into the given total allocation.
  354. * This method requires that you specify
  355. * the total amount of space to be allocated,
  356. * the size requirements for each component to be placed
  357. * (specified as an array of SizeRequirements), and
  358. * the total size requirements of the set of components
  359. * (only the alignment field of which is actually used).
  360. * You can get the total size requirement by invoking
  361. * getAlignedSizeRequirements.
  362. *
  363. * Normal alignment will be done with an alignment value of 0.0f
  364. * representing the left/top edge of a component.
  365. *
  366. * @param allocated the total span to be allocated >= 0.
  367. * @param total the total of the children requests.
  368. * @param children the size requirements for each component.
  369. * @param offsets the offset from 0 for each child where
  370. * the spans were allocated (determines placement of the span).
  371. * @param spans the span allocated for each child to make the
  372. * total target span.
  373. */
  374. public static void calculateAlignedPositions(int allocated,
  375. SizeRequirements total,
  376. SizeRequirements[] children,
  377. int[] offsets,
  378. int[] spans) {
  379. calculateAlignedPositions( allocated, total, children, offsets, spans, true );
  380. }
  381. /**
  382. * Creates a set of offset/span pairs specifying how to
  383. * lay out a set of components with the specified alignments.
  384. * The resulting span allocations will overlap, with each one
  385. * fitting as well as possible into the given total allocation.
  386. * This method requires that you specify
  387. * the total amount of space to be allocated,
  388. * the size requirements for each component to be placed
  389. * (specified as an array of SizeRequirements), and
  390. * the total size requirements of the set of components
  391. * (only the alignment field of which is actually used)
  392. * You can get the total size requirement by invoking
  393. * getAlignedSizeRequirements.
  394. *
  395. * This method also requires a flag indicating whether normal or
  396. * reverse alignment should be performed. With normal alignment
  397. * the value 0.0f represents the left/top edge of the component
  398. * to be aligned. With reverse alignment, 0.0f represents the
  399. * right/bottom edge.
  400. *
  401. * @param allocated the total span to be allocated >= 0.
  402. * @param total the total of the children requests.
  403. * @param children the size requirements for each component.
  404. * @param offsets the offset from 0 for each child where
  405. * the spans were allocated (determines placement of the span).
  406. * @param spans the span allocated for each child to make the
  407. * total target span.
  408. * @param normal when true, the alignment value 0.0f means
  409. * left/top; when false, it means right/bottom.
  410. */
  411. public static void calculateAlignedPositions(int allocated,
  412. SizeRequirements total,
  413. SizeRequirements[] children,
  414. int[] offsets,
  415. int[] spans,
  416. boolean normal) {
  417. float totalAlignment = normal ? total.alignment : 1.0f - total.alignment;
  418. int totalAscent = (int)(allocated * totalAlignment);
  419. int totalDescent = allocated - totalAscent;
  420. for (int i = 0; i < children.length; i++) {
  421. SizeRequirements req = children[i];
  422. float alignment = normal ? req.alignment : 1.0f - req.alignment;
  423. int maxAscent = (int)(req.maximum * alignment);
  424. int maxDescent = req.maximum - maxAscent;
  425. int ascent = Math.min(totalAscent, maxAscent);
  426. int descent = Math.min(totalDescent, maxDescent);
  427. offsets[i] = totalAscent - ascent;
  428. spans[i] = (int) Math.min((long) ascent + (long) descent, Integer.MAX_VALUE);
  429. }
  430. }
  431. // This method was used by the JTable - which now uses a different technique.
  432. /**
  433. * Adjust a specified array of sizes by a given amount.
  434. *
  435. * @param delta an int specifying the size difference
  436. * @param children an array of SizeRequirements objects
  437. * @return an array of ints containing the final size for each item
  438. */
  439. public static int[] adjustSizes(int delta, SizeRequirements[] children) {
  440. return new int[0];
  441. }
  442. }