1. /*
  2. * @(#)BlockView.java 1.34 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.util.Enumeration;
  9. import java.awt.*;
  10. import javax.swing.SizeRequirements;
  11. import javax.swing.border.*;
  12. import javax.swing.event.DocumentEvent;
  13. import javax.swing.text.*;
  14. /**
  15. * A view implementation to display a block (as a box)
  16. * with CSS specifications.
  17. *
  18. * @author Timothy Prinzing
  19. * @version 1.34 01/23/03
  20. */
  21. public class BlockView extends BoxView {
  22. /**
  23. * Creates a new view that represents an
  24. * html box. This can be used for a number
  25. * of elements.
  26. *
  27. * @param elem the element to create a view for
  28. * @param axis either View.X_AXIS or View.Y_AXIS
  29. */
  30. public BlockView(Element elem, int axis) {
  31. super(elem, axis);
  32. }
  33. /**
  34. * Establishes the parent view for this view. This is
  35. * guaranteed to be called before any other methods if the
  36. * parent view is functioning properly.
  37. * <p>
  38. * This is implemented
  39. * to forward to the superclass as well as call the
  40. * {@link #setPropertiesFromAttributes()}
  41. * method to set the paragraph properties from the css
  42. * attributes. The call is made at this time to ensure
  43. * the ability to resolve upward through the parents
  44. * view attributes.
  45. *
  46. * @param parent the new parent, or null if the view is
  47. * being removed from a parent it was previously added
  48. * to
  49. */
  50. public void setParent(View parent) {
  51. super.setParent(parent);
  52. if (parent != null) {
  53. setPropertiesFromAttributes();
  54. }
  55. }
  56. /**
  57. * Calculate the requirements of the block along the major
  58. * axis (i.e. the axis along with it tiles). This is implemented
  59. * to provide the superclass behavior and then adjust it if the
  60. * CSS width or height attribute is specified and applicable to
  61. * the axis.
  62. */
  63. protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
  64. if (r == null) {
  65. r = new SizeRequirements();
  66. }
  67. if (! spanSetFromAttributes(axis, r, cssWidth, cssHeight)) {
  68. r = super.calculateMajorAxisRequirements(axis, r);
  69. }
  70. else {
  71. // Offset by the margins so that pref/min/max return the
  72. // right value.
  73. SizeRequirements parentR = super.calculateMajorAxisRequirements(
  74. axis, null);
  75. int margin = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
  76. getTopInset() + getBottomInset();
  77. r.minimum -= margin;
  78. r.preferred -= margin;
  79. r.maximum -= margin;
  80. constrainSize(axis, r, parentR);
  81. }
  82. return r;
  83. }
  84. /**
  85. * Calculate the requirements of the block along the minor
  86. * axis (i.e. the axis orthoginal to the axis along with it tiles).
  87. * This is implemented
  88. * to provide the superclass behavior and then adjust it if the
  89. * CSS width or height attribute is specified and applicable to
  90. * the axis.
  91. */
  92. protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
  93. if (r == null) {
  94. r = new SizeRequirements();
  95. }
  96. if (! spanSetFromAttributes(axis, r, cssWidth, cssHeight)) {
  97. /*
  98. * The requirements were not directly specified by attributes, so
  99. * compute the aggregate of the requirements of the children. The
  100. * children that have a percentage value specified will be treated
  101. * as completely stretchable since that child is not limited in any
  102. * way.
  103. */
  104. /*
  105. int min = 0;
  106. long pref = 0;
  107. int max = 0;
  108. int n = getViewCount();
  109. for (int i = 0; i < n; i++) {
  110. View v = getView(i);
  111. min = Math.max((int) v.getMinimumSpan(axis), min);
  112. pref = Math.max((int) v.getPreferredSpan(axis), pref);
  113. if (
  114. max = Math.max((int) v.getMaximumSpan(axis), max);
  115. }
  116. r.preferred = (int) pref;
  117. r.minimum = min;
  118. r.maximum = max;
  119. */
  120. r = super.calculateMinorAxisRequirements(axis, r);
  121. }
  122. else {
  123. // Offset by the margins so that pref/min/max return the
  124. // right value.
  125. SizeRequirements parentR = super.calculateMinorAxisRequirements(
  126. axis, null);
  127. int margin = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
  128. getTopInset() + getBottomInset();
  129. r.minimum -= margin;
  130. r.preferred -= margin;
  131. r.maximum -= margin;
  132. constrainSize(axis, r, parentR);
  133. }
  134. /*
  135. * Set the alignment based upon the CSS properties if it is
  136. * specified. For X_AXIS this would be text-align, for
  137. * Y_AXIS this would be vertical-align.
  138. */
  139. if (axis == X_AXIS) {
  140. Object o = getAttributes().getAttribute(CSS.Attribute.TEXT_ALIGN);
  141. if (o != null) {
  142. String align = o.toString();
  143. if (align.equals("center")) {
  144. r.alignment = 0.5f;
  145. } else if (align.equals("right")) {
  146. r.alignment = 1.0f;
  147. } else {
  148. r.alignment = 0.0f;
  149. }
  150. }
  151. }
  152. // Y_AXIS TBD
  153. return r;
  154. }
  155. boolean isPercentage(int axis, AttributeSet a) {
  156. if (axis == X_AXIS) {
  157. if (cssWidth != null) {
  158. return cssWidth.isPercentage();
  159. }
  160. } else {
  161. if (cssHeight != null) {
  162. return cssHeight.isPercentage();
  163. }
  164. }
  165. return false;
  166. }
  167. /**
  168. * Adjust the given requirements to the CSS width or height if
  169. * it is specified along the applicable axis. Return true if the
  170. * size is exactly specified, false if the span is not specified
  171. * in an attribute or the size specified is a percentage.
  172. */
  173. static boolean spanSetFromAttributes(int axis, SizeRequirements r,
  174. CSS.LengthValue cssWidth,
  175. CSS.LengthValue cssHeight) {
  176. if (axis == X_AXIS) {
  177. if ((cssWidth != null) && (! cssWidth.isPercentage())) {
  178. r.minimum = r.preferred = r.maximum = (int) cssWidth.getValue();
  179. return true;
  180. }
  181. } else {
  182. if ((cssHeight != null) && (! cssHeight.isPercentage())) {
  183. r.minimum = r.preferred = r.maximum = (int) cssHeight.getValue();
  184. return true;
  185. }
  186. }
  187. return false;
  188. }
  189. /**
  190. * Perform layout for the minor axis of the box (i.e. the
  191. * axis orthoginal to the axis that it represents). The results
  192. * of the layout should be placed in the given arrays which represent
  193. * the allocations to the children along the minor axis.
  194. *
  195. * @param targetSpan the total span given to the view, which
  196. * whould be used to layout the childre.
  197. * @param axis the axis being layed out
  198. * @param offsets the offsets from the origin of the view for
  199. * each of the child views; this is a return value and is
  200. * filled in by the implementation of this method
  201. * @param spans the span of each child view; this is a return
  202. * value and is filled in by the implementation of this method
  203. * @return the offset and span for each child view in the
  204. * offsets and spans parameters
  205. */
  206. protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
  207. int n = getViewCount();
  208. Object key = (axis == X_AXIS) ? CSS.Attribute.WIDTH : CSS.Attribute.HEIGHT;
  209. for (int i = 0; i < n; i++) {
  210. View v = getView(i);
  211. int min = (int) v.getMinimumSpan(axis);
  212. int max = (int) v.getMaximumSpan(axis);
  213. // check for percentage span
  214. AttributeSet a = v.getAttributes();
  215. CSS.LengthValue lv = (CSS.LengthValue) a.getAttribute(key);
  216. if ((lv != null) && lv.isPercentage()) {
  217. // bound the span to the percentage specified
  218. min = Math.max((int) lv.getValue(targetSpan), min);
  219. max = min;
  220. }
  221. // assign the offset and span for the child
  222. if (max < targetSpan) {
  223. // can't make the child this wide, align it
  224. float align = v.getAlignment(axis);
  225. offsets[i] = (int) ((targetSpan - max) * align);
  226. spans[i] = max;
  227. } else {
  228. // make it the target width, or as small as it can get.
  229. offsets[i] = 0;
  230. spans[i] = Math.max(min, targetSpan);
  231. }
  232. }
  233. }
  234. /**
  235. * Renders using the given rendering surface and area on that
  236. * surface. This is implemented to delegate to the css box
  237. * painter to paint the border and background prior to the
  238. * interior.
  239. *
  240. * @param g the rendering surface to use
  241. * @param allocation the allocated region to render into
  242. * @see View#paint
  243. */
  244. public void paint(Graphics g, Shape allocation) {
  245. Rectangle a = (Rectangle) allocation;
  246. painter.paint(g, a.x, a.y, a.width, a.height, this);
  247. super.paint(g, a);
  248. }
  249. /**
  250. * Fetches the attributes to use when rendering. This is
  251. * implemented to multiplex the attributes specified in the
  252. * model with a StyleSheet.
  253. */
  254. public AttributeSet getAttributes() {
  255. if (attr == null) {
  256. StyleSheet sheet = getStyleSheet();
  257. attr = sheet.getViewAttributes(this);
  258. }
  259. return attr;
  260. }
  261. /**
  262. * Gets the resize weight.
  263. *
  264. * @param axis may be either X_AXIS or Y_AXIS
  265. * @return the weight
  266. * @exception IllegalArgumentException for an invalid axis
  267. */
  268. public int getResizeWeight(int axis) {
  269. switch (axis) {
  270. case View.X_AXIS:
  271. return 1;
  272. case View.Y_AXIS:
  273. return 0;
  274. default:
  275. throw new IllegalArgumentException("Invalid axis: " + axis);
  276. }
  277. }
  278. /**
  279. * Gets the alignment.
  280. *
  281. * @param axis may be either X_AXIS or Y_AXIS
  282. * @return the alignment
  283. */
  284. public float getAlignment(int axis) {
  285. switch (axis) {
  286. case View.X_AXIS:
  287. return 0;
  288. case View.Y_AXIS:
  289. if (getViewCount() == 0) {
  290. return 0;
  291. }
  292. float span = getPreferredSpan(View.Y_AXIS);
  293. View v = getView(0);
  294. float above = v.getPreferredSpan(View.Y_AXIS);
  295. float a = (((int)span) != 0) ? (above * v.getAlignment(View.Y_AXIS)) / span: 0;
  296. return a;
  297. default:
  298. throw new IllegalArgumentException("Invalid axis: " + axis);
  299. }
  300. }
  301. public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
  302. super.changedUpdate(changes, a, f);
  303. int pos = changes.getOffset();
  304. if (pos <= getStartOffset() && (pos + changes.getLength()) >=
  305. getEndOffset()) {
  306. setPropertiesFromAttributes();
  307. }
  308. }
  309. /**
  310. * Determines the preferred span for this view along an
  311. * axis.
  312. *
  313. * @param axis may be either <code>View.X_AXIS</code>
  314. * or <code>View.Y_AXIS</code>
  315. * @return the span the view would like to be rendered into >= 0;
  316. * typically the view is told to render into the span
  317. * that is returned, although there is no guarantee;
  318. * the parent may choose to resize or break the view
  319. * @exception IllegalArgumentException for an invalid axis type
  320. */
  321. public float getPreferredSpan(int axis) {
  322. return super.getPreferredSpan(axis);
  323. }
  324. /**
  325. * Determines the minimum span for this view along an
  326. * axis.
  327. *
  328. * @param axis may be either <code>View.X_AXIS</code>
  329. * or <code>View.Y_AXIS</code>
  330. * @return the span the view would like to be rendered into >= 0;
  331. * typically the view is told to render into the span
  332. * that is returned, although there is no guarantee;
  333. * the parent may choose to resize or break the view
  334. * @exception IllegalArgumentException for an invalid axis type
  335. */
  336. public float getMinimumSpan(int axis) {
  337. return super.getMinimumSpan(axis);
  338. }
  339. /**
  340. * Determines the maximum span for this view along an
  341. * axis.
  342. *
  343. * @param axis may be either <code>View.X_AXIS</code>
  344. * or <code>View.Y_AXIS</code>
  345. * @return the span the view would like to be rendered into >= 0;
  346. * typically the view is told to render into the span
  347. * that is returned, although there is no guarantee;
  348. * the parent may choose to resize or break the view
  349. * @exception IllegalArgumentException for an invalid axis type
  350. */
  351. public float getMaximumSpan(int axis) {
  352. return super.getMaximumSpan(axis);
  353. }
  354. /**
  355. * Update any cached values that come from attributes.
  356. */
  357. protected void setPropertiesFromAttributes() {
  358. // update attributes
  359. StyleSheet sheet = getStyleSheet();
  360. attr = sheet.getViewAttributes(this);
  361. // Reset the painter
  362. painter = sheet.getBoxPainter(attr);
  363. if (attr != null) {
  364. setInsets((short) painter.getInset(TOP, this),
  365. (short) painter.getInset(LEFT, this),
  366. (short) painter.getInset(BOTTOM, this),
  367. (short) painter.getInset(RIGHT, this));
  368. }
  369. // Get the width/height
  370. cssWidth = (CSS.LengthValue) attr.getAttribute(CSS.Attribute.WIDTH);
  371. cssHeight = (CSS.LengthValue) attr.getAttribute(CSS.Attribute.HEIGHT);
  372. }
  373. protected StyleSheet getStyleSheet() {
  374. HTMLDocument doc = (HTMLDocument) getDocument();
  375. return doc.getStyleSheet();
  376. }
  377. /**
  378. * Constrains <code>want</code> to fit in the minimum size specified
  379. * by <code>min</code>.
  380. */
  381. private void constrainSize(int axis, SizeRequirements want,
  382. SizeRequirements min) {
  383. if (min.minimum > want.minimum) {
  384. want.minimum = want.preferred = min.minimum;
  385. want.maximum = Math.max(want.maximum, min.maximum);
  386. }
  387. }
  388. private AttributeSet attr;
  389. private StyleSheet.BoxPainter painter;
  390. private CSS.LengthValue cssWidth;
  391. private CSS.LengthValue cssHeight;
  392. }