001 /*
002 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025 package javax.swing.text.html;
026
027 import java.util.Enumeration;
028 import java.awt.*;
029 import javax.swing.SizeRequirements;
030 import javax.swing.border.*;
031 import javax.swing.event.DocumentEvent;
032 import javax.swing.text.*;
033
034 /**
035 * A view implementation to display a block (as a box)
036 * with CSS specifications.
037 *
038 * @author Timothy Prinzing
039 * @version 1.44 05/05/07
040 */
041 public class BlockView extends BoxView {
042
043 /**
044 * Creates a new view that represents an
045 * html box. This can be used for a number
046 * of elements.
047 *
048 * @param elem the element to create a view for
049 * @param axis either View.X_AXIS or View.Y_AXIS
050 */
051 public BlockView(Element elem, int axis) {
052 super (elem, axis);
053 }
054
055 /**
056 * Establishes the parent view for this view. This is
057 * guaranteed to be called before any other methods if the
058 * parent view is functioning properly.
059 * <p>
060 * This is implemented
061 * to forward to the superclass as well as call the
062 * {@link #setPropertiesFromAttributes()}
063 * method to set the paragraph properties from the css
064 * attributes. The call is made at this time to ensure
065 * the ability to resolve upward through the parents
066 * view attributes.
067 *
068 * @param parent the new parent, or null if the view is
069 * being removed from a parent it was previously added
070 * to
071 */
072 public void setParent(View parent) {
073 super .setParent(parent);
074 if (parent != null) {
075 setPropertiesFromAttributes();
076 }
077 }
078
079 /**
080 * Calculate the requirements of the block along the major
081 * axis (i.e. the axis along with it tiles). This is implemented
082 * to provide the superclass behavior and then adjust it if the
083 * CSS width or height attribute is specified and applicable to
084 * the axis.
085 */
086 protected SizeRequirements calculateMajorAxisRequirements(int axis,
087 SizeRequirements r) {
088 if (r == null) {
089 r = new SizeRequirements();
090 }
091 if (!spanSetFromAttributes(axis, r, cssWidth, cssHeight)) {
092 r = super .calculateMajorAxisRequirements(axis, r);
093 } else {
094 // Offset by the margins so that pref/min/max return the
095 // right value.
096 SizeRequirements parentR = super
097 .calculateMajorAxisRequirements(axis, null);
098 int margin = (axis == X_AXIS) ? getLeftInset()
099 + getRightInset() : getTopInset()
100 + getBottomInset();
101 r.minimum -= margin;
102 r.preferred -= margin;
103 r.maximum -= margin;
104 constrainSize(axis, r, parentR);
105 }
106 return r;
107 }
108
109 /**
110 * Calculate the requirements of the block along the minor
111 * axis (i.e. the axis orthoginal to the axis along with it tiles).
112 * This is implemented
113 * to provide the superclass behavior and then adjust it if the
114 * CSS width or height attribute is specified and applicable to
115 * the axis.
116 */
117 protected SizeRequirements calculateMinorAxisRequirements(int axis,
118 SizeRequirements r) {
119 if (r == null) {
120 r = new SizeRequirements();
121 }
122
123 if (!spanSetFromAttributes(axis, r, cssWidth, cssHeight)) {
124
125 /*
126 * The requirements were not directly specified by attributes, so
127 * compute the aggregate of the requirements of the children. The
128 * children that have a percentage value specified will be treated
129 * as completely stretchable since that child is not limited in any
130 * way.
131 */
132 /*
133 int min = 0;
134 long pref = 0;
135 int max = 0;
136 int n = getViewCount();
137 for (int i = 0; i < n; i++) {
138 View v = getView(i);
139 min = Math.max((int) v.getMinimumSpan(axis), min);
140 pref = Math.max((int) v.getPreferredSpan(axis), pref);
141 if (
142 max = Math.max((int) v.getMaximumSpan(axis), max);
143
144 }
145 r.preferred = (int) pref;
146 r.minimum = min;
147 r.maximum = max;
148 */
149 r = super .calculateMinorAxisRequirements(axis, r);
150 } else {
151 // Offset by the margins so that pref/min/max return the
152 // right value.
153 SizeRequirements parentR = super
154 .calculateMinorAxisRequirements(axis, null);
155 int margin = (axis == X_AXIS) ? getLeftInset()
156 + getRightInset() : getTopInset()
157 + getBottomInset();
158 r.minimum -= margin;
159 r.preferred -= margin;
160 r.maximum -= margin;
161 constrainSize(axis, r, parentR);
162 }
163
164 /*
165 * Set the alignment based upon the CSS properties if it is
166 * specified. For X_AXIS this would be text-align, for
167 * Y_AXIS this would be vertical-align.
168 */
169 if (axis == X_AXIS) {
170 Object o = getAttributes().getAttribute(
171 CSS.Attribute.TEXT_ALIGN);
172 if (o != null) {
173 String align = o.toString();
174 if (align.equals("center")) {
175 r.alignment = 0.5f;
176 } else if (align.equals("right")) {
177 r.alignment = 1.0f;
178 } else {
179 r.alignment = 0.0f;
180 }
181 }
182 }
183 // Y_AXIS TBD
184 return r;
185 }
186
187 boolean isPercentage(int axis, AttributeSet a) {
188 if (axis == X_AXIS) {
189 if (cssWidth != null) {
190 return cssWidth.isPercentage();
191 }
192 } else {
193 if (cssHeight != null) {
194 return cssHeight.isPercentage();
195 }
196 }
197 return false;
198 }
199
200 /**
201 * Adjust the given requirements to the CSS width or height if
202 * it is specified along the applicable axis. Return true if the
203 * size is exactly specified, false if the span is not specified
204 * in an attribute or the size specified is a percentage.
205 */
206 static boolean spanSetFromAttributes(int axis, SizeRequirements r,
207 CSS.LengthValue cssWidth, CSS.LengthValue cssHeight) {
208 if (axis == X_AXIS) {
209 if ((cssWidth != null) && (!cssWidth.isPercentage())) {
210 r.minimum = r.preferred = r.maximum = (int) cssWidth
211 .getValue();
212 return true;
213 }
214 } else {
215 if ((cssHeight != null) && (!cssHeight.isPercentage())) {
216 r.minimum = r.preferred = r.maximum = (int) cssHeight
217 .getValue();
218 return true;
219 }
220 }
221 return false;
222 }
223
224 /**
225 * Performs layout for the minor axis of the box (i.e. the
226 * axis orthoginal to the axis that it represents). The results
227 * of the layout (the offset and span for each children) are
228 * placed in the given arrays which represent the allocations to
229 * the children along the minor axis.
230 *
231 * @param targetSpan the total span given to the view, which
232 * whould be used to layout the childre.
233 * @param axis the axis being layed out
234 * @param offsets the offsets from the origin of the view for
235 * each of the child views; this is a return value and is
236 * filled in by the implementation of this method
237 * @param spans the span of each child view; this is a return
238 * value and is filled in by the implementation of this method
239 */
240 protected void layoutMinorAxis(int targetSpan, int axis,
241 int[] offsets, int[] spans) {
242 int n = getViewCount();
243 Object key = (axis == X_AXIS) ? CSS.Attribute.WIDTH
244 : CSS.Attribute.HEIGHT;
245 for (int i = 0; i < n; i++) {
246 View v = getView(i);
247 int min = (int) v.getMinimumSpan(axis);
248 int max;
249
250 // check for percentage span
251 AttributeSet a = v.getAttributes();
252 CSS.LengthValue lv = (CSS.LengthValue) a.getAttribute(key);
253 if ((lv != null) && lv.isPercentage()) {
254 // bound the span to the percentage specified
255 min = Math.max((int) lv.getValue(targetSpan), min);
256 max = min;
257 } else {
258 max = (int) v.getMaximumSpan(axis);
259 }
260
261 // assign the offset and span for the child
262 if (max < targetSpan) {
263 // can't make the child this wide, align it
264 float align = v.getAlignment(axis);
265 offsets[i] = (int) ((targetSpan - max) * align);
266 spans[i] = max;
267 } else {
268 // make it the target width, or as small as it can get.
269 offsets[i] = 0;
270 spans[i] = Math.max(min, targetSpan);
271 }
272 }
273 }
274
275 /**
276 * Renders using the given rendering surface and area on that
277 * surface. This is implemented to delegate to the css box
278 * painter to paint the border and background prior to the
279 * interior.
280 *
281 * @param g the rendering surface to use
282 * @param allocation the allocated region to render into
283 * @see View#paint
284 */
285 public void paint(Graphics g, Shape allocation) {
286 Rectangle a = (Rectangle) allocation;
287 painter.paint(g, a.x, a.y, a.width, a.height, this );
288 super .paint(g, a);
289 }
290
291 /**
292 * Fetches the attributes to use when rendering. This is
293 * implemented to multiplex the attributes specified in the
294 * model with a StyleSheet.
295 */
296 public AttributeSet getAttributes() {
297 if (attr == null) {
298 StyleSheet sheet = getStyleSheet();
299 attr = sheet.getViewAttributes(this );
300 }
301 return attr;
302 }
303
304 /**
305 * Gets the resize weight.
306 *
307 * @param axis may be either X_AXIS or Y_AXIS
308 * @return the weight
309 * @exception IllegalArgumentException for an invalid axis
310 */
311 public int getResizeWeight(int axis) {
312 switch (axis) {
313 case View.X_AXIS:
314 return 1;
315 case View.Y_AXIS:
316 return 0;
317 default:
318 throw new IllegalArgumentException("Invalid axis: " + axis);
319 }
320 }
321
322 /**
323 * Gets the alignment.
324 *
325 * @param axis may be either X_AXIS or Y_AXIS
326 * @return the alignment
327 */
328 public float getAlignment(int axis) {
329 switch (axis) {
330 case View.X_AXIS:
331 return 0;
332 case View.Y_AXIS:
333 if (getViewCount() == 0) {
334 return 0;
335 }
336 float span = getPreferredSpan(View.Y_AXIS);
337 View v = getView(0);
338 float above = v.getPreferredSpan(View.Y_AXIS);
339 float a = (((int) span) != 0) ? (above * v
340 .getAlignment(View.Y_AXIS))
341 / span : 0;
342 return a;
343 default:
344 throw new IllegalArgumentException("Invalid axis: " + axis);
345 }
346 }
347
348 public void changedUpdate(DocumentEvent changes, Shape a,
349 ViewFactory f) {
350 super .changedUpdate(changes, a, f);
351 int pos = changes.getOffset();
352 if (pos <= getStartOffset()
353 && (pos + changes.getLength()) >= getEndOffset()) {
354 setPropertiesFromAttributes();
355 }
356 }
357
358 /**
359 * Determines the preferred span for this view along an
360 * axis.
361 *
362 * @param axis may be either <code>View.X_AXIS</code>
363 * or <code>View.Y_AXIS</code>
364 * @return the span the view would like to be rendered into >= 0;
365 * typically the view is told to render into the span
366 * that is returned, although there is no guarantee;
367 * the parent may choose to resize or break the view
368 * @exception IllegalArgumentException for an invalid axis type
369 */
370 public float getPreferredSpan(int axis) {
371 return super .getPreferredSpan(axis);
372 }
373
374 /**
375 * Determines the minimum span for this view along an
376 * axis.
377 *
378 * @param axis may be either <code>View.X_AXIS</code>
379 * or <code>View.Y_AXIS</code>
380 * @return the span the view would like to be rendered into >= 0;
381 * typically the view is told to render into the span
382 * that is returned, although there is no guarantee;
383 * the parent may choose to resize or break the view
384 * @exception IllegalArgumentException for an invalid axis type
385 */
386 public float getMinimumSpan(int axis) {
387 return super .getMinimumSpan(axis);
388 }
389
390 /**
391 * Determines the maximum span for this view along an
392 * axis.
393 *
394 * @param axis may be either <code>View.X_AXIS</code>
395 * or <code>View.Y_AXIS</code>
396 * @return the span the view would like to be rendered into >= 0;
397 * typically the view is told to render into the span
398 * that is returned, although there is no guarantee;
399 * the parent may choose to resize or break the view
400 * @exception IllegalArgumentException for an invalid axis type
401 */
402 public float getMaximumSpan(int axis) {
403 return super .getMaximumSpan(axis);
404 }
405
406 /**
407 * Update any cached values that come from attributes.
408 */
409 protected void setPropertiesFromAttributes() {
410
411 // update attributes
412 StyleSheet sheet = getStyleSheet();
413 attr = sheet.getViewAttributes(this );
414
415 // Reset the painter
416 painter = sheet.getBoxPainter(attr);
417 if (attr != null) {
418 setInsets((short) painter.getInset(TOP, this ),
419 (short) painter.getInset(LEFT, this ),
420 (short) painter.getInset(BOTTOM, this ),
421 (short) painter.getInset(RIGHT, this ));
422 }
423
424 // Get the width/height
425 cssWidth = (CSS.LengthValue) attr
426 .getAttribute(CSS.Attribute.WIDTH);
427 cssHeight = (CSS.LengthValue) attr
428 .getAttribute(CSS.Attribute.HEIGHT);
429 }
430
431 protected StyleSheet getStyleSheet() {
432 HTMLDocument doc = (HTMLDocument) getDocument();
433 return doc.getStyleSheet();
434 }
435
436 /**
437 * Constrains <code>want</code> to fit in the minimum size specified
438 * by <code>min</code>.
439 */
440 private void constrainSize(int axis, SizeRequirements want,
441 SizeRequirements min) {
442 if (min.minimum > want.minimum) {
443 want.minimum = want.preferred = min.minimum;
444 want.maximum = Math.max(want.maximum, min.maximum);
445 }
446 }
447
448 private AttributeSet attr;
449 private StyleSheet.BoxPainter painter;
450
451 private CSS.LengthValue cssWidth;
452 private CSS.LengthValue cssHeight;
453
454 }
|