/* * Copyright (c) 2012 Data Harmonisation Panel * * All rights reserved. This program and the accompanying materials are made * available under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * HUMBOLDT EU Integrated Project #030962 * Data Harmonisation Panel <http://www.dhpanel.eu> */ package eu.esdihumboldt.hale.ui.style.dialog; import java.util.ArrayList; import java.util.List; import org.eclipse.jface.dialogs.IDialogPage; import org.eclipse.jface.dialogs.InputDialog; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ListViewer; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.geotools.styling.FeatureTypeStyle; import org.geotools.styling.LineSymbolizer; import org.geotools.styling.PointSymbolizer; import org.geotools.styling.PolygonSymbolizer; import org.geotools.styling.Rule; import org.geotools.styling.Style; import org.geotools.styling.StyleBuilder; import org.geotools.styling.Symbolizer; import org.opengis.filter.Filter; import de.fhg.igd.slf4jplus.ALogger; import de.fhg.igd.slf4jplus.ALoggerFactory; import eu.esdihumboldt.hale.common.schema.model.TypeDefinition; import eu.esdihumboldt.hale.ui.style.StyleHelper; import eu.esdihumboldt.hale.ui.style.editors.Editor; import eu.esdihumboldt.hale.ui.style.editors.EditorFactory; import eu.esdihumboldt.hale.ui.style.editors.LineSymbolizerEditor; import eu.esdihumboldt.hale.ui.style.editors.PointSymbolizerEditor; import eu.esdihumboldt.hale.ui.style.editors.PolygonSymbolizerEditor; import eu.esdihumboldt.hale.ui.style.editors.RuleEditor; import eu.esdihumboldt.hale.ui.style.internal.InstanceStylePlugin; import eu.esdihumboldt.hale.ui.style.internal.Messages; /** * Rule based style editor page * * @author Simon Templer * @partner 01 / Fraunhofer Institute for Computer Graphics Research */ public class RuleStylePage extends FeatureStylePage { /** * {@link Rule} list item */ private class RuleItem { private Rule rule; /** * Creates a new rule item * * @param rule the rule */ public RuleItem(Rule rule) { this.rule = rule; } /** * Get the item's rule * * @return the rule */ public Rule getRule() { return rule; } /** * Set the item's rule * * @param rule the rule to set */ public void setRule(Rule rule) { this.rule = rule; } /** * @see Object#toString() */ @Override public String toString() { String name = getRule().getName(); if (name == null || name.isEmpty()) { return Messages.RuleStylePage_Rule + rules.indexOf(this); } else { return name; } } } private static final StyleBuilder styleBuilder = new StyleBuilder(); private static Image addImage = null; private static Image removeImage = null; private static Image renameImage = null; private static Image upImage = null; private static Image downImage = null; private Button addButton; private Button removeButton; private Button renameButton; private Button upButton; private Button downButton; private Composite editorArea; private ListViewer listViewer; private int currentIndex = -1; private Editor<Rule> currentEditor = null; private List<RuleItem> rules; private boolean changed = false; private static final ALogger log = ALoggerFactory.getLogger(RuleStylePage.class); /** * Creates a new editor page * * @param parent the parent dialog */ public RuleStylePage(FeatureStyleDialog parent) { super(parent, Messages.RuleStylePage_SuperTitle); if (addImage == null) { addImage = InstanceStylePlugin.getImageDescriptor("/icons/add.gif").createImage(); //$NON-NLS-1$ } if (removeImage == null) { removeImage = InstanceStylePlugin.getImageDescriptor("/icons/remove.gif").createImage(); //$NON-NLS-1$ } if (renameImage == null) { renameImage = InstanceStylePlugin.getImageDescriptor("/icons/rename.gif").createImage(); //$NON-NLS-1$ } if (upImage == null) { upImage = InstanceStylePlugin.getImageDescriptor("/icons/arrow_up.gif").createImage(); //$NON-NLS-1$ } if (downImage == null) { downImage = InstanceStylePlugin .getImageDescriptor("/icons/arrow_down.gif").createImage(); //$NON-NLS-1$ } } /** * @see FeatureStylePage#getStyle(boolean) */ @Override public Style getStyle(boolean force) throws Exception { updateCurrentRule(); if (force || changed) { Rule[] ruleArray = new Rule[rules.size()]; for (int i = 0; i < rules.size(); i++) { Rule rule = rules.get(i).getRule(); // set else filter rule.setIsElseFilter(rule.getFilter() == null); // TODO other rule manipulation? ruleArray[i] = rule; } // create style FeatureTypeStyle fts = styleBuilder.createFeatureTypeStyle("Feature", ruleArray); //$NON-NLS-1$ Style style = styleBuilder.createStyle(); style.featureTypeStyles().add(fts); return style; } else { return null; } } /** * Update the {@link Rule} whose editor is currently open * * @throws Exception if an error occurs during the update */ private void updateCurrentRule() throws Exception { if (currentEditor != null && currentEditor.isChanged()) { Rule rule = currentEditor.getValue(); if (rule != null) { rules.get(currentIndex).setRule(rule); changed = true; } } } /** * @see IDialogPage#createControl(Composite) */ @Override public void createControl(Composite parent) { changed = false; Composite page = new Composite(parent, SWT.NONE); page.setLayout(new GridLayout(2, false)); // DISABLED - this method seems to change the rule order - Rule[] // ruleArray = SLD.rules(getParent().getStyle()); // use list instead: List<Rule> ruleList; try { ruleList = getParent().getStyle().featureTypeStyles().get(0).rules(); } catch (Exception e) { ruleList = new ArrayList<Rule>(); } // init index if (ruleList.size() > 0) { currentIndex = 0; } else { currentIndex = -1; } currentEditor = null; // populate rule map rules = new ArrayList<RuleItem>(ruleList.size() + 5); for (int i = 0; i < ruleList.size(); i++) { Rule rule = ruleList.get(i); if (rule != null) { rules.add(new RuleItem(rule)); } } // rule list Composite ruleArea = new Composite(page, SWT.NONE); ruleArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true)); GridLayout leftLayout = new GridLayout(5, true); leftLayout.horizontalSpacing = 1; leftLayout.verticalSpacing = 1; ruleArea.setLayout(leftLayout); // label Label rulesLabel = new Label(ruleArea, SWT.NONE); rulesLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 5, 1)); rulesLabel.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DEFAULT_FONT)); rulesLabel.setText(Messages.RuleStylePage_RuleLabelText); // viewer listViewer = new ListViewer(ruleArea); listViewer.getList().setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true, 5, 1)); listViewer.setContentProvider(new IStructuredContentProvider() { @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { // ignore } @Override public void dispose() { // ignore } @SuppressWarnings("unchecked") @Override public Object[] getElements(Object inputElement) { try { List<RuleItem> rules = (List<RuleItem>) inputElement; return rules.toArray(); } catch (Exception e) { return null; } } }); listViewer.setInput(rules); if (currentIndex >= 0 && currentIndex < rules.size()) { listViewer.setSelection(new StructuredSelection(rules.get(currentIndex))); } listViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { RuleItem item = (RuleItem) ((IStructuredSelection) event.getSelection()) .getFirstElement(); int newIndex = rules.indexOf(item); if (currentIndex != newIndex) { try { updateCurrentRule(); } catch (Exception e) { log.userError("Invalid editor state, could not update rule.", e); return; } currentIndex = newIndex; updateEditor(); } } }); // buttons addButton = new Button(ruleArea, SWT.PUSH); addButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false)); addButton.setImage(addImage); addButton.addSelectionListener(new SelectionListener() { @Override public void widgetSelected(SelectionEvent e) { addRule(); } @Override public void widgetDefaultSelected(SelectionEvent e) { // ignore } }); addButton.setToolTipText(Messages.RuleStylePage_AddRuleButtonToolTippText); removeButton = new Button(ruleArea, SWT.PUSH); removeButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false)); removeButton.setImage(removeImage); removeButton.addSelectionListener(new SelectionListener() { @Override public void widgetSelected(SelectionEvent e) { removeCurrentRule(); } @Override public void widgetDefaultSelected(SelectionEvent e) { // ignore } }); removeButton.setToolTipText(Messages.RuleStylePage_RemoveRuleButtonToolTippText); upButton = new Button(ruleArea, SWT.PUSH); upButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false)); upButton.setImage(upImage); upButton.addSelectionListener(new SelectionListener() { @Override public void widgetSelected(SelectionEvent e) { moveCurrentRuleUp(); } @Override public void widgetDefaultSelected(SelectionEvent e) { // ignore } }); upButton.setToolTipText(Messages.RuleStylePage_UpRuleButtonToolTippText); downButton = new Button(ruleArea, SWT.PUSH); downButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false)); downButton.setImage(downImage); downButton.addSelectionListener(new SelectionListener() { @Override public void widgetSelected(SelectionEvent e) { moveCurrentRuleDown(); } @Override public void widgetDefaultSelected(SelectionEvent e) { // ignore } }); downButton.setToolTipText(Messages.RuleStylePage_DownRuleButtonToolTippText); renameButton = new Button(ruleArea, SWT.PUSH); renameButton.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false)); renameButton.setImage(renameImage); renameButton.addSelectionListener(new SelectionListener() { @Override public void widgetSelected(SelectionEvent e) { renameCurrentRule(); } @Override public void widgetDefaultSelected(SelectionEvent e) { // ignore } }); renameButton.setToolTipText(Messages.RuleStylePage_RenameRuleButtonToolTippText); // editor area editorArea = new Composite(page, SWT.NONE); editorArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); editorArea.setLayout(new FillLayout()); setControl(page); updateEditor(); } /** * Move the current rule up */ protected void moveCurrentRuleUp() { if (currentIndex >= 1 && currentIndex < rules.size()) { try { updateCurrentRule(); } catch (Exception e) { log.userError("Invalid editor state, could not update rule.", e); return; } RuleItem item1 = rules.get(currentIndex); Rule temp = item1.getRule(); RuleItem item2 = rules.get(--currentIndex); item1.setRule(item2.getRule()); item2.setRule(temp); listViewer.refresh(true); listViewer.setSelection(new StructuredSelection(item2)); updateEditor(); changed = true; } } /** * Move the current rule down */ protected void moveCurrentRuleDown() { if (currentIndex >= 0 && currentIndex < rules.size() - 1) { try { updateCurrentRule(); } catch (Exception e) { log.userError("Invalid editor state, could not update rule.", e); return; } RuleItem item1 = rules.get(currentIndex); Rule temp = item1.getRule(); RuleItem item2 = rules.get(++currentIndex); item1.setRule(item2.getRule()); item2.setRule(temp); listViewer.refresh(true); listViewer.setSelection(new StructuredSelection(item2)); updateEditor(); changed = true; } } /** * Rename the current rule */ protected void renameCurrentRule() { if (currentIndex >= 0 && currentIndex < rules.size()) { RuleItem item = rules.get(currentIndex); Rule rule = item.getRule(); InputDialog dlg = new InputDialog(getShell(), Messages.RuleStylePage_InputDialogTitle, Messages.RuleStylePage_InputDialogDescription, rule.getName(), null); if (dlg.open() == InputDialog.OK) { rule.setName(dlg.getValue()); listViewer.update(item, null); changed = true; } } } /** * Remove the current rule */ protected void removeCurrentRule() { if (currentIndex >= 0 && currentIndex < rules.size()) { RuleItem item = rules.remove(currentIndex); currentIndex--; listViewer.remove(item); listViewer.refresh(true); updateEditor(); changed = true; } } /** * Add a new {@link Rule} */ protected void addRule() { SymbolizerDialog symDlg = new SymbolizerDialog(getShell()); symDlg.open(); Symbolizer symbolizer = symDlg.getSymbolizer(); if (symbolizer != null) { Rule rule = styleBuilder.createRule(symbolizer); RuleItem item = new RuleItem(rule); rules.add(item); listViewer.add(item); updateButtonState(); changed = true; } } /** * Display the editor for the current rule in the editor area */ private void updateEditor() { if (currentEditor != null) { currentEditor.getControl().dispose(); } if (currentIndex >= 0 && currentIndex < rules.size()) { Rule rule = rules.get(currentIndex).getRule(); currentEditor = createEditor(rule, editorArea); editorArea.layout(true); } updateButtonState(); } /** * Update the button states */ private void updateButtonState() { boolean valid = currentIndex >= 0 && currentIndex < rules.size(); removeButton.setEnabled(valid); renameButton.setEnabled(valid); upButton.setEnabled(valid && currentIndex >= 1); downButton.setEnabled(valid && currentIndex < rules.size() - 1); } /** * Create a rule editor * * @param rule the rule * @param parent the parent composite * * @return the {@link Rule} editor */ private Editor<Rule> createEditor(Rule rule, Composite parent) { TypeDefinition type = getParent().getType(); Filter filter = rule.getFilter(); Symbolizer symbolizer = null; Symbolizer[] symbolizers = rule.getSymbolizers(); if (symbolizers != null && symbolizers.length > 0) { symbolizer = symbolizers[0]; } if (symbolizer == null) { // fallback if there is no symbolizer defined FeatureTypeStyle fts = StyleHelper.getDefaultStyle(type, getParent().getDataSet()); symbolizer = fts.rules().get(0).getSymbolizers()[0]; } Editor<Rule> editor; if (symbolizer instanceof PointSymbolizer) { editor = createEditor(parent, type, filter, PointSymbolizer.class, (PointSymbolizer) symbolizer); } else if (symbolizer instanceof PolygonSymbolizer) { editor = createEditor(parent, type, filter, PolygonSymbolizer.class, (PolygonSymbolizer) symbolizer); } else { // TODO support other symbolizers // default: LineSymbolizer editor = createEditor(parent, type, filter, LineSymbolizer.class, (LineSymbolizer) symbolizer); } return editor; } private static <T extends Symbolizer> RuleEditor<?> createEditor(Composite parent, TypeDefinition ft, Filter filter, Class<T> type, T symbolizer) { if (PointSymbolizer.class.isAssignableFrom(type)) { return new RuleEditor<PointSymbolizer>(parent, ft, filter, PointSymbolizer.class, (PointSymbolizer) symbolizer, new EditorFactory<PointSymbolizer>() { @Override public Editor<PointSymbolizer> createEditor(Composite parent, PointSymbolizer value) { return new PointSymbolizerEditor(parent, value); } }); } else if (PolygonSymbolizer.class.isAssignableFrom(type)) { return new RuleEditor<PolygonSymbolizer>(parent, ft, filter, PolygonSymbolizer.class, (PolygonSymbolizer) symbolizer, new EditorFactory<PolygonSymbolizer>() { @Override public Editor<PolygonSymbolizer> createEditor(Composite parent, PolygonSymbolizer value) { return new PolygonSymbolizerEditor(parent, value); } }); } else { return new RuleEditor<LineSymbolizer>(parent, ft, filter, LineSymbolizer.class, (LineSymbolizer) symbolizer, new EditorFactory<LineSymbolizer>() { @Override public Editor<LineSymbolizer> createEditor(Composite parent, LineSymbolizer value) { return new LineSymbolizerEditor(parent, value); } }); } // FIXME Does not work properly } }