/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo 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 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo 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 OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.fge.graphics;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Stroke;
import java.util.Observable;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import org.openflexo.fge.FGEIconLibrary;
import org.openflexo.fge.GraphicalRepresentation;
import org.openflexo.fge.GraphicalRepresentation.GRParameter;
import org.openflexo.fge.notifications.FGENotification;
import org.openflexo.inspector.HasIcon;
import org.openflexo.localization.FlexoLocalization;
import org.openflexo.xmlcode.XMLSerializable;
public class ForegroundStyle extends Observable implements XMLSerializable, Cloneable {
@SuppressWarnings("unused")
private static final Logger logger = Logger.getLogger(ForegroundStyle.class.getPackage().getName());
public static enum Parameters implements GRParameter {
color, lineWidth, capStyle, joinStyle, dashStyle, noStroke, useTransparency, transparencyLevel
}
private boolean noStroke = false;
private Color color;
private double lineWidth;
private JoinStyle joinStyle;
private CapStyle capStyle;
private DashStyle dashStyle;
private boolean useTransparency = false;
private float transparencyLevel = 0.5f; // Between 0.0 and 1.0
private Stroke stroke;
private double strokeScale;
public static enum JoinStyle implements HasIcon {
/**
* Joins path segments by extending their outside edges until they meet.
*/
JOIN_MITER,
/**
* Joins path segments by rounding off the corner at a radius of half the line width.
*/
JOIN_ROUND,
/**
* Joins path segments by connecting the outer corners of their wide outlines with a straight segment.
*/
JOIN_BEVEL;
@Override
public ImageIcon getIcon() {
if (this == JOIN_MITER) {
return FGEIconLibrary.JOIN_MITER_ICON;
} else if (this == JOIN_ROUND) {
return FGEIconLibrary.JOIN_ROUND_ICON;
} else if (this == JOIN_BEVEL) {
return FGEIconLibrary.JOIN_BEVEL_ICON;
}
return null;
}
}
public static enum CapStyle implements HasIcon {
/**
* Ends unclosed subpaths and dash segments with no added decoration.
*/
CAP_BUTT,
/**
* Ends unclosed subpaths and dash segments with a round decoration that has a radius equal to half of the width of the pen.
*/
CAP_ROUND,
/**
* Ends unclosed subpaths and dash segments with a square projection that extends beyond the end of the segment to a distance equal
* to half of the line width.
*/
CAP_SQUARE;
@Override
public ImageIcon getIcon() {
if (this == CAP_BUTT) {
return FGEIconLibrary.CAP_BUTT_ICON;
} else if (this == CAP_ROUND) {
return FGEIconLibrary.CAP_ROUND_ICON;
} else if (this == CAP_SQUARE) {
return FGEIconLibrary.CAP_SQUARE_ICON;
}
return null;
}
}
public static enum DashStyle implements HasIcon {
PLAIN_STROKE, SMALL_DASHES, MEDIUM_DASHES, MEDIUM_SPACED_DASHES, BIG_DASHES, DOTS_DASHES, DOT_LINES_DASHES;
@Override
public ImageIcon getIcon() {
if (this == PLAIN_STROKE) {
return FGEIconLibrary.PLAIN_STROKE_ICON;
} else if (this == SMALL_DASHES) {
return FGEIconLibrary.SMALL_DASHES_ICON;
} else if (this == MEDIUM_DASHES) {
return FGEIconLibrary.MEDIUM_DASHES_ICON;
} else if (this == MEDIUM_SPACED_DASHES) {
return FGEIconLibrary.MEDIUM_SPACED_DASHES_ICON;
} else if (this == BIG_DASHES) {
return FGEIconLibrary.BIG_DASHES_ICON;
} else if (this == DOTS_DASHES) {
return FGEIconLibrary.DOTS_DASHES_ICON;
} else if (this == DOT_LINES_DASHES) {
return FGEIconLibrary.DOTS_LINES_DASHES_ICON;
}
return null;
}
/**
* Returns the array representing the lengths of the dash segments. Alternate entries in the array represent the user space lengths
* of the opaque and transparent segments of the dashes. As the pen moves along the outline of the <code>Shape</code> to be stroked,
* the user space distance that the pen travels is accumulated. The distance value is used to index into the dash array. The pen is
* opaque when its current cumulative distance maps to an even element of the dash array and transparent otherwise.
*
* @return the dash array.
*/
public float[] getDashArray() {
if (this == PLAIN_STROKE) {
return null;
} else if (this == SMALL_DASHES) {
float[] da = { 3, 2 };
return da;
} else if (this == MEDIUM_DASHES) {
float[] da = { 5, 3 };
return da;
} else if (this == MEDIUM_SPACED_DASHES) {
float[] da = { 5, 5 };
return da;
} else if (this == BIG_DASHES) {
float[] da = { 10, 5 };
return da;
} else if (this == DOTS_DASHES) {
float[] da = { 1, 4 };
return da;
} else if (this == DOT_LINES_DASHES) {
float[] da = { 15, 3, 3, 3 };
return da;
}
return null;
}
/**
* Returns the current dash phase. The dash phase is a distance specified in user coordinates that represents an offset into the
* dashing pattern. In other words, the dash phase defines the point in the dashing pattern that will correspond to the beginning of
* the stroke.
*
* @return the dash phase as a <code>float</code> value.
*/
public float getDashPhase() {
if (this == PLAIN_STROKE) {
return 0;
} else if (this == SMALL_DASHES) {
return 0;
} else if (this == MEDIUM_DASHES) {
return 0;
} else if (this == MEDIUM_SPACED_DASHES) {
return 0;
} else if (this == BIG_DASHES) {
return 0;
} else if (this == DOTS_DASHES) {
return 0;
} else if (this == DOT_LINES_DASHES) {
return 0;
}
return 0;
}
}
public ForegroundStyle() {
super();
noStroke = false;
color = Color.BLACK;
lineWidth = 1.0;
joinStyle = JoinStyle.JOIN_MITER;
capStyle = CapStyle.CAP_SQUARE;
dashStyle = DashStyle.PLAIN_STROKE;
}
public ForegroundStyle(Color aColor) {
this();
color = aColor;
}
public static ForegroundStyle makeDefault() {
return new ForegroundStyle();
}
public static ForegroundStyle makeNone() {
ForegroundStyle returned = new ForegroundStyle();
returned.setNoStroke(true);
return returned;
}
public static ForegroundStyle makeStyle(Color aColor) {
return new ForegroundStyle(aColor);
}
public static ForegroundStyle makeStyle(Color aColor, float aLineWidth) {
ForegroundStyle returned = new ForegroundStyle(aColor);
returned.setLineWidth(aLineWidth);
return returned;
}
public static ForegroundStyle makeStyle(Color aColor, float aLineWidth, JoinStyle joinStyle, CapStyle capStyle, DashStyle dashStyle) {
ForegroundStyle returned = new ForegroundStyle(aColor);
returned.setLineWidth(aLineWidth);
returned.setJoinStyle(joinStyle);
returned.setCapStyle(capStyle);
returned.setDashStyle(dashStyle);
return returned;
}
public static ForegroundStyle makeStyle(Color aColor, float aLineWidth, DashStyle dashStyle) {
ForegroundStyle returned = new ForegroundStyle(aColor);
returned.setLineWidth(aLineWidth);
returned.setDashStyle(dashStyle);
return returned;
}
public CapStyle getCapStyle() {
return capStyle;
}
public void setCapStyle(CapStyle aCapStyle) {
if (requireChange(this.color, aCapStyle)) {
CapStyle oldCapStyle = capStyle;
this.capStyle = aCapStyle;
stroke = null;
setChanged();
notifyObservers(new FGENotification(Parameters.capStyle, oldCapStyle, aCapStyle));
}
}
public Color getColor() {
return color;
}
public void setColor(Color aColor) {
if (requireChange(this.color, aColor)) {
java.awt.Color oldColor = color;
this.color = aColor;
setChanged();
notifyObservers(new FGENotification(Parameters.color, oldColor, aColor));
}
}
public void setColorNoNotification(Color aColor) {
this.color = aColor;
}
public DashStyle getDashStyle() {
return dashStyle;
}
public void setDashStyle(DashStyle aDashStyle) {
if (requireChange(this.color, aDashStyle)) {
DashStyle oldDashStyle = dashStyle;
this.dashStyle = aDashStyle;
stroke = null;
setChanged();
notifyObservers(new FGENotification(Parameters.dashStyle, oldDashStyle, dashStyle));
}
}
public JoinStyle getJoinStyle() {
return joinStyle;
}
public void setJoinStyle(JoinStyle aJoinStyle) {
if (requireChange(this.joinStyle, aJoinStyle)) {
JoinStyle oldJoinStyle = joinStyle;
this.joinStyle = aJoinStyle;
stroke = null;
setChanged();
notifyObservers(new FGENotification(Parameters.joinStyle, oldJoinStyle, aJoinStyle));
}
}
public double getLineWidth() {
return lineWidth;
}
public void setLineWidth(double aLineWidth) {
if (requireChange(this.lineWidth, aLineWidth)) {
double oldLineWidth = lineWidth;
lineWidth = aLineWidth;
stroke = null;
setChanged();
notifyObservers(new FGENotification(Parameters.lineWidth, oldLineWidth, aLineWidth));
}
}
public boolean getNoStroke() {
return noStroke;
}
public void setNoStroke(boolean aFlag) {
if (requireChange(this.noStroke, aFlag)) {
boolean oldValue = noStroke;
this.noStroke = aFlag;
setChanged();
notifyObservers(new FGENotification(Parameters.noStroke, oldValue, aFlag));
}
}
public Stroke getStroke(double scale) {
if (stroke == null || scale != strokeScale) {
if (dashStyle == DashStyle.PLAIN_STROKE) {
stroke = new BasicStroke((float) (lineWidth * scale), capStyle.ordinal(), joinStyle.ordinal());
} else {
float[] scaledDashArray = new float[dashStyle.getDashArray().length];
for (int i = 0; i < dashStyle.getDashArray().length; i++) {
scaledDashArray[i] = (float) (dashStyle.getDashArray()[i] * scale * lineWidth);
}
float scaledDashedPhase = (float) (dashStyle.getDashPhase() * scale * lineWidth);
stroke = new BasicStroke((float) (lineWidth * scale), capStyle.ordinal(), joinStyle.ordinal(), 10, scaledDashArray,
scaledDashedPhase);
}
strokeScale = scale;
}
return stroke;
}
public float getTransparencyLevel() {
return transparencyLevel;
}
public void setTransparencyLevel(float aLevel) {
if (requireChange(this.transparencyLevel, aLevel)) {
float oldValue = transparencyLevel;
this.transparencyLevel = aLevel;
setChanged();
notifyObservers(new FGENotification(Parameters.transparencyLevel, oldValue, aLevel));
}
}
public boolean getUseTransparency() {
return useTransparency;
}
public void setUseTransparency(boolean aFlag) {
if (requireChange(this.useTransparency, aFlag)) {
boolean oldValue = useTransparency;
this.useTransparency = aFlag;
setChanged();
notifyObservers(new FGENotification(Parameters.useTransparency, oldValue, aFlag));
}
}
@Override
public ForegroundStyle clone() {
try {
ForegroundStyle returned = (ForegroundStyle) super.clone();
return returned;
} catch (CloneNotSupportedException e) {
// cannot happen since we are clonable
e.printStackTrace();
return null;
}
}
@Override
public String toString() {
return "ForegroundStyle " + Integer.toHexString(hashCode()) + " [noStroke=" + noStroke + ",lineWidth=" + lineWidth + ",color="
+ color + ",joinStyle=" + joinStyle + ",capStyle=" + capStyle + ",dashStyle=" + dashStyle + ",useTransparency="
+ useTransparency + ",transparencyLevel=" + transparencyLevel + "]";
}
public String toNiceString() {
if (getNoStroke()) {
return FlexoLocalization.localizedForKey(GraphicalRepresentation.LOCALIZATION, "no_stroke");
} else {
return lineWidth + "pt, " + color;
}
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ForegroundStyle) {
// logger.info("Equals called for ForegroundStyle !!!!!!!!!");
ForegroundStyle fs = (ForegroundStyle) obj;
return getNoStroke() == fs.getNoStroke() && getLineWidth() == fs.getLineWidth() && getColor() == fs.getColor()
&& getJoinStyle() == fs.getJoinStyle() && getCapStyle() == fs.getCapStyle() && getDashStyle() == fs.getDashStyle()
&& getUseTransparency() == fs.getUseTransparency() && getTransparencyLevel() == fs.getTransparencyLevel();
}
return super.equals(obj);
}
private boolean requireChange(Object oldObject, Object newObject) {
if (oldObject == null) {
if (newObject == null) {
return false;
} else {
return true;
}
}
return !oldObject.equals(newObject);
}
}