// BlogBridge -- RSS feed reader, manager, and web based service
// Copyright (C) 2002-2006 by R. Pito Salas
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software Foundation;
// either version 2 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with this program;
// if not, write to the Free Software Foundation, Inc., 59 Temple Place,
// Suite 330, Boston, MA 02111-1307 USA
//
// Contact: R. Pito Salas
// mailto:pitosalas@users.sourceforge.net
// More information: about BlogBridge
// http://www.blogbridge.com
// http://sourceforge.net/projects/blogbridge
//
// $Id: FloatableImageView.java,v 1.10 2008/04/09 06:07:11 spyromus Exp $
//
package com.salas.bb.utils.uif.html;
import javax.swing.text.AttributeSet;
import javax.swing.text.Element;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.View;
import javax.swing.text.html.CSS;
import javax.swing.text.html.HTML;
import java.awt.*;
/**
* Adds support for left/right floated images. These images do not appear
* in the flow (i.e. they are invisible), but are painted by the enclosing
* <code>FloatParagraphView</code>.
*
* @see FloatParagraphView
*/
public class FloatableImageView extends CustomImageView
{
/**
* Inline layout type constant.
*/
public static final int INLINE = 0;
/**
* Left-aligned layout type constant.
*/
public static final int FLOAT_LEFT = 1;
/**
* Right-aligned layout type constant.
*/
public static final int FLOAT_RIGHT = 2;
/**
* The internal alignment state for this image (one of the static constants, e.g.
* <code>INLINE</code>).
*/
private int hAlign;
/**
* The horizontal distance from the margin of the floated image.
* Ignored for non-floating images.
*/
protected int hFloatPos = 0;
/**
* The vertical distance of the floated image from its assigned margin.
* Ignored for non-floating images.
*/
protected int vFloatPos = 0;
/**
* Creates a new view that represents an IMG element.
*
* @param elem the element to create a view for
*/
public FloatableImageView(Element elem)
{
super(elem);
// Add default 3px margins if element is floated and has no HSPACE/VSPACE
MutableAttributeSet attr = (MutableAttributeSet)elem.getAttributes();
Object alignment = attr.getAttribute(HTML.Attribute.ALIGN);
AttributeSet cssAttr = getAttributes();
Object cssFloat = cssAttr.getAttribute(CSS.Attribute.FLOAT);
if ("left".equals(alignment) || "right".equals(alignment) ||
"left".equals(cssFloat) || "right".equals(cssFloat))
{
if (getIntAttr(HTML.Attribute.HSPACE, -1) < 0)
{
attr.addAttribute(HTML.Attribute.HSPACE, "3");
}
if (getIntAttr(HTML.Attribute.VSPACE, -1) < 0)
{
attr.addAttribute(HTML.Attribute.VSPACE, "3");
}
}
}
/**
* Update any cached values that come from attributes. Updates <code>layoutType</code>
* property from HTML <code>align</code> attribute and CSS <code>float</code> property.
*/
protected void setPropertiesFromAttributes()
{
super.setPropertiesFromAttributes();
AttributeSet attr = getElement().getAttributes();
Object alignment = attr.getAttribute(HTML.Attribute.ALIGN);
AttributeSet cssAttr = getAttributes();
Object cssFloat = cssAttr.getAttribute(CSS.Attribute.FLOAT);
hAlign = INLINE;
if (alignment != null)
{
alignment = alignment.toString();
if ("left".equals(alignment))
{
hAlign = FLOAT_LEFT;
} else if ("right".equals(alignment))
{
hAlign = FLOAT_RIGHT;
}
} else if (cssFloat != null)
{
cssFloat = cssFloat.toString();
if ("left".equals(cssFloat))
{
hAlign = FLOAT_LEFT;
} else if ("right".equals(cssFloat))
{
hAlign = FLOAT_RIGHT;
}
}
// Apply margin rules
rightInset += getIntAttr(CSS.Attribute.MARGIN_RIGHT, 0);
leftInset += getIntAttr(CSS.Attribute.MARGIN_LEFT, 0);
bottomInset += getIntAttr(CSS.Attribute.MARGIN_BOTTOM, 0);
topInset += getIntAttr(CSS.Attribute.MARGIN_TOP, 0);
}
/**
* Convenience method for getting an integer attribute from the element's
* <code>AttributeSet</code>.
*
* @param name The <code>HTML.Attribute</code> constant to fetch.
* @param deflt The default value to return.
*
* @return The integer value of the HTML attribute <code>name</code>, or <code>deflt</code>
* if attribute value could not be converted to into, or if it has no value.
*/
private int getIntAttr(HTML.Attribute name, int deflt)
{
AttributeSet attr = getElement().getAttributes();
if (attr.isDefined(name)) // does not check parents!
{
int i;
String val = attr.getAttribute(name).toString();
if (val == null)
{
i = deflt;
} else
{
try
{
i = Math.max(0, Integer.parseInt(val));
} catch (NumberFormatException x)
{
i = deflt;
}
}
return i;
} else return deflt;
}
/**
* Returns the layout type for the image.
* @return <code>FLOAT_LEFT</code>, <code>FLOAT_RIGHT</code>, or <code>INLINE</code>
*/
public int getLayoutType()
{
return hAlign;
}
/**
* Is this a floating (left/right-aligned) image?
* @return true if floating
*/
public boolean isFloat()
{
return getLayoutType() != INLINE;
}
/**
* Paints this <code>View</code>.
*
* @param g the rendering surface to use
* @param a the allocated region to render into
*/
public void paint(Graphics g, Shape a)
{
paint(g, a, false);
}
/**
* Paints this <code>View</code>. If image is floated, the image will not
* actually be painted unless <code>paintFloat</code> is true.
*
* @param g the rendering surface to use
* @param a the allocated region to render into
* @param paintFloat even if the image is floated, actually paint it
*/
public void paint(Graphics g, Shape a, boolean paintFloat)
{
if (isFloat() && !paintFloat) return;
super.paint(g, a);
}
/**
* Determines the preferred span for this view along an
* axis.
*
* @param axis may be either <code>X_AXIS</code> or <code>Y_AXIS</code>
* @return the span the view would like to be rendered into;
* typically the view is told to render into the span
* that is returned, although there is no guarantee;
* the parent may choose to resize or break the view
*/
public float getPreferredSpan(int axis)
{
return getPreferredSpan(axis, false);
}
/**
* Gets preferred size along a given axis for the image. If image
* is floated, the preferred span will be zero unless <code>paintFloat</code>
* is true or the image is at the start of its row (in which case it occupies
* space on this row).
*
* @param axis may be either <code>X_AXIS</code> or <code>Y_AXIS</code>
* @param paintFloat If true and image is a floater, return the
* dimension of the image inline (zero). If false,
* return the actual image dimension.
* @return the span the view would like to be rendered into;
* typically the view is told to render into the span
* that is returned, although there is no guarantee;
* the parent may choose to resize or break the view.
*/
public float getPreferredSpan(int axis, boolean paintFloat)
{
View v;
if (!isFloat() || paintFloat) return super.getPreferredSpan(axis);
if (axis == View.Y_AXIS) return 0;
View lv = getParent(); // The logical view
View fv = lv == null ? null : lv.getParent(); // The flow (paragraph) view
if (fv != null && fv instanceof FloatParagraphView)
{
int rowIndex = ((FloatParagraphView)fv).getViewIndexAtPosition(getStartOffset());
View row = rowIndex < 0 ? null : fv.getView(rowIndex);
// If this view has already been placed in a row
if (row != null)
{
// Find out if this view is among any floated images at the start the row
for (int i = 0; true; i++)
{
v = row.getView(i);
if (v == this || !(v instanceof FloatableImageView) ||
!((FloatableImageView)v).isFloat())
{
break;
}
}
if (v != this)
{
// This image won't appear in this row, so its span for this row is 0
return 0;
}
} else if (fv.getViewCount() > 0)
// If this view is looking to be placed in a row
{
// Find the last row (the one currently being layed out)
row = fv.getView(fv.getViewCount() - 1);
// Find out if the row contains anything other than floated images so far
int n = row.getViewCount();
for (int i = 0; i < n; i++)
{
v = row.getView(i);
if (!(v instanceof FloatableImageView) || !((FloatableImageView)v).isFloat())
{
// This image won't appear in this row, so its span for this row is 0
return 0;
}
}
}
}
// This image will appear in this row
return super.getPreferredSpan(axis);
}
}