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