package com.revolsys.swing.map.layer.record.style.panel;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.measure.Measure;
import javax.measure.quantity.Length;
import javax.measure.unit.NonSI;
import javax.measure.unit.Unit;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import org.jdesktop.swingx.VerticalLayout;
import com.revolsys.geometry.model.LineCap;
import com.revolsys.geometry.model.LineJoin;
import com.revolsys.swing.Icons;
import com.revolsys.swing.Panels;
import com.revolsys.swing.SwingUtil;
import com.revolsys.swing.action.I18nAction;
import com.revolsys.swing.component.Form;
import com.revolsys.swing.component.TogglePanel;
import com.revolsys.swing.field.CheckBox;
import com.revolsys.swing.field.ColorChooserField;
import com.revolsys.swing.field.ComboBox;
import com.revolsys.swing.field.Field;
import com.revolsys.swing.field.FontChooserField;
import com.revolsys.swing.field.FunctionStringConverter;
import com.revolsys.swing.field.LengthMeasureTextField;
import com.revolsys.swing.field.TextField;
import com.revolsys.swing.layout.GroupLayouts;
import com.revolsys.swing.map.MapPanel;
import com.revolsys.swing.map.component.MapScale;
import com.revolsys.swing.map.component.MarkerField;
import com.revolsys.swing.map.layer.Layer;
import com.revolsys.swing.map.layer.LayerRenderer;
import com.revolsys.swing.map.layer.record.AbstractRecordLayer;
import com.revolsys.swing.map.layer.record.style.GeometryStyle;
import com.revolsys.swing.map.layer.record.style.MarkerStyle;
import com.revolsys.swing.map.layer.record.style.TextStyle;
import com.revolsys.util.CaseConverter;
import com.revolsys.util.Property;
import com.revolsys.util.PropertyDescriptorCache;
public class BaseStylePanel extends Form implements PropertyChangeListener {
public static final List<Action> HORIZONTAL_ALIGNMENT_ACTIONS = getTextAlignActions("left",
"center", "right");
public static final List<Action> LINE_CAP_ACTIONS = getLineActions("cap", "BUTT", "ROUND",
"SQUARE");
public static final List<Action> LINE_JOIN_ACTIONS = getLineActions("join", "MITER", "ROUND",
"BEVEL");
private static final long serialVersionUID = 1L;
public static final List<Action> VERTICAL_ALIGNMENT_ACTIONS = getTextAlignActions("top", "middle",
"bottom");
public static List<Action> getLineActions(final String type, final String... alignmentTypes) {
final List<Action> actions = new ArrayList<>();
for (final String alignmentType : alignmentTypes) {
final String iconName = ("line_" + type + "_" + alignmentType).toLowerCase();
final Icon icon = Icons.getIcon(iconName);
final String toolTip = CaseConverter.toCapitalizedWords(alignmentType + " " + type);
final I18nAction action = new I18nAction(alignmentType, null, toolTip, icon);
actions.add(action);
}
return actions;
}
public static List<Action> getTextAlignActions(final String... alignmentTypes) {
final List<Action> actions = new ArrayList<>();
for (final String alignmentType : alignmentTypes) {
final I18nAction action = new I18nAction(alignmentType, null,
CaseConverter.toCapitalizedWords(alignmentType),
Icons.getIcon("text_align_" + alignmentType));
actions.add(action);
}
return actions;
}
private final Set<String> readOnlyFieldNames = new HashSet<>();
private final Set<String> rendererFieldNames = new HashSet<>();
private Field visibleField;
private final LayerRenderer<?> renderer;
public BaseStylePanel(final LayerRenderer<?> renderer, final boolean showScaleFields) {
super(new VerticalLayout());
this.renderer = renderer;
addReadOnlyFieldName("type");
Property.addListener(renderer, this);
addPanel(this, "General", renderer, "name", "type", "visible");
if (showScaleFields) {
addPanel(this, "Scales", renderer, "minimumScale", "maximumScale");
}
if (PropertyDescriptorCache.getPropertyDescriptor(renderer, "queryFilter") != null) {
addPanel(this, "Filter", renderer, "queryFilter");
}
}
protected void addCheckBoxField(final JPanel container, final Object object,
final String fieldName) {
SwingUtil.addLabel(container, fieldName);
final CheckBox field = new CheckBox(fieldName);
final Object value = Property.get(object, fieldName);
field.setFieldValue(value);
Property.addListener(field, fieldName, this);
container.add(field);
}
protected void addColorField(final JPanel container, final Object object,
final String fieldName) {
SwingUtil.addLabel(container, fieldName);
final Color value = Property.get(object, fieldName);
final ColorChooserField field = new ColorChooserField(fieldName, value);
Property.addListener(field, fieldName, this);
container.add(field);
}
protected Field addField(final JPanel container, final Object object, final String fieldName) {
final Class<?> fieldClass = Property.getClass(object, fieldName);
if (fieldClass == null) {
return null;
} else {
final Object value = Property.get(object, fieldName);
SwingUtil.addLabel(container, fieldName);
final Field field = newField(fieldName, fieldClass, value);
setField(field);
if (this.readOnlyFieldNames.contains(fieldName)) {
field.setEditable(false);
}
if (field instanceof JTextArea) {
container.add(new JScrollPane((Component)field));
} else {
container.add((Component)field);
}
Property.addListener(field, "fieldValue", this);
Property.addListener(field, fieldName, this);
if (object instanceof LayerRenderer) {
this.rendererFieldNames.add(fieldName);
}
return field;
}
}
protected void addFields(final JPanel container, final Object object,
final String... fieldNames) {
for (final String fieldName : fieldNames) {
addField(container, object, fieldName);
}
}
protected void addLengthMeasureField(final JPanel container, final Object object,
final String fieldName) {
SwingUtil.addLabel(container, fieldName);
final Measure<Length> value = Property.get(object, fieldName);
Unit<Length> unit;
if (value == null) {
unit = NonSI.PIXEL;
} else {
unit = value.getUnit();
}
final LengthMeasureTextField field = new LengthMeasureTextField(fieldName, value, unit);
Property.addListener(field, fieldName, this);
container.add(field);
}
protected void addLineStylePanel(final JPanel stylePanels, final GeometryStyle geometryStyle) {
final JPanel panel = Panels.titledTransparent("Line Style");
panel.setMinimumSize(new Dimension(300, 0));
addField(panel, geometryStyle, "lineColor");
addLengthMeasureField(panel, geometryStyle, "lineWidth");
addField(panel, geometryStyle, "lineJoin");
addField(panel, geometryStyle, "lineCap");
addField(panel, geometryStyle, "lineDashArray");
addField(panel, geometryStyle, "lineDashOffset");
GroupLayouts.makeColumns(panel, 2, true);
stylePanels.add(panel);
}
protected void addMarkerStylePanel(final JPanel stylePanels, final MarkerStyle markerStyle) {
addPanel(stylePanels, "Marker Style", markerStyle, "markerLineColor", "markerLineWidth",
"markerFill", "markerWidth", "markerHeight", "marker");
addPanel(stylePanels, "Marker Position", markerStyle, "markerHorizontalAlignment",
"markerVerticalAlignment", "markerDx", "markerDy", "markerOrientationType",
"markerOrientation", "markerPlacementType");
}
protected JPanel addPanel(final Container container, final String title, final Object object,
final String... fieldNames) {
final JPanel panel = Panels.titledTransparent(title);
addFields(panel, object, fieldNames);
GroupLayouts.makeColumns(panel, 2, true);
container.add(panel);
return panel;
}
protected void addPolygonStylePanel(final JPanel stylePanels, final GeometryStyle geometryStyle) {
addPanel(stylePanels, "Polygon Style", geometryStyle, "polygonFill");
}
public void addReadOnlyFieldName(final String fieldName) {
this.readOnlyFieldNames.add(fieldName);
}
protected void addTextField(final JPanel container, final Object object, final String fieldName,
final int columns) {
SwingUtil.addLabel(container, fieldName);
final TextField field = new TextField(fieldName, columns);
field.setFieldValue(Property.get(object, fieldName));
Property.addListener(field, fieldName, this);
container.add(field);
}
protected void addTextStylePanels(final JPanel stylePanels, final TextStyle textStyle) {
addPanel(stylePanels, "Text Label", textStyle, "textName", "textSize", "textFaceName");
addPanel(stylePanels, "Text Color", textStyle, "textFill", "textBoxColor", "textHaloFill",
"textHaloRadius");
addPanel(stylePanels, "Text Position", textStyle, "textHorizontalAlignment",
"textVerticalAlignment", "textDx", "textDy", "textOrientationType", "textOrientation",
"textPlacementType");
}
@SuppressWarnings("unchecked")
public <L extends Layer> L getLayer() {
final LayerRenderer<?> renderer = getRenderer();
return (L)renderer.getLayer();
}
@SuppressWarnings("unchecked")
public <L extends Layer, R extends LayerRenderer<L>> R getRenderer() {
return (R)this.renderer;
}
@SuppressWarnings("unchecked")
protected Field newField(final String fieldName, final Class<?> fieldClass, final Object value) {
Field field;
if (fieldName.equals("visible")) {
this.visibleField = new CheckBox(fieldName, value);
field = this.visibleField;
} else if (fieldName.equals("textFaceName")) {
field = new FontChooserField(fieldName, (String)value);
} else if (fieldName.endsWith("HorizontalAlignment")) {
field = newHorizontalAlignmentField(fieldName, (String)value);
} else if (fieldName.endsWith("VerticalAlignment")) {
field = newVerticalAlignmentField(fieldName, (String)value);
} else if (fieldName.equals("lineCap")) {
field = newLineCapField((LineCap)value);
} else if (fieldName.equals("lineJoin")) {
field = newLineJoinField((LineJoin)value);
} else if (fieldName.equals("lineDashArray")) {
field = new DashField(fieldName, (List<Measure<Length>>)value);
} else if (fieldName.equals("queryFilter")) {
final AbstractRecordLayer layer = getLayer();
field = new QueryFilterField(layer, fieldName, (String)value);
field.setFieldValue(value);
Property.addListener(field, fieldName, this);
} else if (fieldName.equals("marker")) {
field = new MarkerField(fieldName, value);
} else if (fieldName.endsWith("OrientationType")) {
final ComboBox<String> orientationTypeField = ComboBox.newComboBox(fieldName, "auto", "none");
orientationTypeField.setFieldValue(value);
field = orientationTypeField;
} else if (fieldName.equals("markerPlacementType")) {
final ComboBox<String> placementField = ComboBox.newComboBox(fieldName, "auto", "center",
"vertex(0)", "vertex(n)", "vertices", "segment(0)", "segment(n)", "segments");
placementField.setFieldValue(value);
field = placementField;
} else if (fieldName.equals("textPlacementType")) {
final ComboBox<String> placementField = ComboBox.newComboBox(fieldName, "auto", "center",
"vertex(0)", "vertex(n)", "segment(0)", "segment(n)");
placementField.setFieldValue(value);
field = placementField;
} else if (fieldName.endsWith("Scale")) {
field = newScaleField(fieldName, (Long)value);
} else if (Color.class.equals(fieldClass)) {
field = new ColorChooserField(fieldName, (Color)value);
} else if (Boolean.TYPE.equals(fieldClass) || Boolean.class.equals(fieldClass)) {
field = new CheckBox(fieldName, value);
} else if (Measure.class.equals(fieldClass)) {
field = new LengthMeasureTextField(fieldName, (Measure<Length>)value, NonSI.PIXEL);
} else {
field = new TextField(fieldName, value, 20);
}
return field;
}
protected TogglePanel newHorizontalAlignmentField(final String fieldName, String aligment) {
if (!"left".equalsIgnoreCase(aligment) && !"right".equalsIgnoreCase(aligment)) {
aligment = "center";
}
return new TogglePanel(fieldName, aligment, new Dimension(28, 28),
HORIZONTAL_ALIGNMENT_ACTIONS);
}
protected TogglePanel newLineCapField(final LineCap lineCap) {
return new TogglePanel("lineCap", lineCap.toString(), new Dimension(28, 28), LINE_CAP_ACTIONS);
}
protected TogglePanel newLineJoinField(final LineJoin lineJoin) {
return new TogglePanel("lineJoin", lineJoin.toString(), new Dimension(28, 28),
LINE_JOIN_ACTIONS);
}
private Field newScaleField(final String fieldName, final Long value) {
final List<Long> scales = new ArrayList<>();
scales.add(Long.MAX_VALUE);
scales.addAll(MapPanel.SCALES);
final ComboBox<Long> field = ComboBox.newComboBox(fieldName, scales, MapScale::formatScale);
((FunctionStringConverter<?>)field.getRenderer()).setHorizontalAlignment(SwingConstants.RIGHT);
((JTextField)field.getEditor().getEditorComponent())
.setHorizontalAlignment(SwingConstants.RIGHT);
field.setSelectedItem(value);
field.setPreferredSize(new Dimension(150, 22));
return field;
}
protected TogglePanel newVerticalAlignmentField(final String fieldName, String aligment) {
if (!"top".equalsIgnoreCase(aligment) && !"middle".equalsIgnoreCase(aligment)) {
aligment = "bottom";
}
return new TogglePanel(fieldName, aligment, new Dimension(28, 28), VERTICAL_ALIGNMENT_ACTIONS);
}
@Override
public void propertyChange(final PropertyChangeEvent event) {
if (event.getSource() == getRenderer()) {
if ("visible".equals(event.getPropertyName())) {
this.visibleField.setFieldValue(event.getNewValue());
}
}
if (!rendererPropertyChange(event)) {
propertyChangeDo(event);
}
}
protected void propertyChangeDo(final PropertyChangeEvent event) {
}
protected boolean rendererPropertyChange(final PropertyChangeEvent event) {
final Object source = event.getSource();
if (source instanceof Field) {
final LayerRenderer<?> renderer = getRenderer();
final Field field = (Field)source;
final String fieldName = field.getFieldName();
final Object fieldValue = field.getFieldValue();
if (fieldName.endsWith("Scale")) {
long scale = 0;
if (fieldValue instanceof Number) {
final Number number = (Number)fieldValue;
scale = number.longValue();
}
if ("minimumScale".equals(fieldName)) {
if (scale <= 0) {
scale = Long.MAX_VALUE;
}
} else if ("maximumScale".equals(fieldName)) {
if (scale == Long.MAX_VALUE) {
scale = 0;
}
}
renderer.setProperty(fieldName, scale);
return true;
} else if (this.rendererFieldNames.contains(fieldName)) {
renderer.setProperty(fieldName, fieldValue);
}
}
return false;
}
}