1. /*
  2. * @(#)BlockView.java 1.36 04/03/05
  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.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.36 03/05/04
  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;
  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. } else {
  221. max = (int)v.getMaximumSpan(axis);
  222. }
  223. // assign the offset and span for the child
  224. if (max < targetSpan) {
  225. // can't make the child this wide, align it
  226. float align = v.getAlignment(axis);
  227. offsets[i] = (int) ((targetSpan - max) * align);
  228. spans[i] = max;
  229. } else {
  230. // make it the target width, or as small as it can get.
  231. offsets[i] = 0;
  232. spans[i] = Math.max(min, targetSpan);
  233. }
  234. }
  235. }
  236. /**
  237. * Renders using the given rendering surface and area on that
  238. * surface. This is implemented to delegate to the css box
  239. * painter to paint the border and background prior to the
  240. * interior.
  241. *
  242. * @param g the rendering surface to use
  243. * @param allocation the allocated region to render into
  244. * @see View#paint
  245. */
  246. public void paint(Graphics g, Shape allocation) {
  247. Rectangle a = (Rectangle) allocation;
  248. painter.paint(g, a.x, a.y, a.width, a.height, this);
  249. super.paint(g, a);
  250. }
  251. /**
  252. * Fetches the attributes to use when rendering. This is
  253. * implemented to multiplex the attributes specified in the
  254. * model with a StyleSheet.
  255. */
  256. public AttributeSet getAttributes() {
  257. if (attr == null) {
  258. StyleSheet sheet = getStyleSheet();
  259. attr = sheet.getViewAttributes(this);
  260. }
  261. return attr;
  262. }
  263. /**
  264. * Gets the resize weight.
  265. *
  266. * @param axis may be either X_AXIS or Y_AXIS
  267. * @return the weight
  268. * @exception IllegalArgumentException for an invalid axis
  269. */
  270. public int getResizeWeight(int axis) {
  271. switch (axis) {
  272. case View.X_AXIS:
  273. return 1;
  274. case View.Y_AXIS:
  275. return 0;
  276. default:
  277. throw new IllegalArgumentException("Invalid axis: " + axis);
  278. }
  279. }
  280. /**
  281. * Gets the alignment.
  282. *
  283. * @param axis may be either X_AXIS or Y_AXIS
  284. * @return the alignment
  285. */
  286. public float getAlignment(int axis) {
  287. switch (axis) {
  288. case View.X_AXIS:
  289. return 0;
  290. case View.Y_AXIS:
  291. if (getViewCount() == 0) {
  292. return 0;
  293. }
  294. float span = getPreferredSpan(View.Y_AXIS);
  295. View v = getView(0);
  296. float above = v.getPreferredSpan(View.Y_AXIS);
  297. float a = (((int)span) != 0) ? (above * v.getAlignment(View.Y_AXIS)) / span: 0;
  298. return a;
  299. default:
  300. throw new IllegalArgumentException("Invalid axis: " + axis);
  301. }
  302. }
  303. public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
  304. super.changedUpdate(changes, a, f);
  305. int pos = changes.getOffset();
  306. if (pos <= getStartOffset() && (pos + changes.getLength()) >=
  307. getEndOffset()) {
  308. setPropertiesFromAttributes();
  309. }
  310. }
  311. /**
  312. * Determines the preferred span for this view along an
  313. * axis.
  314. *
  315. * @param axis may be either <code>View.X_AXIS</code>
  316. * or <code>View.Y_AXIS</code>
  317. * @return the span the view would like to be rendered into >= 0;
  318. * typically the view is told to render into the span
  319. * that is returned, although there is no guarantee;
  320. * the parent may choose to resize or break the view
  321. * @exception IllegalArgumentException for an invalid axis type
  322. */
  323. public float getPreferredSpan(int axis) {
  324. return super.getPreferredSpan(axis);
  325. }
  326. /**
  327. * Determines the minimum span for this view along an
  328. * axis.
  329. *
  330. * @param axis may be either <code>View.X_AXIS</code>
  331. * or <code>View.Y_AXIS</code>
  332. * @return the span the view would like to be rendered into >= 0;
  333. * typically the view is told to render into the span
  334. * that is returned, although there is no guarantee;
  335. * the parent may choose to resize or break the view
  336. * @exception IllegalArgumentException for an invalid axis type
  337. */
  338. public float getMinimumSpan(int axis) {
  339. return super.getMinimumSpan(axis);
  340. }
  341. /**
  342. * Determines the maximum span for this view along an
  343. * axis.
  344. *
  345. * @param axis may be either <code>View.X_AXIS</code>
  346. * or <code>View.Y_AXIS</code>
  347. * @return the span the view would like to be rendered into >= 0;
  348. * typically the view is told to render into the span
  349. * that is returned, although there is no guarantee;
  350. * the parent may choose to resize or break the view
  351. * @exception IllegalArgumentException for an invalid axis type
  352. */
  353. public float getMaximumSpan(int axis) {
  354. return super.getMaximumSpan(axis);
  355. }
  356. /**
  357. * Update any cached values that come from attributes.
  358. */
  359. protected void setPropertiesFromAttributes() {
  360. // update attributes
  361. StyleSheet sheet = getStyleSheet();
  362. attr = sheet.getViewAttributes(this);
  363. // Reset the painter
  364. painter = sheet.getBoxPainter(attr);
  365. if (attr != null) {
  366. setInsets((short) painter.getInset(TOP, this),
  367. (short) painter.getInset(LEFT, this),
  368. (short) painter.getInset(BOTTOM, this),
  369. (short) painter.getInset(RIGHT, this));
  370. }
  371. // Get the width/height
  372. cssWidth = (CSS.LengthValue) attr.getAttribute(CSS.Attribute.WIDTH);
  373. cssHeight = (CSS.LengthValue) attr.getAttribute(CSS.Attribute.HEIGHT);
  374. }
  375. protected StyleSheet getStyleSheet() {
  376. HTMLDocument doc = (HTMLDocument) getDocument();
  377. return doc.getStyleSheet();
  378. }
  379. /**
  380. * Constrains <code>want</code> to fit in the minimum size specified
  381. * by <code>min</code>.
  382. */
  383. private void constrainSize(int axis, SizeRequirements want,
  384. SizeRequirements min) {
  385. if (min.minimum > want.minimum) {
  386. want.minimum = want.preferred = min.minimum;
  387. want.maximum = Math.max(want.maximum, min.maximum);
  388. }
  389. }
  390. private AttributeSet attr;
  391. private StyleSheet.BoxPainter painter;
  392. private CSS.LengthValue cssWidth;
  393. private CSS.LengthValue cssHeight;
  394. }