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