
com.globalmentor.swing.text.xml.XMLObjectView Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of globalmentor-swing Show documentation
Show all versions of globalmentor-swing Show documentation
GlobalMentor Java Swing library.
The newest version!
/*
* Copyright © 1996-2009 GlobalMentor, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.globalmentor.swing.text.xml;
import java.awt.*;
import javax.swing.text.*;
import com.globalmentor.swing.text.ViewHidable;
/**
* Abstract base class to represent a resizable object, such as a component, image, or applet. The object is automatically resized to be proportional to the
* requested width.
*
* Implements {@link ViewHidable} so that it can be notified if the view is being hidden so that it can hide the object.
*
*
* A class should be derived from this abstract class that sets the appropriate width and height of the object.
*
* @see #setWidth(int)
* @see #setWidth(int)
* @author Garret Wilson
*/
public abstract class XMLObjectView extends View implements ViewHidable //TODO should we change this to Hidable and make ViewComponentManager implement it as well?
{
/**
* The minimum amount of size to allow for changing; any change lower than this will result in no size change.
*/
protected static final int MINIMUM_SIZE_CHANGE = 2;
/** The current bounds of the object. */
private Rectangle bounds = new Rectangle();
/**
* @return The painting bounds of the object, only valid if painting has occurred. The size of this rectangle may not match the size of the object.
* @see #paint
*/
public Rectangle getBounds() {
return bounds;
}
/** The standard, maximum height of the object. */
private int height;
/** @return The standard, maximum height of the object. */
public int getHeight() {
return height;
}
/**
* Sets the standard, maximum height of the object and resets the current height.
* @param newHeight The new standard height of the object.
* @see #setCurrentHeight
*/
public void setHeight(final int newHeight) {
height = newHeight;
setCurrentHeight(newHeight); //set the current height to match the standard height
}
/** The standard, maximum width of the object. */
private int width;
/** @return The standard, maximum width of the object. */
public int getWidth() {
return width;
}
/**
* Sets the standard, maximum width of the object and resets the current width.
* @param newWidth The new standard width of the object.
* @see #setCurrentWidth
*/
public void setWidth(final int newWidth) {
width = newWidth;
setCurrentWidth(newWidth); //set the current width to match the standard width
}
/** The current, resized height of the object. */
private int currentHeight;
/** @return The current, resized height of the object. */
public int getCurrentHeight() {
return currentHeight;
}
/**
* Sets the current, resized height of the object.
* @param newCurrentHeight The new current height of the object.
*/
public void setCurrentHeight(final int newCurrentHeight) {
currentHeight = newCurrentHeight;
}
/** The current, resized width of the object. */
private int currentWidth;
/** @return The current, resized width of the object. */
public int getCurrentWidth() {
return currentWidth;
}
/**
* Sets the current, resized width of the object.
* @param newCurrentWidth The new current width of the object.
*/
public void setCurrentWidth(final int newCurrentWidth) {
currentWidth = newCurrentWidth;
}
//TODO testing
/** The height of the object requested by the parent view. */
private int requestedHeight;
/** @return The height of the object requested by the parent view. */
public int getRequestedHeight() {
return requestedHeight;
}
/**
* Sets the height of the object requested by the parent view.
* @param newRequestedHeight The new requested height of the object.
*/
public void setRequestedHeight(final int newRequestedHeight) {
requestedHeight = newRequestedHeight;
}
/** The width of the object requested by the parent view. */
private int requestedWidth;
/** @return The width of the object requested by the parent view. */
public int getRequestedWidth() {
return requestedWidth;
}
/**
* Sets the height of the object requested by the parent view.
* @param newRequestedWidth The new requested width of the object.
*/
public void setRequestedWidth(final int newRequestedWidth) {
requestedWidth = newRequestedWidth;
}
/** Whether the object is showing; this defaults to false
. */
private boolean showing = false;
/**
* Returns whether the object is showing. This is not necessarily the same as visible, because the associated object could be set as visible, yet not be
* showing because it is displayed in a paged view, for example.
* @return Whether the object is showing.
*/
public boolean isShowing() {
return showing;
}
/**
* Sets whether or not an view is showing. This method is called to show the view when needed if isShowing()
returns false
, and is
* called when the view is being hidden by a parent that hides views, such as a paged view. If this view is overridden, this version should be called to
* correctly update the variable for isShowing()
to function correctly.
* @param newShowing true
if the view is beginning to be shown, false
if the view is beginning to be hidden.
* @see #isShowing
*/
public void setShowing(final boolean newShowing) {
showing = newShowing;
}
/**
* Creates a new view that represents an object.
* @param element The element for which to create the view.
*/
public XMLObjectView(final Element element) {
super(element); //do the default constructing
}
/**
* Paints the object. This version does not actually do any painting. Instead, the method shows the object if needed by calling setShowing()
.
* @param graphics The rendering surface to use.
* @param allocation The allocated region to render into.
* @see View#paint
* @see #setShowing
*/
public void paint(Graphics graphics, Shape allocation) {
//get the bounding rectangle of the painting area and update our bounds variable
getBounds().setBounds((allocation instanceof Rectangle) ? (Rectangle)allocation : allocation.getBounds());
if(!isShowing()) //if the object isn't currently showing
setShowing(true); //show the object
}
/**
* Determines the preferred span for this view along an axis. This returns the currently calculated spans.
* @param axis The axis, either X_AXIS
or Y_AXIS
.
* @returns 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, although object views cannot normally be broken.
* @throws IllegalArgumentException Thrown if the axis is not recognized.
* @see #getCurrentHeight
* @see #getCurrentWidth
*/
public float getPreferredSpan(int axis) {
switch(axis) { //see which axis is being requested
case View.X_AXIS: //if the x-axis is requested
return getCurrentWidth(); //return the currently calculated width
case View.Y_AXIS: //if the y-axis is requested
return getCurrentHeight(); //return the currently calculated height
default: //if we don't recognize the axis
throw new IllegalArgumentException("Invalid axis: " + axis); //report that we don't recognize the axis
}
}
/**
* Determines the minimum span for this view along an axis. Since object views are by default resizable, this method returns zero. This zero is required
* because setSize()
expects this value and will refuse to be set to this value, preventing objects from being scaled down to nothing.
* @param axis The axis, either X_AXIS
or Y_AXIS
.
* @returns Zero as the minimum span the view can be rendered into.
* @throws IllegalArgumentException Thrown if the axis is not recognized.
* @see View#getPreferredSpan
* @see #setSize
*/
public float getMinimumSpan(int axis) {
return 0; //report that the object can be scaled away to nothing, although setSize() will not actually allow this to occur
}
/**
* Determines the maximum span for this view along an axis. This currently returns the permanent size of the object, initialized by setWidth()
* and setHeight()
.
* @param axis The axis, either X_AXIS
or Y_AXIS
.
* @returns The maximum span the view can be rendered into.
* @throws IllegalArgumentException Thrown if the axis is not recognized.
* @see View#getPreferredSpan
* @see #getHeight
* @see #getWidth
*/
public float getMaximumSpan(int axis) {
switch(axis) { //see which axis we're looking at
case View.X_AXIS: //if the x-axis is requested
return getWidth(); //return the width of the object as the maximum width
case View.Y_AXIS: //if the y-axis is requested
return getHeight(); //return the height of the object as the maximum height
default: //if we don't recognize the axis
throw new IllegalArgumentException("Invalid axis: " + axis); //report that we don't recognize the axis
}
}
/**
* Determines the desired alignment for this view along an axis. This is implemented to give the alignment to the bottom of the icon along the y axis, and the
* default along the x axis.
*
* @param axis may be either X_AXIS or Y_AXIS
* @returns the desired alignment. This should be a value between 0.0 and 1.0 where 0 indicates alignment at the origin and 1.0 indicates alignment to the
* full span away from the origin. An alignment of 0.5 would be the center of the view.
*/
/*TODO fix
public float getAlignment(int axis) {
switch (axis) {
case View.Y_AXIS:
return 1.0f;
default:
return super.getAlignment(axis);
}
}
*/
/**
* Provides a mapping from the document model coordinate space to the coordinate space of the view mapped to it.
*
* @param pos the position to convert
* @param a the allocated region to render into
* @return the bounding box of the given position
* @throws BadLocationException if the given position does not represent a valid location in the associated document
* @see View#modelToView
*/
/*TODO fix
public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
int p0 = getStartOffset();
int p1 = getEndOffset();
if ((pos >= p0) && (pos <= p1)) {
Rectangle r = a.getBounds();
if (pos == p1) {
r.x += r.width;
}
r.width = 0;
return r;
}
return null;
}
*/
/**
* Provides a mapping from the coordinate space of the model to that of the view.
* @param pos The position to convert (>=0).
* @param allocation The allocated region to render into.
* @return The bounding box of the given position.
* @throws BadLocationException Thrown if the given position does not represent a valid location governed by the view in the associated document.
* @see View#modelToView
*/
public Shape modelToView(int pos, Shape allocation, Position.Bias b) throws BadLocationException {
final int p0 = getStartOffset(); //get the starting offset of the view
final int p1 = getEndOffset(); //get the ending offset of the view
if((pos >= p0) && (pos <= p1)) { //if the given position is valid
final Rectangle rectangle = (allocation instanceof Rectangle) ? (Rectangle)allocation : allocation.getBounds(); //get the bounding rectangle of the painting area
if(pos == p1) { //if the position given is the last position we govern
rectangle.x += rectangle.width; //move the rectangle to the right
}
rectangle.width = 0; //set the width of the rectangle to zero
return rectangle; //return the rectangle
} else
//if the position isn't valid
throw new BadLocationException(pos + " not in range " + p0 + "," + p1, pos); //report that we don't recognize the position
}
/**
* Provides a mapping from the view coordinate space to the logical coordinate space of the model.
*
* @param x the X coordinate
* @param y the Y coordinate
* @param a the allocated region to render into
* @return the location within the model that best represents the given point of view
* @see View#viewToModel
*/
/*TODO fix
public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
Rectangle alloc = (Rectangle) a;
if (x < alloc.x + alloc.width) {
bias[0] = Position.Bias.Forward;
return getStartOffset();
}
bias[0] = Position.Bias.Backward;
return getEndOffset();
}
*/
/**
* Provides a mapping from the view coordinate space to the logical coordinate space of the model.
* @param x The X coordinate (>= 0).
* @param y The Y coordinate (>= 0).
* @param allocation The allocated region to render into.
* @return The location within the model that best represents the given point in the view
* @see View#viewToModel
*/
public int viewToModel(float x, float y, Shape allocation, Position.Bias[] bias) {
final Rectangle rectangle = allocation instanceof Rectangle ? (Rectangle)allocation : allocation.getBounds(); //get the allocation rectangle
//TODO del; this code would only accept mouse clicks on the left side of the object, for example: if(x=0).
* @param height The height (>=0).
*/
public void setSize(float width, float height) {
/*TODO del
setRequestedWidth(width); //TODO testing
setRequestedHeight(height); //TODO testing
*/
//TODO the problem here seems to be that a table wants to fill the entire page, and tries to set the
//TODO size of the iamge to fill the table; the image tries to stay in proportion, tells the table
//TODO of that fact, and the table tries to resize again, starting the whole process over
//TODO del Log.trace("XMLObjectView.setSize(), width: "+width+" height: "+height);
if(width != 0 && height != 0) { //if valid sizes are passed (this might be set to zero from our getPreferredSize(), and checking that here keeps the object from being scaled away to nothing)
final int oldCurrentWidth = getCurrentWidth(); //get the current current width
final int oldCurrentHeight = getCurrentHeight(); //get the current current height
//Sometimes, when an object is in a table, the table will keep readjusting
// the size forever if we keep readjusting the size to match. Therefore,
// we'll only change the size if the size is being changed significantly.
// This is not the best way to do this; we should instead modify the
// resizing logic in the table. This is a short-term fix. TODO
//TODO del Log.trace("old currents, width: "+oldCurrentWidth+" height: "+oldCurrentHeight);
//TODO del Log.trace("absolute width difference: "+Math.abs(width-oldCurrentWidth)+" absolute heigh difference: "+Math.abs(height-oldCurrentHeight));
//TODO fix if(Math.abs(width-oldCurrentWidth)>MINIMUM_SIZE_CHANGE || Math.abs(height-oldCurrentHeight)>MINIMUM_SIZE_CHANGE)
{
//TODO del Log.trace("old currents, width: "+currentWidth+" height: "+currentHeight);
final int newCurrentWidth = (int)width; //set the current width to the new value
int newCurrentHeight; //we'll put the new current height here
if(newCurrentWidth == getWidth()) //if the width is the same as our standard width
newCurrentHeight = getHeight(); //the way to ensure proportionality is to set the height to what was originally set as the standard height, since that's what the width is set to
else { //if the object is being scaled
//TODO fix if(newCurrentWidth!=oldCurrentWidth) //TODO testing
{
//TODO del Log.trace("Object is being scaled.");
final float ratio = (float)getWidth() / (float)getHeight(); //find the ratio of the object TODO optimize perhaps keep this updated automatically when the size is set
//TODO del Log.trace("making proportional with ratio: "+ratio);
newCurrentHeight = (int)((float)newCurrentWidth / ratio); //set the height to be proportional to the width
}
//TODO fix else
{
//TODO fix newCurrentHeight=(int)height; //TODO testing
}
}
//TODO del newCurrentHeight=(int)height; //TODO testing; del
setCurrentWidth(newCurrentWidth); //set the new current width
setCurrentHeight(newCurrentHeight); //set the new current height
//TODO del Log.trace("old currents, width: "+oldCurrentWidth+" height: "+oldCurrentHeight);
//TODO del Log.trace("new currents, width: "+newCurrentWidth+" height: "+newCurrentHeight);
if(newCurrentWidth != oldCurrentWidth || newCurrentHeight != oldCurrentHeight) { //if the size has changed
//TODO del Log.trace("old currents, width: "+oldCurrentWidth+" height: "+oldCurrentHeight);
//TODO del Log.trace("new currents, width: "+newCurrentWidth+" height: "+newCurrentHeight);
//TODO del Log.trace("Notifying parent that preferences have changed.");
final View parent = getParent(); //get the parent view
if(parent != null) //if there is a parent view
parent.preferenceChanged(this, newCurrentWidth != oldCurrentWidth, newCurrentHeight != oldCurrentHeight); //report to the parent view that this view's preferences have changed
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy