1. /*
  2. * @(#)HRuleView.java 1.32 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 javax.swing.event.DocumentEvent;
  10. import javax.swing.text.*;
  11. import java.util.Enumeration;
  12. import java.lang.Integer;
  13. /**
  14. * A view implementation to display an html horizontal
  15. * rule.
  16. *
  17. * @author Timothy Prinzing
  18. * @author Sara Swanson
  19. * @version 1.32 12/19/03
  20. */
  21. class HRuleView extends View {
  22. /**
  23. * Creates a new view that represents an <hr> element.
  24. *
  25. * @param elem the element to create a view for
  26. */
  27. public HRuleView(Element elem) {
  28. super(elem);
  29. setPropertiesFromAttributes();
  30. }
  31. /**
  32. * Update any cached values that come from attributes.
  33. */
  34. protected void setPropertiesFromAttributes() {
  35. StyleSheet sheet = ((HTMLDocument)getDocument()).getStyleSheet();
  36. AttributeSet eAttr = getElement().getAttributes();
  37. attr = sheet.getViewAttributes(this);
  38. alignment = StyleConstants.ALIGN_CENTER;
  39. size = 0;
  40. noshade = null;
  41. widthValue = null;
  42. if (attr != null) {
  43. // getAlignment() returns ALIGN_LEFT by default, and HR should
  44. // use ALIGN_CENTER by default, so we check if the alignment
  45. // attribute is actually defined
  46. if (attr.getAttribute(StyleConstants.Alignment) != null) {
  47. alignment = StyleConstants.getAlignment(attr);
  48. }
  49. noshade = (String)eAttr.getAttribute(HTML.Attribute.NOSHADE);
  50. Object value = eAttr.getAttribute(HTML.Attribute.SIZE);
  51. if (value != null && (value instanceof String))
  52. size = Integer.parseInt((String)value);
  53. value = attr.getAttribute(CSS.Attribute.WIDTH);
  54. if (value != null && (value instanceof CSS.LengthValue)) {
  55. widthValue = (CSS.LengthValue)value;
  56. }
  57. topMargin = getLength(CSS.Attribute.MARGIN_TOP, attr);
  58. bottomMargin = getLength(CSS.Attribute.MARGIN_BOTTOM, attr);
  59. leftMargin = getLength(CSS.Attribute.MARGIN_LEFT, attr);
  60. rightMargin = getLength(CSS.Attribute.MARGIN_RIGHT, attr);
  61. }
  62. else {
  63. topMargin = bottomMargin = leftMargin = rightMargin = 0;
  64. }
  65. size = Math.max(2, size);
  66. }
  67. // This will be removed and centralized at some point, need to unify this
  68. // and avoid private classes.
  69. private float getLength(CSS.Attribute key, AttributeSet a) {
  70. CSS.LengthValue lv = (CSS.LengthValue) a.getAttribute(key);
  71. float len = (lv != null) ? lv.getValue() : 0;
  72. return len;
  73. }
  74. // --- View methods ---------------------------------------------
  75. /**
  76. * Paints the view.
  77. *
  78. * @param g the graphics context
  79. * @param a the allocation region for the view
  80. * @see View#paint
  81. */
  82. public void paint(Graphics g, Shape a) {
  83. Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a :
  84. a.getBounds();
  85. int x = 0;
  86. int y = alloc.y + SPACE_ABOVE + (int)topMargin;
  87. int width = alloc.width - (int)(leftMargin + rightMargin);
  88. if (widthValue != null) {
  89. width = (int)widthValue.getValue((float)width);
  90. }
  91. int height = alloc.height - (SPACE_ABOVE + SPACE_BELOW +
  92. (int)topMargin + (int)bottomMargin);
  93. if (size > 0)
  94. height = size;
  95. // Align the rule horizontally.
  96. switch (alignment) {
  97. case StyleConstants.ALIGN_CENTER:
  98. x = alloc.x + (alloc.width / 2) - (width / 2);
  99. break;
  100. case StyleConstants.ALIGN_RIGHT:
  101. x = alloc.x + alloc.width - width - (int)rightMargin;
  102. break;
  103. case StyleConstants.ALIGN_LEFT:
  104. default:
  105. x = alloc.x + (int)leftMargin;
  106. break;
  107. }
  108. // Paint either a shaded rule or a solid line.
  109. if (noshade != null) {
  110. g.setColor(Color.black);
  111. g.fillRect(x, y, width, height);
  112. }
  113. else {
  114. Color bg = getContainer().getBackground();
  115. Color bottom, top;
  116. if (bg == null || bg.equals(Color.white)) {
  117. top = Color.darkGray;
  118. bottom = Color.lightGray;
  119. }
  120. else {
  121. top = Color.darkGray;
  122. bottom = Color.white;
  123. }
  124. g.setColor(bottom);
  125. g.drawLine(x + width - 1, y, x + width - 1, y + height - 1);
  126. g.drawLine(x, y + height - 1, x + width - 1, y + height - 1);
  127. g.setColor(top);
  128. g.drawLine(x, y, x + width - 1, y);
  129. g.drawLine(x, y, x, y + height - 1);
  130. }
  131. }
  132. /**
  133. * Calculates the desired shape of the rule... this is
  134. * basically the preferred size of the border.
  135. *
  136. * @param axis may be either X_AXIS or Y_AXIS
  137. * @return the desired span
  138. * @see View#getPreferredSpan
  139. */
  140. public float getPreferredSpan(int axis) {
  141. switch (axis) {
  142. case View.X_AXIS:
  143. return 1;
  144. case View.Y_AXIS:
  145. if (size > 0) {
  146. return size + SPACE_ABOVE + SPACE_BELOW + topMargin +
  147. bottomMargin;
  148. } else {
  149. if (noshade != null) {
  150. return 2 + SPACE_ABOVE + SPACE_BELOW + topMargin +
  151. bottomMargin;
  152. } else {
  153. return SPACE_ABOVE + SPACE_BELOW + topMargin +bottomMargin;
  154. }
  155. }
  156. default:
  157. throw new IllegalArgumentException("Invalid axis: " + axis);
  158. }
  159. }
  160. /**
  161. * Gets the resize weight for the axis.
  162. * The rule is: rigid vertically and flexible horizontally.
  163. *
  164. * @param axis may be either X_AXIS or Y_AXIS
  165. * @return the weight
  166. */
  167. public int getResizeWeight(int axis) {
  168. if (axis == View.X_AXIS) {
  169. return 1;
  170. } else if (axis == View.Y_AXIS) {
  171. return 0;
  172. } else {
  173. return 0;
  174. }
  175. }
  176. /**
  177. * Determines how attractive a break opportunity in
  178. * this view is. This is implemented to request a forced break.
  179. *
  180. * @param axis may be either View.X_AXIS or View.Y_AXIS
  181. * @param pos the potential location of the start of the
  182. * broken view (greater than or equal to zero).
  183. * This may be useful for calculating tab
  184. * positions.
  185. * @param len specifies the relative length from <em>pos</em>
  186. * where a potential break is desired. The value must be greater
  187. * than or equal to zero.
  188. * @return the weight, which should be a value between
  189. * ForcedBreakWeight and BadBreakWeight.
  190. */
  191. public int getBreakWeight(int axis, float pos, float len) {
  192. if (axis == X_AXIS) {
  193. return ForcedBreakWeight;
  194. }
  195. return BadBreakWeight;
  196. }
  197. public View breakView(int axis, int offset, float pos, float len) {
  198. return null;
  199. }
  200. /**
  201. * Provides a mapping from the document model coordinate space
  202. * to the coordinate space of the view mapped to it.
  203. *
  204. * @param pos the position to convert
  205. * @param a the allocated region to render into
  206. * @return the bounding box of the given position
  207. * @exception BadLocationException if the given position does not
  208. * represent a valid location in the associated document
  209. * @see View#modelToView
  210. */
  211. public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
  212. int p0 = getStartOffset();
  213. int p1 = getEndOffset();
  214. if ((pos >= p0) && (pos <= p1)) {
  215. Rectangle r = a.getBounds();
  216. if (pos == p1) {
  217. r.x += r.width;
  218. }
  219. r.width = 0;
  220. return r;
  221. }
  222. return null;
  223. }
  224. /**
  225. * Provides a mapping from the view coordinate space to the logical
  226. * coordinate space of the model.
  227. *
  228. * @param x the X coordinate
  229. * @param y the Y coordinate
  230. * @param a the allocated region to render into
  231. * @return the location within the model that best represents the
  232. * given point of view
  233. * @see View#viewToModel
  234. */
  235. public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
  236. Rectangle alloc = (Rectangle) a;
  237. if (x < alloc.x + (alloc.width / 2)) {
  238. bias[0] = Position.Bias.Forward;
  239. return getStartOffset();
  240. }
  241. bias[0] = Position.Bias.Backward;
  242. return getEndOffset();
  243. }
  244. /**
  245. * Fetches the attributes to use when rendering. This is
  246. * implemented to multiplex the attributes specified in the
  247. * model with a StyleSheet.
  248. */
  249. public AttributeSet getAttributes() {
  250. return attr;
  251. }
  252. public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
  253. super.changedUpdate(changes, a, f);
  254. int pos = changes.getOffset();
  255. if (pos <= getStartOffset() && (pos + changes.getLength()) >=
  256. getEndOffset()) {
  257. setPropertiesFromAttributes();
  258. }
  259. }
  260. // --- variables ------------------------------------------------
  261. private float topMargin;
  262. private float bottomMargin;
  263. private float leftMargin;
  264. private float rightMargin;
  265. private int alignment = StyleConstants.ALIGN_CENTER;
  266. private String noshade = null;
  267. private int size = 0;
  268. private CSS.LengthValue widthValue;
  269. private static final int SPACE_ABOVE = 3;
  270. private static final int SPACE_BELOW = 3;
  271. /** View Attributes. */
  272. private AttributeSet attr;
  273. }