1. /*
  2. * @(#)FrameSetView.java 1.11 01/11/29
  3. *
  4. * Copyright 2002 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.11 11/29/01
  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. * @returns 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; i < n; i++) {
  146. View v = getView(i);
  147. if (! (v instanceof NoFramesView)) {
  148. reqs[i] = new SizeRequirements((int) v.getMinimumSpan(axis), //span[i],
  149. span[i],
  150. (int) v.getMaximumSpan(axis), //span[i],
  151. 0.5f);
  152. } else {
  153. int min = (int) v.getMinimumSpan(axis);
  154. int pref = (int) v.getPreferredSpan(axis);
  155. int max = (int) v.getMaximumSpan(axis);
  156. float a = v.getAlignment(axis);
  157. reqs[i] = new SizeRequirements(min, pref, max, a);
  158. }
  159. }
  160. return reqs;
  161. }
  162. /**
  163. * This method is responsible for returning in span[] the
  164. * span for each child view along the major axis. it
  165. * computes this based on the information that extracted
  166. * from the value of the ROW/COL attribute.
  167. */
  168. private void spread(int targetSpan, int span[]) {
  169. if (targetSpan == 0) {
  170. return;
  171. }
  172. int tempSpace = 0;
  173. int remainingSpace = targetSpan;
  174. // allocate the absolute's first, they have
  175. // precedence
  176. //
  177. for (int i = 0; i < span.length; i++) {
  178. if (absoluteChildren[i] > 0) {
  179. span[i] = absoluteChildren[i];
  180. remainingSpace -= span[i];
  181. }
  182. }
  183. // then deal with percents.
  184. //
  185. tempSpace = remainingSpace;
  186. for (int i = 0; i < span.length; i++) {
  187. if (percentChildren[i] > 0 && tempSpace > 0) {
  188. span[i] = (percentChildren[i] * tempSpace) / 100;
  189. remainingSpace -= span[i];
  190. } else if (percentChildren[i] > 0 && tempSpace <= 0) {
  191. span[i] = targetSpan / span.length;
  192. remainingSpace -= span[i];
  193. }
  194. }
  195. // allocate remainingSpace to relative
  196. if (remainingSpace > 0 && relativeTotals > 0) {
  197. for (int i = 0; i < span.length; i++) {
  198. if (relativeChildren[i] > 0) {
  199. span[i] = (remainingSpace *
  200. relativeChildren[i]) / relativeTotals;
  201. }
  202. }
  203. } else if (remainingSpace > 0) {
  204. // There are no relative columns and the space has been
  205. // under- or overallocated. In this case, turn all the
  206. // percentage and pixel specified columns to percentage
  207. // columns based on the ratio of their pixel count to the
  208. // total "virtual" size. (In the case of percentage columns,
  209. // the pixel count would equal the specified percentage
  210. // of the screen size.
  211. // This action is in accordance with the HTML
  212. // 4.0 spec (see section 8.3, the end of the discussion of
  213. // the FRAMESET tag). The precedence of percentage and pixel
  214. // specified columns is unclear (spec seems to indicate that
  215. // they share priority, however, unspecified what happens when
  216. // overallocation occurs.)
  217. // addendum is that we behave similiar to netscape in that specified
  218. // widths have precedance over percentage widths...
  219. float vTotal = (float)(targetSpan - remainingSpace);
  220. float[] tempPercents = new float[span.length];
  221. remainingSpace = targetSpan;
  222. for (int i = 0; i < span.length; i++) {
  223. // ok we know what our total space is, and we know how large each
  224. // column should be relative to each other... therefore we can use
  225. // that relative information to deduce their percentages of a whole
  226. // and then scale them appropriately for the correct size
  227. tempPercents[i] = ((float)span[i] / vTotal) * 100.00f;
  228. span[i] = (int) ( ((float)targetSpan * tempPercents[i]) / 100.00f);
  229. remainingSpace -= span[i];
  230. }
  231. // this is for just in case there is something left over.. if there is we just
  232. // add it one pixel at a time to the frames in order.. We shouldn't really ever get
  233. // here and if we do it shouldn't be with more than 1 pixel, maybe two.
  234. int i = 0;
  235. while (remainingSpace != 0) {
  236. if (remainingSpace < 0) {
  237. span[i++]--;
  238. remainingSpace++;
  239. }
  240. else {
  241. span[i++]++;
  242. remainingSpace--;
  243. }
  244. // just in case there are more pixels than frames...should never happen..
  245. if (i == span.length)i = 0;
  246. }
  247. }
  248. }
  249. /*
  250. * Users have been known to type things like "%25" and "25 %". Deal
  251. * with it.
  252. */
  253. private int parseDigits(String mixedStr) {
  254. int result = 0;
  255. for (int i = 0; i < mixedStr.length(); i++) {
  256. char ch = mixedStr.charAt(i);
  257. if (Character.isDigit(ch)) {
  258. result = (result * 10) + Character.digit(ch, 10);
  259. }
  260. }
  261. return result;
  262. }
  263. /*
  264. * WORKAROUND for bugid: 4156333
  265. * removeUpdate() needs to recursively descend the tree
  266. * to enable child components to remove themselves.
  267. * it needs to remove. Once that is fixed, this method
  268. * can removed. If the fix involves calling the child
  269. * views removeUpdate() method, then a removeUpdate()
  270. * method will have to be added to FrameView that merely
  271. * calls, setParent(null). However if the fix directly
  272. * calls setParent(null) on the child views, then
  273. * no methods need to be added to FrameView.
  274. */
  275. public void setParent(View v) {
  276. super.setParent(v);
  277. if (v == null) {
  278. for (int i = 0; i < getViewCount(); i++) {
  279. View child = getView(i);
  280. child.setParent(null);
  281. }
  282. }
  283. }
  284. }