// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/omGraphics/DrawingAttributes.java,v $
// $RCSfile: DrawingAttributes.java,v $
// $Revision: 1.28 $
// $Date: 2009/01/21 01:24:41 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.omGraphics;
/*
* Java Core
*/
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.TexturePaint;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.JToggleButton;
import javax.swing.SwingConstants;
import com.bbn.openmap.Environment;
import com.bbn.openmap.I18n;
import com.bbn.openmap.PropertyConsumer;
import com.bbn.openmap.image.BufferedImageHelper;
import com.bbn.openmap.omGraphics.awt.ShapeRenderer;
import com.bbn.openmap.omGraphics.geom.NonRegional;
import com.bbn.openmap.tools.icon.IconPartList;
import com.bbn.openmap.tools.icon.OMIconFactory;
import com.bbn.openmap.tools.icon.OpenMapAppPartCollection;
import com.bbn.openmap.util.HashCodeUtil;
import com.bbn.openmap.util.PropUtils;
import com.bbn.openmap.util.propertyEditor.OptionPropertyEditor;
/**
* DrawingAttributes provides a mechanism for loading and managing different
* drawing attributes that may be used. Several layers need to be able to have
* Properties define how objects should be drawn, and the list of these drawing
* attributes tend to be the same. The DrawingAttributes class fishes out the
* applicable properties for you, creates the objects needed, and then lets you
* get those objects when needed.
* <P>
*
* The list of properties that the DrawingAttributes object can handle are
* listed below. If a property is not set, the default value will be used.
*
* <pre>
*
* # The Edge or Line color
* lineColor=AARRGGBB (Hex ARGB Color, black is default)
* # The Fill color for 2D shapes
* fillColor=AARRGGBB (Hex ARGB Color, clean is default)
* # A highlight color to switch a graphic to when "selected".
* selectColor=AARRGGBB (Hex ARGB Color, black is default)
* # A file or URL that can be used for a fill pattern, in place of the fill color.
* fillPattern=file://file (default is N/A)
* # The line width of the edge of the graphic
* lineWidth=int (1 is default)
* # A pattern to use for a dashed line, reflected as a
* # space-separated list of numbers, which are interpreted as on dash
* # length, off dash length, on dash length, etc.
* dashPattern=10 5 3 5 (5 5 is the default if an error occurs reading the numbers, a non-dashed line is the default.)
* The phase for the dash pattern,
* dashPhase=0.0f (0 is the default)
* # The scale to use for certain measurements, so that fill patterns
* # can be scaled depending on the map scale compared to the
* # baseScale.
* baseScale=XXXXXX (where 1:XXXXXX is the scale to use. N/A for the default).
* # Set whether any OMPoints that are given to the DrawingAttributes object are oval or rectangle.
* pointOval=false
* # Set the pixel radius of any OMPoint given to the DrawingAttributes object.
* pointRadius=2
*
* </pre>
*/
public class DrawingAttributes implements ActionListener, Serializable, Cloneable,
PropertyConsumer, PropertyChangeListener, ShapeRenderer {
protected static Logger logger = Logger.getLogger("com.bbn.openmap.omGraphics.DrawingAttributes");
/**
*
*/
private static final long serialVersionUID = -3375553175496133974L;
/**
* The name of the property that holds the line paint of the graphics.
*/
public final static String linePaintProperty = "lineColor";
// /**
// * The name of the property that holds the text paint for Text,
// * in case that should be different for labels, etc.
// */
// public final static String textPaintProperty = "textColor";
/**
* The name of the property that holds the fill paint of the graphics.
*/
public final static String fillPaintProperty = "fillColor";
/**
* The name of the property that holds the select paint of the graphics,
* which is the line paint that gets set with the default OMGraphic.select()
* action.
*/
public final static String selectPaintProperty = "selectColor";
/**
* The name of the property that holds the matting paint of the graphics,
* which is the wider line paint that gets set when matting is enabled.
*/
public final static String mattingPaintProperty = "mattingColor";
/**
* The property that specifies an URL or file a image file to be used to
* construct the Paint object for a texture fill pattern. If the fillPattern
* is null, the fillPaint will be used.
*/
public static final String fillPatternProperty = "fillPattern";
/**
* The name of the property that holds the lineWidth of the graphics.
*/
public final static String lineWidthProperty = "lineWidth";
/**
* The name of the property that holds a dashed pattern for lines. This will
* be used to build the stroke object for lines. This pattern should be two
* space-separated numbers, the first representing the pixel length of the
* line in the dash, the second being the space pixel length of the dash.
*/
public final static String dashPatternProperty = "dashPattern";
/**
* The name of the property that holds a dashed phase for lines. This will
* be used to build the stroke object for lines.
*/
public final static String dashPhaseProperty = "dashPhase";
/**
* The name of the property that holds the cap for the ends of lines.
* BasicStroke values apply, CAP_BUTT, CAP_ROUND, CAP_SQUARE.
*/
public final static String capProperty = "cap";
/**
* The name of the property that holds the join for lines. BasicStroke
* values apply, JOIN_MITER, JOIN_ROUND, JOIN_BEVEL.
*/
public final static String joinProperty = "join";
/**
* The name of the property that controls miterLimits.
*/
public final static String miterLimitProperty = "miterLimit";
/**
* The base scale to use for the image provided for the fill pattern. As the
* scale of the map changes, the base scale can be used as a reference to
* change the resolution of the pattern. This scale will also be used for
* strokes.
*/
public static final String baseScaleProperty = "baseScale";
/**
* Set whether a thin black matting should be drawing around the OMGraphic.
*/
public static final String mattedProperty = "matted";
/**
* Property for whether OMPoints should be oval. "pointOval"
*/
public static final String PointOvalProperty = "pointOval";
/**
* Property for the pixel radius of OMPoints. "pointRadius"
*/
public static final String PointRadiusProperty = "pointRadius";
public final static int NONE = -1;
/**
* The default line paint. (black)
*/
public final static String defaultLinePaintString = "0"; // black
/**
* The default fill paint. (none)
*/
public final static String defaultFillPaintString = "-1"; // none
/**
* The default fill paint. (black)
*/
public final static String defaultSelectPaintString = "0"; // black
/**
* The default matting paint. (black)
*/
public final static String defaultMattingPaintString = "0"; // black
/**
* The default line width
*/
public final static float defaultLineWidth = 1f;
/**
* The default dash phase, which is zero.
*/
public final static float defaultDashPhase = 0f;
/**
* The default dash length, for opaque and transparent parts.
*/
public final static float defaultDashLength = 5f;
/**
* The paint to outline the shapes.
*/
protected Paint linePaint = Color.black;
// /** The paint for text. Default to black. */
// protected Paint textPaint = linePaint;
/**
* The select paint for the shapes.
*/
protected Paint selectPaint = Color.black;
/**
* The paint to fill the shapes.
*/
protected Paint fillPaint = OMColor.clear;
/**
* The paint to use for matting.
*/
protected Paint mattingPaint = Color.black;
/**
* A TexturePaint pattern, if defined. Overrules fillPaint if fillPaint is
* null or clear.
*/
protected TexturePaint fillPattern = null;
/**
* The line stroke, for dashes, etc.
*/
protected transient Stroke stroke = new BasicStroke(1);
/**
* The base scale for scaling the fill pattern image. If NONE, then the
* resolution of the raw image will always be used.
*/
protected float baseScale = NONE;
/**
* Whether a thin black matting line should be rendered around the
* OMGraphic.
*/
protected boolean matted = false;
protected String propertyPrefix = null;
protected String fPattern = null; // for writing out the
// properties
/**
* The isOval setting to set on OMPoints.
*/
protected boolean pointOval = OMPoint.DEFAULT_ISOVAL;
/**
* The pixel radius to set on OMPoints.
*/
protected int pointRadius = OMPoint.DEFAULT_RADIUS;
/**
* A good ol' generic DrawingAttributes object for all to use. Black lines,
* clear fill paint.
*/
public final static DrawingAttributes DEFAULT = new DrawingAttributes();
/**
* Support object to notify listeners when something has changed.
*/
protected PropertyChangeSupport propertyChangeSupport = null;
/**
* For internationalization.
*/
protected I18n i18n = Environment.getI18n();
/**
* Command for line color string adjustments.
*/
public final static String LineColorCommand = "LineColor";
/**
* Command for fill color string adjustments.
*/
public final static String FillColorCommand = "FillColor";
/**
* Command for select color string adjustments.
*/
public final static String SelectColorCommand = "SelectColor";
/**
* Command for matting color string adjustments.
*/
public final static String MattingColorCommand = "MattingColor";
/**
* Command for adding matting.
*/
public final static String MattedCommand = "MattedCommand";
private JButton lineColorButton;
private JButton fillColorButton;
private JButton selectColorButton;
private JButton mattingColorButton;
private JToggleButton mattedCheckBox;
protected JMenuItem lineColorItem;
protected JMenuItem fillColorItem;
protected JMenuItem selectColorItem;
protected JMenuItem mattingColorItem;
protected JCheckBoxMenuItem mattedEnabledItem;
protected final static int icon_width = 20;
protected final static int icon_height = 20;
/**
* Flag to disable choice of fill paint selection, from an external source.
*/
protected boolean enableFillPaintChoice = true;
public static boolean alwaysSetTextToBlack = false;
protected transient BasicStrokeEditorMenu bse;
protected int orientation = SwingConstants.HORIZONTAL;
/**
* Any additional JMenu items that should be added to the line menu.
*/
protected JMenu[] lineMenuAdditions = null;
/**
* Create a DrawingAttributes with the default settings - clear fill paint
* and pattern, sold black edge line of width 1.
*/
public DrawingAttributes() {
propertyChangeSupport = new PropertyChangeSupport(this);
}
/**
* Create the DrawingAttributes and call setProperties without a prefix for
* the properties. Call setProperties without a prefix for the properties.
*
* @param props the Properties to look in.
*/
public DrawingAttributes(Properties props) {
this();
setProperties(null, props);
}
/**
* Create the DrawingAttributes and call setProperties with a prefix for the
* properties.
*
* @param prefix the prefix marker to use for a property, like
* prefix.propertyName. The period is added in this function.
* @param props the Properties to look in.
*/
public DrawingAttributes(String prefix, Properties props) {
this();
setProperties(prefix, props);
}
/**
* Shallow clone.
*/
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
public Stroke cloneBasicStroke() {
if (stroke instanceof BasicStroke) {
BasicStroke bs = (BasicStroke) stroke;
return new BasicStroke(bs.getLineWidth(), bs.getEndCap(), bs.getLineJoin(), bs.getMiterLimit(), bs.getDashArray(), bs.getDashPhase());
} else {
return new BasicStroke(1);
}
}
/**
* Shallow.
*/
public void setTo(DrawingAttributes clone) {
clone.linePaint = linePaint;
// clone.textPaint = textPaint;
clone.selectPaint = selectPaint;
clone.fillPaint = fillPaint;
clone.mattingPaint = mattingPaint;
clone.fillPattern = fillPattern;
clone.setStroke(stroke);
clone.baseScale = baseScale;
clone.matted = matted;
clone.pointOval = pointOval;
clone.pointRadius = pointRadius;
clone.enableFillPaintChoice = enableFillPaintChoice;
}
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DrawingAttributes da = (DrawingAttributes) obj;
return eqTest(da.linePaint, linePaint) && eqTest(da.selectPaint, selectPaint)
&& eqTest(da.fillPaint, fillPaint) && eqTest(da.mattingPaint, mattingPaint)
&& eqTest(da.fillPattern, fillPattern) && eqTest(da.stroke, stroke)
&& eqTest(da.baseScale, baseScale) && eqTest(da.matted, matted)
&& da.pointOval == pointOval && da.pointRadius == pointRadius;
}
/**
* Space saver method used by equals(Object).
*
* @param obj1
* @param obj2
* @return true if objects are equal. Object equivalence and equals() test.
*/
private boolean eqTest(Object obj1, Object obj2) {
// Should handle object equivalence and nulls
if (obj1 == obj2) {
return true;
}
return obj1 != null && obj1.equals(obj2);
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int result = HashCodeUtil.SEED;
// collect the contributions of various fields
result = HashCodeUtil.hash(result, linePaint);
result = HashCodeUtil.hash(result, fillPaint);
result = HashCodeUtil.hash(result, selectPaint);
result = HashCodeUtil.hash(result, mattingPaint);
result = HashCodeUtil.hash(result, fillPattern);
result = HashCodeUtil.hash(result, pointOval);
result = HashCodeUtil.hash(result, pointRadius);
result = HashCodeUtil.hash(result, fillPattern);
result = HashCodeUtil.hash(result, stroke);
result = HashCodeUtil.hash(result, matted);
return result;
}
/**
* If you want to get a DEFAULT DrawingAttributes object that you may
* modify, get your own copy.
*/
public static DrawingAttributes getDefaultClone() {
return (DrawingAttributes) DEFAULT.clone();
}
/**
* Simple push of OMGraphic attributes from one to another.
*/
public void transfer(OMGraphic from, OMGraphic to) {
setFrom(from);
setTo(to);
}
/**
* Simple push of OMGraphic attributes from one to another. Don't put in a
* loop, creates a DrawingAttributes object to do the transfer. If in a
* loop, create one and reuse it with the none-static method.
*
* @param from
* @param to
*/
public static void sTransfer(OMGraphic from, OMGraphic to) {
DrawingAttributes da = getDefaultClone();
da.transfer(from, to);
}
/**
* Call setProperties without a prefix for the properties.
*
* @param props the Properties to look in.
* @deprecated use setProperties(props).
*/
public void init(Properties props) {
setProperties(null, props);
}
/**
* Look at the Properties, and fill in the drawing attributes based in it's
* contents. If a property is not in the properties, it's set to its default
* setting.
*
* @param prefix the prefix marker to use for a property, like
* prefix.propertyName. The period is added in this function.
* @param props the Properties to look in.
* @deprecated use setProperties(prefix, props).
*/
public void init(String prefix, Properties props) {
setProperties(prefix, props);
}
/**
* Set the Stroke to use for the edge of a graphic.
*/
public void setStroke(Stroke stroke) {
Stroke oldStroke = this.stroke;
this.stroke = stroke;
// We don't want to call getBasicStrokeEditor, that creates
// the editor if it doesn't exist, which may be problematic
// for cases where there is no Graphics Display.
if (stroke instanceof BasicStroke && bse != null) {
bse.setBasicStroke((BasicStroke) stroke);
// This requires that the JRE has a display, which may be
// unnecessary in some situations where the editor is
// never used.
// BasicStrokeEditorMenu tmpbse = getBasicStrokeEditor();
// if (tmpbse != null) {
// tmpbse.setBasicStroke((BasicStroke) stroke);
// }
}
if (propertyChangeSupport != null) {
propertyChangeSupport.firePropertyChange("stroke", oldStroke, stroke);
}
}
/**
* Get the Stroke used for the lines of a graphic.
*/
public Stroke getStroke() {
return stroke;
}
/**
* Get the Stroke object, scaled for comparison to the base scale. If the
* base scale equals NONE, it's the same as getStroke().
*
* @param scale scale to compare to the base scale.
*/
public Stroke getStrokeForScale(float scale) {
if (baseScale != NONE && stroke instanceof BasicStroke) {
BasicStroke bs = (BasicStroke) stroke;
float lineWidth = bs.getLineWidth();
float[] dash = bs.getDashArray();
float scaleFactor = scale / baseScale;
int endCaps = bs.getEndCap();
int lineJoins = bs.getLineJoin();
float miterLimit = bs.getMiterLimit();
lineWidth *= scaleFactor;
for (int i = 0; i < dash.length; i++) {
dash[i] *= scaleFactor;
}
return new BasicStroke(lineWidth, endCaps, lineJoins, miterLimit, dash, bs.getDashPhase());
}
return stroke;
}
/**
* Get the Paint for these attributes, and scale it for the scale compared
* to the base scale set if the fill Paint is a TexturePattern. If the base
* scale equals NONE, or if the Paint is not a TexturePaint, it's the same
* as getFillPaint().
*
* @param scale scale to compare to the base scale.
* @return a Paint object to use for the fill, scaled if necessary.
*/
public Paint getFillPaintForScale(float scale) {
if (fillPattern != null) {
if (baseScale != NONE) {
BufferedImage bi = fillPattern.getImage();
float scaleFactor = scale / baseScale;
Image image = bi.getScaledInstance((int) (bi.getWidth() * scaleFactor), (int) (bi.getHeight() * scaleFactor), Image.SCALE_SMOOTH);
try {
bi = BufferedImageHelper.getBufferedImage(image, 0, 0, -1, -1);
return new TexturePaint(bi, new Rectangle(0, 0, bi.getWidth(), bi.getHeight()));
} catch (InterruptedException ie) {
if (logger.isLoggable(Level.FINE)) {
logger.warning("DrawingAttributes: Interrupted Exception scaling texture paint");
}
}
}
return fillPattern;
} else {
return fillPaint;
}
}
/**
* Set the edge paint for the graphics created for the coverage type.
*
* @param lPaint the paint.
*/
public void setLinePaint(Paint lPaint) {
if (lPaint == linePaint) {
return;
}
Paint oldPaint = linePaint;
linePaint = lPaint;
if (lineColorButton != null) {
lineColorButton.setIcon(getIconForPaint(linePaint, false));
}
if (mattedCheckBox != null) {
mattedCheckBox.setIcon(getMattedIcon(mattingPaint, linePaint));
}
propertyChangeSupport.firePropertyChange("linePaint", oldPaint, linePaint);
}
/**
* Get the line paint for the graphics created for the coverage type.
*
* @return the line paint to use for the edges.
*/
public Paint getLinePaint() {
return linePaint;
}
/**
* Set the selected edge paint for the graphics created for the coverage
* type.
*
* @param sPaint the paint.
*/
public void setSelectPaint(Paint sPaint) {
if (sPaint == selectPaint) {
return;
}
Paint oldPaint = selectPaint;
selectPaint = sPaint;
if (selectColorButton != null) {
selectColorButton.setIcon(getIconForPaint(selectPaint, false));
}
propertyChangeSupport.firePropertyChange("selectPaint", oldPaint, selectPaint);
}
/**
* Get the line paint for the graphics created for the coverage type.
*
* @return the select line paint to use for the edges.
*/
public Paint getSelectPaint() {
return selectPaint;
}
/**
* Set the fill paint for the graphics created for the coverage type.
*
* @param fPaint the paint.
*/
public void setFillPaint(Paint fPaint) {
if (fPaint == fillPaint) {
return;
}
Paint oldPaint = fillPaint;
fillPaint = fPaint;
if (fillColorButton != null) {
fillColorButton.setIcon(getIconForPaint(fillPaint, true));
}
propertyChangeSupport.firePropertyChange("fillPaint", oldPaint, fillPaint);
}
/**
* Get the fill paint for the graphics created for the coverage type. This
* used to return the fillPattern if it was defined. Now, it always returns
* the fillPaint.
*
* @return the fill paint to use for the areas.
*/
public Paint getFillPaint() {
return fillPaint;
}
/**
* Set the matting paint for the graphics created for the coverage type. The
* matting paint is the paint used for the matting line painted around the
* edge, two pixels wider than the edge line width. Black by default, only
* painted when the matting variable is set to true.
*
* @param mPaint the paint.
*/
public void setMattingPaint(Paint mPaint) {
if (mPaint == mattingPaint) {
return;
}
Paint oldPaint = mattingPaint;
mattingPaint = mPaint;
if (mattingColorButton != null) {
mattingColorButton.setIcon(getMattingIconForPaint());
}
if (mattedCheckBox != null) {
mattedCheckBox.setIcon(getMattedIcon(mattingPaint, linePaint));
}
propertyChangeSupport.firePropertyChange("mattingPaint", oldPaint, mattingPaint);
}
/**
* Get the matting paint for the OMGraphics
*
* @return the matting paint to use for the areas.
*/
public Paint getMattingPaint() {
return mattingPaint;
}
/**
* Set the fill pattern TexturePaint to be used as the fill color. If not
* null, the fillPattern will be returned from getFillPaint() instead of
* fillPaint.
*
* @param fPattern the TexturePaint to set.
*/
public void setFillPattern(TexturePaint fPattern) {
Paint oldPattern = fPattern;
fillPattern = fPattern;
if (fillColorButton != null) {
// GUI doesn't handle fill patterns yet.
}
propertyChangeSupport.firePropertyChange("fillPattern", oldPattern, fillPattern);
}
/**
* Get the TexturePaint set as the fill pattern.
*
* @return TexturePaint.
*/
public TexturePaint getFillPattern() {
return fillPattern;
}
/**
* Set the base scale to use for the texture paint and stroke. If this is
* set to a negative number, then no scaling of the paint or stroke will be
* performed.
*
* @param bScale the base scale to use - 1:bScale.
*/
public void setBaseScale(float bScale) {
if (bScale > 0) {
baseScale = bScale;
} else {
baseScale = NONE;
}
}
/**
* Get the base scale that the texture paint and dashes are set for. If the
* texture paint and stroke are asked for with a scale, those values will be
* adjusted accordingly.
*
* @return base scale for paint and stroke.
*/
public float getBaseScale() {
return baseScale;
}
/**
* Return whether the OMGraphic has matting around the edge.
*/
public boolean isMatted() {
return matted;
}
/**
* Set whether the OMGraphic should have matting around the edge.
*/
public void setMatted(boolean set) {
boolean oldMatted = matted;
matted = set;
if (mattedCheckBox != null) {
mattedCheckBox.setSelected(matted);
}
propertyChangeSupport.firePropertyChange("matted", oldMatted, matted);
}
/**
* Set the pixel radius given to OMPoint objects.
*/
public void setPointRadius(int radius) {
pointRadius = radius;
}
/**
* Get the pixel radius given to OMPoint objects.
*/
public int getPointRadius() {
return pointRadius;
}
/**
* Set the oval setting given to OMPoint objects.
*/
public void setPointOval(boolean value) {
pointOval = value;
}
/**
* Get the oval setting given to OMPoint objects.
*/
public boolean isPointOval() {
return pointOval;
}
/**
* Set the DrawingAttributes parameters based on the current settings of an
* OMGraphic.
*/
public void setFrom(OMGraphic graphic) {
setFrom(graphic, false);
}
/**
* Set the DrawingAttributes parameters based on the current settings of an
* OMGraphic, and reset the GUI of the DrawingAttributes object if desired.
*/
public void setFrom(OMGraphic graphic, boolean resetGUI) {
if (graphic == null) {
return;
}
matted = graphic.isMatted();
mattingPaint = graphic.getMattingPaint();
linePaint = graphic.getLinePaint();
selectPaint = graphic.getSelectPaint();
fillPaint = graphic.getFillPaint();
fillPattern = graphic.getTextureMask();
// Need to put this in to keep the gui up to date. Calling
// setStroke fires off a propertyChange reaction that
// potentially harms other parameters, like renderType.
stroke = graphic.getStroke();
if (graphic instanceof OMPoint) {
pointRadius = ((OMPoint) graphic).getRadius();
pointOval = ((OMPoint) graphic).isOval();
}
enableFillPaintChoice = !(graphic instanceof NonRegional);
// Don't want to call this here, it is CPU intensive.
// resetGUI should be called only when the GUI needs to be
// updated.
if (resetGUI) {
resetGUI();
}
if (propertyChangeSupport != null) {
propertyChangeSupport.firePropertyChange("all", true, true);
}
}
/**
* Set all the attributes for the graphic that are contained within this
* DrawingAttributes class.
* <P>
*
* If the fillPattern is set to a TexturePaint, and the fillPaint is null or
* clear, then the fillPattern will be set as the fill paint. Otherwise, the
* fillPaint will be set in the OMGraphic, and the fillPattern will be set
* too. If the OMGraphic.textureMask is != null, then it will get painted on
* top of the fillPaint. Makes for effects if the fillPattern has some
* transparent spots.
*
* @param graphic OMGraphic.
*/
public void setTo(OMGraphic graphic) {
setTo(graphic, false);
}
/**
* Set all the attributes for the graphic that are contained within this
* DrawingAttributes class.
* <P>
*
* If the fillPattern is set to a TexturePaint, and the fillPaint is null or
* clear, then the fillPattern will be set as the fill paint. Otherwise, the
* fillPaint will be set in the OMGraphic, and the fillPattern will be set
* too. If the OMGraphic.textureMask is != null, then it will get painted on
* top of the fillPaint. Makes for effects if the fillPattern has some
* transparent spots.
*
* @param graphic OMGraphic.
* @param resetGUI reset the GUI if desired, set the enableFillPaintChoice
* option if OMGraphic allows it.
*/
public void setTo(OMGraphic graphic, boolean resetGUI) {
if (graphic == null) {
return;
}
// More efficient to touch each OMGraphic once if this is a list,
// instead of cycling through for each parameter.
if (graphic instanceof OMGraphicList) {
for (OMGraphic omg : ((OMGraphicList) graphic)) {
setTo(omg, resetGUI);
}
return;
}
setOMGraphicEdgeAttributes(graphic);
// If the fillPattern is set to a TexturePaint, and the
// fillPaint is null or clear, then the fillPattern will be
// set as the fill paint. Otherwise, the fillPaint will be
// set in the OMGraphic, and the fillPattern will be set too.
// If the OMGraphic.textureMask is != null, then it will get
// painted on top of the fillPaint. Makes for effects if the
// fillPattern has some transparent spots.
if (fillPattern != null && (fillPaint == null || isClear(fillPaint))) {
graphic.setFillPaint(fillPattern);
} else {
graphic.setFillPaint(fillPaint);
graphic.setTextureMask(fillPattern);
}
graphic.setMatted(matted);
graphic.setMattingPaint(mattingPaint);
if (graphic instanceof OMPoint) {
((OMPoint) graphic).setRadius(pointRadius);
((OMPoint) graphic).setOval(pointOval);
}
// The GraphicAttribute might be rendering options for this graphic,
// needs to know if fill paint choices are available.
if (resetGUI) {
enableFillPaintChoice = !(graphic instanceof NonRegional);
resetGUI();
}
}
/**
* Set the graphic attributes that only pertain to boundaries. This is good
* for polylines, where setting the fill paint will close up the polyline
* making it a polygon. So if you want to paint edge data, use this
* function. Sets line paint, line width, and stroke if graphic is a
* OMGraphic
*
* @param graphic OMGraphic
*/
public void setOMGraphicEdgeAttributes(OMGraphic graphic) {
graphic.setLinePaint(linePaint);
graphic.setSelectPaint(selectPaint);
if (stroke != null) {
graphic.setStroke(stroke);
} else {
graphic.setStroke(OMGraphic.BASIC_STROKE);
}
}
/**
* Set all the attributes for the graphic that are contained within this
* DrawingAttributes class. Get the TexturePaint for these attributes, and
* scale it for the scale compared to the base scale set. If the base scale
* equals NONE, the fill pattern is not changed with relation to scale.
*
* @param graphic OMGraphic.
* @param scale scale to compare to the base scale.
*/
public void setOMGraphicAttributesForScale(OMGraphic graphic, float scale) {
setOMGraphicEdgeAttributesForScale(graphic, scale);
graphic.setFillPaint(getFillPaintForScale(scale));
}
/**
* Set the graphic attributes that only pertain to boundaries. This is good
* for polylines, where setting the fill paint will close up the polyline
* making it a polygon. So if you want to paint edge data, use this
* function. Sets line paint, line width, and stroke if graphic is a
* OMGraphic The stroke, if the base scale is set, is adjusted accordingly.
*
* @param graphic OMGraphic.
* @param scale scale to compare to the base scale.
*/
public void setOMGraphicEdgeAttributesForScale(OMGraphic graphic, float scale) {
graphic.setLinePaint(linePaint);
graphic.setSelectPaint(selectPaint);
if (stroke != null) {
graphic.setStroke(getStrokeForScale(scale));
} else {
graphic.setStroke(OMGraphic.BASIC_STROKE);
}
}
/**
* A lock to use to limit the number of JColorChoosers that can pop up for a
* given DrawingAttributes GUI.
*/
private boolean colorChooserLock = false;
/**
* Get the lock to use a JColorChooser. Returns true if you got the lock,
* false if you didn't.
*/
protected synchronized boolean getLock() {
if (colorChooserLock == false) {
colorChooserLock = true;
return colorChooserLock;
} else {
return false;
}
}
/**
* Release the lock on the JColorChooser.
*/
protected synchronized void releaseLock() {
colorChooserLock = false;
}
/**
* The DrawingAttributes method for handling ActionEvents. Used to handle
* the GUI actions, like changing the colors, line widths, etc.
*/
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
String command = e.getActionCommand();
String interString;
Paint tmpPaint;
if (command == LineColorCommand && linePaint instanceof Color) {
interString = i18n.get(DrawingAttributes.class, "chooseLineColor", "Choose Line Color");
tmpPaint = getNewPaint((Component) source, interString, (Color) linePaint);
if (tmpPaint != null) {
setLinePaint(tmpPaint);
}
} else if (command == FillColorCommand && fillPaint instanceof Color) {
interString = i18n.get(DrawingAttributes.class, "chooseFillColor", "Choose Fill Color");
tmpPaint = getNewPaint((Component) source, interString, (Color) fillPaint);
if (tmpPaint != null) {
setFillPaint(tmpPaint);
}
} else if (command == SelectColorCommand && selectPaint instanceof Color) {
interString = i18n.get(DrawingAttributes.class, "chooseSelectColor", "Choose Select Color");
tmpPaint = getNewPaint((Component) source, interString, (Color) selectPaint);
if (tmpPaint != null) {
setSelectPaint(tmpPaint);
}
} else if (command == MattingColorCommand && mattingPaint instanceof Color) {
interString = i18n.get(DrawingAttributes.class, "chooseMattingColor", "Choose Matting Color");
tmpPaint = getNewPaint((Component) source, interString, (Color) mattingPaint);
if (tmpPaint != null) {
setMattingPaint(tmpPaint);
}
} else if (command == MattedCommand) {
setMatted(mattedEnabledItem.getState());
} else {
if (logger.isLoggable(Level.FINE)) {
logger.fine("unrecognized command > " + command);
}
}
}
/**
* A convenience method to get a color from a JColorChooser. Null will be
* returned if the JColorChooser lock is in place, or if something else is
* done where the JColorChooser would normally return null.
*
* @param source the source component for the JColorChooser.
* @param title the String to label the JColorChooser window.
* @param startingColor the color to give to the JColorChooser to start
* with. Returned if the cancel button is pressed.
* @return Color chosen from the JColorChooser, null if lock for chooser
* can't be acquired.
*/
protected Color getNewPaint(Component source, String title, Color startingColor) {
Color newPaint = null;
if (getLock()) {
newPaint = OMColorChooser.showDialog(source, title, startingColor);
releaseLock();
}
return newPaint;
}
/**
* Get the GUI components that control the DrawingAttributes.
*
* @return JButton returns a JButton that triggers a pop-up menu.
*/
public Component getGUI() {
if (logger.isLoggable(Level.FINE)) {
logger.fine("DrawingAttributes: creating palette.");
}
return getALineButton();
}
/**
* Callout method to add stuff to popup menu before the stroke editor is
* consulted for additions. Adds colors and line menu additions (arrowhead
* controls for lines, for instance).
*
* @param popup
*/
protected void setPreStrokeMenuOptions(JComponent popup) {
popup.add(getColorMenu());
JMenu[] menus = getLineMenuAdditions();
if (menus != null) {
for (int i = 0; i < menus.length; i++) {
JMenu menu = menus[i];
if (menu != null) {
popup.add(menu);
}
}
}
}
/**
* Calls the editor for strokes to get popup menu addition for stroke
* editing.
*
* @param popup
*/
protected void setStrokeMenuOptions(JComponent popup) {
if (stroke instanceof BasicStroke) {
BasicStrokeEditorMenu tmpbse = getBasicStrokeEditor();
if (tmpbse != null) {
tmpbse.setGUI(popup);
}
}
}
/**
* Callout method to add stuff to menu after the stroke menus.
*
* @param popup
*/
protected void setPostStrokeMenuOptions(JComponent popup) {
// Nothing to add here...
}
/**
* Get a JButton used to bring up the line menu. An ActionListener is added
* that will bring up line/color popup menu.
*
* @return a new JButton is created, every time.
*/
public JButton getALineButton() {
JButton lineButton = new DrawingAttributesGUIButton(this);
lineButton.setToolTipText(i18n.get(DrawingAttributes.class, "drawingAttributesButton", I18n.TOOLTIP, "Modify Drawing Parameters"));
lineButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
JButton button = (JButton) ae.getSource();
JPopupMenu popup = new JPopupMenu();
setPreStrokeMenuOptions(popup);
setStrokeMenuOptions(popup);
setPostStrokeMenuOptions(popup);
popup.show(button, button.getWidth(), 0);
}
});
return lineButton;
}
public JMenu getColorAndLineMenu() {
JMenu menu = new JMenu(i18n.get(DrawingAttributes.class, "drawingAttributesButton", "Colors and Lines"));
setPreStrokeMenuOptions(menu);
setStrokeMenuOptions(menu);
setPostStrokeMenuOptions(menu);
return menu;
}
/**
* Gets the JMenu that has the color control options.
*
* @return JMenu with options to bring up color interfaces.
*/
public JMenu getColorMenu() {
JMenu colorMenu = null;
colorMenu = new JMenu(i18n.get(GraphicAttributes.class, "Color", "Color"));
if (lineColorItem == null) {
resetGUI();
}
colorMenu.add(lineColorItem);
colorMenu.add(fillColorItem);
colorMenu.add(selectColorItem);
colorMenu.add(mattingColorItem);
colorMenu.add(new JSeparator());
colorMenu.add(mattedEnabledItem);
fillColorItem.setEnabled(enableFillPaintChoice);
return colorMenu;
}
/**
* A hook to add to the line menu brought up in the GUI for the
* DrawingAttributes.
*/
public void setLineMenuAdditions(JMenu[] lma) {
lineMenuAdditions = lma;
}
public JMenu[] getLineMenuAdditions() {
return lineMenuAdditions;
}
/**
* Updates the color and line stroke control buttons to match the current
* settings.
*/
public void resetGUI() {
String interString;
if (lineColorItem == null) {
interString = i18n.get(DrawingAttributes.class, "lineColorItem", "Change Edge Color");
lineColorItem = new JMenuItem(interString);
lineColorItem.setActionCommand(LineColorCommand);
lineColorItem.addActionListener(this);
interString = i18n.get(DrawingAttributes.class, "lineColorItem", I18n.TOOLTIP, "Change edge color for rendering.");
lineColorItem.setToolTipText(interString);
}
if (fillColorItem == null) {
interString = i18n.get(DrawingAttributes.class, "fillColorItem", "Change Fill Color");
fillColorItem = new JMenuItem(interString);
fillColorItem.setActionCommand(FillColorCommand);
fillColorItem.addActionListener(this);
interString = i18n.get(DrawingAttributes.class, "fillColorItem", I18n.TOOLTIP, "Change fill color for rendering.");
fillColorItem.setToolTipText(interString);
}
if (selectColorItem == null) {
interString = i18n.get(DrawingAttributes.class, "selectColorItem", "Change Highlight Edge Color");
selectColorItem = new JMenuItem(interString);
selectColorItem.setActionCommand(SelectColorCommand);
selectColorItem.addActionListener(this);
interString = i18n.get(DrawingAttributes.class, "selectColorItem", I18n.TOOLTIP, "Change highlight edge color rendered during selection.");
selectColorItem.setToolTipText(interString);
}
if (mattingColorItem == null) {
interString = i18n.get(DrawingAttributes.class, "mattingColorItem", "Change Matted Edge Color");
mattingColorItem = new JMenuItem(interString);
mattingColorItem.setActionCommand(MattingColorCommand);
mattingColorItem.addActionListener(this);
interString = i18n.get(DrawingAttributes.class, "mattingColorItem", I18n.TOOLTIP, "Change the color of the border around the edge.");
mattingColorItem.setToolTipText(interString);
}
if (mattedCheckBox == null) {
interString = i18n.get(DrawingAttributes.class, "mattedEnableItem", "Enable Matting on Edge");
mattedEnabledItem = new JCheckBoxMenuItem(interString, matted);
mattedEnabledItem.setActionCommand(MattedCommand);
mattedEnabledItem.addActionListener(this);
interString = i18n.get(DrawingAttributes.class, "mattedEnableItem", I18n.TOOLTIP, "Enable/Disable matting on edge.");
mattedEnabledItem.setToolTipText(interString);
}
if (stroke instanceof BasicStroke) {
BasicStrokeEditorMenu tmpbse = getBasicStrokeEditor();
if (tmpbse != null) {
tmpbse.setBasicStroke((BasicStroke) stroke);
}
}
}
/**
* Create an ImageIcon from a java.awt.Paint.
*
* @param paint java.awt.Paint
* @param width icon pixel width
* @param height icon pixel height
*/
public static ImageIcon getPaletteIcon(Paint paint, int width, int height) {
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = (Graphics2D) bufferedImage.getGraphics();
graphics.setPaint(paint);
graphics.fillRect(0, 0, width, height);
return new ImageIcon(bufferedImage);
}
/**
* Get the PropertyChangeSupport object to register anything that is
* interested in finding out when some parameter has changed.
*/
public PropertyChangeSupport getPropertyChangeSupport() {
return propertyChangeSupport;
}
public void setPropertyChangeSupport(PropertyChangeSupport support) {
propertyChangeSupport = support;
}
public static Color calculateTextColor(Color color) {
if (alwaysSetTextToBlack) // Mac OS X
{
return Color.black;
}
int red = color.getRed();
int green = color.getGreen();
int blue = color.getBlue();
int alpha = color.getAlpha();
if (alpha < 128) {
return Color.black;
}
int newred, newgreen, newblue;
newred = normalizeOn128(red);
newgreen = normalizeOn128(green);
newblue = normalizeOn128(blue);
return new Color(newred, newgreen, newblue);
}
public static int normalizeOn128(int value) {
if (value >= 255) {
return 0;
} else if (value <= 0) {
return 255;
} else if (value <= 128) {
return 192;
}
return 64;
}
/**
* Sets the properties for the <code>DrawingAttributes</code>. This
* particular method assumes that the marker name is not needed, because all
* of the contents of this Properties object are to be used for this object,
* and scoping the properties with a prefix is unnecessary.
*
* @param props the <code>Properties</code> object.
*/
public void setProperties(java.util.Properties props) {
setProperties(getPropertyPrefix(), props);
}
public BasicStrokeEditorMenu getBasicStrokeEditor() {
if (bse == null && stroke instanceof BasicStroke) {
try {
bse = new BasicStrokeEditorMenu((BasicStroke) getStroke());
bse.getPropertyChangeSupport().addPropertyChangeListener(this);
} catch (Exception e) {
// This happens if a java Toolkit is not available.
bse = null;
}
}
return bse;
}
/**
* Sets the properties for the <code>DrawingAttributes</code>. Part of the
* PropertyConsumer interface. DrawingAttributess which override this method
* should do something like:
*
* <pre>
* public void setProperties(String prefix, Properties props) {
* super.setProperties(prefix, props);
* // do local stuff
* }
* </pre>
*
* If the addToBeanContext property is not defined, it is set to false here.
*
* @param prefix the token to prefix the property names
* @param props the <code>Properties</code> object
*/
public void setProperties(String prefix, Properties props) {
setPropertyPrefix(prefix);
if (props == null) {
return;
}
String realPrefix = PropUtils.getScopedPropertyPrefix(prefix);
// Set up the drawing attributes.
linePaint = PropUtils.parseColorFromProperties(props, realPrefix + linePaintProperty, linePaint);
selectPaint = PropUtils.parseColorFromProperties(props, realPrefix + selectPaintProperty, selectPaint);
mattingPaint = PropUtils.parseColorFromProperties(props, realPrefix + mattingPaintProperty, mattingPaint);
// textPaint =
// PropUtils.parseColorFromProperties(
// props, realPrefix + textPaintProperty,
// textPaint);
fillPaint = PropUtils.parseColorFromProperties(props, realPrefix + fillPaintProperty, fillPaint);
matted = PropUtils.booleanFromProperties(props, realPrefix + mattedProperty, matted);
pointRadius = PropUtils.intFromProperties(props, realPrefix + PointRadiusProperty, pointRadius);
pointOval = PropUtils.booleanFromProperties(props, realPrefix + PointOvalProperty, pointOval);
float lineWidth;
boolean basicStrokeDefined = false;
if (stroke instanceof BasicStroke) {
basicStrokeDefined = true;
}
lineWidth = PropUtils.floatFromProperties(props, realPrefix + lineWidthProperty, (basicStrokeDefined ? ((BasicStroke) stroke).getLineWidth()
: defaultLineWidth));
baseScale = PropUtils.floatFromProperties(props, realPrefix + baseScaleProperty, baseScale);
// Look for a dash pattern properties to come up with a stroke
String dPattern = props.getProperty(realPrefix + dashPatternProperty);
if (basicStrokeDefined && dPattern != null && dPattern.length() > 0) {
float dashPhase;
float[] lineDash;
// OK, it exists, come up with a stroke.
try {
StringTokenizer t = new StringTokenizer(dPattern);
int arraySize = t.countTokens();
lineDash = new float[arraySize];
int dashCount = 0;
while (t.hasMoreTokens()) {
String segment = t.nextToken();
lineDash[dashCount++] = Float.parseFloat(segment);
if (logger.isLoggable(Level.FINE)) {
logger.fine("read " + segment);
}
}
} catch (NoSuchElementException nsee) {
logger.fine("DrawingAttributes.init: dash pattern attributes wrong - should be dashPattern=(number pixels on) (number pixels off)");
lineDash = null;
} catch (NumberFormatException nfe) {
logger.fine("DrawingAttributes.init: Number format exception for dashPattern");
lineDash = null;
} catch (NullPointerException npe) {
logger.fine("DrawingAttributes.init: Caught null pointer exception - probably resulting from non-float number format exception for dashPattern");
lineDash = null;
}
if (lineDash == null) {
if (basicStrokeDefined) {
lineDash = ((BasicStroke) stroke).getDashArray();
} else {
lineDash = new float[2];
lineDash[0] = defaultDashLength;
lineDash[1] = defaultDashLength;
}
}
int dashCount = 0;
for (int x = 0; x < lineDash.length; x++) {
dashCount += lineDash[x];
}
if (dashCount == 0) {
lineDash = null;
}
String dPhase = props.getProperty(realPrefix + dashPhaseProperty);
if (dPhase != null && dPhase.length() > 0) {
try {
dashPhase = Float.valueOf(dPhase).floatValue();
} catch (NumberFormatException nfe) {
logger.fine("DrawingAttributes.init: Number format exception for dashPhase");
dashPhase = defaultDashPhase;
}
} else {
if (basicStrokeDefined) {
dashPhase = ((BasicStroke) stroke).getDashPhase();
} else {
dashPhase = defaultDashPhase;
}
}
String capPropertyString = props.getProperty(realPrefix + capProperty);
int cap = BasicStroke.CAP_BUTT;
if (capPropertyString != null) {
try {
cap = java.awt.BasicStroke.class.getField(capPropertyString).getInt(null);
} catch (NoSuchFieldException nsfe) {
} catch (IllegalAccessException iae) {
}
}
String joinPropertyString = props.getProperty(realPrefix + capProperty);
int join = BasicStroke.JOIN_MITER;
if (joinPropertyString != null) {
try {
join = java.awt.BasicStroke.class.getField(joinPropertyString).getInt(null);
} catch (NoSuchFieldException nsfe) {
} catch (IllegalAccessException iae) {
}
}
float miterLimit = PropUtils.floatFromProperties(props, realPrefix + miterLimitProperty, 10.0f);
setStroke(new BasicStroke(lineWidth, cap, join, miterLimit, lineDash, dashPhase));
} else if (basicStrokeDefined) {
BasicStroke currentStroke = (BasicStroke) getStroke();
setStroke(new BasicStroke(lineWidth, currentStroke.getEndCap(), currentStroke.getLineJoin(), currentStroke.getMiterLimit(), currentStroke.getDashArray(), currentStroke.getDashPhase()));
}
// OK, Fill pattern next...
fPattern = props.getProperty(realPrefix + fillPatternProperty);
if (fPattern != null && fPattern.length() != 0) {
try {
URL textureImageURL = PropUtils.getResourceOrFileOrURL(fPattern);
if (textureImageURL != null) {
BufferedImage bi = BufferedImageHelper.getBufferedImage(textureImageURL, 0, 0, -1, -1);
fillPattern = new TexturePaint(bi, new Rectangle(0, 0, bi.getWidth(), bi.getHeight()));
}
} catch (MalformedURLException murle) {
logger.fine("DrawingAttributes.init: bad texture URL - \n " + realPrefix
+ fillPatternProperty);
fillPattern = null;
} catch (InterruptedException ie) {
logger.fine("DrawingAttributes.init: bad problems getting texture URL - \n" + ie);
fillPattern = null;
}
}
}
/**
* PropertyConsumer method, to fill in a Properties object, reflecting the
* current values of the layer. If the layer has a propertyPrefix set, the
* property keys should have that prefix plus a separating '.' prepended to
* each property key it uses for configuration.
*
* @param props a Properties object to load the PropertyConsumer properties
* into. If props equals null, then a new Properties object should be
* created.
* @return Properties object containing PropertyConsumer property values. If
* getList was not null, this should equal getList. Otherwise, it
* should be the Properties object created by the PropertyConsumer.
*/
public Properties getProperties(Properties props) {
if (props == null) {
props = new Properties();
}
String prefix = PropUtils.getScopedPropertyPrefix(this);
if (linePaint instanceof Color) {
props.put(prefix + linePaintProperty, PropUtils.getProperty((Color) linePaint));
}
// if (textPaint instanceof Color) {
// props.put(prefix + textPaintProperty,
// PropUtils.getProperty((Color)textPaint));
// }
if (fillPaint instanceof Color) {
props.put(prefix + fillPaintProperty, PropUtils.getProperty((Color) fillPaint));
}
if (selectPaint instanceof Color) {
props.put(prefix + selectPaintProperty, PropUtils.getProperty((Color) selectPaint));
}
if (mattingPaint instanceof Color) {
props.put(prefix + mattingPaintProperty, PropUtils.getProperty((Color) mattingPaint));
}
props.put(prefix + PointRadiusProperty, Integer.toString(pointRadius));
props.put(prefix + PointOvalProperty, new Boolean(pointOval).toString());
props.put(prefix + fillPatternProperty, (fPattern == null ? "" : fPattern));
Stroke bs = getStroke();
if (bs == null) {
bs = new BasicStroke();
}
if (bs instanceof BasicStroke) {
props.put(prefix + lineWidthProperty, Float.toString(((BasicStroke) bs).getLineWidth()));
float[] fa = ((BasicStroke) bs).getDashArray();
if (fa != null) {
StringBuffer dp = new StringBuffer();
for (int i = 0; i < fa.length; i++) {
dp.append(" ").append(Float.toString(fa[i]));
}
props.put(prefix + dashPatternProperty, dp.toString().trim());
props.put(prefix + dashPhaseProperty, Float.toString(((BasicStroke) bs).getDashPhase()));
} else {
props.put(prefix + dashPatternProperty, "");
props.put(prefix + dashPhaseProperty, "");
}
props.put(prefix + capProperty, Integer.toString(((BasicStroke) bs).getEndCap()));
props.put(prefix + joinProperty, Integer.toString(((BasicStroke) bs).getLineJoin()));
props.put(prefix + miterLimitProperty, Float.toString(((BasicStroke) bs).getMiterLimit()));
}
if (baseScale != NONE) {
props.put(prefix + baseScaleProperty, Float.toString(baseScale));
}
props.put(prefix + mattedProperty, new Boolean(matted).toString());
return props;
}
/**
* Method to fill in a Properties object with values reflecting the
* properties able to be set on this PropertyConsumer. The key for each
* property should be the raw property name (without a prefix) with a value
* that is a String that describes what the property key represents, along
* with any other information about the property that would be helpful
* (range, default value, etc.).
*
* @param list a Properties object to load the PropertyConsumer properties
* into. If getList equals null, then a new Properties object should
* be created.
* @return Properties object containing PropertyConsumer property values. If
* getList was not null, this should equal getList. Otherwise, it
* should be the Properties object created by the PropertyConsumer.
*/
public Properties getPropertyInfo(Properties list) {
if (list == null) {
list = new Properties();
}
String interString;
interString = i18n.get(DrawingAttributes.class, linePaintProperty, I18n.TOOLTIP, "Edge color for graphics.");
list.put(linePaintProperty, interString);
interString = i18n.get(DrawingAttributes.class, linePaintProperty, linePaintProperty);
list.put(linePaintProperty + LabelEditorProperty, interString);
list.put(linePaintProperty + ScopedEditorProperty, "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");
// list.put(textPaintProperty, "Text color for graphics.");
// list.put(textPaintProperty + ScopedEditorProperty,
// "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");
interString = i18n.get(DrawingAttributes.class, fillPaintProperty, I18n.TOOLTIP, "Fill color for graphics.");
list.put(fillPaintProperty, interString);
interString = i18n.get(DrawingAttributes.class, fillPaintProperty, fillPaintProperty);
list.put(fillPaintProperty + LabelEditorProperty, interString);
list.put(fillPaintProperty + ScopedEditorProperty, "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");
interString = i18n.get(DrawingAttributes.class, selectPaintProperty, I18n.TOOLTIP, "Selected edge color for graphics.");
list.put(selectPaintProperty, interString);
interString = i18n.get(DrawingAttributes.class, selectPaintProperty, selectPaintProperty);
list.put(selectPaintProperty + LabelEditorProperty, interString);
list.put(selectPaintProperty + ScopedEditorProperty, "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");
interString = i18n.get(DrawingAttributes.class, mattingPaintProperty, I18n.TOOLTIP, "Matting edge color for graphics.");
list.put(mattingPaintProperty, interString);
interString = i18n.get(DrawingAttributes.class, mattingPaintProperty, mattingPaintProperty);
list.put(mattingPaintProperty + LabelEditorProperty, interString);
list.put(mattingPaintProperty + ScopedEditorProperty, "com.bbn.openmap.util.propertyEditor.ColorPropertyEditor");
interString = i18n.get(DrawingAttributes.class, fillPatternProperty, I18n.TOOLTIP, "Image file to use for fill pattern for graphics (optional).");
list.put(fillPatternProperty, interString);
interString = i18n.get(DrawingAttributes.class, fillPatternProperty, fillPatternProperty);
list.put(fillPatternProperty + LabelEditorProperty, interString);
list.put(fillPatternProperty + ScopedEditorProperty, "com.bbn.openmap.util.propertyEditor.FUPropertyEditor");
interString = i18n.get(DrawingAttributes.class, lineWidthProperty, I18n.TOOLTIP, "Line width for edges of graphics");
list.put(lineWidthProperty, interString);
interString = i18n.get(DrawingAttributes.class, lineWidthProperty, lineWidthProperty);
list.put(lineWidthProperty + LabelEditorProperty, interString);
// list.put(dashPatternProperty, "<HTML><BODY>Line dash
// pattern, represented by<br>space separated numbers<br> (on
// off on ...)</BODY></HTML>");
interString = i18n.get(DrawingAttributes.class, dashPatternProperty, I18n.TOOLTIP, "Line dash pattern, represented by space separated numbers (on off on ...)");
list.put(dashPatternProperty, interString);
interString = i18n.get(DrawingAttributes.class, dashPatternProperty, dashPatternProperty);
list.put(dashPatternProperty + LabelEditorProperty, interString);
interString = i18n.get(DrawingAttributes.class, dashPhaseProperty, I18n.TOOLTIP, "Phase for dash pattern (Default is 0)");
list.put(dashPhaseProperty, interString);
interString = i18n.get(DrawingAttributes.class, dashPhaseProperty, dashPhaseProperty);
list.put(dashPhaseProperty + LabelEditorProperty, interString);
interString = i18n.get(DrawingAttributes.class, baseScaleProperty, I18n.TOOLTIP, "<HTML><BODY>Scale which should be used as the base scale for the <br>patterns and line width. If set, size of pattern and <br>widths will be adjusted to the map scale</BODY></HTML>");
list.put(baseScaleProperty, interString);
interString = i18n.get(DrawingAttributes.class, baseScaleProperty, baseScaleProperty);
list.put(baseScaleProperty + LabelEditorProperty, interString);
interString = i18n.get(DrawingAttributes.class, mattedProperty, I18n.TOOLTIP, "Flag to enable a thin black matting to be drawn around graphics.");
list.put(mattedProperty, interString);
interString = i18n.get(DrawingAttributes.class, mattedProperty, mattedProperty);
list.put(mattedProperty + LabelEditorProperty, interString);
list.put(mattedProperty + ScopedEditorProperty, "com.bbn.openmap.util.propertyEditor.OnOffPropertyEditor");
interString = i18n.get(DrawingAttributes.class, PointRadiusProperty, I18n.TOOLTIP, "Pixel radius of point objects.");
list.put(PointRadiusProperty, interString);
interString = i18n.get(DrawingAttributes.class, PointRadiusProperty, "Point pixel radius");
list.put(PointRadiusProperty + LabelEditorProperty, interString);
interString = i18n.get(DrawingAttributes.class, PointOvalProperty, I18n.TOOLTIP, "Set points to be oval or rectangular.");
list.put(PointOvalProperty, interString);
interString = i18n.get(DrawingAttributes.class, PointOvalProperty, "Points are oval");
list.put(PointOvalProperty + LabelEditorProperty, interString);
list.put(PointOvalProperty + ScopedEditorProperty, "com.bbn.openmap.util.propertyEditor.YesNoPropertyEditor");
PropUtils.setI18NPropertyInfo(i18n, list, DrawingAttributes.class, capProperty, "Line Cap", "Type of cap to use on end of lines.", "com.bbn.openmap.util.propertyEditor.ComboBoxPropertyEditor");
list.put(capProperty + OptionPropertyEditor.ScopedOptionsProperty, "butt round square");
list.put(capProperty + ".butt", "CAP_BUTT");
list.put(capProperty + ".round", "CAP_ROUND");
list.put(capProperty + ".square", "CAP_SQUARE");
PropUtils.setI18NPropertyInfo(i18n, list, DrawingAttributes.class, joinProperty, "Line Join", "Type of joint to use on corner of line joins.", "com.bbn.openmap.util.propertyEditor.ComboBoxPropertyEditor");
list.put(joinProperty + OptionPropertyEditor.ScopedOptionsProperty, "miter round bevel");
list.put(joinProperty + ".miter", "JOIN_MITER");
list.put(joinProperty + ".round", "JOIN_ROUND");
list.put(joinProperty + ".bevel", "JOIN_BEVEL");
PropUtils.setI18NPropertyInfo(i18n, list, DrawingAttributes.class, miterLimitProperty, "Miter Limit", "Number of pixels to use for line joints.", null);
// This line messes order up when called by classes using
// DrawingAttributes.
// list.put(initPropertiesProperty, getInitPropertiesOrder());
return list;
}
public String getInitPropertiesOrder() {
return " " + linePaintProperty + " " + selectPaintProperty + " " + fillPaintProperty + " "
+ /*
* textPaintProperty + " " +
*/mattingPaintProperty + " " + fillPatternProperty + " " + mattedProperty + " "
+ lineWidthProperty + " " + dashPatternProperty + " " + dashPhaseProperty + " "
+ capProperty + " " + joinProperty + " " + miterLimitProperty + " "
+ PointRadiusProperty + " " + PointOvalProperty;
}
/**
* Set the property key prefix that should be used by the PropertyConsumer.
* The prefix, along with a '.', should be prepended to the property keys
* known by the PropertyConsumer.
*
* @param prefix the prefix String.
*/
public void setPropertyPrefix(String prefix) {
propertyPrefix = prefix;
}
/**
* Get the property key prefix that is being used to prepend to the property
* keys for Properties lookups.
*
* @return the prefix String.
*/
public String getPropertyPrefix() {
return propertyPrefix;
}
public void propertyChange(PropertyChangeEvent pce) {
if (pce.getSource() instanceof BasicStrokeEditorMenu) {
setStroke((BasicStroke) pce.getNewValue());
}
}
public String toString() {
StringBuffer sb = new StringBuffer("DrawingAttributes[");
sb.append("linePaint(").append(linePaint).append("), ");
sb.append("selectPaint(").append(selectPaint).append("), ");
// sb.append("textPaint(").append(textPaint).append("), ");
sb.append("mattingPaint(").append(mattingPaint).append("), ");
sb.append("fillPaint(").append(fillPaint).append("), ");
sb.append("fillPattern(").append(fillPattern).append("), ");
sb.append("stroke(").append(stroke).append("), ");
sb.append("baseScale(").append(baseScale).append("), ");
sb.append("matted(").append(new Boolean(matted).toString()).append(")]");
return sb.toString();
}
/**
* Render the Shape into the Graphics2D object, using the mattingPaint,
* fillPaint, fillPattern, linePaint and stroke contained in this
* DrawingAttributes object.
*/
public void render(Graphics2D g, Shape shape) {
render(g, shape, false);
}
/**
* Render the Shape into the Graphics2D object, using the mattingPaint,
* fillPaint, fillPattern, linePaint and stroke contained in this
* DrawingAttributes object.
*
* @param g java.awt.Graphics2D object to render into
* @param shape java.awt.Shape to draw
* @param replaceColorWithGradient flag to specify replacement of fill and
* edge colors with a GradientPaint to give a light to dark look. You
* can set the Paints in the DrawingAttributes object with
* GradientPaints if you want more control over the GradientPaint,
* but this will let the DrawingAttributes object take a shot at
* creating one for a Color that fits the shape given.
*/
public void render(Graphics2D g, Shape shape, boolean replaceColorWithGradient) {
if (matted) {
if (stroke instanceof BasicStroke) {
g.setStroke(new BasicStroke(((BasicStroke) stroke).getLineWidth() + 2f));
g.setPaint(mattingPaint);
g.draw(shape);
}
}
if (!isClear(fillPaint)) {
g.setStroke(OMGraphicConstants.BASIC_STROKE);
if (replaceColorWithGradient) {
g.setPaint(getGradientPaintForShape(shape, fillPaint));
} else {
g.setPaint(fillPaint);
}
g.fill(shape);
// Seems to help with a rendering problem, not sure why.
// Without this the DrawingAttributes fill icon would not
// be drawn until it was set again. This way, it always
// appears. Might be a Mac thing.
g.draw(shape);
if (fillPattern != null && fillPattern != fillPaint) {
g.setPaint(fillPattern);
g.fill(shape);
}
}
if (linePaint != fillPaint) {
g.setStroke(getStroke());
if (replaceColorWithGradient) {
g.setPaint(getGradientPaintForShape(shape, linePaint));
} else {
g.setPaint(linePaint);
}
g.draw(shape);
}
}
/**
* Create a GradientPaint object for the given shape.
*
* @param shape shape to take measurements from to set GradientPaint
* settings - .3 h/w lighter to .7 h/w darker.
* @param paint the base color to use for gradient.
* @return GradientPaint for shape.
*/
public static Paint getGradientPaintForShape(Shape shape, Paint paint) {
if (paint instanceof Color) {
Color color = (Color) paint;
Rectangle rect = shape.getBounds();
paint = new GradientPaint((float) rect.getWidth() * .3f, (float) rect.getHeight() * .3f, color.brighter().brighter(), (float) rect.getWidth() * .7f, (float) rect.getHeight() * .7f, color.darker().darker());
}
return paint;
}
/**
* @return a matting paint choice icon for the current settings of this
* DrawingAttributes object, with the matting paint used.
*/
public ImageIcon getMattingIconForPaint() {
Paint paint = getMattingPaint();
DrawingAttributes da = new DrawingAttributes();
da.setLinePaint(paint);
da.setStroke(new BasicStroke(3));
DrawingAttributes innerda = new DrawingAttributes();
innerda.setLinePaint(Color.white);
innerda.setStroke(new BasicStroke(1));
OpenMapAppPartCollection collection = OpenMapAppPartCollection.getInstance();
IconPartList parts = new IconPartList();
if (paint instanceof Color) {
Color color = (Color) paint;
Paint opaqueColor = new Color(color.getRed(), color.getGreen(), color.getBlue(), 255);
DrawingAttributes opaqueDA = new DrawingAttributes();
opaqueDA.setLinePaint(opaqueColor);
opaqueDA.setStroke(new BasicStroke(3));
parts.add(collection.get("LR_TRI", opaqueDA));
parts.add(collection.get("UL_TRI", da));
parts.add(collection.get("LR_TRI", innerda));
parts.add(collection.get("UL_TRI", innerda));
} else {
parts.add(collection.get("BIG_BOX", da));
parts.add(collection.get("BIG_BOX", innerda));
}
return OMIconFactory.getIcon(icon_width, icon_height, parts);
}
/**
* @param paint the paint to use for the icon.
* @param fill if fill color should be used.
* @return an ImageIcon for the provided paint object, two triangles in
* upper left and lower right. Upper left version has transparency
* set.
*/
public static ImageIcon getIconForPaint(Paint paint, boolean fill) {
if (paint == null) {
paint = Color.black;
}
DrawingAttributes da = new DrawingAttributes();
da.setLinePaint(paint);
da.setStroke(new BasicStroke(2));
if (fill) {
da.setFillPaint(paint);
}
OpenMapAppPartCollection collection = OpenMapAppPartCollection.getInstance();
IconPartList parts = new IconPartList();
if (paint instanceof Color || paint == OMColor.clear) {
Color color = (Color) paint;
Color opaqueColor = new Color(color.getRed(), color.getGreen(), color.getBlue());
DrawingAttributes opaqueDA = new DrawingAttributes();
opaqueDA.setLinePaint(opaqueColor);
opaqueDA.setStroke(new BasicStroke(2));
if (fill) {
opaqueDA.setFillPaint(opaqueColor);
}
parts.add(collection.get("LR_TRI", opaqueDA));
parts.add(collection.get("UL_TRI", da));
} else {
parts.add(collection.get("BIG_BOX", da));
}
return OMIconFactory.getIcon(icon_width, icon_height, parts);
}
/**
* @param mattingPaint
* @param linePaint
* @return an ImageIcon that shows a square with the matting paint and line
* paint.
*/
public static ImageIcon getMattedIcon(Paint mattingPaint, Paint linePaint) {
DrawingAttributes da = new DrawingAttributes();
da.setMattingPaint(mattingPaint);
da.setStroke(new BasicStroke(2));
DrawingAttributes fillda = new DrawingAttributes();
fillda.setLinePaint(linePaint);
fillda.setFillPaint(linePaint);
da.setStroke(new BasicStroke(2));
OpenMapAppPartCollection collection = OpenMapAppPartCollection.getInstance();
IconPartList parts = new IconPartList();
parts.add(collection.get("FILL_BOX", fillda));
parts.add(collection.get("BIG_BOX", da));
parts.add(collection.get("SMALL_BOX", da));
return OMIconFactory.getIcon(icon_width, icon_height, parts);
}
/**
* Given a BasicStroke, create an ImageIcon that shows it.
*
* @param attributes attributes to use for drawing the icon for the stroke.
* @param width the width of the icon.
* @param height the height of the icon.
* @param horizontalOrientation if true, draw line on the icon horizontally,
* else draw it vertically.
*/
public static ImageIcon getDrawingAttributesIcon(DrawingAttributes attributes, int width,
int height, boolean horizontalOrientation) {
BufferedImage bigImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D) bigImage.getGraphics();
g.setBackground(OMColor.clear);
if (attributes.enableFillPaintChoice) {
g.setPaint(attributes.fillPaint);
g.fillRect(0, 0, width, height);
}
if (attributes.matted) {
BasicStroke mattedStroke = new BasicStroke(((BasicStroke) attributes.stroke).getLineWidth() + 2f);
g.setStroke(mattedStroke);
g.setPaint(attributes.mattingPaint);
g.drawLine(0, height / 2, width, height / 2);
}
g.setPaint(attributes.linePaint);
g.setStroke(attributes.stroke);
if (horizontalOrientation) {
g.drawLine(0 + 3, height / 2, width - 3, height / 2);
} else {
g.drawLine(width / 2, 0 + 3, width / 2, height - 3);
}
return new ImageIcon(bigImage);
}
public boolean isEnableFillPaintChoice() {
return enableFillPaintChoice;
}
public void setEnableFillPaintChoice(boolean enableFillPaintChoice) {
this.enableFillPaintChoice = enableFillPaintChoice;
}
public int getOrientation() {
return orientation;
}
public void setOrientation(int orientation) {
this.orientation = orientation;
}
/**
* Checks if the Paint is clear.
*
* @param paint Paint or null.
* @return true if Paint is null or is a Color with a 0 alpha value.
*/
public static boolean isClear(Paint paint) {
if (paint instanceof Color) {
return ((((Color) paint).getRGB() & 0xff000000) == 0);
} else {
return false;
}
}
/**
* A JButton that listens for PropertyChange notifications from a
* DrawingAttributes object, so it can update its icon.
*
* @author dietrick
*/
protected static class DrawingAttributesGUIButton extends JButton implements
PropertyChangeListener {
DrawingAttributes parentDA;
protected DrawingAttributesGUIButton(DrawingAttributes da) {
super(getDrawingAttributesIcon(da, icon_width, icon_height, true));
da.propertyChangeSupport.addPropertyChangeListener(DrawingAttributesGUIButton.this);
parentDA = da;
}
/*
* (non-Javadoc)
*
* @see java.beans.PropertyChangeListener#propertyChange(java.beans.
* PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent evt) {
DrawingAttributesGUIButton.this.setIcon(getDrawingAttributesIcon(parentDA, icon_width, icon_height, true));
}
}
/**
* Builders are usually for immutable objects, but it seems kinda handy to
* have one for this class, to eliminate lines of code.
*
* @author ddietrick
*/
public static class Builder {
DrawingAttributes da = null;
public Builder() {
da = DrawingAttributes.getDefaultClone();
}
public Builder setLinePaint(Paint lPaint) {
da.setLinePaint(lPaint);
return this;
}
public Builder setFillPaint(Paint fPaint) {
da.setFillPaint(fPaint);
return this;
}
public Builder setSelectPaint(Paint sPaint) {
da.setSelectPaint(sPaint);
return this;
}
public Builder setStroke(Stroke s) {
da.setStroke(s);
return this;
}
public Builder setLineWidth(int lineWidth) {
da.setStroke(new BasicStroke(lineWidth));
return this;
}
public Builder setFillPattern(TexturePaint tPaint) {
da.setFillPattern(tPaint);
return this;
}
public Builder setMattingPaint(Paint mPaint) {
da.setMattingPaint(mPaint);
return this;
}
public Builder setMatted(boolean matted) {
da.setMatted(matted);
return this;
}
public Builder setPointOval(boolean oval) {
da.setPointOval(oval);
return this;
}
public Builder setPointRadius(int radius) {
da.setPointRadius(radius);
return this;
}
public Builder setOrientation(int orientation) {
da.setOrientation(orientation);
return this;
}
public Builder setFrom(DrawingAttributes anotherDa) {
anotherDa.setTo(da);
return this;
}
public Builder setFrom(OMGraphic omg) {
da.setFrom(omg);
return this;
}
public DrawingAttributes build() {
return da;
}
}
}