001: /* uDig - User Friendly Desktop Internet GIS client
002: * http://udig.refractions.net
003: * (C) 2004, Refractions Research Inc.
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation;
008: * version 2.1 of the License.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: */
015: package net.refractions.udig.style.sld.simple;
016:
017: import java.awt.Color;
018: import java.util.ArrayList;
019: import java.util.List;
020:
021: import net.refractions.udig.project.internal.ProjectPlugin;
022: import net.refractions.udig.project.preferences.PreferenceConstants;
023: import net.refractions.udig.style.sld.AbstractSimpleConfigurator;
024: import net.refractions.udig.style.sld.SLD;
025: import net.refractions.udig.style.sld.internal.FontEditor;
026: import net.refractions.udig.style.sld.internal.Messages;
027: import net.refractions.udig.ui.graphics.SLDs;
028:
029: import org.eclipse.swt.SWT;
030: import org.eclipse.swt.events.KeyListener;
031: import org.eclipse.swt.events.ModifyEvent;
032: import org.eclipse.swt.events.ModifyListener;
033: import org.eclipse.swt.events.SelectionEvent;
034: import org.eclipse.swt.events.SelectionListener;
035: import org.eclipse.swt.graphics.FontData;
036: import org.eclipse.swt.widgets.Button;
037: import org.eclipse.swt.widgets.Combo;
038: import org.eclipse.swt.widgets.Composite;
039: import org.geotools.feature.AttributeType;
040: import org.geotools.feature.FeatureType;
041: import org.geotools.filter.Expression;
042: import org.geotools.filter.FilterFactoryFinder;
043: import org.geotools.styling.AnchorPoint;
044: import org.geotools.styling.Fill;
045: import org.geotools.styling.Font;
046: import org.geotools.styling.LabelPlacement;
047: import org.geotools.styling.LinePlacement;
048: import org.geotools.styling.PointPlacement;
049: import org.geotools.styling.StyleBuilder;
050: import org.geotools.styling.TextSymbolizer;
051:
052: /**
053: * Allows editing/viewing of a Style Layer Descriptor "TextSymbolizer".
054: * <p>
055: * Here is the pretty picture: <pre><code>
056: * +-+ +------------+ +------+ +------+
057: * Label: |x| | title\/| | Font | |Offset|
058: * +-+ +------------+ +------+ +------+
059: * or
060: * +-+ +------------+ +------+ +------+ +------+ +--------+
061: * Label: |x| | title\/| | Font | |VAlign| |HAlign| |Rotation|
062: * +-+ +------------+ +------+ +------+ +------+ +--------+
063: * </code></pre>
064: * </p>
065: * <p>
066: * Workflow:
067: * <ol>
068: * <li>createControl( parent ) - set up controls
069: * <li>set( FeatureType, TextSymbolizer, Mode ) - provide content from SimpleStyleConfigurator
070: * <ol>
071: * <li> Symbolizer values copied into fields based on mode
072: * <li> fields copied into controls
073: * <li> controls enabled based on mode & fields
074: * </ol>
075: * <li>Listener.widgetSelected/modifyText - User performs an "edit"
076: * <li>Listener.sync( SelectionEvent ) - update fields with values of controls
077: * <li>fire( SelectionSevent ) - notify SimpleStyleConfigurator of change
078: * <li>get( StyleBuilder ) - construct based on fields
079: * </ul>
080: * </p>
081: * @author Jody Garnett
082: * @since 1.0.0
083: */
084: public class LabelViewer {
085: boolean enabled;
086: FeatureType schema;
087: String labelType;
088: FontData[] font;
089: Color colour;
090:
091: /**
092: * Use PointPlacement or LinePlacement?
093: */
094: boolean pointPlacement = true;
095: LabelPlacement labelPlacement = null;
096:
097: Button on;
098: Combo field;
099: FontEditor fonter;
100: Composite part;
101: KeyListener klisten;
102:
103: //generic combos for PointPlacement/LinePlacement use
104: Combo place;
105: Combo place2;
106: Combo place3;
107:
108: private class Listener implements SelectionListener, ModifyListener {
109: public void widgetSelected(SelectionEvent e) {
110: sync(e);
111: };
112:
113: public void widgetDefaultSelected(SelectionEvent e) {
114: sync(e);
115: };
116:
117: public void modifyText(final ModifyEvent e) {
118: sync(AbstractSimpleConfigurator.selectionEvent(e));
119: };
120:
121: private void sync(SelectionEvent selectionEvent) {
122: try {
123: LabelViewer.this .enabled = LabelViewer.this .on
124: .getSelection();
125: LabelViewer.this .colour = LabelViewer.this .fonter
126: .getAWTColor();
127: LabelViewer.this .font = LabelViewer.this .fonter
128: .getFontList();
129: LabelViewer.this .labelType = LabelViewer.this .field
130: .getText();
131: } catch (Throwable t) {
132: // meh
133: } finally {
134: LabelViewer.this .field
135: .setEnabled(LabelViewer.this .enabled);
136: LabelViewer.this .fonter
137: .setEnabled(LabelViewer.this .enabled);
138: LabelViewer.this .place
139: .setEnabled(LabelViewer.this .enabled);
140: if (LabelViewer.this .pointPlacement) {
141: LabelViewer.this .place2
142: .setEnabled(LabelViewer.this .enabled);
143: LabelViewer.this .place2.setVisible(true);
144: LabelViewer.this .place3
145: .setEnabled(LabelViewer.this .enabled);
146: LabelViewer.this .place3.setVisible(true);
147: } else {
148: if (LabelViewer.this .place2 != null) {
149: LabelViewer.this .place2.setVisible(false);
150: }
151: }
152: }
153: fire(selectionEvent);
154: }
155: };
156:
157: Listener sync = new Listener();
158:
159: private SelectionListener listener;
160:
161: /**
162: * Accepts a listener that will be notified when content changes.
163: * @param listener1
164: */
165: public void addListener(SelectionListener listener1) {
166: this .listener = listener1;
167: }
168:
169: /**
170: * Remove listener.
171: * @param listener1
172: */
173: public void removeListener(SelectionListener listener1) {
174: if (this .listener == listener1)
175: this .listener = null;
176: }
177:
178: /**
179: * TODO summary sentence for fire ...
180: *
181: * @param event
182: */
183: protected void fire(SelectionEvent event) {
184: if (this .listener == null)
185: return;
186: this .listener.widgetSelected(event);
187: }
188:
189: /**
190: * Constructs a TextSymbolizer from the inputs
191: * @param build
192: *
193: * @return TextSymbolizer defined by this model
194: */
195: public TextSymbolizer get(StyleBuilder build) {
196: if (!this .enabled) {
197: return null;
198: }
199: if (this .font == null || this .font.length == 0) {
200: return null;
201: }
202: if (this .labelType == null || "".equals(this .labelType)) { //$NON-NLS-1$
203: return null;
204: }
205:
206: String fontName = this .font[0].getName();
207: boolean fontBold = (this .font[0].getStyle() == SWT.BOLD);
208: boolean fontItalic = (this .font[0].getStyle() == SWT.ITALIC);
209: double fontSize = this .font[0].getHeight();
210: Font gtFont = build.createFont(fontName, fontItalic, fontBold,
211: fontSize);
212: Fill fill = build.createFill(this .colour);
213:
214: LabelPlacement placement;
215: if (pointPlacement) {
216: //PointPlacement
217: double horiz;
218: if (this .place.getSelectionIndex() < 3) {
219: switch (this .place.getSelectionIndex()) {
220: case 0:
221: horiz = SLDs.ALIGN_LEFT;
222: break;
223: case 1:
224: horiz = SLDs.ALIGN_CENTER;
225: break;
226: case 2:
227: horiz = SLDs.ALIGN_RIGHT;
228: break;
229:
230: default:
231: horiz = SLDs.ALIGN_CENTER;
232: break;
233: }
234: } else { //custom value
235: horiz = Double.parseDouble(this .place.getText());
236: }
237:
238: double vert;
239: if (this .place2.getSelectionIndex() < 3) {
240: switch (this .place2.getSelectionIndex()) {
241: case 0:
242: vert = SLDs.ALIGN_BOTTOM;
243: break;
244: case 1:
245: vert = SLDs.ALIGN_MIDDLE;
246: break;
247: case 2:
248: vert = SLDs.ALIGN_TOP;
249: break;
250:
251: default:
252: vert = SLDs.ALIGN_MIDDLE;
253: break;
254: }
255: } else { //custom value
256: vert = Double.parseDouble(this .place2.getText());
257: }
258:
259: double rotation = Double.parseDouble(this .place3.getText());
260:
261: placement = build.createPointPlacement(vert, horiz,
262: rotation);
263: } else {
264: //LinePlacement
265: double offset = Double.parseDouble(this .place.getText());
266: placement = build.createLinePlacement(offset);
267: }
268: this .labelPlacement = placement;
269:
270: Expression exp = FilterFactoryFinder.createFilterFactory()
271: .createAttributeExpression(this .labelType);
272: TextSymbolizer text = build.createTextSymbolizer(fill,
273: new Font[] { gtFont }, null, exp, placement, null);
274: if (SLD.isLine(this .schema)) {
275: text.addToOptions("group", "yes"); //$NON-NLS-1$ //$NON-NLS-2$
276: }
277: text.addToOptions("spaceAround", "2"); //$NON-NLS-1$ //$NON-NLS-2$
278: return text;
279: }
280:
281: /**
282: * Start editing the provided symbolizer.
283: *
284: * @param schema
285: * @param sym
286: * @param mode
287: */
288: public void set(FeatureType schema, TextSymbolizer sym, Mode mode) {
289: listen(false);
290: try {
291: this .schema = schema;
292: this .enabled = (mode != Mode.NONE && sym != null);
293:
294: this .font = SLDs.textFont(sym);
295: if (this .font == null || this .font.length == 0) {
296: this .font = new FontData[] { new FontData(
297: "Arial", 12, SWT.NORMAL) }; //$NON-NLS-1$
298: }
299: this .labelType = SLDs.textLabelString(sym);
300: this .colour = SLDs.textFontFill(sym);
301: if (this .colour == null) {
302: this .colour = Color.BLACK;
303: }
304:
305: this .on.setEnabled(mode != Mode.NONE);
306: this .fonter.setColorValue(this .colour);
307: this .fonter.setFontList(this .font);
308:
309: if (schema != null) {
310: AttributeType[] attrs = schema.getAttributeTypes();
311: List<String> list = new ArrayList<String>();
312: for (int i = 0; i < attrs.length; i++) {
313: Class cls = attrs[i].getType();
314: if (String.class.isAssignableFrom(cls)) {
315: list.add(attrs[i].getName());
316: } else if (Number.class.isAssignableFrom(cls)) {
317: list.add(attrs[i].getName());
318: }
319: }
320: this .field.removeAll();
321: this .field.setItems(list.toArray(new String[0]));
322: if (this .labelType == null) {
323: this .field.select(0);
324: } else {
325: this .field.setText(this .labelType);
326: }
327: }
328:
329: this .on.setSelection(this .enabled);
330: this .field.setEnabled(this .enabled);
331: this .fonter.setEnabled(this .enabled);
332:
333: if (schema != null
334: && (SLD.isLine(schema) == pointPlacement || this .place == null)) {
335: pointPlacement = !SLD.isLine(schema);
336: if (pointPlacement) {
337: initPlacementContentsPoint();
338: } else {
339: initPlacementContentsLine();
340: }
341: }
342: this .place.setEnabled(this .enabled);
343: if (pointPlacement) {
344: //PointPlacement
345: this .place2.setEnabled(this .enabled);
346: this .place3.setEnabled(this .enabled);
347: if (this .labelPlacement == null
348: || !(this .labelPlacement instanceof PointPlacement)) {
349: //defaults
350: if (mode == Mode.POINT) {
351: //don't cover the point!
352: this .place.select(2); //top
353: this .place2.select(2); //right
354: } else {
355: this .place.select(1); //middle
356: this .place2.select(1); //center
357: }
358: this .place3.select(0); //0 degrees rotation
359: } else {
360: AnchorPoint anchor = ((PointPlacement) labelPlacement)
361: .getAnchorPoint();
362: String anchorX = anchor.getAnchorPointX()
363: .toString();
364: String anchorY = anchor.getAnchorPointY()
365: .toString();
366: //use labels if 0, 0.5, or 1, otherwise use value for align
367: if (anchorX
368: .equals(Double.toString(SLDs.ALIGN_LEFT))) {
369: this .place2.select(0);
370: } else if (anchorX.equals(Double
371: .toString(SLDs.ALIGN_CENTER))) {
372: this .place2.select(1);
373: } else if (anchorX.equals(Double
374: .toString(SLDs.ALIGN_RIGHT))) {
375: this .place2.select(2);
376: } else {
377: this .place2.setText(anchorX);
378: }
379: if (anchorY.equals(Double
380: .toString(SLDs.ALIGN_BOTTOM))) {
381: this .place.select(0);
382: } else if (anchorY.equals(Double
383: .toString(SLDs.ALIGN_MIDDLE))) {
384: this .place.select(1);
385: } else if (anchorY.equals(Double
386: .toString(SLDs.ALIGN_TOP))) {
387: this .place.select(2);
388: } else {
389: this .place.setText(anchorY);
390: }
391: //rotation
392: this .place3
393: .setText(((PointPlacement) labelPlacement)
394: .getRotation().toString());
395: }
396: } else {
397: //LinePlacement
398: if (this .labelPlacement == null
399: || !(this .labelPlacement instanceof LinePlacement)) {
400: //defaults
401: this .place
402: .setText(ProjectPlugin
403: .getPlugin()
404: .getPreferenceStore()
405: .getString(
406: PreferenceConstants.P_STYLE_DEFAULT_PERPENDICULAR_OFFSET));
407: } else {
408: String offset = ((LinePlacement) labelPlacement)
409: .getPerpendicularOffset().toString();
410: this .place.setText(offset);
411: }
412: }
413: } finally {
414: listen(true);
415: }
416:
417: }
418:
419: /**
420: * TODO summary sentence for listen ...
421: *
422: * @param listen
423: */
424: public void listen(boolean listen) {
425: if (listen) {
426: this .on.addSelectionListener(this .sync);
427: this .field.addSelectionListener(this .sync);
428: this .field.addModifyListener(this .sync);
429: this .fonter.setListener(this .sync);
430: if (this .place != null) {
431: this .place.addSelectionListener(this .sync);
432: this .place.addModifyListener(this .sync);
433: }
434: if (this .place2 != null) {
435: this .place2.addSelectionListener(this .sync);
436: this .place2.addModifyListener(this .sync);
437: }
438: if (this .place3 != null) {
439: this .place3.addSelectionListener(this .sync);
440: this .place3.addModifyListener(this .sync);
441: }
442: } else {
443: this .on.removeSelectionListener(this .sync);
444: this .field.removeSelectionListener(this .sync);
445: this .field.removeModifyListener(this .sync);
446: this .fonter.clearListener();
447: if (this .place != null) {
448: this .place.removeSelectionListener(this .sync);
449: this .place.removeModifyListener(this .sync);
450: }
451: if (this .place2 != null) {
452: this .place2.removeSelectionListener(this .sync);
453: this .place2.removeModifyListener(this .sync);
454: }
455: if (this .place3 != null) {
456: this .place3.removeSelectionListener(this .sync);
457: this .place3.removeModifyListener(this .sync);
458: }
459: }
460: }
461:
462: /**
463: * TODO summary sentence for createControl ...
464: *
465: * @param parent
466: * @param klisten
467: * @return Generated composite
468: */
469: public Composite createControl(Composite parent, KeyListener klisten) {
470: this .part = AbstractSimpleConfigurator.subpart(parent,
471: Messages.SimpleStyleConfigurator_label_label);
472: this .klisten = klisten;
473:
474: this .on = new Button(part, SWT.CHECK);
475:
476: this .field = new Combo(part, SWT.DROP_DOWN | SWT.READ_ONLY);
477: this .field.addKeyListener(klisten);
478: if (this .schema != null) {
479: AttributeType[] types = this .schema.getAttributeTypes();
480: String[] typeStrings = new String[types.length];
481: for (int i = 0; i < types.length; i++) {
482: typeStrings[i] = types[i].getName();
483: }
484: this .field.setItems(typeStrings);
485: }
486: this .field.setToolTipText(Messages.LabelViewer_field_tooltip);
487:
488: this .fonter = new FontEditor(part);
489:
490: //determine which placement to use
491: if (schema != null) {
492: if (SLD.isLine(schema)) {
493: pointPlacement = false;
494: } else {
495: pointPlacement = true;
496: }
497: if (pointPlacement) {
498: //point placement (3 combos: AnchorPoint (Horiz, Vert) + Rotation)
499: initPlacementContentsPoint();
500: } else {
501: //line placement (1 combo: Perpendicular Offset)
502: initPlacementContentsLine();
503: }
504: }
505:
506: listen(true);
507:
508: return part;
509: }
510:
511: private void initPlacementContentsLine() {
512: if (this .place == null) {
513: this .place = new Combo(part, SWT.DROP_DOWN);
514: this .place.addKeyListener(klisten);
515: }
516: this .place.setToolTipText(Messages.LabelViewer_offset);
517:
518: String[] itemsO = new String[] { "0", "5", "10", "15", "20" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
519: this .place.setItems(itemsO);
520:
521: if (this .place2 != null) {
522: this .place2.setVisible(false);
523: }
524: if (this .place3 != null) {
525: this .place3.setVisible(false);
526: }
527: }
528:
529: private void initPlacementContentsPoint() {
530: //TODO internatialize
531: String[] itemsH = new String[] { Messages.LabelViewer_left,
532: Messages.LabelViewer_center, Messages.LabelViewer_right };
533: String[] itemsV = new String[] { Messages.LabelViewer_bottom,
534: Messages.LabelViewer_middle, Messages.LabelViewer_top };
535: String[] itemsR = new String[] {
536: "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$
537:
538: if (this .place == null) {
539: this .place = new Combo(part, SWT.DROP_DOWN);
540: this .place.addKeyListener(klisten);
541: }
542: this .place.setToolTipText(Messages.LabelViewer_vertAlign);
543: this .place.setItems(itemsV);
544:
545: if (this .place2 == null) {
546: this .place2 = new Combo(part, SWT.DROP_DOWN);
547: this .place2.addKeyListener(klisten);
548: }
549: this .place2.setToolTipText(Messages.LabelViewer_horizAlign);
550: this .place2.setItems(itemsH);
551: this .place2.setVisible(true);
552:
553: if (this .place3 == null) {
554: this .place3 = new Combo(part, SWT.DROP_DOWN);
555: this .place3.addKeyListener(klisten);
556: }
557: this .place3.setToolTipText(Messages.LabelViewer_rotation);
558: this .place3.setItems(itemsR);
559: this .place3.setVisible(true);
560: }
561:
562: }
|