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      */
  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      */
  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      */
  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      */
 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 
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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 
 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}
 354      *           or {@code View.Y_AXIS}
 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      */
 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}
 370      *           or {@code View.Y_AXIS}
 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      */
 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}
 386      *           or {@code View.Y_AXIS}
 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      */
 393     public float getMaximumSpan(int axis) {
 394         return super.getMaximumSpan(axis);
 395     }
 396 
 397     /**
 398      * Update any cached values that come from attributes.
 399      */
 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      */
 425     protected StyleSheet getStyleSheet() {
 426         HTMLDocument doc = (HTMLDocument) getDocument();
 427         return doc.getStyleSheet();
 428     }
 429 
 430     /**
 431      * Constrains {@code want} to fit in the minimum size specified
 432      * by {@code min}.
 433      */
 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 }
--- EOF ---