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