1. /*
  2. * @(#)FrameSetView.java 1.18 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.text.html;
  8. import java.awt.*;
  9. import java.util.*;
  10. import javax.swing.*;
  11. import javax.swing.text.*;
  12. import javax.swing.event.*;
  13. /**
  14. * Implements a FrameSetView, intended to support the HTML
  15. * <FRAMESET> tag. Supports the ROWS and COLS attributes.
  16. *
  17. * @author Sunita Mani
  18. *
  19. * Credit also to the hotjava browser engineers that
  20. * worked on making the allocation of space algorithms
  21. * conform to the HTML 4.0 standard and also be netscape
  22. * compatible.
  23. *
  24. * @version 1.18 01/23/03
  25. */
  26. class FrameSetView extends javax.swing.text.BoxView {
  27. String[] children;
  28. int[] percentChildren;
  29. int[] absoluteChildren;
  30. int[] relativeChildren;
  31. int percentTotals;
  32. int absoluteTotals;
  33. int relativeTotals;
  34. /**
  35. * Constructs a FrameSetView for the given element.
  36. *
  37. * @param elem the element that this view is responsible for
  38. */
  39. public FrameSetView(Element elem, int axis) {
  40. super(elem, axis);
  41. if (axis == View.Y_AXIS) {
  42. children = parseRowColSpec(HTML.Attribute.ROWS);
  43. } else {
  44. children = parseRowColSpec(HTML.Attribute.COLS);
  45. }
  46. init();
  47. }
  48. /**
  49. * Parses the ROW or COL attributes and returns
  50. * an array of strings that represent the space
  51. * distribution.
  52. *
  53. */
  54. private String[] parseRowColSpec(HTML.Attribute key) {
  55. AttributeSet attributes = getElement().getAttributes();
  56. String spec = "*";
  57. if (attributes != null) {
  58. if (attributes.getAttribute(key) != null) {
  59. spec = (String)attributes.getAttribute(key);
  60. }
  61. }
  62. StringTokenizer tokenizer = new StringTokenizer(spec, ",");
  63. int nTokens = tokenizer.countTokens();
  64. String[] items = new String[nTokens];
  65. for (int i = 0; i < nTokens; i++) {
  66. items[i] = tokenizer.nextToken().trim();
  67. // As per the spec, 100% is the same as *
  68. // hence the mapping.
  69. //
  70. if (items[i].equals("100%")) {
  71. items[i] = "*";
  72. }
  73. }
  74. return items;
  75. }
  76. /**
  77. * Initializes a number of internal state variables
  78. * that store information about space allocation
  79. * for the frames contained within the frameset.
  80. */
  81. private void init() {
  82. percentChildren = new int[children.length];
  83. relativeChildren = new int[children.length];
  84. absoluteChildren = new int[children.length];
  85. for (int i = 0; i < children.length; i++) {
  86. percentChildren[i] = -1;
  87. relativeChildren[i] = -1;
  88. absoluteChildren[i] = -1;
  89. if (children[i].endsWith("*")) {
  90. if (children[i].length() > 1) {
  91. relativeChildren[i] =
  92. Integer.parseInt(children[i].substring(
  93. 0, children[i].length()-1));
  94. relativeTotals += relativeChildren[i];
  95. } else {
  96. relativeChildren[i] = 1;
  97. relativeTotals += 1;
  98. }
  99. } else if (children[i].indexOf('%') != -1) {
  100. percentChildren[i] = parseDigits(children[i]);
  101. percentTotals += percentChildren[i];
  102. } else {
  103. absoluteChildren[i] = Integer.parseInt(children[i]);
  104. }
  105. }
  106. if (percentTotals > 100) {
  107. for (int i = 0; i < percentChildren.length; i++) {
  108. if (percentChildren[i] > 0) {
  109. percentChildren[i] =
  110. (percentChildren[i] * 100) / percentTotals;
  111. }
  112. }
  113. percentTotals = 100;
  114. }
  115. }
  116. /**
  117. * Perform layout for the major axis of the box (i.e. the
  118. * axis that it represents). The results of the layout should
  119. * be placed in the given arrays which represent the allocations
  120. * to the children along the major axis.
  121. *
  122. * @param targetSpan the total span given to the view, which
  123. * whould be used to layout the children
  124. * @param axis the axis being layed out
  125. * @param offsets the offsets from the origin of the view for
  126. * each of the child views; this is a return value and is
  127. * filled in by the implementation of this method
  128. * @param spans the span of each child view; this is a return
  129. * value and is filled in by the implementation of this method
  130. * @return the offset and span for each child view in the
  131. * offsets and spans parameters
  132. */
  133. protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
  134. int[] spans) {
  135. SizeRequirements.calculateTiledPositions(targetSpan, null,
  136. getChildRequests(targetSpan,
  137. axis),
  138. offsets, spans);
  139. }
  140. protected SizeRequirements[] getChildRequests(int targetSpan, int axis) {
  141. int span[] = new int[children.length];
  142. spread(targetSpan, span);
  143. int n = getViewCount();
  144. SizeRequirements[] reqs = new SizeRequirements[n];
  145. for (int i = 0, sIndex = 0; i < n; i++) {
  146. View v = getView(i);
  147. if ((v instanceof FrameView) || (v instanceof FrameSetView)) {
  148. reqs[i] = new SizeRequirements((int) v.getMinimumSpan(axis),
  149. span[sIndex],
  150. (int) v.getMaximumSpan(axis),
  151. 0.5f);
  152. sIndex++;
  153. } else {
  154. int min = (int) v.getMinimumSpan(axis);
  155. int pref = (int) v.getPreferredSpan(axis);
  156. int max = (int) v.getMaximumSpan(axis);
  157. float a = v.getAlignment(axis);
  158. reqs[i] = new SizeRequirements(min, pref, max, a);
  159. }
  160. }
  161. return reqs;
  162. }
  163. /**
  164. * This method is responsible for returning in span[] the
  165. * span for each child view along the major axis. it
  166. * computes this based on the information that extracted
  167. * from the value of the ROW/COL attribute.
  168. */
  169. private void spread(int targetSpan, int span[]) {
  170. if (targetSpan == 0) {
  171. return;
  172. }
  173. int tempSpace = 0;
  174. int remainingSpace = targetSpan;
  175. // allocate the absolute's first, they have
  176. // precedence
  177. //
  178. for (int i = 0; i < span.length; i++) {
  179. if (absoluteChildren[i] > 0) {
  180. span[i] = absoluteChildren[i];
  181. remainingSpace -= span[i];
  182. }
  183. }
  184. // then deal with percents.
  185. //
  186. tempSpace = remainingSpace;
  187. for (int i = 0; i < span.length; i++) {
  188. if (percentChildren[i] > 0 && tempSpace > 0) {
  189. span[i] = (percentChildren[i] * tempSpace) / 100;
  190. remainingSpace -= span[i];
  191. } else if (percentChildren[i] > 0 && tempSpace <= 0) {
  192. span[i] = targetSpan / span.length;
  193. remainingSpace -= span[i];
  194. }
  195. }
  196. // allocate remainingSpace to relative
  197. if (remainingSpace > 0 && relativeTotals > 0) {
  198. for (int i = 0; i < span.length; i++) {
  199. if (relativeChildren[i] > 0) {
  200. span[i] = (remainingSpace *
  201. relativeChildren[i]) / relativeTotals;
  202. }
  203. }
  204. } else if (remainingSpace > 0) {
  205. // There are no relative columns and the space has been
  206. // under- or overallocated. In this case, turn all the
  207. // percentage and pixel specified columns to percentage
  208. // columns based on the ratio of their pixel count to the
  209. // total "virtual" size. (In the case of percentage columns,
  210. // the pixel count would equal the specified percentage
  211. // of the screen size.
  212. // This action is in accordance with the HTML
  213. // 4.0 spec (see section 8.3, the end of the discussion of
  214. // the FRAMESET tag). The precedence of percentage and pixel
  215. // specified columns is unclear (spec seems to indicate that
  216. // they share priority, however, unspecified what happens when
  217. // overallocation occurs.)
  218. // addendum is that we behave similiar to netscape in that specified
  219. // widths have precedance over percentage widths...
  220. float vTotal = (float)(targetSpan - remainingSpace);
  221. float[] tempPercents = new float[span.length];
  222. remainingSpace = targetSpan;
  223. for (int i = 0; i < span.length; i++) {
  224. // ok we know what our total space is, and we know how large each
  225. // column should be relative to each other... therefore we can use
  226. // that relative information to deduce their percentages of a whole
  227. // and then scale them appropriately for the correct size
  228. tempPercents[i] = ((float)span[i] / vTotal) * 100.00f;
  229. span[i] = (int) ( ((float)targetSpan * tempPercents[i]) / 100.00f);
  230. remainingSpace -= span[i];
  231. }
  232. // this is for just in case there is something left over.. if there is we just
  233. // add it one pixel at a time to the frames in order.. We shouldn't really ever get
  234. // here and if we do it shouldn't be with more than 1 pixel, maybe two.
  235. int i = 0;
  236. while (remainingSpace != 0) {
  237. if (remainingSpace < 0) {
  238. span[i++]--;
  239. remainingSpace++;
  240. }
  241. else {
  242. span[i++]++;
  243. remainingSpace--;
  244. }
  245. // just in case there are more pixels than frames...should never happen..
  246. if (i == span.length)i = 0;
  247. }
  248. }
  249. }
  250. /*
  251. * Users have been known to type things like "%25" and "25 %". Deal
  252. * with it.
  253. */
  254. private int parseDigits(String mixedStr) {
  255. int result = 0;
  256. for (int i = 0; i < mixedStr.length(); i++) {
  257. char ch = mixedStr.charAt(i);
  258. if (Character.isDigit(ch)) {
  259. result = (result * 10) + Character.digit(ch, 10);
  260. }
  261. }
  262. return result;
  263. }
  264. }