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 &gt;= 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 &gt;= 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 &gt;= 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