/* uDig - User Friendly Desktop Internet GIS client
* http://udig.refractions.net
* (C) 2004, Refractions Research Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotools.swt.styling.simple;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.factory.GeoTools;
import org.geotools.styling.AnchorPoint;
import org.geotools.styling.Fill;
import org.geotools.styling.Font;
import org.geotools.styling.LabelPlacement;
import org.geotools.styling.LinePlacement;
import org.geotools.styling.PointPlacement;
import org.geotools.styling.StyleBuilder;
import org.geotools.styling.TextSymbolizer;
import org.geotools.swt.utils.Messages;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.filter.expression.Expression;
/**
* Allows editing/viewing of a Style Layer Descriptor "TextSymbolizer".
* <p>
* Here is the pretty picture: <pre><code>
* +-+ +------------+ +------+ +------+
* Label: |x| | title\/| | Font | |Offset|
* +-+ +------------+ +------+ +------+
* or
* +-+ +------------+ +------+ +------+ +------+ +--------+
* Label: |x| | title\/| | Font | |VAlign| |HAlign| |Rotation|
* +-+ +------------+ +------+ +------+ +------+ +--------+
* </code></pre>
* </p>
* <p>
* Workflow:
* <ol>
* <li>createControl( parent ) - set up controls
* <li>set( SimpleFeatureType, TextSymbolizer, Mode ) - provide content from SimpleStyleConfigurator
* <ol>
* <li> Symbolizer values copied into fields based on mode
* <li> fields copied into controls
* <li> controls enabled based on mode & fields
* </ol>
* <li>Listener.widgetSelected/modifyText - User performs an "edit"
* <li>Listener.sync( SelectionEvent ) - update fields with values of controls
* <li>fire( SelectionSevent ) - notify SimpleStyleConfigurator of change
* <li>get( StyleBuilder ) - construct based on fields
* </ul>
* </p>
* @author Jody Garnett
* @since 1.0.0
*
*
* @source $URL: http://svn.osgeo.org/geotools/trunk/modules/unsupported/swt/src/main/java/org/geotools/swt/styling/simple/LabelViewer.java $
*/
public class LabelViewer {
boolean enabled;
SimpleFeatureType schema;
String labelType;
FontData[] font;
Color colour;
/**
* Use PointPlacement or LinePlacement?
*/
boolean pointPlacement = true;
LabelPlacement labelPlacement = null;
Button on;
Combo field;
FontEditor fonter;
Composite part;
KeyListener klisten;
// generic combos for PointPlacement/LinePlacement use
Combo place;
Combo place2;
Combo place3;
private class Listener implements SelectionListener, ModifyListener {
public void widgetSelected( SelectionEvent e ) {
sync(e);
};
public void widgetDefaultSelected( SelectionEvent e ) {
sync(e);
};
public void modifyText( final ModifyEvent e ) {
sync(AbstractSimpleConfigurator.selectionEvent(e));
};
private void sync( SelectionEvent selectionEvent ) {
try {
LabelViewer.this.enabled = LabelViewer.this.on.getSelection();
LabelViewer.this.colour = LabelViewer.this.fonter.getAWTColor();
LabelViewer.this.font = LabelViewer.this.fonter.getFontList();
LabelViewer.this.labelType = LabelViewer.this.field.getText();
} catch (Throwable t) {
// meh
} finally {
LabelViewer.this.field.setEnabled(LabelViewer.this.enabled);
LabelViewer.this.fonter.setEnabled(LabelViewer.this.enabled);
LabelViewer.this.place.setEnabled(LabelViewer.this.enabled);
if (LabelViewer.this.pointPlacement) {
LabelViewer.this.place2.setEnabled(LabelViewer.this.enabled);
LabelViewer.this.place2.setVisible(true);
LabelViewer.this.place3.setEnabled(LabelViewer.this.enabled);
LabelViewer.this.place3.setVisible(true);
} else {
if (LabelViewer.this.place2 != null) {
LabelViewer.this.place2.setVisible(false);
}
}
}
fire(selectionEvent);
}
};
Listener sync = new Listener();
private SelectionListener listener;
/**
* Accepts a listener that will be notified when content changes.
* @param listener1
*/
public void addListener( SelectionListener listener1 ) {
this.listener = listener1;
}
/**
* Remove listener.
* @param listener1
*/
public void removeListener( SelectionListener listener1 ) {
if (this.listener == listener1)
this.listener = null;
}
protected void fire( SelectionEvent event ) {
if (this.listener == null)
return;
this.listener.widgetSelected(event);
}
/**
* Constructs a TextSymbolizer from the inputs
* @param build
*
* @return TextSymbolizer defined by this model
*/
public TextSymbolizer get( StyleBuilder build ) {
if (!this.enabled) {
return null;
}
if (this.font == null || this.font.length == 0) {
return null;
}
if (this.labelType == null || "".equals(this.labelType)) { //$NON-NLS-1$
return null;
}
String fontName = this.font[0].getName();
boolean fontBold = (this.font[0].getStyle() == SWT.BOLD);
boolean fontItalic = (this.font[0].getStyle() == SWT.ITALIC);
double fontSize = this.font[0].getHeight();
Font gtFont = build.createFont(fontName, fontItalic, fontBold, fontSize);
Fill fill = build.createFill(this.colour);
LabelPlacement placement;
if (pointPlacement) {
// PointPlacement
double horiz;
if (this.place.getSelectionIndex() < 3) {
switch( this.place.getSelectionIndex() ) {
case 0:
horiz = SLDs.ALIGN_LEFT;
break;
case 1:
horiz = SLDs.ALIGN_CENTER;
break;
case 2:
horiz = SLDs.ALIGN_RIGHT;
break;
default:
horiz = SLDs.ALIGN_CENTER;
break;
}
} else { // custom value
horiz = Double.parseDouble(this.place.getText());
}
double vert;
if (this.place2.getSelectionIndex() < 3) {
switch( this.place2.getSelectionIndex() ) {
case 0:
vert = SLDs.ALIGN_BOTTOM;
break;
case 1:
vert = SLDs.ALIGN_MIDDLE;
break;
case 2:
vert = SLDs.ALIGN_TOP;
break;
default:
vert = SLDs.ALIGN_MIDDLE;
break;
}
} else { // custom value
vert = Double.parseDouble(this.place2.getText());
}
double rotation = Double.parseDouble(this.place3.getText());
placement = build.createPointPlacement(vert, horiz, rotation);
} else {
// LinePlacement
double offset = Double.parseDouble(this.place.getText());
placement = build.createLinePlacement(offset);
}
this.labelPlacement = placement;
Expression exp = (Expression) CommonFactoryFinder.getFilterFactory(GeoTools.getDefaultHints()).property(this.labelType);
TextSymbolizer text = build.createTextSymbolizer(fill, new Font[]{gtFont}, null, exp, placement, null);
if (SLDs.isLine(this.schema)) {
text.getOptions().put("group", "yes"); //$NON-NLS-1$ //$NON-NLS-2$
}
text.getOptions().put("spaceAround", "2"); //$NON-NLS-1$ //$NON-NLS-2$
return text;
}
/**
* Start editing the provided symbolizer.
*
* @param schema
* @param sym
* @param mode
*/
public void set( SimpleFeatureType schema, TextSymbolizer sym, Mode mode ) {
listen(false);
try {
this.schema = schema;
this.enabled = (mode != Mode.NONE && sym != null);
this.font = SLDs.textFont(sym);
if (this.font == null || this.font.length == 0) {
this.font = new FontData[]{new FontData("Arial", 12, SWT.NORMAL)}; //$NON-NLS-1$
}
this.labelType = SLDs.textLabelString(sym);
this.colour = SLDs.textFontFill(sym);
if (this.colour == null) {
this.colour = Color.BLACK;
}
this.on.setEnabled(mode != Mode.NONE);
this.fonter.setColorValue(this.colour);
this.fonter.setFontList(this.font);
if (schema != null) {
AttributeDescriptor[] attrs = schema.getAttributeDescriptors().toArray(new AttributeDescriptor[0]);
List<String> list = new ArrayList<String>();
for( int i = 0; i < attrs.length; i++ ) {
Class< ? > cls = attrs[i].getType().getBinding();
if (String.class.isAssignableFrom(cls)) {
list.add(attrs[i].getName().getLocalPart());
} else if (Number.class.isAssignableFrom(cls)) {
list.add(attrs[i].getName().getLocalPart());
}
}
this.field.removeAll();
this.field.setItems(list.toArray(new String[0]));
if (this.labelType == null) {
this.field.select(0);
} else {
this.field.setText(this.labelType);
}
}
this.on.setSelection(this.enabled);
this.field.setEnabled(this.enabled);
this.fonter.setEnabled(this.enabled);
if (schema != null && (SLDs.isLine(schema) == pointPlacement || this.place == null)) {
pointPlacement = !SLDs.isLine(schema);
if (pointPlacement) {
initPlacementContentsPoint();
} else {
initPlacementContentsLine();
}
}
this.place.setEnabled(this.enabled);
if (pointPlacement) {
// PointPlacement
this.place2.setEnabled(this.enabled);
this.place3.setEnabled(this.enabled);
if (this.labelPlacement == null || !(this.labelPlacement instanceof PointPlacement)) {
// defaults
if (mode == Mode.POINT) {
// don't cover the point!
this.place.select(2); // top
this.place2.select(2); // right
} else {
this.place.select(1); // middle
this.place2.select(1); // center
}
this.place3.select(0); // 0 degrees rotation
} else {
AnchorPoint anchor = ((PointPlacement) labelPlacement).getAnchorPoint();
String anchorX = anchor.getAnchorPointX().toString();
String anchorY = anchor.getAnchorPointY().toString();
// use labels if 0, 0.5, or 1, otherwise use value for align
if (anchorX.equals(Double.toString(SLDs.ALIGN_LEFT))) {
this.place2.select(0);
} else if (anchorX.equals(Double.toString(SLDs.ALIGN_CENTER))) {
this.place2.select(1);
} else if (anchorX.equals(Double.toString(SLDs.ALIGN_RIGHT))) {
this.place2.select(2);
} else {
this.place2.setText(anchorX);
}
if (anchorY.equals(Double.toString(SLDs.ALIGN_BOTTOM))) {
this.place.select(0);
} else if (anchorY.equals(Double.toString(SLDs.ALIGN_MIDDLE))) {
this.place.select(1);
} else if (anchorY.equals(Double.toString(SLDs.ALIGN_TOP))) {
this.place.select(2);
} else {
this.place.setText(anchorY);
}
// rotation
this.place3.setText(((PointPlacement) labelPlacement).getRotation().toString());
}
} else {
// LinePlacement
if (this.labelPlacement == null || !(this.labelPlacement instanceof LinePlacement)) {
// defaults
this.place.setText("0");
} else {
String offset = ((LinePlacement) labelPlacement).getPerpendicularOffset().toString();
this.place.setText(offset);
}
}
} finally {
listen(true);
}
}
public void listen( boolean listen ) {
if (listen) {
this.on.addSelectionListener(this.sync);
this.field.addSelectionListener(this.sync);
this.field.addModifyListener(this.sync);
this.fonter.setListener(this.sync);
if (this.place != null) {
this.place.addSelectionListener(this.sync);
this.place.addModifyListener(this.sync);
}
if (this.place2 != null) {
this.place2.addSelectionListener(this.sync);
this.place2.addModifyListener(this.sync);
}
if (this.place3 != null) {
this.place3.addSelectionListener(this.sync);
this.place3.addModifyListener(this.sync);
}
} else {
this.on.removeSelectionListener(this.sync);
this.field.removeSelectionListener(this.sync);
this.field.removeModifyListener(this.sync);
this.fonter.clearListener();
if (this.place != null) {
this.place.removeSelectionListener(this.sync);
this.place.removeModifyListener(this.sync);
}
if (this.place2 != null) {
this.place2.removeSelectionListener(this.sync);
this.place2.removeModifyListener(this.sync);
}
if (this.place3 != null) {
this.place3.removeSelectionListener(this.sync);
this.place3.removeModifyListener(this.sync);
}
}
}
public Composite createControl( Composite parent, KeyListener klisten ) {
this.part = AbstractSimpleConfigurator.subpart(parent, Messages.getString("SimpleStyleConfigurator_label_label"));
this.klisten = klisten;
this.on = new Button(part, SWT.CHECK);
this.field = new Combo(part, SWT.DROP_DOWN | SWT.READ_ONLY);
this.field.addKeyListener(klisten);
if (this.schema != null) {
List<AttributeDescriptor> types = this.schema.getAttributeDescriptors();
List<String> typeStrings = new ArrayList<String>();
for( AttributeDescriptor attributeDescriptor : types ) {
typeStrings.add(attributeDescriptor.getLocalName());
}
this.field.setItems(typeStrings.toArray(new String[0]));
}
this.field.setToolTipText(Messages.getString("LabelViewer_field_tooltip"));
this.fonter = new FontEditor(part);
// determine which placement to use
if (schema != null) {
if (SLDs.isLine(schema)) {
pointPlacement = false;
} else {
pointPlacement = true;
}
if (pointPlacement) {
// point placement (3 combos: AnchorPoint (Horiz, Vert) + Rotation)
initPlacementContentsPoint();
} else {
// line placement (1 combo: Perpendicular Offset)
initPlacementContentsLine();
}
}
listen(true);
return part;
}
private void initPlacementContentsLine() {
if (this.place == null) {
this.place = new Combo(part, SWT.DROP_DOWN);
this.place.addKeyListener(klisten);
}
this.place.setToolTipText(Messages.getString("LabelViewer_offset"));
String[] itemsO = new String[]{"0", "5", "10", "15", "20"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
this.place.setItems(itemsO);
if (this.place2 != null) {
this.place2.setVisible(false);
}
if (this.place3 != null) {
this.place3.setVisible(false);
}
}
private void initPlacementContentsPoint() {
String[] itemsH = new String[]{Messages.getString("LabelViewer_left"), Messages.getString("LabelViewer_center"),
Messages.getString("LabelViewer_right")};
String[] itemsV = new String[]{Messages.getString("LabelViewer_bottom"), Messages.getString("LabelViewer_middle"),
Messages.getString("LabelViewer_top")};
String[] itemsR = new String[]{"0", "45", "90", "135", "180", "225", "270", "315", "360"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$
if (this.place == null) {
this.place = new Combo(part, SWT.DROP_DOWN);
this.place.addKeyListener(klisten);
}
this.place.setToolTipText(Messages.getString("LabelViewer_vertAlign"));
this.place.setItems(itemsV);
if (this.place2 == null) {
this.place2 = new Combo(part, SWT.DROP_DOWN);
this.place2.addKeyListener(klisten);
}
this.place2.setToolTipText(Messages.getString("LabelViewer_horizAlign"));
this.place2.setItems(itemsH);
this.place2.setVisible(true);
if (this.place3 == null) {
this.place3 = new Combo(part, SWT.DROP_DOWN);
this.place3.addKeyListener(klisten);
}
this.place3.setToolTipText(Messages.getString("LabelViewer_rotation"));
this.place3.setItems(itemsR);
this.place3.setVisible(true);
}
}